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 plane_t *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];
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 if ( nummapplanes + 2 > MAX_MAP_PLANES ) {
117 Error( "MAX_MAP_PLANES" );
120 p = &mapplanes[nummapplanes];
121 VectorCopy( normal, p->normal );
123 p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
125 VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
126 ( p + 1 )->dist = -dist;
130 // allways put axial planes facing positive first
132 if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
139 AddPlaneToHash( p + 1 );
140 return nummapplanes - 1;
145 AddPlaneToHash( p + 1 );
146 return nummapplanes - 2;
153 Snaps a near-axial normal vector.
154 Returns qtrue if and only if the normal was adjusted.
157 qboolean SnapNormal( vec3_t normal ){
158 #if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
160 qboolean adjusted = qfalse;
162 // A change from the original SnapNormal() is that we snap each
163 // component that's close to 0. So for example if a normal is
164 // (0.707, 0.707, 0.0000001), it will get snapped to lie perfectly in the
165 // XY plane (its Z component will be set to 0 and its length will be
166 // normalized). The original SnapNormal() didn't snap such vectors - it
167 // only snapped vectors that were near a perfect axis.
169 for ( i = 0; i < 3; i++ )
171 if ( normal[i] != 0.0 && -normalEpsilon < normal[i] && normal[i] < normalEpsilon ) {
178 VectorNormalize( normal, normal );
185 // I would suggest that you uncomment the following code and look at the
189 Sys_Printf("normalEpsilon is %f\n", normalEpsilon);
194 normal[2] = i * 0.000001;
195 VectorNormalize(normal, normal);
196 if (1.0 - normal[0] >= normalEpsilon) {
197 Sys_Printf("(%f %f %f)\n", normal[0], normal[1], normal[2]);
198 Error("SnapNormal: test completed");
203 // When the normalEpsilon is 0.00001, the loop will break out when normal is
204 // (0.999990 0.000000 0.004469). In other words, this is the vector closest
205 // to axial that will NOT be snapped. Anything closer will be snaped. Now,
206 // 0.004469 is close to 1/225. The length of a circular quarter-arc of radius
207 // 1 is PI/2, or about 1.57. And 0.004469/1.57 is about 0.0028, or about
208 // 1/350. Expressed a different way, 1/350 is also about 0.26/90.
209 // This means is that a normal with an angle that is within 1/4 of a degree
210 // from axial will be "snapped". My belief is that the person who wrote the
211 // code below did not intend it this way. I think the person intended that
212 // the epsilon be measured against the vector components close to 0, not 1.0.
213 // I think the logic should be: if 2 of the normal components are within
214 // epsilon of 0, then the vector can be snapped to be perfectly axial.
215 // We may consider adjusting the epsilon to a larger value when we make this
218 for ( i = 0; i < 3; i++ )
220 if ( fabs( normal[ i ] - 1 ) < normalEpsilon ) {
221 VectorClear( normal );
225 if ( fabs( normal[ i ] - -1 ) < normalEpsilon ) {
226 VectorClear( normal );
239 snaps a plane to normal/distance epsilons
242 void SnapPlane( vec3_t normal, vec_t *dist ){
243 // SnapPlane disabled by LordHavoc because it often messes up collision
244 // brushes made from triangles of embedded models, and it has little effect
245 // on anything else (axial planes are usually derived from snapped points)
247 SnapPlane reenabled by namespace because of multiple reports of
248 q3map2-crashes which were triggered by this patch.
250 SnapNormal( normal );
252 // TODO: Rambetter has some serious comments here as well. First off,
253 // in the case where a normal is non-axial, there is nothing special
254 // about integer distances. I would think that snapping a distance might
255 // make sense for axial normals, but I'm not so sure about snapping
256 // non-axial normals. A shift by 0.01 in a plane, multiplied by a clipping
257 // against another plane that is 5 degrees off, and we introduce 0.1 error
258 // easily. A 0.1 error in a vertex is where problems start to happen, such
259 // as disappearing triangles.
261 // Second, assuming we have snapped the normal above, let's say that the
262 // plane we just snapped was defined for some points that are actually
263 // quite far away from normal * dist. Well, snapping the normal in this
264 // case means that we've just moved those points by potentially many units!
265 // Therefore, if we are going to snap the normal, we need to know the
266 // points we're snapping for so that the plane snaps with those points in
267 // mind (points remain close to the plane).
269 // I would like to know exactly which problems SnapPlane() is trying to
270 // solve so that we can better engineer it (I'm not saying that SnapPlane()
271 // should be removed altogether). Fix all this snapping code at some point!
273 if ( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon ) {
274 *dist = Q_rint( *dist );
280 snaps a plane to normal/distance epsilons, improved code
282 void SnapPlaneImproved( vec3_t normal, vec_t *dist, int numPoints, const vec3_t *points ){
285 vec_t distNearestInt;
287 if ( SnapNormal( normal ) ) {
288 if ( numPoints > 0 ) {
289 // Adjust the dist so that the provided points don't drift away.
290 VectorClear( center );
291 for ( i = 0; i < numPoints; i++ )
293 VectorAdd( center, points[i], center );
295 for ( i = 0; i < 3; i++ ) { center[i] = center[i] / numPoints; }
296 *dist = DotProduct( normal, center );
300 if ( VectorIsOnAxis( normal ) ) {
301 // Only snap distance if the normal is an axis. Otherwise there
302 // is nothing "natural" about snapping the distance to an integer.
303 distNearestInt = Q_rint( *dist );
304 if ( -distanceEpsilon < *dist - distNearestInt && *dist - distNearestInt < distanceEpsilon ) {
305 *dist = distNearestInt;
313 ydnar: changed to allow a number of test points to be supplied that
314 must be within an epsilon distance of the plane
317 int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
327 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
328 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
330 SnapPlane( normal, &dist );
333 hash = ( PLANE_HASHES - 1 ) & (int) fabs( dist );
335 /* search the border bins as well */
336 for ( i = -1; i <= 1; i++ )
338 h = ( hash + i ) & ( PLANE_HASHES - 1 );
339 for ( p = planehash[ h ]; p != NULL; p = p->hash_chain )
341 /* do standard plane compare */
342 if ( !PlaneEqual( p, normal, dist ) ) {
346 /* ydnar: uncomment the following line for old-style plane finding */
347 //% return p - mapplanes;
349 /* ydnar: test supplied points against this plane */
350 for ( j = 0; j < numPoints; j++ )
352 // NOTE: When dist approaches 2^16, the resolution of 32 bit floating
353 // point number is greatly decreased. The distanceEpsilon cannot be
354 // very small when world coordinates extend to 2^16. Making the
355 // dot product here in 64 bit land will not really help the situation
356 // because the error will already be carried in dist.
357 d = DotProduct( points[ j ], normal ) - dist;
359 if ( d != 0.0 && d >= distanceEpsilon ) {
360 break; // Point is too far from plane.
364 /* found a matching plane */
365 if ( j >= numPoints ) {
366 return p - mapplanes;
371 /* none found, so create a new one */
372 return CreateNewFloatPlane( normal, dist );
381 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
382 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
384 SnapPlane( normal, &dist );
386 for ( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
388 if ( PlaneEqual( p, normal, dist ) ) {
391 // TODO: Note that the non-USE_HASHING code does not compute epsilons
392 // for the provided points. It should do that. I think this code
393 // is unmaintained because nobody sets USE_HASHING to off.
396 return CreateNewFloatPlane( normal, dist );
405 takes 3 points and finds the plane they lie in
408 int MapPlaneFromPoints( vec3_t *p ){
409 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
410 vec3_accu_t paccu[3];
411 vec3_accu_t t1, t2, normalAccu;
415 VectorCopyRegularToAccu( p[0], paccu[0] );
416 VectorCopyRegularToAccu( p[1], paccu[1] );
417 VectorCopyRegularToAccu( p[2], paccu[2] );
419 VectorSubtractAccu( paccu[0], paccu[1], t1 );
420 VectorSubtractAccu( paccu[2], paccu[1], t2 );
421 CrossProductAccu( t1, t2, normalAccu );
422 VectorNormalizeAccu( normalAccu, normalAccu );
423 // TODO: A 32 bit float for the plane distance isn't enough resolution
424 // if the plane is 2^16 units away from the origin (the "epsilon" approaches
425 // 0.01 in that case).
426 dist = (vec_t) DotProductAccu( paccu[0], normalAccu );
427 VectorCopyAccuToRegular( normalAccu, normal );
429 return FindFloatPlane( normal, dist, 3, p );
431 vec3_t t1, t2, normal;
435 /* calc plane normal */
436 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
437 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
438 CrossProduct( t1, t2, normal );
439 VectorNormalize( normal, normal );
441 /* calc plane distance */
442 dist = DotProduct( p[ 0 ], normal );
444 /* store the plane */
445 return FindFloatPlane( normal, dist, 3, p );
453 the content flags and compile flags on all sides of a brush should be the same
456 void SetBrushContents( brush_t *b ){
457 int contentFlags, compileFlags;
463 /* get initial compile flags from first side */
465 contentFlags = s->contentFlags;
466 compileFlags = s->compileFlags;
467 b->contentShader = s->shaderInfo;
470 /* get the content/compile flags for every side in the brush */
471 for ( i = 1; i < b->numsides; i++, s++ )
474 if ( s->shaderInfo == NULL ) {
477 if ( s->contentFlags != contentFlags || s->compileFlags != compileFlags ) {
482 /* ydnar: getting rid of this stupid warning */
484 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
486 /* check for detail & structural */
487 if ( ( compileFlags & C_DETAIL ) && ( compileFlags & C_STRUCTURAL ) ) {
488 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
489 compileFlags &= ~C_DETAIL;
492 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
494 compileFlags &= ~C_DETAIL;
497 /* all translucent brushes that aren't specifically made structural will be detail */
498 if ( ( compileFlags & C_TRANSLUCENT ) && !( compileFlags & C_STRUCTURAL ) ) {
499 compileFlags |= C_DETAIL;
503 if ( compileFlags & C_DETAIL ) {
514 if ( compileFlags & C_TRANSLUCENT ) {
522 if ( compileFlags & C_AREAPORTAL ) {
526 /* set brush flags */
527 b->contentFlags = contentFlags;
528 b->compileFlags = compileFlags;
535 adds any additional planes necessary to allow the brush being
536 built to be expanded against axial bounding boxes
537 ydnar 2003-01-20: added mrelusive fixes
540 void AddBrushBevels( void ){
542 int i, j, k, l, order;
552 // add the axial planes
555 for ( axis = 0; axis < 3; axis++ ) {
556 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
557 // see if the plane is allready present
558 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
560 /* ydnar: testing disabling of mre code */
563 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
568 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
573 if ( ( dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
574 ( dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f ) ) {
580 if ( i == buildBrush->numsides ) {
582 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
583 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
585 memset( s, 0, sizeof( *s ) );
586 buildBrush->numsides++;
587 VectorClear( normal );
591 /* ydnar: adding bevel plane snapping for fewer bsp planes */
592 if ( bevelSnap > 0 ) {
593 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
596 dist = buildBrush->maxs[ axis ];
601 /* ydnar: adding bevel plane snapping for fewer bsp planes */
602 if ( bevelSnap > 0 ) {
603 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
606 dist = -buildBrush->mins[ axis ];
610 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
611 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
616 // if the plane is not in it canonical order, swap it
618 sidetemp = buildBrush->sides[order];
619 buildBrush->sides[order] = buildBrush->sides[i];
620 buildBrush->sides[i] = sidetemp;
626 // add the edge bevels
628 if ( buildBrush->numsides == 6 ) {
629 return; // pure axial
632 // test the non-axial plane edges
633 for ( i = 6; i < buildBrush->numsides; i++ ) {
634 s = buildBrush->sides + i;
639 for ( j = 0; j < w->numpoints; j++ ) {
640 k = ( j + 1 ) % w->numpoints;
641 VectorSubtract( w->p[j], w->p[k], vec );
642 if ( VectorNormalize( vec, vec ) < 0.5f ) {
646 for ( k = 0; k < 3; k++ ) {
647 if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) {
652 continue; // only test non-axial edges
656 //% Sys_Printf( "-------------\n" );
658 // try the six possible slanted axials from this edge
659 for ( axis = 0; axis < 3; axis++ ) {
660 for ( dir = -1; dir <= 1; dir += 2 ) {
664 CrossProduct( vec, vec2, normal );
665 if ( VectorNormalize( normal, normal ) < 0.5f ) {
668 dist = DotProduct( w->p[j], normal );
670 // if all the points on all the sides are
671 // behind this plane, it is a proper edge bevel
672 for ( k = 0; k < buildBrush->numsides; k++ ) {
674 // if this plane has allready been used, skip it
675 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
679 w2 = buildBrush->sides[k].winding;
684 for ( l = 0; l < w2->numpoints; l++ ) {
685 d = DotProduct( w2->p[l], normal ) - dist;
687 break; // point in front
693 // if some point was at the front
694 if ( l != w2->numpoints ) {
698 // if no points at the back then the winding is on the bevel plane
699 if ( minBack > -0.1f ) {
700 //% Sys_Printf( "On bevel plane\n" );
705 if ( k != buildBrush->numsides ) {
706 continue; // wasn't part of the outer hull
710 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
713 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
714 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
716 s2 = &buildBrush->sides[buildBrush->numsides];
717 buildBrush->numsides++;
718 memset( s2, 0, sizeof( *s2 ) );
720 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
721 s2->contentFlags = buildBrush->sides[0].contentFlags;
734 produces a final brush based on the buildBrush->sides array
735 and links it to the current entity
738 brush_t *FinishBrush( void ){
742 /* create windings for sides and bounds for brush */
743 if ( !CreateBrushWindings( buildBrush ) ) {
747 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
748 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
749 if ( buildBrush->compileFlags & C_ORIGIN ) {
753 if ( numEntities == 1 ) {
754 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
755 mapEnt->mapEntityNum, entitySourceBrushes );
759 VectorAdd( buildBrush->mins, buildBrush->maxs, origin );
760 VectorScale( origin, 0.5, origin );
762 sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
763 SetKeyValue( &entities[ numEntities - 1 ], "origin", string );
765 VectorCopy( origin, entities[ numEntities - 1 ].origin );
767 /* don't keep this brush */
771 /* determine if the brush is an area portal */
772 if ( buildBrush->compileFlags & C_AREAPORTAL ) {
773 if ( numEntities != 1 ) {
774 Sys_Printf( "Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
779 /* add bevel planes */
783 b = CopyBrush( buildBrush );
785 /* set map entity and brush numbering */
786 b->entityNum = mapEnt->mapEntityNum;
787 b->brushNum = entitySourceBrushes;
792 /* link opaque brushes to head of list, translucent brushes to end */
793 if ( b->opaque || mapEnt->lastBrush == NULL ) {
794 b->next = mapEnt->brushes;
796 if ( mapEnt->lastBrush == NULL ) {
797 mapEnt->lastBrush = b;
803 mapEnt->lastBrush->next = b;
804 mapEnt->lastBrush = b;
807 /* link colorMod volume brushes to the entity directly */
808 if ( b->contentShader != NULL &&
809 b->contentShader->colorMod != NULL &&
810 b->contentShader->colorMod->type == CM_VOLUME ) {
811 b->nextColorModBrush = mapEnt->colorModBrushes;
812 mapEnt->colorModBrushes = b;
815 /* return to sender */
822 TextureAxisFromPlane()
823 determines best orthagonal axis to project a texture onto a wall
824 (must be identical in radiant!)
827 vec3_t baseaxis[18] =
829 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
830 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
831 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
832 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
833 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
834 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
837 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ){
845 for ( i = 0 ; i < 6 ; i++ )
847 dot = DotProduct( pln->normal, baseaxis[i * 3] );
848 if ( dot > best + 0.0001f ) { /* ydnar: bug 637 fix, suggested by jmonroe */
854 VectorCopy( baseaxis[bestaxis * 3 + 1], xv );
855 VectorCopy( baseaxis[bestaxis * 3 + 2], yv );
862 creates world-to-texture mapping vecs for crappy quake plane arrangements
865 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] ){
868 vec_t ang, sinv, cosv;
873 TextureAxisFromPlane( plane, vecs[0], vecs[1] );
886 else if ( rotate == 90 ) {
889 else if ( rotate == 180 ) {
890 sinv = 0 ; cosv = -1;
892 else if ( rotate == 270 ) {
893 sinv = -1 ; cosv = 0;
897 ang = rotate / 180 * Q_PI;
905 else if ( vecs[0][1] ) {
915 else if ( vecs[1][1] ) {
922 for ( i = 0 ; i < 2 ; i++ ) {
923 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
924 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
929 for ( i = 0 ; i < 2 ; i++ )
930 for ( j = 0 ; j < 3 ; j++ )
931 mappingVecs[i][j] = vecs[i][j] / scale[i];
933 mappingVecs[0][3] = shift[0];
934 mappingVecs[1][3] = shift[1];
941 parses the sides into buildBrush->sides[], nothing else.
942 no validation, back plane removal, etc.
945 added brush epairs parsing ( ignoring actually )
947 added exclusive brush primitive parsing
949 support for old brush format back in
950 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
953 static void ParseRawBrush( qboolean onlyLights ){
955 vec3_t planePoints[ 3 ];
961 char name[ MAX_QPATH ];
962 char shader[ MAX_QPATH ];
967 buildBrush->numsides = 0;
968 buildBrush->detail = qfalse;
971 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
978 if ( !GetToken( qtrue ) ) {
981 if ( !strcmp( token, "}" ) ) {
985 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
986 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
989 if ( strcmp( token, "(" ) ) {
1000 /* test side count */
1001 if ( buildBrush->numsides >= MAX_BUILD_SIDES ) {
1002 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
1006 side = &buildBrush->sides[ buildBrush->numsides ];
1007 memset( side, 0, sizeof( *side ) );
1008 buildBrush->numsides++;
1010 /* read the three point plane definition */
1011 Parse1DMatrix( 3, planePoints[ 0 ] );
1012 Parse1DMatrix( 3, planePoints[ 1 ] );
1013 Parse1DMatrix( 3, planePoints[ 2 ] );
1015 /* bp: read the texture matrix */
1016 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1017 Parse2DMatrix( 2, 3, (float*) side->texMat );
1020 /* read shader name */
1022 strcpy( name, token );
1025 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1027 shift[ 0 ] = atof( token );
1029 shift[ 1 ] = atof( token );
1031 rotate = atof( token );
1033 scale[ 0 ] = atof( token );
1035 scale[ 1 ] = atof( token );
1038 /* set default flags and values */
1039 sprintf( shader, "textures/%s", name );
1041 si = &shaderInfo[ 0 ];
1044 si = ShaderInfoForShader( shader );
1046 side->shaderInfo = si;
1047 side->surfaceFlags = si->surfaceFlags;
1048 side->contentFlags = si->contentFlags;
1049 side->compileFlags = si->compileFlags;
1050 side->value = si->value;
1052 /* ydnar: gs mods: bias texture shift */
1053 if ( si->globalTexture == qfalse ) {
1054 shift[ 0 ] -= ( floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth );
1055 shift[ 1 ] -= ( floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight );
1059 historically, there are 3 integer values at the end of a brushside line in a .map file.
1060 in quake 3, the only thing that mattered was the first of these three values, which
1061 was previously the content flags. and only then did a single bit matter, the detail
1062 bit. because every game has its own special flags for specifying detail, the
1063 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
1064 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
1065 is stored in compileFlags, as opposed to contentFlags, for multiple-game
1069 if ( TokenAvailable() ) {
1070 /* get detail bit from map content flags */
1072 flags = atoi( token );
1073 if ( flags & C_DETAIL ) {
1074 side->compileFlags |= C_DETAIL;
1079 //% td.flags = atoi( token );
1081 //% td.value = atoi( token );
1084 /* find the plane number */
1085 planenum = MapPlaneFromPoints( planePoints );
1086 side->planenum = planenum;
1088 /* bp: get the texture mapping for this texturedef / plane combination */
1089 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1090 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
1095 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1105 RemoveDuplicateBrushPlanes
1106 returns false if the brush has a mirrored set of planes,
1107 meaning it encloses no volume.
1108 also removes planes without any normal
1111 qboolean RemoveDuplicateBrushPlanes( brush_t *b ){
1117 for ( i = 1 ; i < b->numsides ; i++ ) {
1119 // check for a degenerate plane
1120 if ( sides[i].planenum == -1 ) {
1121 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
1123 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1124 sides[k - 1] = sides[k];
1131 // check for duplication and mirroring
1132 for ( j = 0 ; j < i ; j++ ) {
1133 if ( sides[i].planenum == sides[j].planenum ) {
1134 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1135 // remove the second duplicate
1136 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1137 sides[k - 1] = sides[k];
1144 if ( sides[i].planenum == ( sides[j].planenum ^ 1 ) ) {
1145 // mirror plane, brush is invalid
1146 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1158 parses a brush out of a map file and sets it up
1161 static void ParseBrush( qboolean onlyLights ){
1165 /* parse the brush out of the map */
1166 ParseRawBrush( onlyLights );
1168 /* only go this far? */
1173 /* set some defaults */
1174 buildBrush->portalareas[ 0 ] = -1;
1175 buildBrush->portalareas[ 1 ] = -1;
1176 buildBrush->entityNum = numMapEntities - 1;
1177 buildBrush->brushNum = entitySourceBrushes;
1179 /* if there are mirrored planes, the entire brush is invalid */
1180 if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
1184 /* get the content for the entire brush */
1185 SetBrushContents( buildBrush );
1187 /* allow detail brushes to be removed */
1188 if ( nodetail && ( buildBrush->compileFlags & C_DETAIL ) ) {
1189 //% FreeBrush( buildBrush );
1193 /* allow liquid brushes to be removed */
1194 if ( nowater && ( buildBrush->compileFlags & C_LIQUID ) ) {
1195 //% FreeBrush( buildBrush );
1199 /* ydnar: allow hint brushes to be removed */
1200 if ( noHint && ( buildBrush->compileFlags & C_HINT ) ) {
1201 //% FreeBrush( buildBrush );
1205 /* finish the brush */
1212 MoveBrushesToWorld()
1213 takes all of the brushes from the current entity and
1214 adds them to the world's brush list
1215 (used by func_group)
1218 void MoveBrushesToWorld( entity_t *ent ){
1224 for ( b = ent->brushes; b != NULL; b = next )
1226 /* get next brush */
1229 /* link opaque brushes to head of list, translucent brushes to end */
1230 if ( b->opaque || entities[ 0 ].lastBrush == NULL ) {
1231 b->next = entities[ 0 ].brushes;
1232 entities[ 0 ].brushes = b;
1233 if ( entities[ 0 ].lastBrush == NULL ) {
1234 entities[ 0 ].lastBrush = b;
1240 entities[ 0 ].lastBrush->next = b;
1241 entities[ 0 ].lastBrush = b;
1244 ent->brushes = NULL;
1246 /* ydnar: move colormod brushes */
1247 if ( ent->colorModBrushes != NULL ) {
1248 for ( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush ) ;
1250 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1251 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1253 ent->colorModBrushes = NULL;
1257 if ( ent->patches != NULL ) {
1258 for ( pm = ent->patches; pm->next != NULL; pm = pm->next ) ;
1260 pm->next = entities[ 0 ].patches;
1261 entities[ 0 ].patches = ent->patches;
1263 ent->patches = NULL;
1270 AdjustBrushesForOrigin()
1273 void AdjustBrushesForOrigin( entity_t *ent ){
1282 /* walk brush list */
1283 for ( b = ent->brushes; b != NULL; b = b->next )
1285 /* offset brush planes */
1286 for ( i = 0; i < b->numsides; i++ )
1288 /* get brush side */
1291 /* offset side plane */
1292 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
1294 /* find a new plane */
1295 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1298 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1299 CreateBrushWindings( b );
1302 /* walk patch list */
1303 for ( p = ent->patches; p != NULL; p = p->next )
1305 for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
1306 VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
1313 SetEntityBounds() - ydnar
1314 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1317 void SetEntityBounds( entity_t *e ){
1327 /* walk the entity's brushes/patches and determine bounds */
1328 ClearBounds( mins, maxs );
1329 for ( b = e->brushes; b; b = b->next )
1331 AddPointToBounds( b->mins, mins, maxs );
1332 AddPointToBounds( b->maxs, mins, maxs );
1334 for ( p = e->patches; p; p = p->next )
1336 for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
1337 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1340 /* try to find explicit min/max key */
1341 value = ValueForKey( e, "min" );
1342 if ( value[ 0 ] != '\0' ) {
1343 GetVectorForKey( e, "min", mins );
1345 value = ValueForKey( e, "max" );
1346 if ( value[ 0 ] != '\0' ) {
1347 GetVectorForKey( e, "max", maxs );
1350 /* store the bounds */
1351 for ( b = e->brushes; b; b = b->next )
1353 VectorCopy( mins, b->eMins );
1354 VectorCopy( maxs, b->eMaxs );
1356 for ( p = e->patches; p; p = p->next )
1358 VectorCopy( mins, p->eMins );
1359 VectorCopy( maxs, p->eMaxs );
1366 LoadEntityIndexMap() - ydnar
1367 based on LoadAlphaMap() from terrain.c, a little more generic
1370 void LoadEntityIndexMap( entity_t *e ){
1371 int i, size, numLayers, w, h;
1372 const char *value, *indexMapFilename, *shader;
1373 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1375 unsigned int *pixels32;
1381 /* this only works with bmodel ents */
1382 if ( e->brushes == NULL && e->patches == NULL ) {
1386 /* determine if there is an index map (support legacy "alphamap" key as well) */
1387 value = ValueForKey( e, "_indexmap" );
1388 if ( value[ 0 ] == '\0' ) {
1389 value = ValueForKey( e, "alphamap" );
1391 if ( value[ 0 ] == '\0' ) {
1394 indexMapFilename = value;
1396 /* get number of layers (support legacy "layers" key as well) */
1397 value = ValueForKey( e, "_layers" );
1398 if ( value[ 0 ] == '\0' ) {
1399 value = ValueForKey( e, "layers" );
1401 if ( value[ 0 ] == '\0' ) {
1402 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1403 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1406 numLayers = atoi( value );
1407 if ( numLayers < 1 ) {
1408 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1409 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1413 /* get base shader name (support legacy "shader" key as well) */
1414 value = ValueForKey( mapEnt, "_shader" );
1415 if ( value[ 0 ] == '\0' ) {
1416 value = ValueForKey( e, "shader" );
1418 if ( value[ 0 ] == '\0' ) {
1419 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1420 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1426 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1428 /* get index map file extension */
1429 ExtractFileExtension( indexMapFilename, ext );
1431 /* handle tga image */
1432 if ( !Q_stricmp( ext, "tga" ) ) {
1434 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1436 /* convert to bytes */
1438 pixels = safe_malloc( size );
1439 for ( i = 0; i < size; i++ )
1441 pixels[ i ] = ( ( pixels32[ i ] & 0xFF ) * numLayers ) / 256;
1442 if ( pixels[ i ] >= numLayers ) {
1443 pixels[ i ] = numLayers - 1;
1447 /* free the 32 bit image */
1453 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1456 //% Sys_Printf( "-------------------------------" );
1458 /* fix up out-of-range values */
1460 for ( i = 0; i < size; i++ )
1462 if ( pixels[ i ] >= numLayers ) {
1463 pixels[ i ] = numLayers - 1;
1467 //% if( (i % w) == 0 )
1468 //% Sys_Printf( "\n" );
1469 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1473 //% Sys_Printf( "\n-------------------------------\n" );
1476 /* the index map must be at least 2x2 pixels */
1477 if ( w < 2 || h < 2 ) {
1478 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1479 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1484 /* create a new index map */
1485 im = safe_malloc( sizeof( *im ) );
1486 memset( im, 0, sizeof( *im ) );
1491 im->numLayers = numLayers;
1492 strcpy( im->name, indexMapFilename );
1493 strcpy( im->shader, shader );
1494 im->pixels = pixels;
1496 /* get height offsets */
1497 value = ValueForKey( mapEnt, "_offsets" );
1498 if ( value[ 0 ] == '\0' ) {
1499 value = ValueForKey( e, "offsets" );
1501 if ( value[ 0 ] != '\0' ) {
1502 /* value is a space-seperated set of numbers */
1503 strcpy( offset, value );
1506 /* get each value */
1507 for ( i = 0; i < 256 && *search != '\0'; i++ )
1509 space = strstr( search, " " );
1510 if ( space != NULL ) {
1513 im->offsets[ i ] = atof( search );
1514 if ( space == NULL ) {
1521 /* store the index map in every brush/patch in the entity */
1522 for ( b = e->brushes; b != NULL; b = b->next )
1524 for ( p = e->patches; p != NULL; p = p->next )
1536 parses a single entity out of a map file
1539 static qboolean ParseMapEntity( qboolean onlyLights ){
1541 const char *classname, *value;
1542 float lightmapScale;
1543 char shader[ MAX_QPATH ];
1544 shaderInfo_t *celShader = NULL;
1548 int castShadows, recvShadows;
1552 if ( !GetToken( qtrue ) ) {
1556 /* conformance check */
1557 if ( strcmp( token, "{" ) ) {
1558 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1559 "Continuing to process map, but resulting BSP may be invalid.\n",
1560 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1565 if ( numEntities >= MAX_MAP_ENTITIES ) {
1566 Error( "numEntities == MAX_MAP_ENTITIES" );
1570 entitySourceBrushes = 0;
1571 mapEnt = &entities[ numEntities ];
1573 memset( mapEnt, 0, sizeof( *mapEnt ) );
1575 /* ydnar: true entity numbering */
1576 mapEnt->mapEntityNum = numMapEntities;
1582 /* get initial token */
1583 if ( !GetToken( qtrue ) ) {
1584 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1585 "Continuing to process map, but resulting BSP may be invalid.\n" );
1589 if ( !strcmp( token, "}" ) ) {
1593 if ( !strcmp( token, "{" ) ) {
1594 /* parse a brush or patch */
1595 if ( !GetToken( qtrue ) ) {
1600 if ( !strcmp( token, "patchDef2" ) ) {
1602 ParsePatch( onlyLights );
1604 else if ( !strcmp( token, "terrainDef" ) ) {
1606 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1608 else if ( !strcmp( token, "brushDef" ) ) {
1609 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1610 Error( "Old brush format not allowed in new brush format map" );
1612 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1614 /* parse brush primitive */
1615 ParseBrush( onlyLights );
1619 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1620 Error( "New brush format not allowed in old brush format map" );
1622 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1624 /* parse old brush format */
1626 ParseBrush( onlyLights );
1628 entitySourceBrushes++;
1632 /* parse a key / value pair */
1635 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1636 if ( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' ) {
1637 ep->next = mapEnt->epairs;
1638 mapEnt->epairs = ep;
1643 /* ydnar: get classname */
1644 classname = ValueForKey( mapEnt, "classname" );
1646 /* ydnar: only lights? */
1647 if ( onlyLights && Q_strncasecmp( classname, "light", 5 ) ) {
1652 /* ydnar: determine if this is a func_group */
1653 if ( !Q_stricmp( "func_group", classname ) ) {
1660 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1661 if ( funcGroup || mapEnt->mapEntityNum == 0 ) {
1662 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1663 castShadows = WORLDSPAWN_CAST_SHADOWS;
1664 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1667 /* other entities don't cast any shadows, but recv worldspawn shadows */
1670 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1671 castShadows = ENTITY_CAST_SHADOWS;
1672 recvShadows = ENTITY_RECV_SHADOWS;
1675 /* get explicit shadow flags */
1676 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1678 /* ydnar: get lightmap scaling value for this entity */
1679 if ( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1680 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ) {
1681 /* get lightmap scale from entity */
1682 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1683 if ( lightmapScale <= 0.0f ) {
1684 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1686 if ( lightmapScale > 0.0f ) {
1687 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1691 lightmapScale = 0.0f;
1694 /* ydnar: get cel shader :) for this entity */
1695 value = ValueForKey( mapEnt, "_celshader" );
1696 if ( value[ 0 ] == '\0' ) {
1697 value = ValueForKey( &entities[ 0 ], "_celshader" );
1699 if ( value[ 0 ] != '\0' ) {
1700 sprintf( shader, "textures/%s", value );
1701 celShader = ShaderInfoForShader( shader );
1702 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1708 /* attach stuff to everything in the entity */
1709 for ( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1711 brush->entityNum = mapEnt->mapEntityNum;
1712 brush->castShadows = castShadows;
1713 brush->recvShadows = recvShadows;
1714 brush->lightmapScale = lightmapScale;
1715 brush->celShader = celShader;
1718 for ( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1720 patch->entityNum = mapEnt->mapEntityNum;
1721 patch->castShadows = castShadows;
1722 patch->recvShadows = recvShadows;
1723 patch->lightmapScale = lightmapScale;
1724 patch->celShader = celShader;
1727 /* ydnar: gs mods: set entity bounds */
1728 SetEntityBounds( mapEnt );
1730 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1731 LoadEntityIndexMap( mapEnt );
1733 /* get entity origin and adjust brushes */
1734 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1735 if ( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] ) {
1736 AdjustBrushesForOrigin( mapEnt );
1739 /* group_info entities are just for editor grouping (fixme: leak!) */
1740 if ( !Q_stricmp( "group_info", classname ) ) {
1745 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1747 MoveBrushesToWorld( mapEnt );
1760 loads a map file into a list of entities
1763 void LoadMapFile( char *filename, qboolean onlyLights ){
1766 int oldNumEntities, numMapBrushes;
1770 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1771 Sys_Printf( "Loading %s\n", filename );
1774 file = SafeOpenRead( filename );
1777 /* load the map file */
1778 LoadScriptFile( filename, -1 );
1782 oldNumEntities = numEntities;
1789 numMapDrawSurfs = 0;
1791 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1793 /* allocate a very large temporary brush for building the brushes as they are loaded */
1794 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1796 /* parse the map file */
1797 while ( ParseMapEntity( onlyLights ) ) ;
1801 /* emit some statistics */
1802 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1806 /* set map bounds */
1807 ClearBounds( mapMins, mapMaxs );
1808 for ( b = entities[ 0 ].brushes; b; b = b->next )
1810 AddPointToBounds( b->mins, mapMins, mapMaxs );
1811 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1814 /* get brush counts */
1815 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1816 if ( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) {
1817 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1820 /* emit some statistics */
1821 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1822 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1823 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches );
1824 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels );
1825 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels );
1826 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1827 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes );
1828 Sys_Printf( "%9d areaportals\n", c_areaportals );
1829 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1830 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1831 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ] );
1833 /* write bogus map */
1835 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );