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 #define NORMAL_EPSILON 0.00001
63 #define DIST_EPSILON 0.01
65 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )
70 /* get local copies */
75 if( fabs( p->dist - dist ) <= de &&
76 fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&
77 fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&
78 fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )
91 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)
111 if (VectorLength(normal) < 0.5)
113 Sys_Printf( "FloatPlane: bad normal\n");
117 // create a new plane
118 if (nummapplanes+2 > MAX_MAP_PLANES)
119 Error ("MAX_MAP_PLANES");
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
159 void SnapNormal( vec3_t normal )
163 for( i = 0; i < 3; i++ )
165 if( fabs( normal[ i ] - 1 ) < normalEpsilon )
167 VectorClear( normal );
171 if( fabs( normal[ i ] - -1 ) < normalEpsilon )
173 VectorClear( normal );
184 snaps a plane to normal/distance epsilons
187 void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
189 // SnapPlane disabled by LordHavoc because it often messes up collision
190 // brushes made from triangles of embedded models, and it has little effect
191 // on anything else (axial planes are usually derived from snapped points)
193 SnapPlane reenabled by namespace because of multiple reports of
194 q3map2-crashes which were triggered by this patch.
196 // div0: ensure the point "center" stays on the plane (actually, this
197 // rotates the plane around the point center).
198 // if center lies on the plane, it is guaranteed to stay on the plane by
200 vec_t centerDist = DotProduct(normal, center);
201 SnapNormal( normal );
202 *dist += (DotProduct(normal, center) - centerDist);
204 if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
205 *dist = Q_rint( *dist );
212 ydnar: changed to allow a number of test points to be supplied that
213 must be within an epsilon distance of the plane
216 int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
224 vec3_t centerofweight;
226 VectorClear(centerofweight);
227 for(i = 0; i < numPoints; ++i)
228 VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
231 SnapPlane( normal, &dist, centerofweight );
232 hash = (PLANE_HASHES - 1) & (int) fabs( dist );
234 /* search the border bins as well */
235 for( i = -1; i <= 1; i++ )
237 h = (hash + i) & (PLANE_HASHES - 1);
238 for( p = planehash[ h ]; p != NULL; p = p->hash_chain )
240 /* do standard plane compare */
241 if( !PlaneEqual( p, normal, dist ) )
244 /* ydnar: uncomment the following line for old-style plane finding */
245 //% return p - mapplanes;
247 /* ydnar: test supplied points against this plane */
248 for( j = 0; j < numPoints; j++ )
250 d = DotProduct( points[ j ], normal ) - dist;
251 if( fabs( d ) > distanceEpsilon )
255 /* found a matching plane */
257 return p - mapplanes;
261 /* none found, so create a new one */
262 return CreateNewFloatPlane( normal, dist );
272 vec3_t centerofweight;
274 VectorClear(centerofweight);
275 for(i = 0; i < numPoints; ++i)
276 VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
278 SnapPlane( normal, &dist, centerofweight );
279 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
281 if( PlaneEqual( p, normal, dist ) )
285 return CreateNewFloatPlane( normal, dist );
294 takes 3 points and finds the plane they lie in
297 int MapPlaneFromPoints( vec3_t *p )
299 vec3_t t1, t2, normal;
303 /* calc plane normal */
304 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
305 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
306 CrossProduct( t1, t2, normal );
307 VectorNormalize( normal, normal );
309 /* calc plane distance */
310 dist = DotProduct( p[ 0 ], normal );
312 /* store the plane */
313 return FindFloatPlane( normal, dist, 3, p );
320 the content flags and compile flags on all sides of a brush should be the same
323 void SetBrushContents( brush_t *b )
325 int contentFlags, compileFlags;
331 /* get initial compile flags from first side */
333 contentFlags = s->contentFlags;
334 compileFlags = s->compileFlags;
335 b->contentShader = s->shaderInfo;
338 /* get the content/compile flags for every side in the brush */
339 for( i = 1; i < b->numsides; i++, s++ )
342 if( s->shaderInfo == NULL )
344 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
348 /* ydnar: getting rid of this stupid warning */
350 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
352 /* check for detail & structural */
353 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
355 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
356 compileFlags &= ~C_DETAIL;
359 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
361 compileFlags &= ~C_DETAIL;
363 /* all translucent brushes that aren't specifically made structural will be detail */
364 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
365 compileFlags |= C_DETAIL;
368 if( compileFlags & C_DETAIL )
380 if( compileFlags & C_TRANSLUCENT )
386 if( compileFlags & C_AREAPORTAL )
389 /* set brush flags */
390 b->contentFlags = contentFlags;
391 b->compileFlags = compileFlags;
398 adds any additional planes necessary to allow the brush being
399 built to be expanded against axial bounding boxes
400 ydnar 2003-01-20: added mrelusive fixes
403 void AddBrushBevels( void )
406 int i, j, k, l, order;
416 // add the axial planes
419 for ( axis = 0; axis < 3; axis++ ) {
420 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
421 // see if the plane is allready present
422 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
424 /* ydnar: testing disabling of mre code */
427 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
432 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
437 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
438 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
443 if ( i == buildBrush->numsides ) {
445 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
446 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
448 memset( s, 0, sizeof( *s ) );
449 buildBrush->numsides++;
450 VectorClear (normal);
455 /* ydnar: adding bevel plane snapping for fewer bsp planes */
457 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
459 dist = buildBrush->maxs[ axis ];
463 /* ydnar: adding bevel plane snapping for fewer bsp planes */
465 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
467 dist = -buildBrush->mins[ axis ];
470 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
471 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
476 // if the plane is not in it canonical order, swap it
478 sidetemp = buildBrush->sides[order];
479 buildBrush->sides[order] = buildBrush->sides[i];
480 buildBrush->sides[i] = sidetemp;
486 // add the edge bevels
488 if ( buildBrush->numsides == 6 ) {
489 return; // pure axial
492 // test the non-axial plane edges
493 for ( i = 6; i < buildBrush->numsides; i++ ) {
494 s = buildBrush->sides + i;
499 for ( j = 0; j < w->numpoints; j++) {
500 k = (j+1)%w->numpoints;
501 VectorSubtract( w->p[j], w->p[k], vec );
502 if ( VectorNormalize( vec, vec ) < 0.5f ) {
506 for ( k = 0; k < 3; k++ ) {
507 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
512 continue; // only test non-axial edges
516 //% Sys_Printf( "-------------\n" );
518 // try the six possible slanted axials from this edge
519 for ( axis = 0; axis < 3; axis++ ) {
520 for ( dir = -1; dir <= 1; dir += 2 ) {
524 CrossProduct( vec, vec2, normal );
525 if ( VectorNormalize( normal, normal ) < 0.5f ) {
528 dist = DotProduct( w->p[j], normal );
530 // if all the points on all the sides are
531 // behind this plane, it is a proper edge bevel
532 for ( k = 0; k < buildBrush->numsides; k++ ) {
534 // if this plane has allready been used, skip it
535 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
539 w2 = buildBrush->sides[k].winding;
544 for ( l = 0; l < w2->numpoints; l++ ) {
545 d = DotProduct( w2->p[l], normal ) - dist;
547 break; // point in front
553 // if some point was at the front
554 if ( l != w2->numpoints ) {
558 // if no points at the back then the winding is on the bevel plane
559 if ( minBack > -0.1f ) {
560 //% Sys_Printf( "On bevel plane\n" );
565 if ( k != buildBrush->numsides ) {
566 continue; // wasn't part of the outer hull
570 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
573 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
574 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
576 s2 = &buildBrush->sides[buildBrush->numsides];
577 buildBrush->numsides++;
578 memset( s2, 0, sizeof( *s2 ) );
580 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
581 s2->contentFlags = buildBrush->sides[0].contentFlags;
594 produces a final brush based on the buildBrush->sides array
595 and links it to the current entity
598 brush_t *FinishBrush( void )
603 /* create windings for sides and bounds for brush */
604 if ( !CreateBrushWindings( buildBrush ) )
607 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
608 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
609 if( buildBrush->compileFlags & C_ORIGIN )
614 if( numEntities == 1 )
616 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
617 mapEnt->mapEntityNum, entitySourceBrushes );
621 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
622 VectorScale (origin, 0.5, origin);
624 sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
625 SetKeyValue( &entities[ numEntities - 1 ], "origin", string);
627 VectorCopy( origin, entities[ numEntities - 1 ].origin);
629 /* don't keep this brush */
633 /* determine if the brush is an area portal */
634 if( buildBrush->compileFlags & C_AREAPORTAL )
636 if( numEntities != 1 )
638 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
643 /* add bevel planes */
647 b = CopyBrush( buildBrush );
649 /* set map entity and brush numbering */
650 b->entityNum = mapEnt->mapEntityNum;
651 b->brushNum = entitySourceBrushes;
656 /* link opaque brushes to head of list, translucent brushes to end */
657 if( b->opaque || mapEnt->lastBrush == NULL )
659 b->next = mapEnt->brushes;
661 if( mapEnt->lastBrush == NULL )
662 mapEnt->lastBrush = b;
667 mapEnt->lastBrush->next = b;
668 mapEnt->lastBrush = b;
671 /* link colorMod volume brushes to the entity directly */
672 if( b->contentShader != NULL &&
673 b->contentShader->colorMod != NULL &&
674 b->contentShader->colorMod->type == CM_VOLUME )
676 b->nextColorModBrush = mapEnt->colorModBrushes;
677 mapEnt->colorModBrushes = b;
680 /* return to sender */
687 TextureAxisFromPlane()
688 determines best orthagonal axis to project a texture onto a wall
689 (must be identical in radiant!)
692 vec3_t baseaxis[18] =
694 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
695 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
696 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
697 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
698 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
699 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
702 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
711 for (i=0 ; i<6 ; i++)
713 dot = DotProduct (pln->normal, baseaxis[i*3]);
714 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
721 VectorCopy (baseaxis[bestaxis*3+1], xv);
722 VectorCopy (baseaxis[bestaxis*3+2], yv);
729 creates world-to-texture mapping vecs for crappy quake plane arrangements
732 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
736 vec_t ang, sinv, cosv;
741 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
750 { sinv = 0 ; cosv = 1; }
751 else if (rotate == 90)
752 { sinv = 1 ; cosv = 0; }
753 else if (rotate == 180)
754 { sinv = 0 ; cosv = -1; }
755 else if (rotate == 270)
756 { sinv = -1 ; cosv = 0; }
759 ang = rotate / 180 * Q_PI;
778 for (i=0 ; i<2 ; i++) {
779 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
780 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
785 for (i=0 ; i<2 ; i++)
786 for (j=0 ; j<3 ; j++)
787 mappingVecs[i][j] = vecs[i][j] / scale[i];
789 mappingVecs[0][3] = shift[0];
790 mappingVecs[1][3] = shift[1];
797 parses the sides into buildBrush->sides[], nothing else.
798 no validation, back plane removal, etc.
801 added brush epairs parsing ( ignoring actually )
803 added exclusive brush primitive parsing
805 support for old brush format back in
806 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
809 static void ParseRawBrush( qboolean onlyLights )
812 vec3_t planePoints[ 3 ];
818 char name[ MAX_QPATH ];
819 char shader[ MAX_QPATH ];
824 buildBrush->numsides = 0;
825 buildBrush->detail = qfalse;
828 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
834 if( !GetToken( qtrue ) )
836 if( !strcmp( token, "}" ) )
839 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
840 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
844 if( strcmp( token, "(" ) )
853 /* test side count */
854 if( buildBrush->numsides >= MAX_BUILD_SIDES )
855 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
858 side = &buildBrush->sides[ buildBrush->numsides ];
859 memset( side, 0, sizeof( *side ) );
860 buildBrush->numsides++;
862 /* read the three point plane definition */
863 Parse1DMatrix( 3, planePoints[ 0 ] );
864 Parse1DMatrix( 3, planePoints[ 1 ] );
865 Parse1DMatrix( 3, planePoints[ 2 ] );
867 /* bp: read the texture matrix */
868 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
869 Parse2DMatrix( 2, 3, (float*) side->texMat );
871 /* read shader name */
873 strcpy( name, token );
876 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
879 shift[ 0 ] = atof( token );
881 shift[ 1 ] = atof( token );
883 rotate = atof( token );
885 scale[ 0 ] = atof( token );
887 scale[ 1 ] = atof( token );
890 /* set default flags and values */
891 sprintf( shader, "textures/%s", name );
893 si = &shaderInfo[ 0 ];
895 si = ShaderInfoForShader( shader );
896 side->shaderInfo = si;
897 side->surfaceFlags = si->surfaceFlags;
898 side->contentFlags = si->contentFlags;
899 side->compileFlags = si->compileFlags;
900 side->value = si->value;
902 /* ydnar: gs mods: bias texture shift */
903 if( si->globalTexture == qfalse )
905 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
906 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
910 historically, there are 3 integer values at the end of a brushside line in a .map file.
911 in quake 3, the only thing that mattered was the first of these three values, which
912 was previously the content flags. and only then did a single bit matter, the detail
913 bit. because every game has its own special flags for specifying detail, the
914 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
915 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
916 is stored in compileFlags, as opposed to contentFlags, for multiple-game
920 if( TokenAvailable() )
922 /* get detail bit from map content flags */
924 flags = atoi( token );
925 if( flags & C_DETAIL )
926 side->compileFlags |= C_DETAIL;
930 //% td.flags = atoi( token );
932 //% td.value = atoi( token );
935 /* find the plane number */
936 planenum = MapPlaneFromPoints( planePoints );
937 side->planenum = planenum;
939 /* bp: get the texture mapping for this texturedef / plane combination */
940 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
941 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
945 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
956 RemoveDuplicateBrushPlanes
957 returns false if the brush has a mirrored set of planes,
958 meaning it encloses no volume.
959 also removes planes without any normal
962 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
969 for ( i = 1 ; i < b->numsides ; i++ ) {
971 // check for a degenerate plane
972 if ( sides[i].planenum == -1) {
973 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
975 for ( k = i + 1 ; k < b->numsides ; k++ ) {
976 sides[k-1] = sides[k];
983 // check for duplication and mirroring
984 for ( j = 0 ; j < i ; j++ ) {
985 if ( sides[i].planenum == sides[j].planenum ) {
986 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
987 // remove the second duplicate
988 for ( k = i + 1 ; k < b->numsides ; k++ ) {
989 sides[k-1] = sides[k];
996 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
997 // mirror plane, brush is invalid
998 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1010 parses a brush out of a map file and sets it up
1013 static void ParseBrush( qboolean onlyLights )
1018 /* parse the brush out of the map */
1019 ParseRawBrush( onlyLights );
1021 /* only go this far? */
1025 /* set some defaults */
1026 buildBrush->portalareas[ 0 ] = -1;
1027 buildBrush->portalareas[ 1 ] = -1;
1028 buildBrush->entityNum = numMapEntities - 1;
1029 buildBrush->brushNum = entitySourceBrushes;
1031 /* if there are mirrored planes, the entire brush is invalid */
1032 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1035 /* get the content for the entire brush */
1036 SetBrushContents( buildBrush );
1038 /* allow detail brushes to be removed */
1039 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1041 //% FreeBrush( buildBrush );
1045 /* allow liquid brushes to be removed */
1046 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1048 //% FreeBrush( buildBrush );
1052 /* ydnar: allow hint brushes to be removed */
1053 if( noHint && (buildBrush->compileFlags & C_HINT) )
1055 //% FreeBrush( buildBrush );
1059 /* finish the brush */
1066 MoveBrushesToWorld()
1067 takes all of the brushes from the current entity and
1068 adds them to the world's brush list
1069 (used by func_group)
1072 void MoveBrushesToWorld( entity_t *ent )
1079 for( b = ent->brushes; b != NULL; b = next )
1081 /* get next brush */
1084 /* link opaque brushes to head of list, translucent brushes to end */
1085 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1087 b->next = entities[ 0 ].brushes;
1088 entities[ 0 ].brushes = b;
1089 if( entities[ 0 ].lastBrush == NULL )
1090 entities[ 0 ].lastBrush = b;
1095 entities[ 0 ].lastBrush->next = b;
1096 entities[ 0 ].lastBrush = b;
1099 ent->brushes = NULL;
1101 /* ydnar: move colormod brushes */
1102 if( ent->colorModBrushes != NULL )
1104 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1106 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1107 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1109 ent->colorModBrushes = NULL;
1113 if( ent->patches != NULL )
1115 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1117 pm->next = entities[ 0 ].patches;
1118 entities[ 0 ].patches = ent->patches;
1120 ent->patches = NULL;
1127 AdjustBrushesForOrigin()
1130 void AdjustBrushesForOrigin( entity_t *ent )
1140 /* walk brush list */
1141 for( b = ent->brushes; b != NULL; b = b->next )
1143 /* offset brush planes */
1144 for( i = 0; i < b->numsides; i++)
1146 /* get brush side */
1149 /* offset side plane */
1150 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
1152 /* find a new plane */
1153 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1156 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1157 CreateBrushWindings( b );
1160 /* walk patch list */
1161 for( p = ent->patches; p != NULL; p = p->next )
1163 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1164 VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
1171 SetEntityBounds() - ydnar
1172 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1175 void SetEntityBounds( entity_t *e )
1186 /* walk the entity's brushes/patches and determine bounds */
1187 ClearBounds( mins, maxs );
1188 for( b = e->brushes; b; b = b->next )
1190 AddPointToBounds( b->mins, mins, maxs );
1191 AddPointToBounds( b->maxs, mins, maxs );
1193 for( p = e->patches; p; p = p->next )
1195 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1196 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1199 /* try to find explicit min/max key */
1200 value = ValueForKey( e, "min" );
1201 if( value[ 0 ] != '\0' )
1202 GetVectorForKey( e, "min", mins );
1203 value = ValueForKey( e, "max" );
1204 if( value[ 0 ] != '\0' )
1205 GetVectorForKey( e, "max", maxs );
1207 /* store the bounds */
1208 for( b = e->brushes; b; b = b->next )
1210 VectorCopy( mins, b->eMins );
1211 VectorCopy( maxs, b->eMaxs );
1213 for( p = e->patches; p; p = p->next )
1215 VectorCopy( mins, p->eMins );
1216 VectorCopy( maxs, p->eMaxs );
1223 LoadEntityIndexMap() - ydnar
1224 based on LoadAlphaMap() from terrain.c, a little more generic
1227 void LoadEntityIndexMap( entity_t *e )
1229 int i, size, numLayers, w, h;
1230 const char *value, *indexMapFilename, *shader;
1231 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1233 unsigned int *pixels32;
1239 /* this only works with bmodel ents */
1240 if( e->brushes == NULL && e->patches == NULL )
1243 /* determine if there is an index map (support legacy "alphamap" key as well) */
1244 value = ValueForKey( e, "_indexmap" );
1245 if( value[ 0 ] == '\0' )
1246 value = ValueForKey( e, "alphamap" );
1247 if( value[ 0 ] == '\0' )
1249 indexMapFilename = value;
1251 /* get number of layers (support legacy "layers" key as well) */
1252 value = ValueForKey( e, "_layers" );
1253 if( value[ 0 ] == '\0' )
1254 value = ValueForKey( e, "layers" );
1255 if( value[ 0 ] == '\0' )
1257 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1258 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1261 numLayers = atoi( value );
1264 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1265 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1269 /* get base shader name (support legacy "shader" key as well) */
1270 value = ValueForKey( mapEnt, "_shader" );
1271 if( value[ 0 ] == '\0' )
1272 value = ValueForKey( e, "shader" );
1273 if( value[ 0 ] == '\0' )
1275 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1276 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1282 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1284 /* get index map file extension */
1285 ExtractFileExtension( indexMapFilename, ext );
1287 /* handle tga image */
1288 if( !Q_stricmp( ext, "tga" ) )
1291 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1293 /* convert to bytes */
1295 pixels = safe_malloc( size );
1296 for( i = 0; i < size; i++ )
1298 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1299 if( pixels[ i ] >= numLayers )
1300 pixels[ i ] = numLayers - 1;
1303 /* free the 32 bit image */
1309 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1312 //% Sys_Printf( "-------------------------------" );
1314 /* fix up out-of-range values */
1316 for( i = 0; i < size; i++ )
1318 if( pixels[ i ] >= numLayers )
1319 pixels[ i ] = numLayers - 1;
1322 //% if( (i % w) == 0 )
1323 //% Sys_Printf( "\n" );
1324 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1328 //% Sys_Printf( "\n-------------------------------\n" );
1331 /* the index map must be at least 2x2 pixels */
1332 if( w < 2 || h < 2 )
1334 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1335 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1340 /* create a new index map */
1341 im = safe_malloc( sizeof( *im ) );
1342 memset( im, 0, sizeof( *im ) );
1347 im->numLayers = numLayers;
1348 strcpy( im->name, indexMapFilename );
1349 strcpy( im->shader, shader );
1350 im->pixels = pixels;
1352 /* get height offsets */
1353 value = ValueForKey( mapEnt, "_offsets" );
1354 if( value[ 0 ] == '\0' )
1355 value = ValueForKey( e, "offsets" );
1356 if( value[ 0 ] != '\0' )
1358 /* value is a space-seperated set of numbers */
1359 strcpy( offset, value );
1362 /* get each value */
1363 for( i = 0; i < 256 && *search != '\0'; i++ )
1365 space = strstr( search, " " );
1368 im->offsets[ i ] = atof( search );
1375 /* store the index map in every brush/patch in the entity */
1376 for( b = e->brushes; b != NULL; b = b->next )
1378 for( p = e->patches; p != NULL; p = p->next )
1390 parses a single entity out of a map file
1393 static qboolean ParseMapEntity( qboolean onlyLights )
1396 const char *classname, *value;
1397 float lightmapScale;
1398 char shader[ MAX_QPATH ];
1399 shaderInfo_t *celShader = NULL;
1403 int castShadows, recvShadows;
1407 if( !GetToken( qtrue ) )
1410 /* conformance check */
1411 if( strcmp( token, "{" ) )
1413 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1414 "Continuing to process map, but resulting BSP may be invalid.\n",
1415 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1420 if( numEntities >= MAX_MAP_ENTITIES )
1421 Error( "numEntities == MAX_MAP_ENTITIES" );
1424 entitySourceBrushes = 0;
1425 mapEnt = &entities[ numEntities ];
1427 memset( mapEnt, 0, sizeof( *mapEnt ) );
1429 /* ydnar: true entity numbering */
1430 mapEnt->mapEntityNum = numMapEntities;
1436 /* get initial token */
1437 if( !GetToken( qtrue ) )
1439 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1440 "Continuing to process map, but resulting BSP may be invalid.\n" );
1444 if( !strcmp( token, "}" ) )
1447 if( !strcmp( token, "{" ) )
1449 /* parse a brush or patch */
1450 if( !GetToken( qtrue ) )
1454 if( !strcmp( token, "patchDef2" ) )
1457 ParsePatch( onlyLights );
1459 else if( !strcmp( token, "terrainDef" ) )
1462 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1464 else if( !strcmp( token, "brushDef" ) )
1466 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1467 Error( "Old brush format not allowed in new brush format map" );
1468 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1470 /* parse brush primitive */
1471 ParseBrush( onlyLights );
1475 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1476 Error( "New brush format not allowed in old brush format map" );
1477 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1479 /* parse old brush format */
1481 ParseBrush( onlyLights );
1483 entitySourceBrushes++;
1487 /* parse a key / value pair */
1490 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1491 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1493 ep->next = mapEnt->epairs;
1494 mapEnt->epairs = ep;
1499 /* ydnar: get classname */
1500 classname = ValueForKey( mapEnt, "classname" );
1502 /* ydnar: only lights? */
1503 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1509 /* ydnar: determine if this is a func_group */
1510 if( !Q_stricmp( "func_group", classname ) )
1515 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1516 if( funcGroup || mapEnt->mapEntityNum == 0 )
1518 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1519 castShadows = WORLDSPAWN_CAST_SHADOWS;
1520 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1523 /* other entities don't cast any shadows, but recv worldspawn shadows */
1526 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1527 castShadows = ENTITY_CAST_SHADOWS;
1528 recvShadows = ENTITY_RECV_SHADOWS;
1531 /* get explicit shadow flags */
1532 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1534 /* ydnar: get lightmap scaling value for this entity */
1535 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1536 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
1538 /* get lightmap scale from entity */
1539 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1540 if( lightmapScale <= 0.0f )
1541 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1542 if( lightmapScale > 0.0f )
1543 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1546 lightmapScale = 0.0f;
1548 /* ydnar: get cel shader :) for this entity */
1549 value = ValueForKey( mapEnt, "_celshader" );
1550 if( value[ 0 ] == '\0' )
1551 value = ValueForKey( &entities[ 0 ], "_celshader" );
1552 if( value[ 0 ] != '\0' )
1554 sprintf( shader, "textures/%s", value );
1555 celShader = ShaderInfoForShader( shader );
1556 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1561 /* attach stuff to everything in the entity */
1562 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1564 brush->entityNum = mapEnt->mapEntityNum;
1565 brush->castShadows = castShadows;
1566 brush->recvShadows = recvShadows;
1567 brush->lightmapScale = lightmapScale;
1568 brush->celShader = celShader;
1571 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1573 patch->entityNum = mapEnt->mapEntityNum;
1574 patch->castShadows = castShadows;
1575 patch->recvShadows = recvShadows;
1576 patch->lightmapScale = lightmapScale;
1577 patch->celShader = celShader;
1580 /* ydnar: gs mods: set entity bounds */
1581 SetEntityBounds( mapEnt );
1583 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1584 LoadEntityIndexMap( mapEnt );
1586 /* get entity origin and adjust brushes */
1587 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1588 if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
1589 AdjustBrushesForOrigin( mapEnt );
1591 /* group_info entities are just for editor grouping (fixme: leak!) */
1592 if( !Q_stricmp( "group_info", classname ) )
1598 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1601 MoveBrushesToWorld( mapEnt );
1614 loads a map file into a list of entities
1617 void LoadMapFile( char *filename, qboolean onlyLights )
1621 int oldNumEntities, numMapBrushes;
1625 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1626 Sys_Printf( "Loading %s\n", filename );
1629 file = SafeOpenRead( filename );
1632 /* load the map file */
1633 LoadScriptFile( filename, -1 );
1637 oldNumEntities = numEntities;
1642 numMapDrawSurfs = 0;
1644 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1646 /* allocate a very large temporary brush for building the brushes as they are loaded */
1647 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1649 /* parse the map file */
1650 while( ParseMapEntity( onlyLights ) );
1655 /* emit some statistics */
1656 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1660 /* set map bounds */
1661 ClearBounds( mapMins, mapMaxs );
1662 for( b = entities[ 0 ].brushes; b; b = b->next )
1664 AddPointToBounds( b->mins, mapMins, mapMaxs );
1665 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1668 /* get brush counts */
1669 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1670 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1671 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1673 /* emit some statistics */
1674 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1675 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1676 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1677 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1678 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1679 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1680 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1681 Sys_Printf( "%9d areaportals\n", c_areaportals);
1682 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1683 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1684 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1686 /* write bogus map */
1688 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );