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 static void MergeOrigin(entity_t *ent, vec3_t origin)
602 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
603 VectorAdd(adjustment, ent->origin, ent->origin);
604 VectorCopy(origin, ent->originbrush_origin);
607 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
608 SetKeyValue(ent, "origin", string);
611 brush_t *FinishBrush( void )
616 /* create windings for sides and bounds for brush */
617 if ( !CreateBrushWindings( buildBrush ) )
620 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
621 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
622 if( buildBrush->compileFlags & C_ORIGIN )
627 if( numEntities == 1 )
629 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
630 mapEnt->mapEntityNum, entitySourceBrushes );
634 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
635 VectorScale (origin, 0.5, origin);
637 MergeOrigin(&entities[ numEntities - 1 ], origin);
639 /* don't keep this brush */
643 /* determine if the brush is an area portal */
644 if( buildBrush->compileFlags & C_AREAPORTAL )
646 if( numEntities != 1 )
648 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
653 /* add bevel planes */
657 b = CopyBrush( buildBrush );
659 /* set map entity and brush numbering */
660 b->entityNum = mapEnt->mapEntityNum;
661 b->brushNum = entitySourceBrushes;
666 /* link opaque brushes to head of list, translucent brushes to end */
667 if( b->opaque || mapEnt->lastBrush == NULL )
669 b->next = mapEnt->brushes;
671 if( mapEnt->lastBrush == NULL )
672 mapEnt->lastBrush = b;
677 mapEnt->lastBrush->next = b;
678 mapEnt->lastBrush = b;
681 /* link colorMod volume brushes to the entity directly */
682 if( b->contentShader != NULL &&
683 b->contentShader->colorMod != NULL &&
684 b->contentShader->colorMod->type == CM_VOLUME )
686 b->nextColorModBrush = mapEnt->colorModBrushes;
687 mapEnt->colorModBrushes = b;
690 /* return to sender */
697 TextureAxisFromPlane()
698 determines best orthagonal axis to project a texture onto a wall
699 (must be identical in radiant!)
702 vec3_t baseaxis[18] =
704 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
705 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
706 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
707 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
708 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
709 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
712 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
721 for (i=0 ; i<6 ; i++)
723 dot = DotProduct (pln->normal, baseaxis[i*3]);
724 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
731 VectorCopy (baseaxis[bestaxis*3+1], xv);
732 VectorCopy (baseaxis[bestaxis*3+2], yv);
739 creates world-to-texture mapping vecs for crappy quake plane arrangements
742 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
746 vec_t ang, sinv, cosv;
751 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
760 { sinv = 0 ; cosv = 1; }
761 else if (rotate == 90)
762 { sinv = 1 ; cosv = 0; }
763 else if (rotate == 180)
764 { sinv = 0 ; cosv = -1; }
765 else if (rotate == 270)
766 { sinv = -1 ; cosv = 0; }
769 ang = rotate / 180 * Q_PI;
788 for (i=0 ; i<2 ; i++) {
789 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
790 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
795 for (i=0 ; i<2 ; i++)
796 for (j=0 ; j<3 ; j++)
797 mappingVecs[i][j] = vecs[i][j] / scale[i];
799 mappingVecs[0][3] = shift[0];
800 mappingVecs[1][3] = shift[1];
807 parses the sides into buildBrush->sides[], nothing else.
808 no validation, back plane removal, etc.
811 added brush epairs parsing ( ignoring actually )
813 added exclusive brush primitive parsing
815 support for old brush format back in
816 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
819 static void ParseRawBrush( qboolean onlyLights )
822 vec3_t planePoints[ 3 ];
828 char name[ MAX_QPATH ];
829 char shader[ MAX_QPATH ];
834 buildBrush->numsides = 0;
835 buildBrush->detail = qfalse;
838 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
844 if( !GetToken( qtrue ) )
846 if( !strcmp( token, "}" ) )
849 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
850 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
854 if( strcmp( token, "(" ) )
863 /* test side count */
864 if( buildBrush->numsides >= MAX_BUILD_SIDES )
865 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
868 side = &buildBrush->sides[ buildBrush->numsides ];
869 memset( side, 0, sizeof( *side ) );
870 buildBrush->numsides++;
872 /* read the three point plane definition */
873 Parse1DMatrix( 3, planePoints[ 0 ] );
874 Parse1DMatrix( 3, planePoints[ 1 ] );
875 Parse1DMatrix( 3, planePoints[ 2 ] );
877 /* bp: read the texture matrix */
878 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
879 Parse2DMatrix( 2, 3, (float*) side->texMat );
881 /* read shader name */
883 strcpy( name, token );
886 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
889 shift[ 0 ] = atof( token );
891 shift[ 1 ] = atof( token );
893 rotate = atof( token );
895 scale[ 0 ] = atof( token );
897 scale[ 1 ] = atof( token );
900 /* set default flags and values */
901 sprintf( shader, "textures/%s", name );
903 si = &shaderInfo[ 0 ];
905 si = ShaderInfoForShader( shader );
906 side->shaderInfo = si;
907 side->surfaceFlags = si->surfaceFlags;
908 side->contentFlags = si->contentFlags;
909 side->compileFlags = si->compileFlags;
910 side->value = si->value;
912 /* ydnar: gs mods: bias texture shift */
913 if( si->globalTexture == qfalse )
915 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
916 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
920 historically, there are 3 integer values at the end of a brushside line in a .map file.
921 in quake 3, the only thing that mattered was the first of these three values, which
922 was previously the content flags. and only then did a single bit matter, the detail
923 bit. because every game has its own special flags for specifying detail, the
924 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
925 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
926 is stored in compileFlags, as opposed to contentFlags, for multiple-game
930 if( TokenAvailable() )
932 /* get detail bit from map content flags */
934 flags = atoi( token );
935 if( flags & C_DETAIL )
936 side->compileFlags |= C_DETAIL;
940 //% td.flags = atoi( token );
942 //% td.value = atoi( token );
945 /* find the plane number */
946 planenum = MapPlaneFromPoints( planePoints );
947 side->planenum = planenum;
949 /* bp: get the texture mapping for this texturedef / plane combination */
950 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
951 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
955 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
966 RemoveDuplicateBrushPlanes
967 returns false if the brush has a mirrored set of planes,
968 meaning it encloses no volume.
969 also removes planes without any normal
972 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
979 for ( i = 1 ; i < b->numsides ; i++ ) {
981 // check for a degenerate plane
982 if ( sides[i].planenum == -1) {
983 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
985 for ( k = i + 1 ; k < b->numsides ; k++ ) {
986 sides[k-1] = sides[k];
993 // check for duplication and mirroring
994 for ( j = 0 ; j < i ; j++ ) {
995 if ( sides[i].planenum == sides[j].planenum ) {
996 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
997 // remove the second duplicate
998 for ( k = i + 1 ; k < b->numsides ; k++ ) {
999 sides[k-1] = sides[k];
1006 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1007 // mirror plane, brush is invalid
1008 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1020 parses a brush out of a map file and sets it up
1023 static void ParseBrush( qboolean onlyLights )
1028 /* parse the brush out of the map */
1029 ParseRawBrush( onlyLights );
1031 /* only go this far? */
1035 /* set some defaults */
1036 buildBrush->portalareas[ 0 ] = -1;
1037 buildBrush->portalareas[ 1 ] = -1;
1038 buildBrush->entityNum = numMapEntities - 1;
1039 buildBrush->brushNum = entitySourceBrushes;
1041 /* if there are mirrored planes, the entire brush is invalid */
1042 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1045 /* get the content for the entire brush */
1046 SetBrushContents( buildBrush );
1048 /* allow detail brushes to be removed */
1049 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1051 //% FreeBrush( buildBrush );
1055 /* allow liquid brushes to be removed */
1056 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1058 //% FreeBrush( buildBrush );
1062 /* ydnar: allow hint brushes to be removed */
1063 if( noHint && (buildBrush->compileFlags & C_HINT) )
1065 //% FreeBrush( buildBrush );
1069 /* finish the brush */
1076 MoveBrushesToWorld()
1077 takes all of the brushes from the current entity and
1078 adds them to the world's brush list
1079 (used by func_group)
1082 void MoveBrushesToWorld( entity_t *ent )
1089 for( b = ent->brushes; b != NULL; b = next )
1091 /* get next brush */
1094 /* link opaque brushes to head of list, translucent brushes to end */
1095 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1097 b->next = entities[ 0 ].brushes;
1098 entities[ 0 ].brushes = b;
1099 if( entities[ 0 ].lastBrush == NULL )
1100 entities[ 0 ].lastBrush = b;
1105 entities[ 0 ].lastBrush->next = b;
1106 entities[ 0 ].lastBrush = b;
1109 ent->brushes = NULL;
1111 /* ydnar: move colormod brushes */
1112 if( ent->colorModBrushes != NULL )
1114 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1116 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1117 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1119 ent->colorModBrushes = NULL;
1123 if( ent->patches != NULL )
1125 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1127 pm->next = entities[ 0 ].patches;
1128 entities[ 0 ].patches = ent->patches;
1130 ent->patches = NULL;
1137 AdjustBrushesForOrigin()
1140 void AdjustBrushesForOrigin( entity_t *ent )
1150 /* walk brush list */
1151 for( b = ent->brushes; b != NULL; b = b->next )
1153 /* offset brush planes */
1154 for( i = 0; i < b->numsides; i++)
1156 /* get brush side */
1159 /* offset side plane */
1160 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
1162 /* find a new plane */
1163 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1166 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1167 CreateBrushWindings( b );
1170 /* walk patch list */
1171 for( p = ent->patches; p != NULL; p = p->next )
1173 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1174 VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
1181 SetEntityBounds() - ydnar
1182 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1185 void SetEntityBounds( entity_t *e )
1196 /* walk the entity's brushes/patches and determine bounds */
1197 ClearBounds( mins, maxs );
1198 for( b = e->brushes; b; b = b->next )
1200 AddPointToBounds( b->mins, mins, maxs );
1201 AddPointToBounds( b->maxs, mins, maxs );
1203 for( p = e->patches; p; p = p->next )
1205 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1206 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1209 /* try to find explicit min/max key */
1210 value = ValueForKey( e, "min" );
1211 if( value[ 0 ] != '\0' )
1212 GetVectorForKey( e, "min", mins );
1213 value = ValueForKey( e, "max" );
1214 if( value[ 0 ] != '\0' )
1215 GetVectorForKey( e, "max", maxs );
1217 /* store the bounds */
1218 for( b = e->brushes; b; b = b->next )
1220 VectorCopy( mins, b->eMins );
1221 VectorCopy( maxs, b->eMaxs );
1223 for( p = e->patches; p; p = p->next )
1225 VectorCopy( mins, p->eMins );
1226 VectorCopy( maxs, p->eMaxs );
1233 LoadEntityIndexMap() - ydnar
1234 based on LoadAlphaMap() from terrain.c, a little more generic
1237 void LoadEntityIndexMap( entity_t *e )
1239 int i, size, numLayers, w, h;
1240 const char *value, *indexMapFilename, *shader;
1241 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1243 unsigned int *pixels32;
1249 /* this only works with bmodel ents */
1250 if( e->brushes == NULL && e->patches == NULL )
1253 /* determine if there is an index map (support legacy "alphamap" key as well) */
1254 value = ValueForKey( e, "_indexmap" );
1255 if( value[ 0 ] == '\0' )
1256 value = ValueForKey( e, "alphamap" );
1257 if( value[ 0 ] == '\0' )
1259 indexMapFilename = value;
1261 /* get number of layers (support legacy "layers" key as well) */
1262 value = ValueForKey( e, "_layers" );
1263 if( value[ 0 ] == '\0' )
1264 value = ValueForKey( e, "layers" );
1265 if( value[ 0 ] == '\0' )
1267 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1268 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1271 numLayers = atoi( value );
1274 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1275 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1279 /* get base shader name (support legacy "shader" key as well) */
1280 value = ValueForKey( mapEnt, "_shader" );
1281 if( value[ 0 ] == '\0' )
1282 value = ValueForKey( e, "shader" );
1283 if( value[ 0 ] == '\0' )
1285 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1286 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1292 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1294 /* get index map file extension */
1295 ExtractFileExtension( indexMapFilename, ext );
1297 /* handle tga image */
1298 if( !Q_stricmp( ext, "tga" ) )
1301 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1303 /* convert to bytes */
1305 pixels = safe_malloc( size );
1306 for( i = 0; i < size; i++ )
1308 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1309 if( pixels[ i ] >= numLayers )
1310 pixels[ i ] = numLayers - 1;
1313 /* free the 32 bit image */
1319 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1322 //% Sys_Printf( "-------------------------------" );
1324 /* fix up out-of-range values */
1326 for( i = 0; i < size; i++ )
1328 if( pixels[ i ] >= numLayers )
1329 pixels[ i ] = numLayers - 1;
1332 //% if( (i % w) == 0 )
1333 //% Sys_Printf( "\n" );
1334 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1338 //% Sys_Printf( "\n-------------------------------\n" );
1341 /* the index map must be at least 2x2 pixels */
1342 if( w < 2 || h < 2 )
1344 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1345 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1350 /* create a new index map */
1351 im = safe_malloc( sizeof( *im ) );
1352 memset( im, 0, sizeof( *im ) );
1357 im->numLayers = numLayers;
1358 strcpy( im->name, indexMapFilename );
1359 strcpy( im->shader, shader );
1360 im->pixels = pixels;
1362 /* get height offsets */
1363 value = ValueForKey( mapEnt, "_offsets" );
1364 if( value[ 0 ] == '\0' )
1365 value = ValueForKey( e, "offsets" );
1366 if( value[ 0 ] != '\0' )
1368 /* value is a space-seperated set of numbers */
1369 strcpy( offset, value );
1372 /* get each value */
1373 for( i = 0; i < 256 && *search != '\0'; i++ )
1375 space = strstr( search, " " );
1378 im->offsets[ i ] = atof( search );
1385 /* store the index map in every brush/patch in the entity */
1386 for( b = e->brushes; b != NULL; b = b->next )
1388 for( p = e->patches; p != NULL; p = p->next )
1400 parses a single entity out of a map file
1403 static qboolean ParseMapEntity( qboolean onlyLights )
1406 const char *classname, *value;
1407 float lightmapScale;
1408 char shader[ MAX_QPATH ];
1409 shaderInfo_t *celShader = NULL;
1413 int castShadows, recvShadows;
1417 if( !GetToken( qtrue ) )
1420 /* conformance check */
1421 if( strcmp( token, "{" ) )
1423 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1424 "Continuing to process map, but resulting BSP may be invalid.\n",
1425 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1430 if( numEntities >= MAX_MAP_ENTITIES )
1431 Error( "numEntities == MAX_MAP_ENTITIES" );
1434 entitySourceBrushes = 0;
1435 mapEnt = &entities[ numEntities ];
1437 memset( mapEnt, 0, sizeof( *mapEnt ) );
1439 /* ydnar: true entity numbering */
1440 mapEnt->mapEntityNum = numMapEntities;
1446 /* get initial token */
1447 if( !GetToken( qtrue ) )
1449 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1450 "Continuing to process map, but resulting BSP may be invalid.\n" );
1454 if( !strcmp( token, "}" ) )
1457 if( !strcmp( token, "{" ) )
1459 /* parse a brush or patch */
1460 if( !GetToken( qtrue ) )
1464 if( !strcmp( token, "patchDef2" ) )
1467 ParsePatch( onlyLights );
1469 else if( !strcmp( token, "terrainDef" ) )
1472 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1474 else if( !strcmp( token, "brushDef" ) )
1476 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1477 Error( "Old brush format not allowed in new brush format map" );
1478 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1480 /* parse brush primitive */
1481 ParseBrush( onlyLights );
1485 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1486 Error( "New brush format not allowed in old brush format map" );
1487 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1489 /* parse old brush format */
1491 ParseBrush( onlyLights );
1493 entitySourceBrushes++;
1497 /* parse a key / value pair */
1500 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1501 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1503 ep->next = mapEnt->epairs;
1504 mapEnt->epairs = ep;
1509 /* ydnar: get classname */
1510 classname = ValueForKey( mapEnt, "classname" );
1512 /* ydnar: only lights? */
1513 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1519 /* ydnar: determine if this is a func_group */
1520 if( !Q_stricmp( "func_group", classname ) )
1525 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1526 if( funcGroup || mapEnt->mapEntityNum == 0 )
1528 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1529 castShadows = WORLDSPAWN_CAST_SHADOWS;
1530 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1533 /* other entities don't cast any shadows, but recv worldspawn shadows */
1536 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1537 castShadows = ENTITY_CAST_SHADOWS;
1538 recvShadows = ENTITY_RECV_SHADOWS;
1541 /* get explicit shadow flags */
1542 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1544 /* ydnar: get lightmap scaling value for this entity */
1545 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1546 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
1548 /* get lightmap scale from entity */
1549 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1550 if( lightmapScale <= 0.0f )
1551 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1552 if( lightmapScale > 0.0f )
1553 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1556 lightmapScale = 0.0f;
1558 /* ydnar: get cel shader :) for this entity */
1559 value = ValueForKey( mapEnt, "_celshader" );
1560 if( value[ 0 ] == '\0' )
1561 value = ValueForKey( &entities[ 0 ], "_celshader" );
1562 if( value[ 0 ] != '\0' )
1564 sprintf( shader, "textures/%s", value );
1565 celShader = ShaderInfoForShader( shader );
1566 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1571 /* attach stuff to everything in the entity */
1572 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1574 brush->entityNum = mapEnt->mapEntityNum;
1575 brush->castShadows = castShadows;
1576 brush->recvShadows = recvShadows;
1577 brush->lightmapScale = lightmapScale;
1578 brush->celShader = celShader;
1581 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1583 patch->entityNum = mapEnt->mapEntityNum;
1584 patch->castShadows = castShadows;
1585 patch->recvShadows = recvShadows;
1586 patch->lightmapScale = lightmapScale;
1587 patch->celShader = celShader;
1590 /* ydnar: gs mods: set entity bounds */
1591 SetEntityBounds( mapEnt );
1593 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1594 LoadEntityIndexMap( mapEnt );
1596 /* get entity origin and adjust brushes */
1597 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1598 if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
1599 AdjustBrushesForOrigin( mapEnt );
1601 /* group_info entities are just for editor grouping (fixme: leak!) */
1602 if( !Q_stricmp( "group_info", classname ) )
1608 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1611 MoveBrushesToWorld( mapEnt );
1624 loads a map file into a list of entities
1627 void LoadMapFile( char *filename, qboolean onlyLights )
1631 int oldNumEntities, numMapBrushes;
1635 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1636 Sys_Printf( "Loading %s\n", filename );
1639 file = SafeOpenRead( filename );
1642 /* load the map file */
1643 LoadScriptFile( filename, -1 );
1647 oldNumEntities = numEntities;
1652 numMapDrawSurfs = 0;
1654 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1656 /* allocate a very large temporary brush for building the brushes as they are loaded */
1657 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1659 /* parse the map file */
1660 while( ParseMapEntity( onlyLights ) );
1665 /* emit some statistics */
1666 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1670 /* set map bounds */
1671 ClearBounds( mapMins, mapMaxs );
1672 for( b = entities[ 0 ].brushes; b; b = b->next )
1674 AddPointToBounds( b->mins, mapMins, mapMaxs );
1675 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1678 /* get brush counts */
1679 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1680 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1681 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1683 /* emit some statistics */
1684 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1685 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1686 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1687 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1688 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1689 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1690 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1691 Sys_Printf( "%9d areaportals\n", c_areaportals);
1692 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1693 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1694 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1696 /* write bogus map */
1698 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );