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 ], p->normal ) - p->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 ) )
290 /* ydnar: uncomment the following line for old-style plane finding */
293 /* ydnar: test supplied points against this plane */
294 for( j = 0; j < numPoints; j++ )
296 d = DotProduct( points[ j ], p->normal ) - p->dist;
297 if( fabs( d ) > distanceEpsilon )
301 /* found a matching plane */
306 return CreateNewFloatPlane( normal, dist );
315 takes 3 points and finds the plane they lie in
318 int MapPlaneFromPoints( vec3_t *p )
320 vec3_t t1, t2, normal;
324 /* calc plane normal */
325 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
326 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
327 CrossProduct( t1, t2, normal );
328 VectorNormalize( normal, normal );
330 /* calc plane distance */
331 dist = DotProduct( p[ 0 ], normal );
333 /* store the plane */
334 return FindFloatPlane( normal, dist, 3, p );
341 the content flags and compile flags on all sides of a brush should be the same
344 void SetBrushContents( brush_t *b )
346 int contentFlags, compileFlags;
352 /* get initial compile flags from first side */
354 contentFlags = s->contentFlags;
355 compileFlags = s->compileFlags;
356 b->contentShader = s->shaderInfo;
359 /* get the content/compile flags for every side in the brush */
360 for( i = 1; i < b->numsides; i++, s++ )
363 if( s->shaderInfo == NULL )
365 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
368 contentFlags |= s->contentFlags;
369 compileFlags |= s->compileFlags;
372 /* ydnar: getting rid of this stupid warning */
374 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
376 /* check for detail & structural */
377 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
379 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
380 compileFlags &= ~C_DETAIL;
383 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
385 compileFlags &= ~C_DETAIL;
387 /* all translucent brushes that aren't specifically made structural will be detail */
388 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
389 compileFlags |= C_DETAIL;
392 if( compileFlags & C_DETAIL )
404 if( compileFlags & C_TRANSLUCENT )
410 if( compileFlags & C_AREAPORTAL )
413 /* set brush flags */
414 b->contentFlags = contentFlags;
415 b->compileFlags = compileFlags;
422 adds any additional planes necessary to allow the brush being
423 built to be expanded against axial bounding boxes
424 ydnar 2003-01-20: added mrelusive fixes
427 void AddBrushBevels( void )
430 int i, j, k, l, order;
440 // add the axial planes
443 for ( axis = 0; axis < 3; axis++ ) {
444 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
445 // see if the plane is allready present
446 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
448 /* ydnar: testing disabling of mre code */
451 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
456 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
461 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
462 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
467 if ( i == buildBrush->numsides ) {
469 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
470 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
472 memset( s, 0, sizeof( *s ) );
473 buildBrush->numsides++;
474 VectorClear (normal);
479 /* ydnar: adding bevel plane snapping for fewer bsp planes */
481 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
483 dist = buildBrush->maxs[ axis ];
487 /* ydnar: adding bevel plane snapping for fewer bsp planes */
489 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
491 dist = -buildBrush->mins[ axis ];
494 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
495 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
500 // if the plane is not in it canonical order, swap it
502 sidetemp = buildBrush->sides[order];
503 buildBrush->sides[order] = buildBrush->sides[i];
504 buildBrush->sides[i] = sidetemp;
510 // add the edge bevels
512 if ( buildBrush->numsides == 6 ) {
513 return; // pure axial
516 // test the non-axial plane edges
517 for ( i = 6; i < buildBrush->numsides; i++ ) {
518 s = buildBrush->sides + i;
523 for ( j = 0; j < w->numpoints; j++) {
524 k = (j+1)%w->numpoints;
525 VectorSubtract( w->p[j], w->p[k], vec );
526 if ( VectorNormalize( vec, vec ) < 0.5f ) {
530 for ( k = 0; k < 3; k++ ) {
531 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
536 continue; // only test non-axial edges
540 //% Sys_Printf( "-------------\n" );
542 // try the six possible slanted axials from this edge
543 for ( axis = 0; axis < 3; axis++ ) {
544 for ( dir = -1; dir <= 1; dir += 2 ) {
548 CrossProduct( vec, vec2, normal );
549 if ( VectorNormalize( normal, normal ) < 0.5f ) {
552 dist = DotProduct( w->p[j], normal );
554 // if all the points on all the sides are
555 // behind this plane, it is a proper edge bevel
556 for ( k = 0; k < buildBrush->numsides; k++ ) {
558 // if this plane has allready been used, skip it
559 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
563 w2 = buildBrush->sides[k].winding;
568 for ( l = 0; l < w2->numpoints; l++ ) {
569 d = DotProduct( w2->p[l], normal ) - dist;
571 break; // point in front
577 // if some point was at the front
578 if ( l != w2->numpoints ) {
582 // if no points at the back then the winding is on the bevel plane
583 if ( minBack > -0.1f ) {
584 //% Sys_Printf( "On bevel plane\n" );
589 if ( k != buildBrush->numsides ) {
590 continue; // wasn't part of the outer hull
594 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
597 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
598 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
600 s2 = &buildBrush->sides[buildBrush->numsides];
601 buildBrush->numsides++;
602 memset( s2, 0, sizeof( *s2 ) );
604 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
605 s2->contentFlags = buildBrush->sides[0].contentFlags;
618 produces a final brush based on the buildBrush->sides array
619 and links it to the current entity
622 static void MergeOrigin(entity_t *ent, vec3_t origin)
627 /* we have not parsed the brush completely yet... */
628 GetVectorForKey( ent, "origin", ent->origin );
630 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
631 VectorAdd(adjustment, ent->origin, ent->origin);
632 VectorCopy(origin, ent->originbrush_origin);
634 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
635 SetKeyValue(ent, "origin", string);
638 brush_t *FinishBrush( qboolean noCollapseGroups )
643 /* create windings for sides and bounds for brush */
644 if ( !CreateBrushWindings( buildBrush ) )
647 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
648 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
649 if( buildBrush->compileFlags & C_ORIGIN )
653 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
654 mapEnt->mapEntityNum, entitySourceBrushes );
656 if( numEntities == 1 )
658 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
659 mapEnt->mapEntityNum, entitySourceBrushes );
663 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
664 VectorScale (origin, 0.5, origin);
666 MergeOrigin(&entities[ numEntities - 1 ], origin);
668 /* don't keep this brush */
672 /* determine if the brush is an area portal */
673 if( buildBrush->compileFlags & C_AREAPORTAL )
675 if( numEntities != 1 )
677 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
682 /* add bevel planes */
683 if(!noCollapseGroups)
687 b = CopyBrush( buildBrush );
689 /* set map entity and brush numbering */
690 b->entityNum = mapEnt->mapEntityNum;
691 b->brushNum = entitySourceBrushes;
696 /* link opaque brushes to head of list, translucent brushes to end */
697 if( b->opaque || mapEnt->lastBrush == NULL )
699 b->next = mapEnt->brushes;
701 if( mapEnt->lastBrush == NULL )
702 mapEnt->lastBrush = b;
707 mapEnt->lastBrush->next = b;
708 mapEnt->lastBrush = b;
711 /* link colorMod volume brushes to the entity directly */
712 if( b->contentShader != NULL &&
713 b->contentShader->colorMod != NULL &&
714 b->contentShader->colorMod->type == CM_VOLUME )
716 b->nextColorModBrush = mapEnt->colorModBrushes;
717 mapEnt->colorModBrushes = b;
720 /* return to sender */
727 TextureAxisFromPlane()
728 determines best orthagonal axis to project a texture onto a wall
729 (must be identical in radiant!)
732 vec3_t baseaxis[18] =
734 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
735 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
736 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
737 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
738 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
739 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
742 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
751 for (i=0 ; i<6 ; i++)
753 dot = DotProduct (pln->normal, baseaxis[i*3]);
754 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
761 VectorCopy (baseaxis[bestaxis*3+1], xv);
762 VectorCopy (baseaxis[bestaxis*3+2], yv);
769 creates world-to-texture mapping vecs for crappy quake plane arrangements
772 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
776 vec_t ang, sinv, cosv;
781 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
790 { sinv = 0 ; cosv = 1; }
791 else if (rotate == 90)
792 { sinv = 1 ; cosv = 0; }
793 else if (rotate == 180)
794 { sinv = 0 ; cosv = -1; }
795 else if (rotate == 270)
796 { sinv = -1 ; cosv = 0; }
799 ang = rotate / 180 * Q_PI;
818 for (i=0 ; i<2 ; i++) {
819 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
820 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
825 for (i=0 ; i<2 ; i++)
826 for (j=0 ; j<3 ; j++)
827 mappingVecs[i][j] = vecs[i][j] / scale[i];
829 mappingVecs[0][3] = shift[0];
830 mappingVecs[1][3] = shift[1];
837 parses the sides into buildBrush->sides[], nothing else.
838 no validation, back plane removal, etc.
841 added brush epairs parsing ( ignoring actually )
843 added exclusive brush primitive parsing
845 support for old brush format back in
846 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
849 static void ParseRawBrush( qboolean onlyLights )
852 vec3_t planePoints[ 3 ];
858 char name[ MAX_QPATH ];
859 char shader[ MAX_QPATH ];
864 buildBrush->numsides = 0;
865 buildBrush->detail = qfalse;
868 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
874 if( !GetToken( qtrue ) )
876 if( !strcmp( token, "}" ) )
879 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
880 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
884 if( strcmp( token, "(" ) )
893 /* test side count */
894 if( buildBrush->numsides >= MAX_BUILD_SIDES )
895 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
898 side = &buildBrush->sides[ buildBrush->numsides ];
899 memset( side, 0, sizeof( *side ) );
900 buildBrush->numsides++;
902 /* read the three point plane definition */
903 Parse1DMatrix( 3, planePoints[ 0 ] );
904 Parse1DMatrix( 3, planePoints[ 1 ] );
905 Parse1DMatrix( 3, planePoints[ 2 ] );
907 /* bp: read the texture matrix */
908 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
909 Parse2DMatrix( 2, 3, (float*) side->texMat );
911 /* read shader name */
913 strcpy( name, token );
916 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
919 shift[ 0 ] = atof( token );
921 shift[ 1 ] = atof( token );
923 rotate = atof( token );
925 scale[ 0 ] = atof( token );
927 scale[ 1 ] = atof( token );
930 /* set default flags and values */
931 sprintf( shader, "textures/%s", name );
933 si = &shaderInfo[ 0 ];
935 si = ShaderInfoForShader( shader );
936 side->shaderInfo = si;
937 side->surfaceFlags = si->surfaceFlags;
938 side->contentFlags = si->contentFlags;
939 side->compileFlags = si->compileFlags;
940 side->value = si->value;
942 /* ydnar: gs mods: bias texture shift */
943 if( si->globalTexture == qfalse )
945 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
946 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
950 historically, there are 3 integer values at the end of a brushside line in a .map file.
951 in quake 3, the only thing that mattered was the first of these three values, which
952 was previously the content flags. and only then did a single bit matter, the detail
953 bit. because every game has its own special flags for specifying detail, the
954 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
955 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
956 is stored in compileFlags, as opposed to contentFlags, for multiple-game
960 if( TokenAvailable() )
962 /* get detail bit from map content flags */
964 flags = atoi( token );
965 if( flags & C_DETAIL )
966 side->compileFlags |= C_DETAIL;
970 //% td.flags = atoi( token );
972 //% td.value = atoi( token );
975 /* find the plane number */
976 planenum = MapPlaneFromPoints( planePoints );
977 side->planenum = planenum;
979 /* bp: get the texture mapping for this texturedef / plane combination */
980 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
981 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
985 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
996 RemoveDuplicateBrushPlanes
997 returns false if the brush has a mirrored set of planes,
998 meaning it encloses no volume.
999 also removes planes without any normal
1002 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
1009 for ( i = 1 ; i < b->numsides ; i++ ) {
1011 // check for a degenerate plane
1012 if ( sides[i].planenum == -1) {
1013 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
1015 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1016 sides[k-1] = sides[k];
1023 // check for duplication and mirroring
1024 for ( j = 0 ; j < i ; j++ ) {
1025 if ( sides[i].planenum == sides[j].planenum ) {
1026 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1027 // remove the second duplicate
1028 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1029 sides[k-1] = sides[k];
1036 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1037 // mirror plane, brush is invalid
1038 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1050 parses a brush out of a map file and sets it up
1053 static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups )
1058 /* parse the brush out of the map */
1059 ParseRawBrush( onlyLights );
1061 /* only go this far? */
1065 /* set some defaults */
1066 buildBrush->portalareas[ 0 ] = -1;
1067 buildBrush->portalareas[ 1 ] = -1;
1068 buildBrush->entityNum = numMapEntities - 1;
1069 buildBrush->brushNum = entitySourceBrushes;
1071 /* if there are mirrored planes, the entire brush is invalid */
1072 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1075 /* get the content for the entire brush */
1076 SetBrushContents( buildBrush );
1078 /* allow detail brushes to be removed */
1079 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1081 //% FreeBrush( buildBrush );
1085 /* allow liquid brushes to be removed */
1086 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1088 //% FreeBrush( buildBrush );
1092 /* ydnar: allow hint brushes to be removed */
1093 if( noHint && (buildBrush->compileFlags & C_HINT) )
1095 //% FreeBrush( buildBrush );
1099 /* finish the brush */
1100 b = FinishBrush(noCollapseGroups);
1106 MoveBrushesToWorld()
1107 takes all of the brushes from the current entity and
1108 adds them to the world's brush list
1109 (used by func_group)
1112 void AdjustBrushesForOrigin( entity_t *ent );
1113 void MoveBrushesToWorld( entity_t *ent )
1118 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1119 VectorScale(ent->origin, -1, ent->originbrush_origin);
1120 AdjustBrushesForOrigin(ent);
1121 VectorClear(ent->originbrush_origin);
1124 for( b = ent->brushes; b != NULL; b = next )
1126 /* get next brush */
1129 /* link opaque brushes to head of list, translucent brushes to end */
1130 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1132 b->next = entities[ 0 ].brushes;
1133 entities[ 0 ].brushes = b;
1134 if( entities[ 0 ].lastBrush == NULL )
1135 entities[ 0 ].lastBrush = b;
1140 entities[ 0 ].lastBrush->next = b;
1141 entities[ 0 ].lastBrush = b;
1144 ent->brushes = NULL;
1146 /* ydnar: move colormod brushes */
1147 if( ent->colorModBrushes != NULL )
1149 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1151 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1152 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1154 ent->colorModBrushes = NULL;
1158 if( ent->patches != NULL )
1160 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1162 pm->next = entities[ 0 ].patches;
1163 entities[ 0 ].patches = ent->patches;
1165 ent->patches = NULL;
1172 AdjustBrushesForOrigin()
1175 void AdjustBrushesForOrigin( entity_t *ent )
1184 /* walk brush list */
1185 for( b = ent->brushes; b != NULL; b = b->next )
1187 /* offset brush planes */
1188 for( i = 0; i < b->numsides; i++)
1190 /* get brush side */
1193 /* offset side plane */
1194 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1196 /* find a new plane */
1197 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1200 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1201 CreateBrushWindings( b );
1204 /* walk patch list */
1205 for( p = ent->patches; p != NULL; p = p->next )
1207 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1208 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1215 SetEntityBounds() - ydnar
1216 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1219 void SetEntityBounds( entity_t *e )
1230 /* walk the entity's brushes/patches and determine bounds */
1231 ClearBounds( mins, maxs );
1232 for( b = e->brushes; b; b = b->next )
1234 AddPointToBounds( b->mins, mins, maxs );
1235 AddPointToBounds( b->maxs, mins, maxs );
1237 for( p = e->patches; p; p = p->next )
1239 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1240 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1243 /* try to find explicit min/max key */
1244 value = ValueForKey( e, "min" );
1245 if( value[ 0 ] != '\0' )
1246 GetVectorForKey( e, "min", mins );
1247 value = ValueForKey( e, "max" );
1248 if( value[ 0 ] != '\0' )
1249 GetVectorForKey( e, "max", maxs );
1251 /* store the bounds */
1252 for( b = e->brushes; b; b = b->next )
1254 VectorCopy( mins, b->eMins );
1255 VectorCopy( maxs, b->eMaxs );
1257 for( p = e->patches; p; p = p->next )
1259 VectorCopy( mins, p->eMins );
1260 VectorCopy( maxs, p->eMaxs );
1267 LoadEntityIndexMap() - ydnar
1268 based on LoadAlphaMap() from terrain.c, a little more generic
1271 void LoadEntityIndexMap( entity_t *e )
1273 int i, size, numLayers, w, h;
1274 const char *value, *indexMapFilename, *shader;
1275 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1277 unsigned int *pixels32;
1283 /* this only works with bmodel ents */
1284 if( e->brushes == NULL && e->patches == NULL )
1287 /* determine if there is an index map (support legacy "alphamap" key as well) */
1288 value = ValueForKey( e, "_indexmap" );
1289 if( value[ 0 ] == '\0' )
1290 value = ValueForKey( e, "alphamap" );
1291 if( value[ 0 ] == '\0' )
1293 indexMapFilename = value;
1295 /* get number of layers (support legacy "layers" key as well) */
1296 value = ValueForKey( e, "_layers" );
1297 if( value[ 0 ] == '\0' )
1298 value = ValueForKey( e, "layers" );
1299 if( value[ 0 ] == '\0' )
1301 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1302 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1305 numLayers = atoi( value );
1308 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1309 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1313 /* get base shader name (support legacy "shader" key as well) */
1314 value = ValueForKey( mapEnt, "_shader" );
1315 if( value[ 0 ] == '\0' )
1316 value = ValueForKey( e, "shader" );
1317 if( value[ 0 ] == '\0' )
1319 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1320 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1326 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1328 /* get index map file extension */
1329 ExtractFileExtension( indexMapFilename, ext );
1331 /* handle tga image */
1332 if( !Q_stricmp( ext, "tga" ) )
1335 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1337 /* convert to bytes */
1339 pixels = safe_malloc( size );
1340 for( i = 0; i < size; i++ )
1342 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1343 if( pixels[ i ] >= numLayers )
1344 pixels[ i ] = numLayers - 1;
1347 /* free the 32 bit image */
1353 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1356 //% Sys_Printf( "-------------------------------" );
1358 /* fix up out-of-range values */
1360 for( i = 0; i < size; i++ )
1362 if( pixels[ i ] >= numLayers )
1363 pixels[ i ] = numLayers - 1;
1366 //% if( (i % w) == 0 )
1367 //% Sys_Printf( "\n" );
1368 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1372 //% Sys_Printf( "\n-------------------------------\n" );
1375 /* the index map must be at least 2x2 pixels */
1376 if( w < 2 || h < 2 )
1378 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1379 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1384 /* create a new index map */
1385 im = safe_malloc( sizeof( *im ) );
1386 memset( im, 0, sizeof( *im ) );
1391 im->numLayers = numLayers;
1392 strcpy( im->name, indexMapFilename );
1393 strcpy( im->shader, shader );
1394 im->pixels = pixels;
1396 /* get height offsets */
1397 value = ValueForKey( mapEnt, "_offsets" );
1398 if( value[ 0 ] == '\0' )
1399 value = ValueForKey( e, "offsets" );
1400 if( value[ 0 ] != '\0' )
1402 /* value is a space-seperated set of numbers */
1403 strcpy( offset, value );
1406 /* get each value */
1407 for( i = 0; i < 256 && *search != '\0'; i++ )
1409 space = strstr( search, " " );
1412 im->offsets[ i ] = atof( search );
1419 /* store the index map in every brush/patch in the entity */
1420 for( b = e->brushes; b != NULL; b = b->next )
1422 for( p = e->patches; p != NULL; p = p->next )
1434 parses a single entity out of a map file
1437 static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups )
1440 const char *classname, *value;
1441 float lightmapScale, shadeAngle;
1442 int lightmapSampleSize;
1443 char shader[ MAX_QPATH ];
1444 shaderInfo_t *celShader = NULL;
1448 int castShadows, recvShadows;
1452 if( !GetToken( qtrue ) )
1455 /* conformance check */
1456 if( strcmp( token, "{" ) )
1458 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1459 "Continuing to process map, but resulting BSP may be invalid.\n",
1460 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1465 if( numEntities >= MAX_MAP_ENTITIES )
1466 Error( "numEntities == MAX_MAP_ENTITIES" );
1469 entitySourceBrushes = 0;
1470 mapEnt = &entities[ numEntities ];
1472 memset( mapEnt, 0, sizeof( *mapEnt ) );
1474 /* ydnar: true entity numbering */
1475 mapEnt->mapEntityNum = numMapEntities;
1481 /* get initial token */
1482 if( !GetToken( qtrue ) )
1484 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1485 "Continuing to process map, but resulting BSP may be invalid.\n" );
1489 if( !strcmp( token, "}" ) )
1492 if( !strcmp( token, "{" ) )
1494 /* parse a brush or patch */
1495 if( !GetToken( qtrue ) )
1499 if( !strcmp( token, "patchDef2" ) )
1502 ParsePatch( onlyLights );
1504 else if( !strcmp( token, "terrainDef" ) )
1507 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1509 else if( !strcmp( token, "brushDef" ) )
1511 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1512 Error( "Old brush format not allowed in new brush format map" );
1513 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1515 /* parse brush primitive */
1516 ParseBrush( onlyLights, noCollapseGroups );
1520 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1521 Error( "New brush format not allowed in old brush format map" );
1522 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1524 /* parse old brush format */
1526 ParseBrush( onlyLights, noCollapseGroups );
1528 entitySourceBrushes++;
1532 /* parse a key / value pair */
1535 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1536 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1538 ep->next = mapEnt->epairs;
1539 mapEnt->epairs = ep;
1544 /* ydnar: get classname */
1545 classname = ValueForKey( mapEnt, "classname" );
1547 /* ydnar: only lights? */
1548 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1554 /* ydnar: determine if this is a func_group */
1555 if( !Q_stricmp( "func_group", classname ) )
1560 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1561 if( funcGroup || mapEnt->mapEntityNum == 0 )
1563 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1564 castShadows = WORLDSPAWN_CAST_SHADOWS;
1565 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1568 /* other entities don't cast any shadows, but recv worldspawn shadows */
1571 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1572 castShadows = ENTITY_CAST_SHADOWS;
1573 recvShadows = ENTITY_RECV_SHADOWS;
1576 /* get explicit shadow flags */
1577 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1579 /* vortex: added _ls key (short name of lightmapscale) */
1580 /* ydnar: get lightmap scaling value for this entity */
1581 lightmapScale = 0.0f;
1582 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1583 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1584 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
1586 /* get lightmap scale from entity */
1587 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1588 if( lightmapScale <= 0.0f )
1589 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1590 if( lightmapScale <= 0.0f )
1591 lightmapScale = FloatForKey( mapEnt, "_ls" );
1592 if( lightmapScale < 0.0f )
1593 lightmapScale = 0.0f;
1594 if( lightmapScale > 0.0f )
1595 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1598 /* ydnar: get cel shader :) for this entity */
1599 value = ValueForKey( mapEnt, "_celshader" );
1600 if( value[ 0 ] == '\0' )
1601 value = ValueForKey( &entities[ 0 ], "_celshader" );
1602 if( value[ 0 ] != '\0' )
1604 if(strcmp(value, "none"))
1606 sprintf( shader, "textures/%s", value );
1607 celShader = ShaderInfoForShader( shader );
1608 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1616 celShader = (*globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL);
1618 /* jal : entity based _shadeangle */
1620 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) )
1621 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1622 /* vortex' aliases */
1623 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) )
1624 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1625 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) )
1626 shadeAngle = FloatForKey( mapEnt, "_sn" );
1627 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) )
1628 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1630 if( shadeAngle < 0.0f )
1633 if( shadeAngle > 0.0f )
1634 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1636 /* jal : entity based _samplesize */
1637 lightmapSampleSize = 0;
1638 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) )
1639 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1640 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) )
1641 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1643 if( lightmapSampleSize < 0 )
1644 lightmapSampleSize = 0;
1646 if( lightmapSampleSize > 0 )
1647 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1649 /* attach stuff to everything in the entity */
1650 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1652 brush->entityNum = mapEnt->mapEntityNum;
1653 brush->castShadows = castShadows;
1654 brush->recvShadows = recvShadows;
1655 brush->lightmapSampleSize = lightmapSampleSize;
1656 brush->lightmapScale = lightmapScale;
1657 brush->celShader = celShader;
1658 brush->shadeAngleDegrees = shadeAngle;
1661 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1663 patch->entityNum = mapEnt->mapEntityNum;
1664 patch->castShadows = castShadows;
1665 patch->recvShadows = recvShadows;
1666 patch->lightmapSampleSize = lightmapSampleSize;
1667 patch->lightmapScale = lightmapScale;
1668 patch->celShader = celShader;
1671 /* ydnar: gs mods: set entity bounds */
1672 SetEntityBounds( mapEnt );
1674 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1675 LoadEntityIndexMap( mapEnt );
1677 /* get entity origin and adjust brushes */
1678 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1679 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1680 AdjustBrushesForOrigin( mapEnt );
1682 /* group_info entities are just for editor grouping (fixme: leak!) */
1683 if( !noCollapseGroups && !Q_stricmp( "group_info", classname ) )
1689 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1690 if( !noCollapseGroups && funcGroup )
1692 MoveBrushesToWorld( mapEnt );
1705 loads a map file into a list of entities
1708 void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups )
1712 int oldNumEntities = 0, numMapBrushes;
1716 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1717 Sys_Printf( "Loading %s\n", filename );
1720 file = SafeOpenRead( filename );
1723 /* load the map file */
1724 LoadScriptFile( filename, -1 );
1728 oldNumEntities = numEntities;
1733 numMapDrawSurfs = 0;
1735 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1737 /* allocate a very large temporary brush for building the brushes as they are loaded */
1738 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1740 /* parse the map file */
1741 while( ParseMapEntity( onlyLights, noCollapseGroups ) );
1746 /* emit some statistics */
1747 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1751 /* set map bounds */
1752 ClearBounds( mapMins, mapMaxs );
1753 for( b = entities[ 0 ].brushes; b; b = b->next )
1755 AddPointToBounds( b->mins, mapMins, mapMaxs );
1756 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1759 /* get brush counts */
1760 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1761 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1762 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1764 /* emit some statistics */
1765 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1766 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1767 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1768 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1769 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1770 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1771 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1772 Sys_Printf( "%9d areaportals\n", c_areaportals);
1773 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1774 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1775 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1777 /* write bogus map */
1779 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );