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 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( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
240 p = &mapplanes[pidx];
242 /* do standard plane compare */
243 if( !PlaneEqual( p, normal, dist ) )
246 /* ydnar: uncomment the following line for old-style plane finding */
247 //% return p - mapplanes;
249 /* ydnar: test supplied points against this plane */
250 for( j = 0; j < numPoints; j++ )
252 d = DotProduct( points[ j ], normal ) - dist;
253 if( fabs( d ) > distanceEpsilon )
257 /* found a matching plane */
259 return p - mapplanes;
263 /* none found, so create a new one */
264 return CreateNewFloatPlane( normal, dist );
274 vec3_t centerofweight;
276 VectorClear(centerofweight);
277 for(i = 0; i < numPoints; ++i)
278 VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
280 SnapPlane( normal, &dist, centerofweight );
281 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
283 if( PlaneEqual( p, normal, dist ) )
287 return CreateNewFloatPlane( normal, dist );
296 takes 3 points and finds the plane they lie in
299 int MapPlaneFromPoints( vec3_t *p )
301 vec3_t t1, t2, normal;
305 /* calc plane normal */
306 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
307 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
308 CrossProduct( t1, t2, normal );
309 VectorNormalize( normal, normal );
311 /* calc plane distance */
312 dist = DotProduct( p[ 0 ], normal );
314 /* store the plane */
315 return FindFloatPlane( normal, dist, 3, p );
322 the content flags and compile flags on all sides of a brush should be the same
325 void SetBrushContents( brush_t *b )
327 int contentFlags, compileFlags;
333 /* get initial compile flags from first side */
335 contentFlags = s->contentFlags;
336 compileFlags = s->compileFlags;
337 b->contentShader = s->shaderInfo;
340 /* get the content/compile flags for every side in the brush */
341 for( i = 1; i < b->numsides; i++, s++ )
344 if( s->shaderInfo == NULL )
346 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
350 /* ydnar: getting rid of this stupid warning */
352 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
354 /* check for detail & structural */
355 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
357 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
358 compileFlags &= ~C_DETAIL;
361 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
363 compileFlags &= ~C_DETAIL;
365 /* all translucent brushes that aren't specifically made structural will be detail */
366 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
367 compileFlags |= C_DETAIL;
370 if( compileFlags & C_DETAIL )
382 if( compileFlags & C_TRANSLUCENT )
388 if( compileFlags & C_AREAPORTAL )
391 /* set brush flags */
392 b->contentFlags = contentFlags;
393 b->compileFlags = compileFlags;
400 adds any additional planes necessary to allow the brush being
401 built to be expanded against axial bounding boxes
402 ydnar 2003-01-20: added mrelusive fixes
405 void AddBrushBevels( void )
408 int i, j, k, l, order;
418 // add the axial planes
421 for ( axis = 0; axis < 3; axis++ ) {
422 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
423 // see if the plane is allready present
424 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
426 /* ydnar: testing disabling of mre code */
429 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
434 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
439 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
440 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
445 if ( i == buildBrush->numsides ) {
447 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
448 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
450 memset( s, 0, sizeof( *s ) );
451 buildBrush->numsides++;
452 VectorClear (normal);
457 /* ydnar: adding bevel plane snapping for fewer bsp planes */
459 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
461 dist = buildBrush->maxs[ axis ];
465 /* ydnar: adding bevel plane snapping for fewer bsp planes */
467 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
469 dist = -buildBrush->mins[ axis ];
472 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
473 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
478 // if the plane is not in it canonical order, swap it
480 sidetemp = buildBrush->sides[order];
481 buildBrush->sides[order] = buildBrush->sides[i];
482 buildBrush->sides[i] = sidetemp;
488 // add the edge bevels
490 if ( buildBrush->numsides == 6 ) {
491 return; // pure axial
494 // test the non-axial plane edges
495 for ( i = 6; i < buildBrush->numsides; i++ ) {
496 s = buildBrush->sides + i;
501 for ( j = 0; j < w->numpoints; j++) {
502 k = (j+1)%w->numpoints;
503 VectorSubtract( w->p[j], w->p[k], vec );
504 if ( VectorNormalize( vec, vec ) < 0.5f ) {
508 for ( k = 0; k < 3; k++ ) {
509 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
514 continue; // only test non-axial edges
518 //% Sys_Printf( "-------------\n" );
520 // try the six possible slanted axials from this edge
521 for ( axis = 0; axis < 3; axis++ ) {
522 for ( dir = -1; dir <= 1; dir += 2 ) {
526 CrossProduct( vec, vec2, normal );
527 if ( VectorNormalize( normal, normal ) < 0.5f ) {
530 dist = DotProduct( w->p[j], normal );
532 // if all the points on all the sides are
533 // behind this plane, it is a proper edge bevel
534 for ( k = 0; k < buildBrush->numsides; k++ ) {
536 // if this plane has allready been used, skip it
537 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
541 w2 = buildBrush->sides[k].winding;
546 for ( l = 0; l < w2->numpoints; l++ ) {
547 d = DotProduct( w2->p[l], normal ) - dist;
549 break; // point in front
555 // if some point was at the front
556 if ( l != w2->numpoints ) {
560 // if no points at the back then the winding is on the bevel plane
561 if ( minBack > -0.1f ) {
562 //% Sys_Printf( "On bevel plane\n" );
567 if ( k != buildBrush->numsides ) {
568 continue; // wasn't part of the outer hull
572 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
575 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
576 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
578 s2 = &buildBrush->sides[buildBrush->numsides];
579 buildBrush->numsides++;
580 memset( s2, 0, sizeof( *s2 ) );
582 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
583 s2->contentFlags = buildBrush->sides[0].contentFlags;
596 produces a final brush based on the buildBrush->sides array
597 and links it to the current entity
600 static void MergeOrigin(entity_t *ent, vec3_t origin)
605 /* we have not parsed the brush completely yet... */
606 GetVectorForKey( ent, "origin", ent->origin );
608 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
609 VectorAdd(adjustment, ent->origin, ent->origin);
610 VectorCopy(origin, ent->originbrush_origin);
612 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
613 SetKeyValue(ent, "origin", string);
616 brush_t *FinishBrush( void )
621 /* create windings for sides and bounds for brush */
622 if ( !CreateBrushWindings( buildBrush ) )
625 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
626 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
627 if( buildBrush->compileFlags & C_ORIGIN )
631 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
632 mapEnt->mapEntityNum, entitySourceBrushes );
634 if( numEntities == 1 )
636 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
637 mapEnt->mapEntityNum, entitySourceBrushes );
641 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
642 VectorScale (origin, 0.5, origin);
644 MergeOrigin(&entities[ numEntities - 1 ], origin);
646 /* don't keep this brush */
650 /* determine if the brush is an area portal */
651 if( buildBrush->compileFlags & C_AREAPORTAL )
653 if( numEntities != 1 )
655 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
660 /* add bevel planes */
664 b = CopyBrush( buildBrush );
666 /* set map entity and brush numbering */
667 b->entityNum = mapEnt->mapEntityNum;
668 b->brushNum = entitySourceBrushes;
673 /* link opaque brushes to head of list, translucent brushes to end */
674 if( b->opaque || mapEnt->lastBrush == NULL )
676 b->next = mapEnt->brushes;
678 if( mapEnt->lastBrush == NULL )
679 mapEnt->lastBrush = b;
684 mapEnt->lastBrush->next = b;
685 mapEnt->lastBrush = b;
688 /* link colorMod volume brushes to the entity directly */
689 if( b->contentShader != NULL &&
690 b->contentShader->colorMod != NULL &&
691 b->contentShader->colorMod->type == CM_VOLUME )
693 b->nextColorModBrush = mapEnt->colorModBrushes;
694 mapEnt->colorModBrushes = b;
697 /* return to sender */
704 TextureAxisFromPlane()
705 determines best orthagonal axis to project a texture onto a wall
706 (must be identical in radiant!)
709 vec3_t baseaxis[18] =
711 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
712 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
713 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
714 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
715 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
716 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
719 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
728 for (i=0 ; i<6 ; i++)
730 dot = DotProduct (pln->normal, baseaxis[i*3]);
731 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
738 VectorCopy (baseaxis[bestaxis*3+1], xv);
739 VectorCopy (baseaxis[bestaxis*3+2], yv);
746 creates world-to-texture mapping vecs for crappy quake plane arrangements
749 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
753 vec_t ang, sinv, cosv;
758 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
767 { sinv = 0 ; cosv = 1; }
768 else if (rotate == 90)
769 { sinv = 1 ; cosv = 0; }
770 else if (rotate == 180)
771 { sinv = 0 ; cosv = -1; }
772 else if (rotate == 270)
773 { sinv = -1 ; cosv = 0; }
776 ang = rotate / 180 * Q_PI;
795 for (i=0 ; i<2 ; i++) {
796 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
797 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
802 for (i=0 ; i<2 ; i++)
803 for (j=0 ; j<3 ; j++)
804 mappingVecs[i][j] = vecs[i][j] / scale[i];
806 mappingVecs[0][3] = shift[0];
807 mappingVecs[1][3] = shift[1];
814 parses the sides into buildBrush->sides[], nothing else.
815 no validation, back plane removal, etc.
818 added brush epairs parsing ( ignoring actually )
820 added exclusive brush primitive parsing
822 support for old brush format back in
823 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
826 static void ParseRawBrush( qboolean onlyLights )
829 vec3_t planePoints[ 3 ];
835 char name[ MAX_QPATH ];
836 char shader[ MAX_QPATH ];
841 buildBrush->numsides = 0;
842 buildBrush->detail = qfalse;
845 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
851 if( !GetToken( qtrue ) )
853 if( !strcmp( token, "}" ) )
856 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
857 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
861 if( strcmp( token, "(" ) )
870 /* test side count */
871 if( buildBrush->numsides >= MAX_BUILD_SIDES )
872 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
875 side = &buildBrush->sides[ buildBrush->numsides ];
876 memset( side, 0, sizeof( *side ) );
877 buildBrush->numsides++;
879 /* read the three point plane definition */
880 Parse1DMatrix( 3, planePoints[ 0 ] );
881 Parse1DMatrix( 3, planePoints[ 1 ] );
882 Parse1DMatrix( 3, planePoints[ 2 ] );
884 /* bp: read the texture matrix */
885 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
886 Parse2DMatrix( 2, 3, (float*) side->texMat );
888 /* read shader name */
890 strcpy( name, token );
893 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
896 shift[ 0 ] = atof( token );
898 shift[ 1 ] = atof( token );
900 rotate = atof( token );
902 scale[ 0 ] = atof( token );
904 scale[ 1 ] = atof( token );
907 /* set default flags and values */
908 sprintf( shader, "textures/%s", name );
910 si = &shaderInfo[ 0 ];
912 si = ShaderInfoForShader( shader );
913 side->shaderInfo = si;
914 side->surfaceFlags = si->surfaceFlags;
915 side->contentFlags = si->contentFlags;
916 side->compileFlags = si->compileFlags;
917 side->value = si->value;
919 /* ydnar: gs mods: bias texture shift */
920 if( si->globalTexture == qfalse )
922 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
923 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
927 historically, there are 3 integer values at the end of a brushside line in a .map file.
928 in quake 3, the only thing that mattered was the first of these three values, which
929 was previously the content flags. and only then did a single bit matter, the detail
930 bit. because every game has its own special flags for specifying detail, the
931 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
932 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
933 is stored in compileFlags, as opposed to contentFlags, for multiple-game
937 if( TokenAvailable() )
939 /* get detail bit from map content flags */
941 flags = atoi( token );
942 if( flags & C_DETAIL )
943 side->compileFlags |= C_DETAIL;
947 //% td.flags = atoi( token );
949 //% td.value = atoi( token );
952 /* find the plane number */
953 planenum = MapPlaneFromPoints( planePoints );
954 side->planenum = planenum;
956 /* bp: get the texture mapping for this texturedef / plane combination */
957 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
958 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
962 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
973 RemoveDuplicateBrushPlanes
974 returns false if the brush has a mirrored set of planes,
975 meaning it encloses no volume.
976 also removes planes without any normal
979 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
986 for ( i = 1 ; i < b->numsides ; i++ ) {
988 // check for a degenerate plane
989 if ( sides[i].planenum == -1) {
990 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
992 for ( k = i + 1 ; k < b->numsides ; k++ ) {
993 sides[k-1] = sides[k];
1000 // check for duplication and mirroring
1001 for ( j = 0 ; j < i ; j++ ) {
1002 if ( sides[i].planenum == sides[j].planenum ) {
1003 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1004 // remove the second duplicate
1005 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1006 sides[k-1] = sides[k];
1013 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1014 // mirror plane, brush is invalid
1015 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1027 parses a brush out of a map file and sets it up
1030 static void ParseBrush( qboolean onlyLights )
1035 /* parse the brush out of the map */
1036 ParseRawBrush( onlyLights );
1038 /* only go this far? */
1042 /* set some defaults */
1043 buildBrush->portalareas[ 0 ] = -1;
1044 buildBrush->portalareas[ 1 ] = -1;
1045 buildBrush->entityNum = numMapEntities - 1;
1046 buildBrush->brushNum = entitySourceBrushes;
1048 /* if there are mirrored planes, the entire brush is invalid */
1049 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1052 /* get the content for the entire brush */
1053 SetBrushContents( buildBrush );
1055 /* allow detail brushes to be removed */
1056 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1058 //% FreeBrush( buildBrush );
1062 /* allow liquid brushes to be removed */
1063 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1065 //% FreeBrush( buildBrush );
1069 /* ydnar: allow hint brushes to be removed */
1070 if( noHint && (buildBrush->compileFlags & C_HINT) )
1072 //% FreeBrush( buildBrush );
1076 /* finish the brush */
1083 MoveBrushesToWorld()
1084 takes all of the brushes from the current entity and
1085 adds them to the world's brush list
1086 (used by func_group)
1089 void AdjustBrushesForOrigin( entity_t *ent );
1090 void MoveBrushesToWorld( entity_t *ent )
1095 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1096 VectorScale(ent->origin, -1, ent->originbrush_origin);
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 /* walk brush list */
1162 for( b = ent->brushes; b != NULL; b = b->next )
1164 /* offset brush planes */
1165 for( i = 0; i < b->numsides; i++)
1167 /* get brush side */
1170 /* offset side plane */
1171 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1173 /* find a new plane */
1174 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1177 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1178 CreateBrushWindings( b );
1181 /* walk patch list */
1182 for( p = ent->patches; p != NULL; p = p->next )
1184 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1185 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1192 SetEntityBounds() - ydnar
1193 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1196 void SetEntityBounds( entity_t *e )
1207 /* walk the entity's brushes/patches and determine bounds */
1208 ClearBounds( mins, maxs );
1209 for( b = e->brushes; b; b = b->next )
1211 AddPointToBounds( b->mins, mins, maxs );
1212 AddPointToBounds( b->maxs, mins, maxs );
1214 for( p = e->patches; p; p = p->next )
1216 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1217 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1220 /* try to find explicit min/max key */
1221 value = ValueForKey( e, "min" );
1222 if( value[ 0 ] != '\0' )
1223 GetVectorForKey( e, "min", mins );
1224 value = ValueForKey( e, "max" );
1225 if( value[ 0 ] != '\0' )
1226 GetVectorForKey( e, "max", maxs );
1228 /* store the bounds */
1229 for( b = e->brushes; b; b = b->next )
1231 VectorCopy( mins, b->eMins );
1232 VectorCopy( maxs, b->eMaxs );
1234 for( p = e->patches; p; p = p->next )
1236 VectorCopy( mins, p->eMins );
1237 VectorCopy( maxs, p->eMaxs );
1244 LoadEntityIndexMap() - ydnar
1245 based on LoadAlphaMap() from terrain.c, a little more generic
1248 void LoadEntityIndexMap( entity_t *e )
1250 int i, size, numLayers, w, h;
1251 const char *value, *indexMapFilename, *shader;
1252 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1254 unsigned int *pixels32;
1260 /* this only works with bmodel ents */
1261 if( e->brushes == NULL && e->patches == NULL )
1264 /* determine if there is an index map (support legacy "alphamap" key as well) */
1265 value = ValueForKey( e, "_indexmap" );
1266 if( value[ 0 ] == '\0' )
1267 value = ValueForKey( e, "alphamap" );
1268 if( value[ 0 ] == '\0' )
1270 indexMapFilename = value;
1272 /* get number of layers (support legacy "layers" key as well) */
1273 value = ValueForKey( e, "_layers" );
1274 if( value[ 0 ] == '\0' )
1275 value = ValueForKey( e, "layers" );
1276 if( value[ 0 ] == '\0' )
1278 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1279 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1282 numLayers = atoi( value );
1285 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1286 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1290 /* get base shader name (support legacy "shader" key as well) */
1291 value = ValueForKey( mapEnt, "_shader" );
1292 if( value[ 0 ] == '\0' )
1293 value = ValueForKey( e, "shader" );
1294 if( value[ 0 ] == '\0' )
1296 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1297 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1303 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1305 /* get index map file extension */
1306 ExtractFileExtension( indexMapFilename, ext );
1308 /* handle tga image */
1309 if( !Q_stricmp( ext, "tga" ) )
1312 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1314 /* convert to bytes */
1316 pixels = safe_malloc( size );
1317 for( i = 0; i < size; i++ )
1319 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1320 if( pixels[ i ] >= numLayers )
1321 pixels[ i ] = numLayers - 1;
1324 /* free the 32 bit image */
1330 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1333 //% Sys_Printf( "-------------------------------" );
1335 /* fix up out-of-range values */
1337 for( i = 0; i < size; i++ )
1339 if( pixels[ i ] >= numLayers )
1340 pixels[ i ] = numLayers - 1;
1343 //% if( (i % w) == 0 )
1344 //% Sys_Printf( "\n" );
1345 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1349 //% Sys_Printf( "\n-------------------------------\n" );
1352 /* the index map must be at least 2x2 pixels */
1353 if( w < 2 || h < 2 )
1355 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1356 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1361 /* create a new index map */
1362 im = safe_malloc( sizeof( *im ) );
1363 memset( im, 0, sizeof( *im ) );
1368 im->numLayers = numLayers;
1369 strcpy( im->name, indexMapFilename );
1370 strcpy( im->shader, shader );
1371 im->pixels = pixels;
1373 /* get height offsets */
1374 value = ValueForKey( mapEnt, "_offsets" );
1375 if( value[ 0 ] == '\0' )
1376 value = ValueForKey( e, "offsets" );
1377 if( value[ 0 ] != '\0' )
1379 /* value is a space-seperated set of numbers */
1380 strcpy( offset, value );
1383 /* get each value */
1384 for( i = 0; i < 256 && *search != '\0'; i++ )
1386 space = strstr( search, " " );
1389 im->offsets[ i ] = atof( search );
1396 /* store the index map in every brush/patch in the entity */
1397 for( b = e->brushes; b != NULL; b = b->next )
1399 for( p = e->patches; p != NULL; p = p->next )
1411 parses a single entity out of a map file
1414 static qboolean ParseMapEntity( qboolean onlyLights )
1417 const char *classname, *value;
1418 float lightmapScale;
1419 char shader[ MAX_QPATH ];
1420 shaderInfo_t *celShader = NULL;
1424 int castShadows, recvShadows;
1428 if( !GetToken( qtrue ) )
1431 /* conformance check */
1432 if( strcmp( token, "{" ) )
1434 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1435 "Continuing to process map, but resulting BSP may be invalid.\n",
1436 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1441 if( numEntities >= MAX_MAP_ENTITIES )
1442 Error( "numEntities == MAX_MAP_ENTITIES" );
1445 entitySourceBrushes = 0;
1446 mapEnt = &entities[ numEntities ];
1448 memset( mapEnt, 0, sizeof( *mapEnt ) );
1450 /* ydnar: true entity numbering */
1451 mapEnt->mapEntityNum = numMapEntities;
1457 /* get initial token */
1458 if( !GetToken( qtrue ) )
1460 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1461 "Continuing to process map, but resulting BSP may be invalid.\n" );
1465 if( !strcmp( token, "}" ) )
1468 if( !strcmp( token, "{" ) )
1470 /* parse a brush or patch */
1471 if( !GetToken( qtrue ) )
1475 if( !strcmp( token, "patchDef2" ) )
1478 ParsePatch( onlyLights );
1480 else if( !strcmp( token, "terrainDef" ) )
1483 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1485 else if( !strcmp( token, "brushDef" ) )
1487 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1488 Error( "Old brush format not allowed in new brush format map" );
1489 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1491 /* parse brush primitive */
1492 ParseBrush( onlyLights );
1496 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1497 Error( "New brush format not allowed in old brush format map" );
1498 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1500 /* parse old brush format */
1502 ParseBrush( onlyLights );
1504 entitySourceBrushes++;
1508 /* parse a key / value pair */
1511 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1512 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1514 ep->next = mapEnt->epairs;
1515 mapEnt->epairs = ep;
1520 /* ydnar: get classname */
1521 classname = ValueForKey( mapEnt, "classname" );
1523 /* ydnar: only lights? */
1524 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1530 /* ydnar: determine if this is a func_group */
1531 if( !Q_stricmp( "func_group", classname ) )
1536 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1537 if( funcGroup || mapEnt->mapEntityNum == 0 )
1539 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1540 castShadows = WORLDSPAWN_CAST_SHADOWS;
1541 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1544 /* other entities don't cast any shadows, but recv worldspawn shadows */
1547 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1548 castShadows = ENTITY_CAST_SHADOWS;
1549 recvShadows = ENTITY_RECV_SHADOWS;
1552 /* get explicit shadow flags */
1553 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1555 /* vortex: added _ls key (short name of lightmapscale) */
1556 /* ydnar: get lightmap scaling value for this entity */
1557 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1558 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1559 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
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 lightmapScale = FloatForKey( mapEnt, "_ls" );
1567 if( lightmapScale > 0.0f )
1568 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1571 lightmapScale = 0.0f;
1573 /* ydnar: get cel shader :) for this entity */
1574 value = ValueForKey( mapEnt, "_celshader" );
1575 if( value[ 0 ] == '\0' )
1576 value = ValueForKey( &entities[ 0 ], "_celshader" );
1577 if( value[ 0 ] != '\0' )
1579 sprintf( shader, "textures/%s", value );
1580 celShader = ShaderInfoForShader( shader );
1581 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1584 celShader = *globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL;
1586 /* attach stuff to everything in the entity */
1587 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1589 brush->entityNum = mapEnt->mapEntityNum;
1590 brush->castShadows = castShadows;
1591 brush->recvShadows = recvShadows;
1592 brush->lightmapScale = lightmapScale;
1593 brush->celShader = celShader;
1596 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1598 patch->entityNum = mapEnt->mapEntityNum;
1599 patch->castShadows = castShadows;
1600 patch->recvShadows = recvShadows;
1601 patch->lightmapScale = lightmapScale;
1602 patch->celShader = celShader;
1605 /* ydnar: gs mods: set entity bounds */
1606 SetEntityBounds( mapEnt );
1608 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1609 LoadEntityIndexMap( mapEnt );
1611 /* get entity origin and adjust brushes */
1612 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1613 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1614 AdjustBrushesForOrigin( mapEnt );
1616 /* group_info entities are just for editor grouping (fixme: leak!) */
1617 if( !Q_stricmp( "group_info", classname ) )
1623 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1626 MoveBrushesToWorld( mapEnt );
1639 loads a map file into a list of entities
1642 void LoadMapFile( char *filename, qboolean onlyLights )
1646 int oldNumEntities, numMapBrushes;
1650 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1651 Sys_Printf( "Loading %s\n", filename );
1654 file = SafeOpenRead( filename );
1657 /* load the map file */
1658 LoadScriptFile( filename, -1 );
1662 oldNumEntities = numEntities;
1667 numMapDrawSurfs = 0;
1669 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1671 /* allocate a very large temporary brush for building the brushes as they are loaded */
1672 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1674 /* parse the map file */
1675 while( ParseMapEntity( onlyLights ) );
1680 /* emit some statistics */
1681 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1685 /* set map bounds */
1686 ClearBounds( mapMins, mapMaxs );
1687 for( b = entities[ 0 ].brushes; b; b = b->next )
1689 AddPointToBounds( b->mins, mapMins, mapMaxs );
1690 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1693 /* get brush counts */
1694 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1695 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1696 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1698 /* emit some statistics */
1699 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1700 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1701 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1702 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1703 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1704 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1705 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1706 Sys_Printf( "%9d areaportals\n", c_areaportals);
1707 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1708 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1709 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1711 /* write bogus map */
1713 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );