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 /* we have not parsed the brush completely yet... */
603 GetVectorForKey( ent, "origin", ent->origin );
605 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
606 VectorAdd(adjustment, ent->origin, ent->origin);
607 VectorCopy(origin, ent->originbrush_origin);
610 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
611 SetKeyValue(ent, "origin", string);
614 brush_t *FinishBrush( void )
619 /* create windings for sides and bounds for brush */
620 if ( !CreateBrushWindings( buildBrush ) )
623 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
624 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
625 if( buildBrush->compileFlags & C_ORIGIN )
630 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
631 mapEnt->mapEntityNum, entitySourceBrushes );
633 if( numEntities == 1 )
635 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
636 mapEnt->mapEntityNum, entitySourceBrushes );
640 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
641 VectorScale (origin, 0.5, origin);
643 MergeOrigin(&entities[ numEntities - 1 ], origin);
645 /* don't keep this brush */
649 /* determine if the brush is an area portal */
650 if( buildBrush->compileFlags & C_AREAPORTAL )
652 if( numEntities != 1 )
654 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
659 /* add bevel planes */
663 b = CopyBrush( buildBrush );
665 /* set map entity and brush numbering */
666 b->entityNum = mapEnt->mapEntityNum;
667 b->brushNum = entitySourceBrushes;
672 /* link opaque brushes to head of list, translucent brushes to end */
673 if( b->opaque || mapEnt->lastBrush == NULL )
675 b->next = mapEnt->brushes;
677 if( mapEnt->lastBrush == NULL )
678 mapEnt->lastBrush = b;
683 mapEnt->lastBrush->next = b;
684 mapEnt->lastBrush = b;
687 /* link colorMod volume brushes to the entity directly */
688 if( b->contentShader != NULL &&
689 b->contentShader->colorMod != NULL &&
690 b->contentShader->colorMod->type == CM_VOLUME )
692 b->nextColorModBrush = mapEnt->colorModBrushes;
693 mapEnt->colorModBrushes = b;
696 /* return to sender */
703 TextureAxisFromPlane()
704 determines best orthagonal axis to project a texture onto a wall
705 (must be identical in radiant!)
708 vec3_t baseaxis[18] =
710 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
711 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
712 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
713 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
714 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
715 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
718 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
727 for (i=0 ; i<6 ; i++)
729 dot = DotProduct (pln->normal, baseaxis[i*3]);
730 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
737 VectorCopy (baseaxis[bestaxis*3+1], xv);
738 VectorCopy (baseaxis[bestaxis*3+2], yv);
745 creates world-to-texture mapping vecs for crappy quake plane arrangements
748 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
752 vec_t ang, sinv, cosv;
757 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
766 { sinv = 0 ; cosv = 1; }
767 else if (rotate == 90)
768 { sinv = 1 ; cosv = 0; }
769 else if (rotate == 180)
770 { sinv = 0 ; cosv = -1; }
771 else if (rotate == 270)
772 { sinv = -1 ; cosv = 0; }
775 ang = rotate / 180 * Q_PI;
794 for (i=0 ; i<2 ; i++) {
795 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
796 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
801 for (i=0 ; i<2 ; i++)
802 for (j=0 ; j<3 ; j++)
803 mappingVecs[i][j] = vecs[i][j] / scale[i];
805 mappingVecs[0][3] = shift[0];
806 mappingVecs[1][3] = shift[1];
813 parses the sides into buildBrush->sides[], nothing else.
814 no validation, back plane removal, etc.
817 added brush epairs parsing ( ignoring actually )
819 added exclusive brush primitive parsing
821 support for old brush format back in
822 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
825 static void ParseRawBrush( qboolean onlyLights )
828 vec3_t planePoints[ 3 ];
834 char name[ MAX_QPATH ];
835 char shader[ MAX_QPATH ];
840 buildBrush->numsides = 0;
841 buildBrush->detail = qfalse;
844 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
850 if( !GetToken( qtrue ) )
852 if( !strcmp( token, "}" ) )
855 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
856 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
860 if( strcmp( token, "(" ) )
869 /* test side count */
870 if( buildBrush->numsides >= MAX_BUILD_SIDES )
871 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
874 side = &buildBrush->sides[ buildBrush->numsides ];
875 memset( side, 0, sizeof( *side ) );
876 buildBrush->numsides++;
878 /* read the three point plane definition */
879 Parse1DMatrix( 3, planePoints[ 0 ] );
880 Parse1DMatrix( 3, planePoints[ 1 ] );
881 Parse1DMatrix( 3, planePoints[ 2 ] );
883 /* bp: read the texture matrix */
884 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
885 Parse2DMatrix( 2, 3, (float*) side->texMat );
887 /* read shader name */
889 strcpy( name, token );
892 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
895 shift[ 0 ] = atof( token );
897 shift[ 1 ] = atof( token );
899 rotate = atof( token );
901 scale[ 0 ] = atof( token );
903 scale[ 1 ] = atof( token );
906 /* set default flags and values */
907 sprintf( shader, "textures/%s", name );
909 si = &shaderInfo[ 0 ];
911 si = ShaderInfoForShader( shader );
912 side->shaderInfo = si;
913 side->surfaceFlags = si->surfaceFlags;
914 side->contentFlags = si->contentFlags;
915 side->compileFlags = si->compileFlags;
916 side->value = si->value;
918 /* ydnar: gs mods: bias texture shift */
919 if( si->globalTexture == qfalse )
921 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
922 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
926 historically, there are 3 integer values at the end of a brushside line in a .map file.
927 in quake 3, the only thing that mattered was the first of these three values, which
928 was previously the content flags. and only then did a single bit matter, the detail
929 bit. because every game has its own special flags for specifying detail, the
930 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
931 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
932 is stored in compileFlags, as opposed to contentFlags, for multiple-game
936 if( TokenAvailable() )
938 /* get detail bit from map content flags */
940 flags = atoi( token );
941 if( flags & C_DETAIL )
942 side->compileFlags |= C_DETAIL;
946 //% td.flags = atoi( token );
948 //% td.value = atoi( token );
951 /* find the plane number */
952 planenum = MapPlaneFromPoints( planePoints );
953 side->planenum = planenum;
955 /* bp: get the texture mapping for this texturedef / plane combination */
956 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
957 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
961 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
972 RemoveDuplicateBrushPlanes
973 returns false if the brush has a mirrored set of planes,
974 meaning it encloses no volume.
975 also removes planes without any normal
978 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
985 for ( i = 1 ; i < b->numsides ; i++ ) {
987 // check for a degenerate plane
988 if ( sides[i].planenum == -1) {
989 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
991 for ( k = i + 1 ; k < b->numsides ; k++ ) {
992 sides[k-1] = sides[k];
999 // check for duplication and mirroring
1000 for ( j = 0 ; j < i ; j++ ) {
1001 if ( sides[i].planenum == sides[j].planenum ) {
1002 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1003 // remove the second duplicate
1004 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1005 sides[k-1] = sides[k];
1012 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1013 // mirror plane, brush is invalid
1014 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1026 parses a brush out of a map file and sets it up
1029 static void ParseBrush( qboolean onlyLights )
1034 /* parse the brush out of the map */
1035 ParseRawBrush( onlyLights );
1037 /* only go this far? */
1041 /* set some defaults */
1042 buildBrush->portalareas[ 0 ] = -1;
1043 buildBrush->portalareas[ 1 ] = -1;
1044 buildBrush->entityNum = numMapEntities - 1;
1045 buildBrush->brushNum = entitySourceBrushes;
1047 /* if there are mirrored planes, the entire brush is invalid */
1048 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1051 /* get the content for the entire brush */
1052 SetBrushContents( buildBrush );
1054 /* allow detail brushes to be removed */
1055 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1057 //% FreeBrush( buildBrush );
1061 /* allow liquid brushes to be removed */
1062 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1064 //% FreeBrush( buildBrush );
1068 /* ydnar: allow hint brushes to be removed */
1069 if( noHint && (buildBrush->compileFlags & C_HINT) )
1071 //% FreeBrush( buildBrush );
1075 /* finish the brush */
1082 MoveBrushesToWorld()
1083 takes all of the brushes from the current entity and
1084 adds them to the world's brush list
1085 (used by func_group)
1088 void AdjustBrushesForOrigin( entity_t *ent );
1089 void MoveBrushesToWorld( entity_t *ent )
1094 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1095 VectorScale(ent->origin, -1, ent->originbrush_origin);
1096 Sys_Printf("func_group: adjusting by %f %f %f\n", ent->originbrush_origin[0], ent->originbrush_origin[1], ent->originbrush_origin[2]);
1097 AdjustBrushesForOrigin(ent);
1098 VectorClear(ent->originbrush_origin);
1101 for( b = ent->brushes; b != NULL; b = next )
1103 /* get next brush */
1106 /* link opaque brushes to head of list, translucent brushes to end */
1107 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1109 b->next = entities[ 0 ].brushes;
1110 entities[ 0 ].brushes = b;
1111 if( entities[ 0 ].lastBrush == NULL )
1112 entities[ 0 ].lastBrush = b;
1117 entities[ 0 ].lastBrush->next = b;
1118 entities[ 0 ].lastBrush = b;
1121 ent->brushes = NULL;
1123 /* ydnar: move colormod brushes */
1124 if( ent->colorModBrushes != NULL )
1126 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1128 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1129 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1131 ent->colorModBrushes = NULL;
1135 if( ent->patches != NULL )
1137 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1139 pm->next = entities[ 0 ].patches;
1140 entities[ 0 ].patches = ent->patches;
1142 ent->patches = NULL;
1149 AdjustBrushesForOrigin()
1152 void AdjustBrushesForOrigin( entity_t *ent )
1161 Sys_Printf("origin: adjusting by %f %f %f\n", ent->originbrush_origin[0], ent->originbrush_origin[1], ent->originbrush_origin[2]);
1163 /* walk brush list */
1164 for( b = ent->brushes; b != NULL; b = b->next )
1166 /* offset brush planes */
1167 for( i = 0; i < b->numsides; i++)
1169 /* get brush side */
1172 /* offset side plane */
1173 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1175 /* find a new plane */
1176 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1179 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1180 CreateBrushWindings( b );
1183 /* walk patch list */
1184 for( p = ent->patches; p != NULL; p = p->next )
1186 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1187 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1194 SetEntityBounds() - ydnar
1195 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1198 void SetEntityBounds( entity_t *e )
1209 /* walk the entity's brushes/patches and determine bounds */
1210 ClearBounds( mins, maxs );
1211 for( b = e->brushes; b; b = b->next )
1213 AddPointToBounds( b->mins, mins, maxs );
1214 AddPointToBounds( b->maxs, mins, maxs );
1216 for( p = e->patches; p; p = p->next )
1218 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1219 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1222 /* try to find explicit min/max key */
1223 value = ValueForKey( e, "min" );
1224 if( value[ 0 ] != '\0' )
1225 GetVectorForKey( e, "min", mins );
1226 value = ValueForKey( e, "max" );
1227 if( value[ 0 ] != '\0' )
1228 GetVectorForKey( e, "max", maxs );
1230 /* store the bounds */
1231 for( b = e->brushes; b; b = b->next )
1233 VectorCopy( mins, b->eMins );
1234 VectorCopy( maxs, b->eMaxs );
1236 for( p = e->patches; p; p = p->next )
1238 VectorCopy( mins, p->eMins );
1239 VectorCopy( maxs, p->eMaxs );
1246 LoadEntityIndexMap() - ydnar
1247 based on LoadAlphaMap() from terrain.c, a little more generic
1250 void LoadEntityIndexMap( entity_t *e )
1252 int i, size, numLayers, w, h;
1253 const char *value, *indexMapFilename, *shader;
1254 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1256 unsigned int *pixels32;
1262 /* this only works with bmodel ents */
1263 if( e->brushes == NULL && e->patches == NULL )
1266 /* determine if there is an index map (support legacy "alphamap" key as well) */
1267 value = ValueForKey( e, "_indexmap" );
1268 if( value[ 0 ] == '\0' )
1269 value = ValueForKey( e, "alphamap" );
1270 if( value[ 0 ] == '\0' )
1272 indexMapFilename = value;
1274 /* get number of layers (support legacy "layers" key as well) */
1275 value = ValueForKey( e, "_layers" );
1276 if( value[ 0 ] == '\0' )
1277 value = ValueForKey( e, "layers" );
1278 if( value[ 0 ] == '\0' )
1280 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1281 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1284 numLayers = atoi( value );
1287 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1288 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1292 /* get base shader name (support legacy "shader" key as well) */
1293 value = ValueForKey( mapEnt, "_shader" );
1294 if( value[ 0 ] == '\0' )
1295 value = ValueForKey( e, "shader" );
1296 if( value[ 0 ] == '\0' )
1298 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1299 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1305 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1307 /* get index map file extension */
1308 ExtractFileExtension( indexMapFilename, ext );
1310 /* handle tga image */
1311 if( !Q_stricmp( ext, "tga" ) )
1314 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1316 /* convert to bytes */
1318 pixels = safe_malloc( size );
1319 for( i = 0; i < size; i++ )
1321 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1322 if( pixels[ i ] >= numLayers )
1323 pixels[ i ] = numLayers - 1;
1326 /* free the 32 bit image */
1332 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1335 //% Sys_Printf( "-------------------------------" );
1337 /* fix up out-of-range values */
1339 for( i = 0; i < size; i++ )
1341 if( pixels[ i ] >= numLayers )
1342 pixels[ i ] = numLayers - 1;
1345 //% if( (i % w) == 0 )
1346 //% Sys_Printf( "\n" );
1347 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1351 //% Sys_Printf( "\n-------------------------------\n" );
1354 /* the index map must be at least 2x2 pixels */
1355 if( w < 2 || h < 2 )
1357 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1358 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1363 /* create a new index map */
1364 im = safe_malloc( sizeof( *im ) );
1365 memset( im, 0, sizeof( *im ) );
1370 im->numLayers = numLayers;
1371 strcpy( im->name, indexMapFilename );
1372 strcpy( im->shader, shader );
1373 im->pixels = pixels;
1375 /* get height offsets */
1376 value = ValueForKey( mapEnt, "_offsets" );
1377 if( value[ 0 ] == '\0' )
1378 value = ValueForKey( e, "offsets" );
1379 if( value[ 0 ] != '\0' )
1381 /* value is a space-seperated set of numbers */
1382 strcpy( offset, value );
1385 /* get each value */
1386 for( i = 0; i < 256 && *search != '\0'; i++ )
1388 space = strstr( search, " " );
1391 im->offsets[ i ] = atof( search );
1398 /* store the index map in every brush/patch in the entity */
1399 for( b = e->brushes; b != NULL; b = b->next )
1401 for( p = e->patches; p != NULL; p = p->next )
1413 parses a single entity out of a map file
1416 static qboolean ParseMapEntity( qboolean onlyLights )
1419 const char *classname, *value;
1420 float lightmapScale;
1421 char shader[ MAX_QPATH ];
1422 shaderInfo_t *celShader = NULL;
1426 int castShadows, recvShadows;
1430 if( !GetToken( qtrue ) )
1433 /* conformance check */
1434 if( strcmp( token, "{" ) )
1436 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1437 "Continuing to process map, but resulting BSP may be invalid.\n",
1438 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1443 if( numEntities >= MAX_MAP_ENTITIES )
1444 Error( "numEntities == MAX_MAP_ENTITIES" );
1447 entitySourceBrushes = 0;
1448 mapEnt = &entities[ numEntities ];
1450 memset( mapEnt, 0, sizeof( *mapEnt ) );
1452 /* ydnar: true entity numbering */
1453 mapEnt->mapEntityNum = numMapEntities;
1459 /* get initial token */
1460 if( !GetToken( qtrue ) )
1462 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1463 "Continuing to process map, but resulting BSP may be invalid.\n" );
1467 if( !strcmp( token, "}" ) )
1470 if( !strcmp( token, "{" ) )
1472 /* parse a brush or patch */
1473 if( !GetToken( qtrue ) )
1477 if( !strcmp( token, "patchDef2" ) )
1480 ParsePatch( onlyLights );
1482 else if( !strcmp( token, "terrainDef" ) )
1485 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1487 else if( !strcmp( token, "brushDef" ) )
1489 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1490 Error( "Old brush format not allowed in new brush format map" );
1491 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1493 /* parse brush primitive */
1494 ParseBrush( onlyLights );
1498 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1499 Error( "New brush format not allowed in old brush format map" );
1500 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1502 /* parse old brush format */
1504 ParseBrush( onlyLights );
1506 entitySourceBrushes++;
1510 /* parse a key / value pair */
1513 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1514 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1516 ep->next = mapEnt->epairs;
1517 mapEnt->epairs = ep;
1522 /* ydnar: get classname */
1523 classname = ValueForKey( mapEnt, "classname" );
1525 /* ydnar: only lights? */
1526 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1532 /* ydnar: determine if this is a func_group */
1533 if( !Q_stricmp( "func_group", classname ) )
1538 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1539 if( funcGroup || mapEnt->mapEntityNum == 0 )
1541 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1542 castShadows = WORLDSPAWN_CAST_SHADOWS;
1543 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1546 /* other entities don't cast any shadows, but recv worldspawn shadows */
1549 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1550 castShadows = ENTITY_CAST_SHADOWS;
1551 recvShadows = ENTITY_RECV_SHADOWS;
1554 /* get explicit shadow flags */
1555 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1557 /* ydnar: get lightmap scaling value for this entity */
1558 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1559 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
1561 /* get lightmap scale from entity */
1562 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1563 if( lightmapScale <= 0.0f )
1564 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1565 if( lightmapScale > 0.0f )
1566 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1569 lightmapScale = 0.0f;
1571 /* ydnar: get cel shader :) for this entity */
1572 value = ValueForKey( mapEnt, "_celshader" );
1573 if( value[ 0 ] == '\0' )
1574 value = ValueForKey( &entities[ 0 ], "_celshader" );
1575 if( value[ 0 ] != '\0' )
1577 sprintf( shader, "textures/%s", value );
1578 celShader = ShaderInfoForShader( shader );
1579 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1584 /* attach stuff to everything in the entity */
1585 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1587 brush->entityNum = mapEnt->mapEntityNum;
1588 brush->castShadows = castShadows;
1589 brush->recvShadows = recvShadows;
1590 brush->lightmapScale = lightmapScale;
1591 brush->celShader = celShader;
1594 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1596 patch->entityNum = mapEnt->mapEntityNum;
1597 patch->castShadows = castShadows;
1598 patch->recvShadows = recvShadows;
1599 patch->lightmapScale = lightmapScale;
1600 patch->celShader = celShader;
1603 /* ydnar: gs mods: set entity bounds */
1604 SetEntityBounds( mapEnt );
1606 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1607 LoadEntityIndexMap( mapEnt );
1609 /* get entity origin and adjust brushes */
1610 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1611 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1612 AdjustBrushesForOrigin( mapEnt );
1614 /* group_info entities are just for editor grouping (fixme: leak!) */
1615 if( !Q_stricmp( "group_info", classname ) )
1621 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1624 MoveBrushesToWorld( mapEnt );
1637 loads a map file into a list of entities
1640 void LoadMapFile( char *filename, qboolean onlyLights )
1644 int oldNumEntities, numMapBrushes;
1648 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1649 Sys_Printf( "Loading %s\n", filename );
1652 file = SafeOpenRead( filename );
1655 /* load the map file */
1656 LoadScriptFile( filename, -1 );
1660 oldNumEntities = numEntities;
1665 numMapDrawSurfs = 0;
1667 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1669 /* allocate a very large temporary brush for building the brushes as they are loaded */
1670 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1672 /* parse the map file */
1673 while( ParseMapEntity( onlyLights ) );
1678 /* emit some statistics */
1679 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1683 /* set map bounds */
1684 ClearBounds( mapMins, mapMaxs );
1685 for( b = entities[ 0 ].brushes; b; b = b->next )
1687 AddPointToBounds( b->mins, mapMins, mapMaxs );
1688 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1691 /* get brush counts */
1692 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1693 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1694 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1696 /* emit some statistics */
1697 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1698 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1699 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1700 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1701 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1702 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1703 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1704 Sys_Printf( "%9d areaportals\n", c_areaportals);
1705 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1706 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1707 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1709 /* write bogus map */
1711 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );