2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ----------------------------------------------------------------------------------
23 This code has been altered significantly from its original form, to support
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26 ------------------------------------------------------------------------------- */
40 /* FIXME: remove these vars */
42 /* undefine to make plane finding use linear sort (note: really slow) */
44 #define PLANE_HASHES 8192
46 plane_t *planehash[ PLANE_HASHES ];
58 ydnar: replaced with variable epsilon for djbob
61 #define NORMAL_EPSILON 0.00001
62 #define DIST_EPSILON 0.01
64 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )
69 /* get local copies */
74 if( fabs( p->dist - dist ) <= de &&
75 fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&
76 fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&
77 fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )
90 void AddPlaneToHash( plane_t *p )
95 hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );
97 p->hash_chain = planehash[hash];
106 int CreateNewFloatPlane (vec3_t normal, vec_t dist)
110 if (VectorLength(normal) < 0.5)
112 Sys_Printf( "FloatPlane: bad normal\n");
116 // create a new plane
117 if (nummapplanes+2 > MAX_MAP_PLANES)
118 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);
130 // allways put axial planes facing positive first
133 if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
141 AddPlaneToHash (p+1);
142 return nummapplanes - 1;
147 AddPlaneToHash (p+1);
148 return nummapplanes - 2;
155 snaps a near-axial normal vector
158 void SnapNormal( vec3_t normal )
162 for( i = 0; i < 3; i++ )
164 if( fabs( normal[ i ] - 1 ) < normalEpsilon )
166 VectorClear( normal );
170 if( fabs( normal[ i ] - -1 ) < normalEpsilon )
172 VectorClear( normal );
183 snaps a plane to normal/distance epsilons
186 void SnapPlane( vec3_t normal, vec_t *dist )
188 SnapNormal( normal );
190 if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
191 *dist = Q_rint( *dist );
198 ydnar: changed to allow a number of test points to be supplied that
199 must be within an epsilon distance of the plane
202 int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
213 SnapPlane( normal, &dist );
214 hash = (PLANE_HASHES - 1) & (int) fabs( dist );
216 /* search the border bins as well */
217 for( i = -1; i <= 1; i++ )
219 h = (hash + i) & (PLANE_HASHES - 1);
220 for( p = planehash[ h ]; p != NULL; p = p->hash_chain )
222 /* do standard plane compare */
223 if( !PlaneEqual( p, normal, dist ) )
226 /* ydnar: uncomment the following line for old-style plane finding */
227 //% return p - mapplanes;
229 /* ydnar: test supplied points against this plane */
230 for( j = 0; j < numPoints; j++ )
232 d = DotProduct( points[ j ], normal ) - dist;
233 if( fabs( d ) > distanceEpsilon )
237 /* found a matching plane */
239 return p - mapplanes;
243 /* none found, so create a new one */
244 return CreateNewFloatPlane( normal, dist );
254 SnapPlane( normal, &dist );
255 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
257 if( PlaneEqual( p, normal, dist ) )
261 return CreateNewFloatPlane( normal, dist );
270 takes 3 points and finds the plane they lie in
273 int MapPlaneFromPoints( vec3_t *p )
275 vec3_t t1, t2, normal;
279 /* calc plane normal */
280 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
281 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
282 CrossProduct( t1, t2, normal );
283 VectorNormalize( normal, normal );
285 /* calc plane distance */
286 dist = DotProduct( p[ 0 ], normal );
288 /* store the plane */
289 return FindFloatPlane( normal, dist, 3, p );
296 the content flags and compile flags on all sides of a brush should be the same
299 void SetBrushContents( brush_t *b )
301 int contentFlags, compileFlags;
307 /* get initial compile flags from first side */
309 contentFlags = s->contentFlags;
310 compileFlags = s->compileFlags;
311 b->contentShader = s->shaderInfo;
314 /* get the content/compile flags for every side in the brush */
315 for( i = 1; i < b->numsides; i++, s++ )
318 if( s->shaderInfo == NULL )
320 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
324 /* ydnar: getting rid of this stupid warning */
326 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
328 /* check for detail & structural */
329 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
331 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
332 compileFlags &= ~C_DETAIL;
335 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
337 compileFlags &= ~C_DETAIL;
339 /* all translucent brushes that aren't specifically made structural will be detail */
340 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
341 compileFlags |= C_DETAIL;
344 if( compileFlags & C_DETAIL )
356 if( compileFlags & C_TRANSLUCENT )
362 if( compileFlags & C_AREAPORTAL )
365 /* set brush flags */
366 b->contentFlags = contentFlags;
367 b->compileFlags = compileFlags;
374 adds any additional planes necessary to allow the brush being
375 built to be expanded against axial bounding boxes
376 ydnar 2003-01-20: added mrelusive fixes
379 void AddBrushBevels( void )
382 int i, j, k, l, order;
392 // add the axial planes
395 for ( axis = 0; axis < 3; axis++ ) {
396 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
397 // see if the plane is allready present
398 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
400 /* ydnar: testing disabling of mre code */
403 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
408 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
413 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
414 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
419 if ( i == buildBrush->numsides ) {
421 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
422 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
424 memset( s, 0, sizeof( *s ) );
425 buildBrush->numsides++;
426 VectorClear (normal);
431 /* ydnar: adding bevel plane snapping for fewer bsp planes */
433 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
435 dist = buildBrush->maxs[ axis ];
439 /* ydnar: adding bevel plane snapping for fewer bsp planes */
441 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
443 dist = -buildBrush->mins[ axis ];
446 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
447 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
452 // if the plane is not in it canonical order, swap it
454 sidetemp = buildBrush->sides[order];
455 buildBrush->sides[order] = buildBrush->sides[i];
456 buildBrush->sides[i] = sidetemp;
462 // add the edge bevels
464 if ( buildBrush->numsides == 6 ) {
465 return; // pure axial
468 // test the non-axial plane edges
469 for ( i = 6; i < buildBrush->numsides; i++ ) {
470 s = buildBrush->sides + i;
475 for ( j = 0; j < w->numpoints; j++) {
476 k = (j+1)%w->numpoints;
477 VectorSubtract( w->p[j], w->p[k], vec );
478 if ( VectorNormalize( vec, vec ) < 0.5f ) {
482 for ( k = 0; k < 3; k++ ) {
483 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
488 continue; // only test non-axial edges
492 //% Sys_Printf( "-------------\n" );
494 // try the six possible slanted axials from this edge
495 for ( axis = 0; axis < 3; axis++ ) {
496 for ( dir = -1; dir <= 1; dir += 2 ) {
500 CrossProduct( vec, vec2, normal );
501 if ( VectorNormalize( normal, normal ) < 0.5f ) {
504 dist = DotProduct( w->p[j], normal );
506 // if all the points on all the sides are
507 // behind this plane, it is a proper edge bevel
508 for ( k = 0; k < buildBrush->numsides; k++ ) {
510 // if this plane has allready been used, skip it
511 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
515 w2 = buildBrush->sides[k].winding;
520 for ( l = 0; l < w2->numpoints; l++ ) {
521 d = DotProduct( w2->p[l], normal ) - dist;
523 break; // point in front
529 // if some point was at the front
530 if ( l != w2->numpoints ) {
534 // if no points at the back then the winding is on the bevel plane
535 if ( minBack > -0.1f ) {
536 //% Sys_Printf( "On bevel plane\n" );
541 if ( k != buildBrush->numsides ) {
542 continue; // wasn't part of the outer hull
546 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
549 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
550 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
552 s2 = &buildBrush->sides[buildBrush->numsides];
553 buildBrush->numsides++;
554 memset( s2, 0, sizeof( *s2 ) );
556 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
557 s2->contentFlags = buildBrush->sides[0].contentFlags;
570 produces a final brush based on the buildBrush->sides array
571 and links it to the current entity
574 brush_t *FinishBrush( void )
579 /* create windings for sides and bounds for brush */
580 if ( !CreateBrushWindings( buildBrush ) )
583 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
584 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
585 if( buildBrush->compileFlags & C_ORIGIN )
590 if( numEntities == 1 )
592 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
593 mapEnt->mapEntityNum, entitySourceBrushes );
597 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
598 VectorScale (origin, 0.5, origin);
600 sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
601 SetKeyValue( &entities[ numEntities - 1 ], "origin", string);
603 VectorCopy( origin, entities[ numEntities - 1 ].origin);
605 /* don't keep this brush */
609 /* determine if the brush is an area portal */
610 if( buildBrush->compileFlags & C_AREAPORTAL )
612 if( numEntities != 1 )
614 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
619 /* add bevel planes */
623 b = CopyBrush( buildBrush );
625 /* set map entity and brush numbering */
626 b->entityNum = mapEnt->mapEntityNum;
627 b->brushNum = entitySourceBrushes;
632 /* link opaque brushes to head of list, translucent brushes to end */
633 if( b->opaque || mapEnt->lastBrush == NULL )
635 b->next = mapEnt->brushes;
637 if( mapEnt->lastBrush == NULL )
638 mapEnt->lastBrush = b;
643 mapEnt->lastBrush->next = b;
644 mapEnt->lastBrush = b;
647 /* return to sender */
654 TextureAxisFromPlane()
655 determines best orthagonal axis to project a texture onto a wall
656 (must be identical in radiant!)
659 vec3_t baseaxis[18] =
661 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
662 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
663 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
664 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
665 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
666 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
669 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
678 for (i=0 ; i<6 ; i++)
680 dot = DotProduct (pln->normal, baseaxis[i*3]);
681 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
688 VectorCopy (baseaxis[bestaxis*3+1], xv);
689 VectorCopy (baseaxis[bestaxis*3+2], yv);
696 creates world-to-texture mapping vecs for crappy quake plane arrangements
699 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
703 vec_t ang, sinv, cosv;
708 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
717 { sinv = 0 ; cosv = 1; }
718 else if (rotate == 90)
719 { sinv = 1 ; cosv = 0; }
720 else if (rotate == 180)
721 { sinv = 0 ; cosv = -1; }
722 else if (rotate == 270)
723 { sinv = -1 ; cosv = 0; }
726 ang = rotate / 180 * Q_PI;
745 for (i=0 ; i<2 ; i++) {
746 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
747 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
752 for (i=0 ; i<2 ; i++)
753 for (j=0 ; j<3 ; j++)
754 mappingVecs[i][j] = vecs[i][j] / scale[i];
756 mappingVecs[0][3] = shift[0];
757 mappingVecs[1][3] = shift[1];
764 parses the sides into buildBrush->sides[], nothing else.
765 no validation, back plane removal, etc.
768 added brush epairs parsing ( ignoring actually )
770 added exclusive brush primitive parsing
772 support for old brush format back in
773 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
776 static void ParseRawBrush( qboolean onlyLights )
779 vec3_t planePoints[ 3 ];
785 char name[ MAX_QPATH ];
786 char shader[ MAX_QPATH ];
791 buildBrush->numsides = 0;
792 buildBrush->detail = qfalse;
795 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
801 if( !GetToken( qtrue ) )
803 if( !strcmp( token, "}" ) )
806 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
807 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
811 if( strcmp( token, "(" ) )
820 /* test side count */
821 if( buildBrush->numsides >= MAX_BUILD_SIDES )
822 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
825 side = &buildBrush->sides[ buildBrush->numsides ];
826 memset( side, 0, sizeof( *side ) );
827 buildBrush->numsides++;
829 /* read the three point plane definition */
830 Parse1DMatrix( 3, planePoints[ 0 ] );
831 Parse1DMatrix( 3, planePoints[ 1 ] );
832 Parse1DMatrix( 3, planePoints[ 2 ] );
834 /* bp: read the texture matrix */
835 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
836 Parse2DMatrix( 2, 3, (float*) side->texMat );
838 /* read shader name */
840 strcpy( name, token );
843 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
846 shift[ 0 ] = atof( token );
848 shift[ 1 ] = atof( token );
850 rotate = atof( token );
852 scale[ 0 ] = atof( token );
854 scale[ 1 ] = atof( token );
857 /* set default flags and values */
858 sprintf( shader, "textures/%s", name );
860 si = &shaderInfo[ 0 ];
862 si = ShaderInfoForShader( shader );
863 side->shaderInfo = si;
864 side->surfaceFlags = si->surfaceFlags;
865 side->contentFlags = si->contentFlags;
866 side->compileFlags = si->compileFlags;
867 side->value = si->value;
869 /* ydnar: gs mods: bias texture shift */
870 if( si->globalTexture == qfalse )
872 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
873 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
877 historically, there are 3 integer values at the end of a brushside line in a .map file.
878 in quake 3, the only thing that mattered was the first of these three values, which
879 was previously the content flags. and only then did a single bit matter, the detail
880 bit. because every game has its own special flags for specifying detail, the
881 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
882 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
883 is stored in compileFlags, as opposed to contentFlags, for multiple-game
887 if( TokenAvailable() )
889 /* get detail bit from map content flags */
891 flags = atoi( token );
892 if( flags & C_DETAIL )
893 side->compileFlags |= C_DETAIL;
897 //% td.flags = atoi( token );
899 //% td.value = atoi( token );
902 /* find the plane number */
903 planenum = MapPlaneFromPoints( planePoints );
904 side->planenum = planenum;
906 /* bp: get the texture mapping for this texturedef / plane combination */
907 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
908 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
912 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
923 RemoveDuplicateBrushPlanes
924 returns false if the brush has a mirrored set of planes,
925 meaning it encloses no volume.
926 also removes planes without any normal
929 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
936 for ( i = 1 ; i < b->numsides ; i++ ) {
938 // check for a degenerate plane
939 if ( sides[i].planenum == -1) {
940 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
942 for ( k = i + 1 ; k < b->numsides ; k++ ) {
943 sides[k-1] = sides[k];
950 // check for duplication and mirroring
951 for ( j = 0 ; j < i ; j++ ) {
952 if ( sides[i].planenum == sides[j].planenum ) {
953 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
954 // remove the second duplicate
955 for ( k = i + 1 ; k < b->numsides ; k++ ) {
956 sides[k-1] = sides[k];
963 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
964 // mirror plane, brush is invalid
965 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
977 parses a brush out of a map file and sets it up
980 static void ParseBrush( qboolean onlyLights )
985 /* parse the brush out of the map */
986 ParseRawBrush( onlyLights );
988 /* only go this far? */
992 /* set some defaults */
993 buildBrush->portalareas[ 0 ] = -1;
994 buildBrush->portalareas[ 1 ] = -1;
995 buildBrush->entityNum = numMapEntities - 1;
996 buildBrush->brushNum = entitySourceBrushes;
998 /* if there are mirrored planes, the entire brush is invalid */
999 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1002 /* get the content for the entire brush */
1003 SetBrushContents( buildBrush );
1005 /* allow detail brushes to be removed */
1006 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1008 //% FreeBrush( buildBrush );
1012 /* allow liquid brushes to be removed */
1013 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1015 //% FreeBrush( buildBrush );
1019 /* ydnar: allow hint brushes to be removed */
1020 if( noHint && (buildBrush->compileFlags & C_HINT) )
1022 //% FreeBrush( buildBrush );
1026 /* finish the brush */
1033 MoveBrushesToWorld()
1034 takes all of the brushes from the current entity and
1035 adds them to the world's brush list
1036 (used by func_group)
1039 void MoveBrushesToWorld( entity_t *ent )
1046 for( b = ent->brushes; b != NULL; b = next )
1048 /* get next brush */
1051 /* link opaque brushes to head of list, translucent brushes to end */
1052 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1054 b->next = entities[ 0 ].brushes;
1055 entities[ 0 ].brushes = b;
1056 if( entities[ 0 ].lastBrush == NULL )
1057 entities[ 0 ].lastBrush = b;
1062 entities[ 0 ].lastBrush->next = b;
1063 entities[ 0 ].lastBrush = b;
1066 //% b->next = entities[ 0 ].brushes;
1067 //% entities[ 0 ].brushes = b;
1069 ent->brushes = NULL;
1072 if( ent->patches != NULL )
1074 for( pm = ent->patches; pm->next; pm = pm->next );
1076 pm->next = entities[ 0 ].patches;
1077 entities[ 0 ].patches = ent->patches;
1079 ent->patches = NULL;
1086 AdjustBrushesForOrigin()
1089 void AdjustBrushesForOrigin( entity_t *ent )
1099 /* walk brush list */
1100 for( b = ent->brushes; b != NULL; b = b->next )
1102 /* offset brush planes */
1103 for( i = 0; i < b->numsides; i++)
1105 /* get brush side */
1108 /* offset side plane */
1109 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
1111 /* find a new plane */
1112 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1115 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1116 CreateBrushWindings( b );
1119 /* walk patch list */
1120 for( p = ent->patches; p != NULL; p = p->next )
1122 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1123 VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
1130 SetEntityBounds() - ydnar
1131 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1134 void SetEntityBounds( entity_t *e )
1145 /* walk the entity's brushes/patches and determine bounds */
1146 ClearBounds( mins, maxs );
1147 for( b = e->brushes; b; b = b->next )
1149 AddPointToBounds( b->mins, mins, maxs );
1150 AddPointToBounds( b->maxs, mins, maxs );
1152 for( p = e->patches; p; p = p->next )
1154 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1155 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1158 /* try to find explicit min/max key */
1159 value = ValueForKey( e, "min" );
1160 if( value[ 0 ] != '\0' )
1161 GetVectorForKey( e, "min", mins );
1162 value = ValueForKey( e, "max" );
1163 if( value[ 0 ] != '\0' )
1164 GetVectorForKey( e, "max", maxs );
1166 /* store the bounds */
1167 for( b = e->brushes; b; b = b->next )
1169 VectorCopy( mins, b->eMins );
1170 VectorCopy( maxs, b->eMaxs );
1172 for( p = e->patches; p; p = p->next )
1174 VectorCopy( mins, p->eMins );
1175 VectorCopy( maxs, p->eMaxs );
1182 LoadEntityIndexMap() - ydnar
1183 based on LoadAlphaMap() from terrain.c, a little more generic
1186 void LoadEntityIndexMap( entity_t *e )
1188 int i, size, numLayers, w, h;
1189 const char *value, *indexMapFilename, *shader;
1190 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1192 unsigned int *pixels32;
1198 /* this only works with bmodel ents */
1199 if( e->brushes == NULL && e->patches == NULL )
1202 /* determine if there is an index map (support legacy "alphamap" key as well) */
1203 value = ValueForKey( e, "_indexmap" );
1204 if( value[ 0 ] == '\0' )
1205 value = ValueForKey( e, "alphamap" );
1206 if( value[ 0 ] == '\0' )
1208 indexMapFilename = value;
1210 /* get number of layers (support legacy "layers" key as well) */
1211 value = ValueForKey( e, "_layers" );
1212 if( value[ 0 ] == '\0' )
1213 value = ValueForKey( e, "layers" );
1214 if( value[ 0 ] == '\0' )
1216 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1217 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1220 numLayers = atoi( value );
1223 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1224 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1228 /* get base shader name (support legacy "shader" key as well) */
1229 value = ValueForKey( mapEnt, "_shader" );
1230 if( value[ 0 ] == '\0' )
1231 value = ValueForKey( e, "shader" );
1232 if( value[ 0 ] == '\0' )
1234 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1235 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1241 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1243 /* get index map file extension */
1244 ExtractFileExtension( indexMapFilename, ext );
1246 /* handle tga image */
1247 if( !Q_stricmp( ext, "tga" ) )
1250 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1252 /* convert to bytes */
1254 pixels = safe_malloc( size );
1255 for( i = 0; i < size; i++ )
1257 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1258 if( pixels[ i ] >= numLayers )
1259 pixels[ i ] = numLayers - 1;
1262 /* free the 32 bit image */
1268 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1271 //% Sys_Printf( "-------------------------------" );
1273 /* fix up out-of-range values */
1275 for( i = 0; i < size; i++ )
1277 if( pixels[ i ] >= numLayers )
1278 pixels[ i ] = numLayers - 1;
1281 //% if( (i % w) == 0 )
1282 //% Sys_Printf( "\n" );
1283 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1287 //% Sys_Printf( "\n-------------------------------\n" );
1290 /* the index map must be at least 2x2 pixels */
1291 if( w < 2 || h < 2 )
1293 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1294 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1299 /* create a new index map */
1300 im = safe_malloc( sizeof( *im ) );
1301 memset( im, 0, sizeof( *im ) );
1306 im->numLayers = numLayers;
1307 strcpy( im->name, indexMapFilename );
1308 strcpy( im->shader, shader );
1309 im->pixels = pixels;
1311 /* get height offsets */
1312 value = ValueForKey( mapEnt, "_offsets" );
1313 if( value[ 0 ] == '\0' )
1314 value = ValueForKey( e, "offsets" );
1315 if( value[ 0 ] != '\0' )
1317 /* value is a space-seperated set of numbers */
1318 strcpy( offset, value );
1321 /* get each value */
1322 for( i = 0; i < 256 && *search != '\0'; i++ )
1324 space = strstr( search, " " );
1327 im->offsets[ i ] = atof( search );
1334 /* store the index map in every brush/patch in the entity */
1335 for( b = e->brushes; b != NULL; b = b->next )
1337 for( p = e->patches; p != NULL; p = p->next )
1349 parses a single entity out of a map file
1352 static qboolean ParseMapEntity( qboolean onlyLights )
1355 const char *classname, *value;
1356 float lightmapScale;
1357 char shader[ MAX_QPATH ];
1358 shaderInfo_t *celShader = NULL;
1362 int castShadows, recvShadows;
1366 if( !GetToken( qtrue ) )
1369 /* conformance check */
1370 if( strcmp( token, "{" ) )
1372 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1373 "Continuing to process map, but resulting BSP may be invalid.\n",
1374 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1379 if( numEntities >= MAX_MAP_ENTITIES )
1380 Error( "numEntities == MAX_MAP_ENTITIES" );
1383 entitySourceBrushes = 0;
1384 mapEnt = &entities[ numEntities ];
1386 memset( mapEnt, 0, sizeof( *mapEnt ) );
1388 /* ydnar: true entity numbering */
1389 mapEnt->mapEntityNum = numMapEntities;
1395 /* get initial token */
1396 if( !GetToken( qtrue ) )
1398 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1399 "Continuing to process map, but resulting BSP may be invalid.\n" );
1403 if( !strcmp( token, "}" ) )
1406 if( !strcmp( token, "{" ) )
1408 /* parse a brush or patch */
1409 if( !GetToken( qtrue ) )
1413 if( !strcmp( token, "patchDef2" ) )
1416 ParsePatch( onlyLights );
1418 else if( !strcmp( token, "terrainDef" ) )
1421 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1423 else if( !strcmp( token, "brushDef" ) )
1425 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1426 Error( "Old brush format not allowed in new brush format map" );
1427 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1429 /* parse brush primitive */
1430 ParseBrush( onlyLights );
1434 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1435 Error( "New brush format not allowed in old brush format map" );
1436 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1438 /* parse old brush format */
1440 ParseBrush( onlyLights );
1442 entitySourceBrushes++;
1446 /* parse a key / value pair */
1449 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1450 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1452 ep->next = mapEnt->epairs;
1453 mapEnt->epairs = ep;
1458 /* ydnar: get classname */
1459 classname = ValueForKey( mapEnt, "classname" );
1461 /* ydnar: only lights? */
1462 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1468 /* ydnar: determine if this is a func_group */
1469 if( !Q_stricmp( "func_group", classname ) )
1474 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1475 if( funcGroup || mapEnt->mapEntityNum == 0 )
1477 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1478 castShadows = WORLDSPAWN_CAST_SHADOWS;
1479 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1482 /* other entities don't cast any shadows, but recv worldspawn shadows */
1485 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1486 castShadows = ENTITY_CAST_SHADOWS;
1487 recvShadows = ENTITY_RECV_SHADOWS;
1490 /* get explicit shadow flags */
1491 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1493 /* ydnar: get lightmap scaling value for this entity */
1494 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1495 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
1497 /* get lightmap scale from entity */
1498 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1499 if( lightmapScale <= 0.0f )
1500 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1501 if( lightmapScale > 0.0f )
1502 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1505 lightmapScale = 0.0f;
1507 /* ydnar: get cel shader :) for this entity */
1508 value = ValueForKey( mapEnt, "_celshader" );
1509 if( value[ 0 ] == '\0' )
1510 value = ValueForKey( &entities[ 0 ], "_celshader" );
1511 if( value[ 0 ] != '\0' )
1513 sprintf( shader, "textures/%s", value );
1514 celShader = ShaderInfoForShader( shader );
1515 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1520 /* attach stuff to everything in the entity */
1521 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1523 brush->entityNum = mapEnt->mapEntityNum;
1524 brush->castShadows = castShadows;
1525 brush->recvShadows = recvShadows;
1526 brush->lightmapScale = lightmapScale;
1527 brush->celShader = celShader;
1530 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1532 patch->entityNum = mapEnt->mapEntityNum;
1533 patch->castShadows = castShadows;
1534 patch->recvShadows = recvShadows;
1535 patch->lightmapScale = lightmapScale;
1536 patch->celShader = celShader;
1539 /* ydnar: gs mods: set entity bounds */
1540 SetEntityBounds( mapEnt );
1542 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1543 LoadEntityIndexMap( mapEnt );
1545 /* get entity origin and adjust brushes */
1546 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1547 if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
1548 AdjustBrushesForOrigin( mapEnt );
1550 /* group_info entities are just for editor grouping (fixme: leak!) */
1551 if( !Q_stricmp( "group_info", classname ) )
1557 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1560 MoveBrushesToWorld( mapEnt );
1573 loads a map file into a list of entities
1576 void LoadMapFile( char *filename, qboolean onlyLights )
1580 int oldNumEntities, numMapBrushes;
1584 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1585 Sys_Printf( "Loading %s\n", filename );
1588 file = SafeOpenRead( filename );
1591 /* load the map file */
1592 LoadScriptFile( filename, -1 );
1596 oldNumEntities = numEntities;
1601 numMapDrawSurfs = 0;
1603 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1605 /* allocate a very large temporary brush for building the brushes as they are loaded */
1606 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1608 /* parse the map file */
1609 while( ParseMapEntity( onlyLights ) );
1614 /* emit some statistics */
1615 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1619 /* set map bounds */
1620 ClearBounds( mapMins, mapMaxs );
1621 for( b = entities[ 0 ].brushes; b; b = b->next )
1623 AddPointToBounds( b->mins, mapMins, mapMaxs );
1624 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1627 /* get brush counts */
1628 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1629 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1630 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1632 /* emit some statistics */
1633 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1634 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1635 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1636 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1637 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1638 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1639 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1640 Sys_Printf( "%9d areaportals\n", c_areaportals);
1641 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1642 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1643 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1645 /* write bogus map */
1647 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );