1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
41 /* FIXME: remove these vars */
43 /* undefine to make plane finding use linear sort (note: really slow) */
45 #define PLANE_HASHES 8192
47 int planehash[ PLANE_HASHES ];
59 ydnar: replaced with variable epsilon for djbob
62 #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];
99 planehash[hash] = p - mapplanes + 1;
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 AUTOEXPAND_BY_REALLOC(mapplanes, nummapplanes+1, allocatedmapplanes, 1024);
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, vec3_t center )
188 // SnapPlane disabled by LordHavoc because it often messes up collision
189 // brushes made from triangles of embedded models, and it has little effect
190 // on anything else (axial planes are usually derived from snapped points)
192 SnapPlane reenabled by namespace because of multiple reports of
193 q3map2-crashes which were triggered by this patch.
195 // div0: ensure the point "center" stays on the plane (actually, this
196 // rotates the plane around the point center).
197 // if center lies on the plane, it is guaranteed to stay on the plane by
199 vec_t centerDist = DotProduct(normal, center);
200 SnapNormal( normal );
201 *dist += (DotProduct(normal, center) - centerDist);
203 if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
204 *dist = Q_rint( *dist );
211 ydnar: changed to allow a number of test points to be supplied that
212 must be within an epsilon distance of the plane
215 int FindFloatPlane( vec3_t innormal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
224 vec3_t centerofweight;
227 VectorClear(centerofweight);
228 for(i = 0; i < numPoints; ++i)
229 VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
232 VectorCopy(innormal, normal);
233 SnapPlane( normal, &dist, centerofweight );
234 hash = (PLANE_HASHES - 1) & (int) fabs( dist );
236 /* search the border bins as well */
237 for( i = -1; i <= 1; i++ )
239 h = (hash + i) & (PLANE_HASHES - 1);
240 for( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
242 p = &mapplanes[pidx];
244 /* do standard plane compare */
245 if( !PlaneEqual( p, normal, dist ) )
248 /* ydnar: uncomment the following line for old-style plane finding */
249 //% return p - mapplanes;
251 /* ydnar: test supplied points against this plane */
252 for( j = 0; j < numPoints; j++ )
254 d = DotProduct( points[ j ], normal ) - dist;
255 if( fabs( d ) > distanceEpsilon )
259 /* found a matching plane */
261 return p - mapplanes;
265 /* none found, so create a new one */
266 return CreateNewFloatPlane( normal, dist );
277 vec3_t centerofweight;
279 VectorClear(centerofweight);
280 for(i = 0; i < numPoints; ++i)
281 VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
283 VectorCopy(innormal, normal);
284 SnapPlane( normal, &dist, centerofweight );
285 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
287 if( PlaneEqual( p, normal, dist ) )
291 return CreateNewFloatPlane( normal, dist );
300 takes 3 points and finds the plane they lie in
303 int MapPlaneFromPoints( vec3_t *p )
305 vec3_t t1, t2, normal;
309 /* calc plane normal */
310 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
311 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
312 CrossProduct( t1, t2, normal );
313 VectorNormalize( normal, normal );
315 /* calc plane distance */
316 dist = DotProduct( p[ 0 ], normal );
318 /* store the plane */
319 return FindFloatPlane( normal, dist, 3, p );
326 the content flags and compile flags on all sides of a brush should be the same
329 void SetBrushContents( brush_t *b )
331 int contentFlags, compileFlags;
337 /* get initial compile flags from first side */
339 contentFlags = s->contentFlags;
340 compileFlags = s->compileFlags;
341 b->contentShader = s->shaderInfo;
344 /* get the content/compile flags for every side in the brush */
345 for( i = 1; i < b->numsides; i++, s++ )
348 if( s->shaderInfo == NULL )
350 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
353 contentFlags |= s->contentFlags;
354 compileFlags |= s->compileFlags;
357 /* ydnar: getting rid of this stupid warning */
359 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
361 /* check for detail & structural */
362 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
364 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
365 compileFlags &= ~C_DETAIL;
368 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
370 compileFlags &= ~C_DETAIL;
372 /* all translucent brushes that aren't specifically made structural will be detail */
373 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
374 compileFlags |= C_DETAIL;
377 if( compileFlags & C_DETAIL )
389 if( compileFlags & C_TRANSLUCENT )
395 if( compileFlags & C_AREAPORTAL )
398 /* set brush flags */
399 b->contentFlags = contentFlags;
400 b->compileFlags = compileFlags;
407 adds any additional planes necessary to allow the brush being
408 built to be expanded against axial bounding boxes
409 ydnar 2003-01-20: added mrelusive fixes
412 void AddBrushBevels( void )
415 int i, j, k, l, order;
425 // add the axial planes
428 for ( axis = 0; axis < 3; axis++ ) {
429 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
430 // see if the plane is allready present
431 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
433 /* ydnar: testing disabling of mre code */
436 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
441 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
446 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
447 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
452 if ( i == buildBrush->numsides ) {
454 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
455 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
457 memset( s, 0, sizeof( *s ) );
458 buildBrush->numsides++;
459 VectorClear (normal);
464 /* ydnar: adding bevel plane snapping for fewer bsp planes */
466 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
468 dist = buildBrush->maxs[ axis ];
472 /* ydnar: adding bevel plane snapping for fewer bsp planes */
474 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
476 dist = -buildBrush->mins[ axis ];
479 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
480 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
485 // if the plane is not in it canonical order, swap it
487 sidetemp = buildBrush->sides[order];
488 buildBrush->sides[order] = buildBrush->sides[i];
489 buildBrush->sides[i] = sidetemp;
495 // add the edge bevels
497 if ( buildBrush->numsides == 6 ) {
498 return; // pure axial
501 // test the non-axial plane edges
502 for ( i = 6; i < buildBrush->numsides; i++ ) {
503 s = buildBrush->sides + i;
508 for ( j = 0; j < w->numpoints; j++) {
509 k = (j+1)%w->numpoints;
510 VectorSubtract( w->p[j], w->p[k], vec );
511 if ( VectorNormalize( vec, vec ) < 0.5f ) {
515 for ( k = 0; k < 3; k++ ) {
516 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
521 continue; // only test non-axial edges
525 //% Sys_Printf( "-------------\n" );
527 // try the six possible slanted axials from this edge
528 for ( axis = 0; axis < 3; axis++ ) {
529 for ( dir = -1; dir <= 1; dir += 2 ) {
533 CrossProduct( vec, vec2, normal );
534 if ( VectorNormalize( normal, normal ) < 0.5f ) {
537 dist = DotProduct( w->p[j], normal );
539 // if all the points on all the sides are
540 // behind this plane, it is a proper edge bevel
541 for ( k = 0; k < buildBrush->numsides; k++ ) {
543 // if this plane has allready been used, skip it
544 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
548 w2 = buildBrush->sides[k].winding;
553 for ( l = 0; l < w2->numpoints; l++ ) {
554 d = DotProduct( w2->p[l], normal ) - dist;
556 break; // point in front
562 // if some point was at the front
563 if ( l != w2->numpoints ) {
567 // if no points at the back then the winding is on the bevel plane
568 if ( minBack > -0.1f ) {
569 //% Sys_Printf( "On bevel plane\n" );
574 if ( k != buildBrush->numsides ) {
575 continue; // wasn't part of the outer hull
579 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
582 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
583 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
585 s2 = &buildBrush->sides[buildBrush->numsides];
586 buildBrush->numsides++;
587 memset( s2, 0, sizeof( *s2 ) );
589 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
590 s2->contentFlags = buildBrush->sides[0].contentFlags;
603 produces a final brush based on the buildBrush->sides array
604 and links it to the current entity
607 static void MergeOrigin(entity_t *ent, vec3_t origin)
612 /* we have not parsed the brush completely yet... */
613 GetVectorForKey( ent, "origin", ent->origin );
615 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
616 VectorAdd(adjustment, ent->origin, ent->origin);
617 VectorCopy(origin, ent->originbrush_origin);
619 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
620 SetKeyValue(ent, "origin", string);
623 brush_t *FinishBrush( void )
628 /* create windings for sides and bounds for brush */
629 if ( !CreateBrushWindings( buildBrush ) )
632 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
633 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
634 if( buildBrush->compileFlags & C_ORIGIN )
638 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
639 mapEnt->mapEntityNum, entitySourceBrushes );
641 if( numEntities == 1 )
643 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
644 mapEnt->mapEntityNum, entitySourceBrushes );
648 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
649 VectorScale (origin, 0.5, origin);
651 MergeOrigin(&entities[ numEntities - 1 ], origin);
653 /* don't keep this brush */
657 /* determine if the brush is an area portal */
658 if( buildBrush->compileFlags & C_AREAPORTAL )
660 if( numEntities != 1 )
662 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
667 /* add bevel planes */
671 b = CopyBrush( buildBrush );
673 /* set map entity and brush numbering */
674 b->entityNum = mapEnt->mapEntityNum;
675 b->brushNum = entitySourceBrushes;
680 /* link opaque brushes to head of list, translucent brushes to end */
681 if( b->opaque || mapEnt->lastBrush == NULL )
683 b->next = mapEnt->brushes;
685 if( mapEnt->lastBrush == NULL )
686 mapEnt->lastBrush = b;
691 mapEnt->lastBrush->next = b;
692 mapEnt->lastBrush = b;
695 /* link colorMod volume brushes to the entity directly */
696 if( b->contentShader != NULL &&
697 b->contentShader->colorMod != NULL &&
698 b->contentShader->colorMod->type == CM_VOLUME )
700 b->nextColorModBrush = mapEnt->colorModBrushes;
701 mapEnt->colorModBrushes = b;
704 /* return to sender */
711 TextureAxisFromPlane()
712 determines best orthagonal axis to project a texture onto a wall
713 (must be identical in radiant!)
716 vec3_t baseaxis[18] =
718 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
719 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
720 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
721 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
722 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
723 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
726 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
735 for (i=0 ; i<6 ; i++)
737 dot = DotProduct (pln->normal, baseaxis[i*3]);
738 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
745 VectorCopy (baseaxis[bestaxis*3+1], xv);
746 VectorCopy (baseaxis[bestaxis*3+2], yv);
753 creates world-to-texture mapping vecs for crappy quake plane arrangements
756 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
760 vec_t ang, sinv, cosv;
765 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
774 { sinv = 0 ; cosv = 1; }
775 else if (rotate == 90)
776 { sinv = 1 ; cosv = 0; }
777 else if (rotate == 180)
778 { sinv = 0 ; cosv = -1; }
779 else if (rotate == 270)
780 { sinv = -1 ; cosv = 0; }
783 ang = rotate / 180 * Q_PI;
802 for (i=0 ; i<2 ; i++) {
803 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
804 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
809 for (i=0 ; i<2 ; i++)
810 for (j=0 ; j<3 ; j++)
811 mappingVecs[i][j] = vecs[i][j] / scale[i];
813 mappingVecs[0][3] = shift[0];
814 mappingVecs[1][3] = shift[1];
821 parses the sides into buildBrush->sides[], nothing else.
822 no validation, back plane removal, etc.
825 added brush epairs parsing ( ignoring actually )
827 added exclusive brush primitive parsing
829 support for old brush format back in
830 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
833 static void ParseRawBrush( qboolean onlyLights )
836 vec3_t planePoints[ 3 ];
842 char name[ MAX_QPATH ];
843 char shader[ MAX_QPATH ];
848 buildBrush->numsides = 0;
849 buildBrush->detail = qfalse;
852 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
858 if( !GetToken( qtrue ) )
860 if( !strcmp( token, "}" ) )
863 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
864 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
868 if( strcmp( token, "(" ) )
877 /* test side count */
878 if( buildBrush->numsides >= MAX_BUILD_SIDES )
879 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
882 side = &buildBrush->sides[ buildBrush->numsides ];
883 memset( side, 0, sizeof( *side ) );
884 buildBrush->numsides++;
886 /* read the three point plane definition */
887 Parse1DMatrix( 3, planePoints[ 0 ] );
888 Parse1DMatrix( 3, planePoints[ 1 ] );
889 Parse1DMatrix( 3, planePoints[ 2 ] );
891 /* bp: read the texture matrix */
892 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
893 Parse2DMatrix( 2, 3, (float*) side->texMat );
895 /* read shader name */
897 strcpy( name, token );
900 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
903 shift[ 0 ] = atof( token );
905 shift[ 1 ] = atof( token );
907 rotate = atof( token );
909 scale[ 0 ] = atof( token );
911 scale[ 1 ] = atof( token );
914 /* set default flags and values */
915 sprintf( shader, "textures/%s", name );
917 si = &shaderInfo[ 0 ];
919 si = ShaderInfoForShader( shader );
920 side->shaderInfo = si;
921 side->surfaceFlags = si->surfaceFlags;
922 side->contentFlags = si->contentFlags;
923 side->compileFlags = si->compileFlags;
924 side->value = si->value;
926 /* ydnar: gs mods: bias texture shift */
927 if( si->globalTexture == qfalse )
929 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
930 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
934 historically, there are 3 integer values at the end of a brushside line in a .map file.
935 in quake 3, the only thing that mattered was the first of these three values, which
936 was previously the content flags. and only then did a single bit matter, the detail
937 bit. because every game has its own special flags for specifying detail, the
938 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
939 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
940 is stored in compileFlags, as opposed to contentFlags, for multiple-game
944 if( TokenAvailable() )
946 /* get detail bit from map content flags */
948 flags = atoi( token );
949 if( flags & C_DETAIL )
950 side->compileFlags |= C_DETAIL;
954 //% td.flags = atoi( token );
956 //% td.value = atoi( token );
959 /* find the plane number */
960 planenum = MapPlaneFromPoints( planePoints );
961 side->planenum = planenum;
963 /* bp: get the texture mapping for this texturedef / plane combination */
964 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
965 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
969 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
980 RemoveDuplicateBrushPlanes
981 returns false if the brush has a mirrored set of planes,
982 meaning it encloses no volume.
983 also removes planes without any normal
986 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
993 for ( i = 1 ; i < b->numsides ; i++ ) {
995 // check for a degenerate plane
996 if ( sides[i].planenum == -1) {
997 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
999 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1000 sides[k-1] = sides[k];
1007 // check for duplication and mirroring
1008 for ( j = 0 ; j < i ; j++ ) {
1009 if ( sides[i].planenum == sides[j].planenum ) {
1010 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1011 // remove the second duplicate
1012 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1013 sides[k-1] = sides[k];
1020 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1021 // mirror plane, brush is invalid
1022 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1034 parses a brush out of a map file and sets it up
1037 static void ParseBrush( qboolean onlyLights )
1042 /* parse the brush out of the map */
1043 ParseRawBrush( onlyLights );
1045 /* only go this far? */
1049 /* set some defaults */
1050 buildBrush->portalareas[ 0 ] = -1;
1051 buildBrush->portalareas[ 1 ] = -1;
1052 buildBrush->entityNum = numMapEntities - 1;
1053 buildBrush->brushNum = entitySourceBrushes;
1055 /* if there are mirrored planes, the entire brush is invalid */
1056 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1059 /* get the content for the entire brush */
1060 SetBrushContents( buildBrush );
1062 /* allow detail brushes to be removed */
1063 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1065 //% FreeBrush( buildBrush );
1069 /* allow liquid brushes to be removed */
1070 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1072 //% FreeBrush( buildBrush );
1076 /* ydnar: allow hint brushes to be removed */
1077 if( noHint && (buildBrush->compileFlags & C_HINT) )
1079 //% FreeBrush( buildBrush );
1083 /* finish the brush */
1090 MoveBrushesToWorld()
1091 takes all of the brushes from the current entity and
1092 adds them to the world's brush list
1093 (used by func_group)
1096 void AdjustBrushesForOrigin( entity_t *ent );
1097 void MoveBrushesToWorld( entity_t *ent )
1102 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1103 VectorScale(ent->origin, -1, ent->originbrush_origin);
1104 AdjustBrushesForOrigin(ent);
1105 VectorClear(ent->originbrush_origin);
1108 for( b = ent->brushes; b != NULL; b = next )
1110 /* get next brush */
1113 /* link opaque brushes to head of list, translucent brushes to end */
1114 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1116 b->next = entities[ 0 ].brushes;
1117 entities[ 0 ].brushes = b;
1118 if( entities[ 0 ].lastBrush == NULL )
1119 entities[ 0 ].lastBrush = b;
1124 entities[ 0 ].lastBrush->next = b;
1125 entities[ 0 ].lastBrush = b;
1128 ent->brushes = NULL;
1130 /* ydnar: move colormod brushes */
1131 if( ent->colorModBrushes != NULL )
1133 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1135 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1136 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1138 ent->colorModBrushes = NULL;
1142 if( ent->patches != NULL )
1144 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1146 pm->next = entities[ 0 ].patches;
1147 entities[ 0 ].patches = ent->patches;
1149 ent->patches = NULL;
1156 AdjustBrushesForOrigin()
1159 void AdjustBrushesForOrigin( entity_t *ent )
1168 /* walk brush list */
1169 for( b = ent->brushes; b != NULL; b = b->next )
1171 /* offset brush planes */
1172 for( i = 0; i < b->numsides; i++)
1174 /* get brush side */
1177 /* offset side plane */
1178 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1180 /* find a new plane */
1181 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1184 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1185 CreateBrushWindings( b );
1188 /* walk patch list */
1189 for( p = ent->patches; p != NULL; p = p->next )
1191 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1192 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1199 SetEntityBounds() - ydnar
1200 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1203 void SetEntityBounds( entity_t *e )
1214 /* walk the entity's brushes/patches and determine bounds */
1215 ClearBounds( mins, maxs );
1216 for( b = e->brushes; b; b = b->next )
1218 AddPointToBounds( b->mins, mins, maxs );
1219 AddPointToBounds( b->maxs, mins, maxs );
1221 for( p = e->patches; p; p = p->next )
1223 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1224 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1227 /* try to find explicit min/max key */
1228 value = ValueForKey( e, "min" );
1229 if( value[ 0 ] != '\0' )
1230 GetVectorForKey( e, "min", mins );
1231 value = ValueForKey( e, "max" );
1232 if( value[ 0 ] != '\0' )
1233 GetVectorForKey( e, "max", maxs );
1235 /* store the bounds */
1236 for( b = e->brushes; b; b = b->next )
1238 VectorCopy( mins, b->eMins );
1239 VectorCopy( maxs, b->eMaxs );
1241 for( p = e->patches; p; p = p->next )
1243 VectorCopy( mins, p->eMins );
1244 VectorCopy( maxs, p->eMaxs );
1251 LoadEntityIndexMap() - ydnar
1252 based on LoadAlphaMap() from terrain.c, a little more generic
1255 void LoadEntityIndexMap( entity_t *e )
1257 int i, size, numLayers, w, h;
1258 const char *value, *indexMapFilename, *shader;
1259 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1261 unsigned int *pixels32;
1267 /* this only works with bmodel ents */
1268 if( e->brushes == NULL && e->patches == NULL )
1271 /* determine if there is an index map (support legacy "alphamap" key as well) */
1272 value = ValueForKey( e, "_indexmap" );
1273 if( value[ 0 ] == '\0' )
1274 value = ValueForKey( e, "alphamap" );
1275 if( value[ 0 ] == '\0' )
1277 indexMapFilename = value;
1279 /* get number of layers (support legacy "layers" key as well) */
1280 value = ValueForKey( e, "_layers" );
1281 if( value[ 0 ] == '\0' )
1282 value = ValueForKey( e, "layers" );
1283 if( value[ 0 ] == '\0' )
1285 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1286 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1289 numLayers = atoi( value );
1292 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1293 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1297 /* get base shader name (support legacy "shader" key as well) */
1298 value = ValueForKey( mapEnt, "_shader" );
1299 if( value[ 0 ] == '\0' )
1300 value = ValueForKey( e, "shader" );
1301 if( value[ 0 ] == '\0' )
1303 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1304 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1310 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1312 /* get index map file extension */
1313 ExtractFileExtension( indexMapFilename, ext );
1315 /* handle tga image */
1316 if( !Q_stricmp( ext, "tga" ) )
1319 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1321 /* convert to bytes */
1323 pixels = safe_malloc( size );
1324 for( i = 0; i < size; i++ )
1326 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1327 if( pixels[ i ] >= numLayers )
1328 pixels[ i ] = numLayers - 1;
1331 /* free the 32 bit image */
1337 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1340 //% Sys_Printf( "-------------------------------" );
1342 /* fix up out-of-range values */
1344 for( i = 0; i < size; i++ )
1346 if( pixels[ i ] >= numLayers )
1347 pixels[ i ] = numLayers - 1;
1350 //% if( (i % w) == 0 )
1351 //% Sys_Printf( "\n" );
1352 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1356 //% Sys_Printf( "\n-------------------------------\n" );
1359 /* the index map must be at least 2x2 pixels */
1360 if( w < 2 || h < 2 )
1362 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1363 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1368 /* create a new index map */
1369 im = safe_malloc( sizeof( *im ) );
1370 memset( im, 0, sizeof( *im ) );
1375 im->numLayers = numLayers;
1376 strcpy( im->name, indexMapFilename );
1377 strcpy( im->shader, shader );
1378 im->pixels = pixels;
1380 /* get height offsets */
1381 value = ValueForKey( mapEnt, "_offsets" );
1382 if( value[ 0 ] == '\0' )
1383 value = ValueForKey( e, "offsets" );
1384 if( value[ 0 ] != '\0' )
1386 /* value is a space-seperated set of numbers */
1387 strcpy( offset, value );
1390 /* get each value */
1391 for( i = 0; i < 256 && *search != '\0'; i++ )
1393 space = strstr( search, " " );
1396 im->offsets[ i ] = atof( search );
1403 /* store the index map in every brush/patch in the entity */
1404 for( b = e->brushes; b != NULL; b = b->next )
1406 for( p = e->patches; p != NULL; p = p->next )
1418 parses a single entity out of a map file
1421 static qboolean ParseMapEntity( qboolean onlyLights )
1424 const char *classname, *value;
1425 float lightmapScale, shadeAngle;
1426 int lightmapSampleSize;
1427 char shader[ MAX_QPATH ];
1428 shaderInfo_t *celShader = NULL;
1432 int castShadows, recvShadows;
1436 if( !GetToken( qtrue ) )
1439 /* conformance check */
1440 if( strcmp( token, "{" ) )
1442 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1443 "Continuing to process map, but resulting BSP may be invalid.\n",
1444 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1449 if( numEntities >= MAX_MAP_ENTITIES )
1450 Error( "numEntities == MAX_MAP_ENTITIES" );
1453 entitySourceBrushes = 0;
1454 mapEnt = &entities[ numEntities ];
1456 memset( mapEnt, 0, sizeof( *mapEnt ) );
1458 /* ydnar: true entity numbering */
1459 mapEnt->mapEntityNum = numMapEntities;
1465 /* get initial token */
1466 if( !GetToken( qtrue ) )
1468 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1469 "Continuing to process map, but resulting BSP may be invalid.\n" );
1473 if( !strcmp( token, "}" ) )
1476 if( !strcmp( token, "{" ) )
1478 /* parse a brush or patch */
1479 if( !GetToken( qtrue ) )
1483 if( !strcmp( token, "patchDef2" ) )
1486 ParsePatch( onlyLights );
1488 else if( !strcmp( token, "terrainDef" ) )
1491 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1493 else if( !strcmp( token, "brushDef" ) )
1495 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1496 Error( "Old brush format not allowed in new brush format map" );
1497 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1499 /* parse brush primitive */
1500 ParseBrush( onlyLights );
1504 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1505 Error( "New brush format not allowed in old brush format map" );
1506 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1508 /* parse old brush format */
1510 ParseBrush( onlyLights );
1512 entitySourceBrushes++;
1516 /* parse a key / value pair */
1519 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1520 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1522 ep->next = mapEnt->epairs;
1523 mapEnt->epairs = ep;
1528 /* ydnar: get classname */
1529 classname = ValueForKey( mapEnt, "classname" );
1531 /* ydnar: only lights? */
1532 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1538 /* ydnar: determine if this is a func_group */
1539 if( !Q_stricmp( "func_group", classname ) )
1544 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1545 if( funcGroup || mapEnt->mapEntityNum == 0 )
1547 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1548 castShadows = WORLDSPAWN_CAST_SHADOWS;
1549 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1552 /* other entities don't cast any shadows, but recv worldspawn shadows */
1555 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1556 castShadows = ENTITY_CAST_SHADOWS;
1557 recvShadows = ENTITY_RECV_SHADOWS;
1560 /* get explicit shadow flags */
1561 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1563 /* vortex: added _ls key (short name of lightmapscale) */
1564 /* ydnar: get lightmap scaling value for this entity */
1565 lightmapScale = 0.0f;
1566 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1567 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1568 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
1570 /* get lightmap scale from entity */
1571 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1572 if( lightmapScale <= 0.0f )
1573 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1574 if( lightmapScale <= 0.0f )
1575 lightmapScale = FloatForKey( mapEnt, "_ls" );
1576 if( lightmapScale < 0.0f )
1577 lightmapScale = 0.0f;
1578 if( lightmapScale > 0.0f )
1579 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1582 /* ydnar: get cel shader :) for this entity */
1583 value = ValueForKey( mapEnt, "_celshader" );
1584 if( value[ 0 ] == '\0' )
1585 value = ValueForKey( &entities[ 0 ], "_celshader" );
1586 if( value[ 0 ] != '\0' )
1588 if(strcmp(value, "none"))
1590 sprintf( shader, "textures/%s", value );
1591 celShader = ShaderInfoForShader( shader );
1592 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1600 celShader = (*globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL);
1602 /* jal : entity based _shadeangle */
1604 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) )
1605 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1606 /* vortex' aliases */
1607 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) )
1608 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1609 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) )
1610 shadeAngle = FloatForKey( mapEnt, "_sn" );
1611 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) )
1612 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1614 if( shadeAngle < 0.0f )
1617 if( shadeAngle > 0.0f )
1618 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1620 /* jal : entity based _samplesize */
1621 lightmapSampleSize = 0;
1622 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) )
1623 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1624 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) )
1625 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1627 if( lightmapSampleSize < 0 )
1628 lightmapSampleSize = 0;
1630 if( lightmapSampleSize > 0 )
1631 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1633 /* attach stuff to everything in the entity */
1634 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1636 brush->entityNum = mapEnt->mapEntityNum;
1637 brush->castShadows = castShadows;
1638 brush->recvShadows = recvShadows;
1639 brush->lightmapSampleSize = lightmapSampleSize;
1640 brush->lightmapScale = lightmapScale;
1641 brush->celShader = celShader;
1642 brush->shadeAngleDegrees = shadeAngle;
1645 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1647 patch->entityNum = mapEnt->mapEntityNum;
1648 patch->castShadows = castShadows;
1649 patch->recvShadows = recvShadows;
1650 patch->lightmapSampleSize = lightmapSampleSize;
1651 patch->lightmapScale = lightmapScale;
1652 patch->celShader = celShader;
1655 /* ydnar: gs mods: set entity bounds */
1656 SetEntityBounds( mapEnt );
1658 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1659 LoadEntityIndexMap( mapEnt );
1661 /* get entity origin and adjust brushes */
1662 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1663 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1664 AdjustBrushesForOrigin( mapEnt );
1666 /* group_info entities are just for editor grouping (fixme: leak!) */
1667 if( !Q_stricmp( "group_info", classname ) )
1673 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1676 MoveBrushesToWorld( mapEnt );
1689 loads a map file into a list of entities
1692 void LoadMapFile( char *filename, qboolean onlyLights )
1696 int oldNumEntities = 0, numMapBrushes;
1700 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1701 Sys_Printf( "Loading %s\n", filename );
1704 file = SafeOpenRead( filename );
1707 /* load the map file */
1708 LoadScriptFile( filename, -1 );
1712 oldNumEntities = numEntities;
1717 numMapDrawSurfs = 0;
1719 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1721 /* allocate a very large temporary brush for building the brushes as they are loaded */
1722 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1724 /* parse the map file */
1725 while( ParseMapEntity( onlyLights ) );
1730 /* emit some statistics */
1731 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1735 /* set map bounds */
1736 ClearBounds( mapMins, mapMaxs );
1737 for( b = entities[ 0 ].brushes; b; b = b->next )
1739 AddPointToBounds( b->mins, mapMins, mapMaxs );
1740 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1743 /* get brush counts */
1744 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1745 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1746 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1748 /* emit some statistics */
1749 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1750 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1751 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1752 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1753 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1754 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1755 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1756 Sys_Printf( "%9d areaportals\n", c_areaportals);
1757 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1758 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1759 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1761 /* write bogus map */
1763 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );