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 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ){
66 /* get local copies */
71 // We check equality of each component since we're using '<', not '<='
72 // (the epsilons may be zero). We want to use '<' instead of '<=' to be
73 // consistent with the true meaning of "epsilon", and also because other
74 // parts of the code uses this inequality.
75 if ( ( p->dist == dist || fabs( p->dist - dist ) < de ) &&
76 ( p->normal[0] == normal[0] || fabs( p->normal[0] - normal[0] ) < ne ) &&
77 ( p->normal[1] == normal[1] || fabs( p->normal[1] - normal[1] ) < ne ) &&
78 ( p->normal[2] == normal[2] || fabs( p->normal[2] - normal[2] ) < ne ) ) {
92 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 ){
110 if ( VectorLength( normal ) < 0.5 ) {
111 Sys_Printf( "FloatPlane: bad normal\n" );
115 // create a new plane
116 AUTOEXPAND_BY_REALLOC( mapplanes, nummapplanes + 1, allocatedmapplanes, 1024 );
118 p = &mapplanes[nummapplanes];
119 VectorCopy( normal, p->normal );
121 p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
123 VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
124 ( p + 1 )->dist = -dist;
128 // allways put axial planes facing positive first
130 if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
137 AddPlaneToHash( p + 1 );
138 return nummapplanes - 1;
143 AddPlaneToHash( p + 1 );
144 return nummapplanes - 2;
151 Snaps a near-axial normal vector.
152 Returns qtrue if and only if the normal was adjusted.
155 qboolean SnapNormal( vec3_t normal ){
156 #if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
158 qboolean adjusted = qfalse;
160 // A change from the original SnapNormal() is that we snap each
161 // component that's close to 0. So for example if a normal is
162 // (0.707, 0.707, 0.0000001), it will get snapped to lie perfectly in the
163 // XY plane (its Z component will be set to 0 and its length will be
164 // normalized). The original SnapNormal() didn't snap such vectors - it
165 // only snapped vectors that were near a perfect axis.
167 //adjusting vectors, that are near perfect axis, with bigger epsilon
168 //they cause precision errors
171 if ( ( normal[0] != 0.0 || normal[1] != 0.0 ) && fabs(normal[0]) < 0.00025 && fabs(normal[1]) < 0.00025){
172 normal[0] = normal[1] = 0.0;
175 else if ( ( normal[0] != 0.0 || normal[2] != 0.0 ) && fabs(normal[0]) < 0.00025 && fabs(normal[2]) < 0.00025){
176 normal[0] = normal[2] = 0.0;
179 else if ( ( normal[2] != 0.0 || normal[1] != 0.0 ) && fabs(normal[2]) < 0.00025 && fabs(normal[1]) < 0.00025){
180 normal[2] = normal[1] = 0.0;
186 for ( i=0; i<30; i++ )
188 double x, y, z, length;
190 y=(double) ( 0.00001 * i );
193 Sys_Printf("(%6.18f %6.18f %6.18f)inNormal\n", x,y,z );
195 length = sqrt( ( x * x ) + ( y * y ) + ( z * z ) );
196 Sys_Printf("(%6.18f)length\n", length);
197 x = (vec_t) ( x / length );
198 y = (vec_t) ( y / length );
199 z = (vec_t) ( z / length );
200 Sys_Printf("(%6.18f %6.18f %6.18f)outNormal\n\n", x,y,z );
202 Error("vectorNormalize test completed");
205 for ( i = 0; i < 3; i++ )
207 if ( normal[i] != 0.0 && -normalEpsilon < normal[i] && normal[i] < normalEpsilon ) {
214 VectorNormalize( normal, normal );
221 // I would suggest that you uncomment the following code and look at the
225 Sys_Printf("normalEpsilon is %f\n", normalEpsilon);
230 normal[2] = i * 0.000001;
231 VectorNormalize(normal, normal);
232 if (1.0 - normal[0] >= normalEpsilon) {
233 Sys_Printf("(%f %f %f)\n", normal[0], normal[1], normal[2]);
234 Error("SnapNormal: test completed");
239 // When the normalEpsilon is 0.00001, the loop will break out when normal is
240 // (0.999990 0.000000 0.004469). In other words, this is the vector closest
241 // to axial that will NOT be snapped. Anything closer will be snaped. Now,
242 // 0.004469 is close to 1/225. The length of a circular quarter-arc of radius
243 // 1 is PI/2, or about 1.57. And 0.004469/1.57 is about 0.0028, or about
244 // 1/350. Expressed a different way, 1/350 is also about 0.26/90.
245 // This means is that a normal with an angle that is within 1/4 of a degree
246 // from axial will be "snapped". My belief is that the person who wrote the
247 // code below did not intend it this way. I think the person intended that
248 // the epsilon be measured against the vector components close to 0, not 1.0.
249 // I think the logic should be: if 2 of the normal components are within
250 // epsilon of 0, then the vector can be snapped to be perfectly axial.
251 // We may consider adjusting the epsilon to a larger value when we make this
254 for ( i = 0; i < 3; i++ )
256 if ( fabs( normal[ i ] - 1 ) < normalEpsilon ) {
257 VectorClear( normal );
261 if ( fabs( normal[ i ] - -1 ) < normalEpsilon ) {
262 VectorClear( normal );
275 snaps a plane to normal/distance epsilons
278 void SnapPlane( vec3_t normal, vec_t *dist ){
279 // SnapPlane disabled by LordHavoc because it often messes up collision
280 // brushes made from triangles of embedded models, and it has little effect
281 // on anything else (axial planes are usually derived from snapped points)
283 SnapPlane reenabled by namespace because of multiple reports of
284 q3map2-crashes which were triggered by this patch.
286 SnapNormal( normal );
288 // TODO: Rambetter has some serious comments here as well. First off,
289 // in the case where a normal is non-axial, there is nothing special
290 // about integer distances. I would think that snapping a distance might
291 // make sense for axial normals, but I'm not so sure about snapping
292 // non-axial normals. A shift by 0.01 in a plane, multiplied by a clipping
293 // against another plane that is 5 degrees off, and we introduce 0.1 error
294 // easily. A 0.1 error in a vertex is where problems start to happen, such
295 // as disappearing triangles.
297 // Second, assuming we have snapped the normal above, let's say that the
298 // plane we just snapped was defined for some points that are actually
299 // quite far away from normal * dist. Well, snapping the normal in this
300 // case means that we've just moved those points by potentially many units!
301 // Therefore, if we are going to snap the normal, we need to know the
302 // points we're snapping for so that the plane snaps with those points in
303 // mind (points remain close to the plane).
305 // I would like to know exactly which problems SnapPlane() is trying to
306 // solve so that we can better engineer it (I'm not saying that SnapPlane()
307 // should be removed altogether). Fix all this snapping code at some point!
309 if ( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon ) {
310 *dist = Q_rint( *dist );
316 snaps a plane to normal/distance epsilons, improved code
318 void SnapPlaneImproved( vec3_t normal, vec_t *dist, int numPoints, const vec3_t *points ){
321 vec_t distNearestInt;
323 if ( SnapNormal( normal ) ) {
324 if ( numPoints > 0 ) {
325 // Adjust the dist so that the provided points don't drift away.
326 VectorClear( center );
327 for ( i = 0; i < numPoints; i++ )
329 VectorAdd( center, points[i], center );
331 for ( i = 0; i < 3; i++ ) { center[i] = center[i] / numPoints; }
332 *dist = DotProduct( normal, center );
336 if ( VectorIsOnAxis( normal ) ) {
337 // Only snap distance if the normal is an axis. Otherwise there
338 // is nothing "natural" about snapping the distance to an integer.
339 distNearestInt = Q_rint( *dist );
340 if ( -distanceEpsilon < *dist - distNearestInt && *dist - distNearestInt < distanceEpsilon ) {
341 *dist = distNearestInt;
350 ydnar: changed to allow a number of test points to be supplied that
351 must be within an epsilon distance of the plane
354 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?
365 VectorCopy( innormal, normal );
366 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
367 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
369 SnapPlane( normal, &dist );
372 hash = ( PLANE_HASHES - 1 ) & (int) fabs( dist );
374 /* search the border bins as well */
375 for ( i = -1; i <= 1; i++ )
377 h = ( hash + i ) & ( PLANE_HASHES - 1 );
378 for ( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
380 p = &mapplanes[pidx];
382 /* do standard plane compare */
383 if ( !PlaneEqual( p, normal, dist ) ) {
387 /* ydnar: uncomment the following line for old-style plane finding */
388 //% return p - mapplanes;
390 /* ydnar: test supplied points against this plane */
391 for ( j = 0; j < numPoints; j++ )
393 // NOTE: When dist approaches 2^16, the resolution of 32 bit floating
394 // point number is greatly decreased. The distanceEpsilon cannot be
395 // very small when world coordinates extend to 2^16. Making the
396 // dot product here in 64 bit land will not really help the situation
397 // because the error will already be carried in dist.
398 d = DotProduct( points[ j ], p->normal ) - p->dist;
400 if ( d != 0.0 && d >= distanceEpsilon ) {
401 break; // Point is too far from plane.
405 /* found a matching plane */
406 if ( j >= numPoints ) {
407 return p - mapplanes;
412 /* none found, so create a new one */
413 return CreateNewFloatPlane( normal, dist );
423 VectorCopy( innormal, normal );
424 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
425 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
427 SnapPlane( normal, &dist );
429 for ( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
431 if ( !PlaneEqual( p, normal, dist ) ) {
435 /* ydnar: uncomment the following line for old-style plane finding */
438 /* ydnar: test supplied points against this plane */
439 for ( j = 0; j < numPoints; j++ )
441 d = DotProduct( points[ j ], p->normal ) - p->dist;
442 if ( fabs( d ) > distanceEpsilon ) {
447 /* found a matching plane */
448 if ( j >= numPoints ) {
451 // TODO: Note that the non-USE_HASHING code does not compute epsilons
452 // for the provided points. It should do that. I think this code
453 // is unmaintained because nobody sets USE_HASHING to off.
456 return CreateNewFloatPlane( normal, dist );
465 takes 3 points and finds the plane they lie in
468 int MapPlaneFromPoints( vec3_t *p ){
469 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
470 vec3_accu_t paccu[3];
471 vec3_accu_t t1, t2, normalAccu;
475 VectorCopyRegularToAccu( p[0], paccu[0] );
476 VectorCopyRegularToAccu( p[1], paccu[1] );
477 VectorCopyRegularToAccu( p[2], paccu[2] );
479 VectorSubtractAccu( paccu[0], paccu[1], t1 );
480 VectorSubtractAccu( paccu[2], paccu[1], t2 );
481 CrossProductAccu( t1, t2, normalAccu );
482 VectorNormalizeAccu( normalAccu, normalAccu );
483 // TODO: A 32 bit float for the plane distance isn't enough resolution
484 // if the plane is 2^16 units away from the origin (the "epsilon" approaches
485 // 0.01 in that case).
486 dist = (vec_t) DotProductAccu( paccu[0], normalAccu );
487 VectorCopyAccuToRegular( normalAccu, normal );
489 return FindFloatPlane( normal, dist, 3, p );
491 vec3_t t1, t2, normal;
495 /* calc plane normal */
496 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
497 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
498 CrossProduct( t1, t2, normal );
499 VectorNormalize( normal, normal );
501 /* calc plane distance */
502 dist = DotProduct( p[ 0 ], normal );
504 /* store the plane */
505 return FindFloatPlane( normal, dist, 3, p );
513 the content flags and compile flags on all sides of a brush should be the same
516 void SetBrushContents( brush_t *b ){
517 int contentFlags, compileFlags;
523 /* get initial compile flags from first side */
525 contentFlags = s->contentFlags;
526 compileFlags = s->compileFlags;
527 b->contentShader = s->shaderInfo;
530 /* get the content/compile flags for every side in the brush */
531 //for ( i = 1; i < b->numsides; i++, s++ )
532 for ( i = 1; i < b->numsides; i++ )
535 if ( s->shaderInfo == NULL ) {
538 //% if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
541 contentFlags |= s->contentFlags;
542 compileFlags |= s->compileFlags;
544 /* resolve inconsistency, when brush content was determined by 1st face */
545 if ( b->contentShader->compileFlags & C_LIQUID ){
548 else if ( s->compileFlags & C_LIQUID ){
549 b->contentShader = s->shaderInfo;
551 else if ( b->contentShader->compileFlags & C_FOG ){
554 else if ( s->compileFlags & C_FOG ){
555 b->contentShader = s->shaderInfo;
558 else if ( b->contentShader->contentFlags & 0x10000 ){
561 else if ( s->contentFlags & 0x10000 ){
562 b->contentShader = s->shaderInfo;
564 else if (!( b->contentShader->compileFlags & C_SOLID )){
567 else if (!( s->compileFlags & C_SOLID )){
568 b->contentShader = s->shaderInfo;
572 /* ydnar: getting rid of this stupid warning */
574 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
576 /* check for detail & structural */
577 if ( ( compileFlags & C_DETAIL ) && ( compileFlags & C_STRUCTURAL ) ) {
578 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
579 compileFlags &= ~C_DETAIL;
582 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
584 compileFlags &= ~C_DETAIL;
587 /* all translucent brushes that aren't specifically made structural will be detail */
588 if ( ( compileFlags & C_TRANSLUCENT ) && !( compileFlags & C_STRUCTURAL ) ) {
589 compileFlags |= C_DETAIL;
593 if ( compileFlags & C_DETAIL ) {
604 if ( compileFlags & C_TRANSLUCENT ) {
612 if ( compileFlags & C_AREAPORTAL ) {
616 /* set brush flags */
617 b->contentFlags = contentFlags;
618 b->compileFlags = compileFlags;
625 adds any additional planes necessary to allow the brush being
626 built to be expanded against axial bounding boxes
627 ydnar 2003-01-20: added mrelusive fixes
630 void AddBrushBevels( void ){
632 int i, j, k, l, order;
642 // add the axial planes
645 for ( axis = 0; axis < 3; axis++ ) {
646 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
647 // see if the plane is allready present
648 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
650 /* ydnar: testing disabling of mre code */
653 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
658 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
663 if ( ( dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
664 ( dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f ) ) {
670 if ( i == buildBrush->numsides ) {
672 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
673 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
675 memset( s, 0, sizeof( *s ) );
676 buildBrush->numsides++;
677 VectorClear( normal );
681 /* ydnar: adding bevel plane snapping for fewer bsp planes */
682 if ( bevelSnap > 0 ) {
683 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
686 dist = buildBrush->maxs[ axis ];
691 /* ydnar: adding bevel plane snapping for fewer bsp planes */
692 if ( bevelSnap > 0 ) {
693 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
696 dist = -buildBrush->mins[ axis ];
700 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
701 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
706 // if the plane is not in it canonical order, swap it
708 sidetemp = buildBrush->sides[order];
709 buildBrush->sides[order] = buildBrush->sides[i];
710 buildBrush->sides[i] = sidetemp;
716 // add the edge bevels
718 if ( buildBrush->numsides == 6 ) {
719 return; // pure axial
722 // test the non-axial plane edges
723 for ( i = 6; i < buildBrush->numsides; i++ ) {
724 s = buildBrush->sides + i;
729 for ( j = 0; j < w->numpoints; j++ ) {
730 k = ( j + 1 ) % w->numpoints;
731 VectorSubtract( w->p[j], w->p[k], vec );
732 if ( VectorNormalize( vec, vec ) < 0.5f ) {
736 for ( k = 0; k < 3; k++ ) {
737 if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) {
742 continue; // only test non-axial edges
746 //% Sys_Printf( "-------------\n" );
748 // try the six possible slanted axials from this edge
749 for ( axis = 0; axis < 3; axis++ ) {
750 for ( dir = -1; dir <= 1; dir += 2 ) {
754 CrossProduct( vec, vec2, normal );
755 if ( VectorNormalize( normal, normal ) < 0.5f ) {
758 dist = DotProduct( w->p[j], normal );
760 // if all the points on all the sides are
761 // behind this plane, it is a proper edge bevel
762 for ( k = 0; k < buildBrush->numsides; k++ ) {
764 // if this plane has allready been used, skip it
765 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
769 w2 = buildBrush->sides[k].winding;
774 for ( l = 0; l < w2->numpoints; l++ ) {
775 d = DotProduct( w2->p[l], normal ) - dist;
777 break; // point in front
783 // if some point was at the front
784 if ( l != w2->numpoints ) {
788 // if no points at the back then the winding is on the bevel plane
789 if ( minBack > -0.1f ) {
790 //% Sys_Printf( "On bevel plane\n" );
795 if ( k != buildBrush->numsides ) {
796 continue; // wasn't part of the outer hull
800 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
803 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
804 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
806 s2 = &buildBrush->sides[buildBrush->numsides];
807 buildBrush->numsides++;
808 memset( s2, 0, sizeof( *s2 ) );
810 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
811 s2->contentFlags = buildBrush->sides[0].contentFlags;
824 produces a final brush based on the buildBrush->sides array
825 and links it to the current entity
828 static void MergeOrigin( entity_t *ent, vec3_t origin ){
832 /* we have not parsed the brush completely yet... */
833 GetVectorForKey( ent, "origin", ent->origin );
835 VectorMA( origin, -1, ent->originbrush_origin, adjustment );
836 VectorAdd( adjustment, ent->origin, ent->origin );
837 VectorCopy( origin, ent->originbrush_origin );
839 sprintf( string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2] );
840 SetKeyValue( ent, "origin", string );
843 brush_t *FinishBrush( qboolean noCollapseGroups ){
847 /* create windings for sides and bounds for brush */
848 if ( !CreateBrushWindings( buildBrush ) ) {
852 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
853 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
854 if ( buildBrush->compileFlags & C_ORIGIN ) {
857 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
858 mapEnt->mapEntityNum, entitySourceBrushes );
860 if ( numEntities == 1 ) {
861 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
862 mapEnt->mapEntityNum, entitySourceBrushes );
866 VectorAdd( buildBrush->mins, buildBrush->maxs, origin );
867 VectorScale( origin, 0.5, origin );
869 MergeOrigin( &entities[ numEntities - 1 ], origin );
871 /* don't keep this brush */
875 /* determine if the brush is an area portal */
876 if ( buildBrush->compileFlags & C_AREAPORTAL ) {
877 if ( numEntities != 1 ) {
878 Sys_Printf( "Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
883 /* add bevel planes */
884 if ( !noCollapseGroups ) {
889 b = CopyBrush( buildBrush );
891 /* set map entity and brush numbering */
892 b->entityNum = mapEnt->mapEntityNum;
893 b->brushNum = entitySourceBrushes;
898 /* link opaque brushes to head of list, translucent brushes to end */
899 if ( b->opaque || mapEnt->lastBrush == NULL ) {
900 b->next = mapEnt->brushes;
902 if ( mapEnt->lastBrush == NULL ) {
903 mapEnt->lastBrush = b;
909 mapEnt->lastBrush->next = b;
910 mapEnt->lastBrush = b;
913 /* link colorMod volume brushes to the entity directly */
914 if ( b->contentShader != NULL &&
915 b->contentShader->colorMod != NULL &&
916 b->contentShader->colorMod->type == CM_VOLUME ) {
917 b->nextColorModBrush = mapEnt->colorModBrushes;
918 mapEnt->colorModBrushes = b;
921 /* return to sender */
928 TextureAxisFromPlane()
929 determines best orthagonal axis to project a texture onto a wall
930 (must be identical in radiant!)
933 vec3_t baseaxis[18] =
935 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
936 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
937 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
938 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
939 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
940 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
943 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ){
951 for ( i = 0 ; i < 6 ; i++ )
953 dot = DotProduct( pln->normal, baseaxis[i * 3] );
954 if ( dot > best + 0.0001f ) { /* ydnar: bug 637 fix, suggested by jmonroe */
960 VectorCopy( baseaxis[bestaxis * 3 + 1], xv );
961 VectorCopy( baseaxis[bestaxis * 3 + 2], yv );
968 creates world-to-texture mapping vecs for crappy quake plane arrangements
971 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] ){
974 vec_t ang, sinv, cosv;
979 TextureAxisFromPlane( plane, vecs[0], vecs[1] );
992 else if ( rotate == 90 ) {
995 else if ( rotate == 180 ) {
996 sinv = 0 ; cosv = -1;
998 else if ( rotate == 270 ) {
999 sinv = -1 ; cosv = 0;
1003 ang = rotate / 180 * Q_PI;
1011 else if ( vecs[0][1] ) {
1021 else if ( vecs[1][1] ) {
1028 for ( i = 0 ; i < 2 ; i++ ) {
1029 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
1030 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
1035 for ( i = 0 ; i < 2 ; i++ )
1036 for ( j = 0 ; j < 3 ; j++ )
1037 mappingVecs[i][j] = vecs[i][j] / scale[i];
1039 mappingVecs[0][3] = shift[0];
1040 mappingVecs[1][3] = shift[1];
1047 parses the sides into buildBrush->sides[], nothing else.
1048 no validation, back plane removal, etc.
1051 added brush epairs parsing ( ignoring actually )
1053 added exclusive brush primitive parsing
1055 support for old brush format back in
1056 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
1059 static void ParseRawBrush( qboolean onlyLights ){
1061 vec3_t planePoints[ 3 ];
1067 char name[ MAX_QPATH ];
1068 char shader[ MAX_QPATH ];
1073 buildBrush->numsides = 0;
1074 buildBrush->detail = qfalse;
1077 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1084 if ( !GetToken( qtrue ) ) {
1087 if ( !strcmp( token, "}" ) ) {
1091 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
1092 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1095 if ( strcmp( token, "(" ) ) {
1106 /* test side count */
1107 if ( buildBrush->numsides >= MAX_BUILD_SIDES ) {
1108 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
1112 side = &buildBrush->sides[ buildBrush->numsides ];
1113 memset( side, 0, sizeof( *side ) );
1114 buildBrush->numsides++;
1116 /* read the three point plane definition */
1117 Parse1DMatrix( 3, planePoints[ 0 ] );
1118 Parse1DMatrix( 3, planePoints[ 1 ] );
1119 Parse1DMatrix( 3, planePoints[ 2 ] );
1121 /* bp: read the texture matrix */
1122 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1123 Parse2DMatrix( 2, 3, (float*) side->texMat );
1126 /* read shader name */
1128 strcpy( name, token );
1131 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1133 shift[ 0 ] = atof( token );
1135 shift[ 1 ] = atof( token );
1137 rotate = atof( token );
1139 scale[ 0 ] = atof( token );
1141 scale[ 1 ] = atof( token );
1144 /* set default flags and values */
1145 sprintf( shader, "textures/%s", name );
1147 si = &shaderInfo[ 0 ];
1150 si = ShaderInfoForShader( shader );
1152 side->shaderInfo = si;
1153 side->surfaceFlags = si->surfaceFlags;
1154 side->contentFlags = si->contentFlags;
1155 side->compileFlags = si->compileFlags;
1156 side->value = si->value;
1158 /* ydnar: gs mods: bias texture shift */
1159 if ( si->globalTexture == qfalse ) {
1160 shift[ 0 ] -= ( floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth );
1161 shift[ 1 ] -= ( floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight );
1165 historically, there are 3 integer values at the end of a brushside line in a .map file.
1166 in quake 3, the only thing that mattered was the first of these three values, which
1167 was previously the content flags. and only then did a single bit matter, the detail
1168 bit. because every game has its own special flags for specifying detail, the
1169 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
1170 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
1171 is stored in compileFlags, as opposed to contentFlags, for multiple-game
1175 if ( TokenAvailable() ) {
1176 /* get detail bit from map content flags */
1178 flags = atoi( token );
1179 if ( flags & C_DETAIL ) {
1180 side->compileFlags |= C_DETAIL;
1185 //% td.flags = atoi( token );
1187 //% td.value = atoi( token );
1190 /* find the plane number */
1191 planenum = MapPlaneFromPoints( planePoints );
1192 side->planenum = planenum;
1194 /* bp: get the texture mapping for this texturedef / plane combination */
1195 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1196 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
1201 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1211 RemoveDuplicateBrushPlanes
1212 returns false if the brush has a mirrored set of planes,
1213 meaning it encloses no volume.
1214 also removes planes without any normal
1217 qboolean RemoveDuplicateBrushPlanes( brush_t *b ){
1223 for ( i = 1 ; i < b->numsides ; i++ ) {
1225 // check for a degenerate plane
1226 if ( sides[i].planenum == -1 ) {
1227 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
1229 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1230 sides[k - 1] = sides[k];
1237 // check for duplication and mirroring
1238 for ( j = 0 ; j < i ; j++ ) {
1239 if ( sides[i].planenum == sides[j].planenum ) {
1240 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1241 // remove the second duplicate
1242 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1243 sides[k - 1] = sides[k];
1250 if ( sides[i].planenum == ( sides[j].planenum ^ 1 ) ) {
1251 // mirror plane, brush is invalid
1252 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1264 parses a brush out of a map file and sets it up
1267 static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups ){
1268 /* parse the brush out of the map */
1269 ParseRawBrush( onlyLights );
1271 /* only go this far? */
1276 /* set some defaults */
1277 buildBrush->portalareas[ 0 ] = -1;
1278 buildBrush->portalareas[ 1 ] = -1;
1279 buildBrush->entityNum = numMapEntities - 1;
1280 buildBrush->brushNum = entitySourceBrushes;
1282 /* if there are mirrored planes, the entire brush is invalid */
1283 if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
1287 /* get the content for the entire brush */
1288 SetBrushContents( buildBrush );
1290 /* allow detail brushes to be removed */
1291 if ( nodetail && ( buildBrush->compileFlags & C_DETAIL ) ) {
1292 //% FreeBrush( buildBrush );
1296 /* allow liquid brushes to be removed */
1297 if ( nowater && ( buildBrush->compileFlags & C_LIQUID ) ) {
1298 //% FreeBrush( buildBrush );
1302 /* ydnar: allow hint brushes to be removed */
1303 if ( noHint && ( buildBrush->compileFlags & C_HINT ) ) {
1304 //% FreeBrush( buildBrush );
1308 /* finish the brush */
1309 FinishBrush( noCollapseGroups );
1315 MoveBrushesToWorld()
1316 takes all of the brushes from the current entity and
1317 adds them to the world's brush list
1318 (used by func_group)
1321 void AdjustBrushesForOrigin( entity_t *ent );
1322 void MoveBrushesToWorld( entity_t *ent ){
1326 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1327 VectorScale( ent->origin, -1, ent->originbrush_origin );
1328 AdjustBrushesForOrigin( ent );
1329 VectorClear( ent->originbrush_origin );
1332 for ( b = ent->brushes; b != NULL; b = next )
1334 /* get next brush */
1337 /* link opaque brushes to head of list, translucent brushes to end */
1338 if ( b->opaque || entities[ 0 ].lastBrush == NULL ) {
1339 b->next = entities[ 0 ].brushes;
1340 entities[ 0 ].brushes = b;
1341 if ( entities[ 0 ].lastBrush == NULL ) {
1342 entities[ 0 ].lastBrush = b;
1348 entities[ 0 ].lastBrush->next = b;
1349 entities[ 0 ].lastBrush = b;
1352 ent->brushes = NULL;
1354 /* ydnar: move colormod brushes */
1355 if ( ent->colorModBrushes != NULL ) {
1356 for ( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush ) ;
1358 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1359 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1361 ent->colorModBrushes = NULL;
1365 if ( ent->patches != NULL ) {
1366 for ( pm = ent->patches; pm->next != NULL; pm = pm->next ) ;
1368 pm->next = entities[ 0 ].patches;
1369 entities[ 0 ].patches = ent->patches;
1371 ent->patches = NULL;
1378 AdjustBrushesForOrigin()
1381 void AdjustBrushesForOrigin( entity_t *ent ){
1389 /* walk brush list */
1390 for ( b = ent->brushes; b != NULL; b = b->next )
1392 /* offset brush planes */
1393 for ( i = 0; i < b->numsides; i++ )
1395 /* get brush side */
1398 /* offset side plane */
1399 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1401 /* find a new plane */
1402 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1405 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1406 CreateBrushWindings( b );
1409 /* walk patch list */
1410 for ( p = ent->patches; p != NULL; p = p->next )
1412 for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
1413 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1420 SetEntityBounds() - ydnar
1421 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1424 void SetEntityBounds( entity_t *e ){
1434 /* walk the entity's brushes/patches and determine bounds */
1435 ClearBounds( mins, maxs );
1436 for ( b = e->brushes; b; b = b->next )
1438 AddPointToBounds( b->mins, mins, maxs );
1439 AddPointToBounds( b->maxs, mins, maxs );
1441 for ( p = e->patches; p; p = p->next )
1443 for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
1444 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1447 /* try to find explicit min/max key */
1448 value = ValueForKey( e, "min" );
1449 if ( value[ 0 ] != '\0' ) {
1450 GetVectorForKey( e, "min", mins );
1452 value = ValueForKey( e, "max" );
1453 if ( value[ 0 ] != '\0' ) {
1454 GetVectorForKey( e, "max", maxs );
1457 /* store the bounds */
1458 for ( b = e->brushes; b; b = b->next )
1460 VectorCopy( mins, b->eMins );
1461 VectorCopy( maxs, b->eMaxs );
1463 for ( p = e->patches; p; p = p->next )
1465 VectorCopy( mins, p->eMins );
1466 VectorCopy( maxs, p->eMaxs );
1473 LoadEntityIndexMap() - ydnar
1474 based on LoadAlphaMap() from terrain.c, a little more generic
1477 void LoadEntityIndexMap( entity_t *e ){
1478 int i, size, numLayers, w, h;
1479 const char *value, *indexMapFilename, *shader;
1480 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1482 unsigned int *pixels32;
1488 /* this only works with bmodel ents */
1489 if ( e->brushes == NULL && e->patches == NULL ) {
1493 /* determine if there is an index map (support legacy "alphamap" key as well) */
1494 value = ValueForKey( e, "_indexmap" );
1495 if ( value[ 0 ] == '\0' ) {
1496 value = ValueForKey( e, "alphamap" );
1498 if ( value[ 0 ] == '\0' ) {
1501 indexMapFilename = value;
1503 /* get number of layers (support legacy "layers" key as well) */
1504 value = ValueForKey( e, "_layers" );
1505 if ( value[ 0 ] == '\0' ) {
1506 value = ValueForKey( e, "layers" );
1508 if ( value[ 0 ] == '\0' ) {
1509 Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1510 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1513 numLayers = atoi( value );
1514 if ( numLayers < 1 ) {
1515 Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1516 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1520 /* get base shader name (support legacy "shader" key as well) */
1521 value = ValueForKey( mapEnt, "_shader" );
1522 if ( value[ 0 ] == '\0' ) {
1523 value = ValueForKey( e, "shader" );
1525 if ( value[ 0 ] == '\0' ) {
1526 Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1527 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1533 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1535 /* get index map file extension */
1536 ExtractFileExtension( indexMapFilename, ext );
1538 /* handle tga image */
1539 if ( !Q_stricmp( ext, "tga" ) ) {
1541 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1543 /* convert to bytes */
1545 pixels = safe_malloc( size );
1546 for ( i = 0; i < size; i++ )
1548 pixels[ i ] = ( ( pixels32[ i ] & 0xFF ) * numLayers ) / 256;
1549 if ( pixels[ i ] >= numLayers ) {
1550 pixels[ i ] = numLayers - 1;
1554 /* free the 32 bit image */
1560 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1563 //% Sys_Printf( "-------------------------------" );
1565 /* fix up out-of-range values */
1567 for ( i = 0; i < size; i++ )
1569 if ( pixels[ i ] >= numLayers ) {
1570 pixels[ i ] = numLayers - 1;
1574 //% if( (i % w) == 0 )
1575 //% Sys_Printf( "\n" );
1576 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1580 //% Sys_Printf( "\n-------------------------------\n" );
1583 /* the index map must be at least 2x2 pixels */
1584 if ( w < 2 || h < 2 ) {
1585 Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1586 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1591 /* create a new index map */
1592 im = safe_malloc0( sizeof( *im ) );
1597 im->numLayers = numLayers;
1598 strcpy( im->name, indexMapFilename );
1599 strcpy( im->shader, shader );
1600 im->pixels = pixels;
1602 /* get height offsets */
1603 value = ValueForKey( mapEnt, "_offsets" );
1604 if ( value[ 0 ] == '\0' ) {
1605 value = ValueForKey( e, "offsets" );
1607 if ( value[ 0 ] != '\0' ) {
1608 /* value is a space-seperated set of numbers */
1609 strcpy( offset, value );
1612 /* get each value */
1613 for ( i = 0; i < 256 && *search != '\0'; i++ )
1615 space = strstr( search, " " );
1616 if ( space != NULL ) {
1619 im->offsets[ i ] = atof( search );
1620 if ( space == NULL ) {
1627 /* store the index map in every brush/patch in the entity */
1628 for ( b = e->brushes; b != NULL; b = b->next )
1630 for ( p = e->patches; p != NULL; p = p->next )
1642 parses a single entity out of a map file
1645 static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups ){
1647 const char *classname, *value;
1648 float lightmapScale, shadeAngle;
1649 int lightmapSampleSize;
1650 char shader[ MAX_QPATH ];
1651 shaderInfo_t *celShader = NULL;
1655 int castShadows, recvShadows;
1659 if ( !GetToken( qtrue ) ) {
1663 /* conformance check */
1664 if ( strcmp( token, "{" ) ) {
1665 Sys_FPrintf( SYS_WRN, "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1666 "Continuing to process map, but resulting BSP may be invalid.\n",
1667 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1672 AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
1675 entitySourceBrushes = 0;
1676 mapEnt = &entities[ numEntities ];
1678 memset( mapEnt, 0, sizeof( *mapEnt ) );
1680 /* ydnar: true entity numbering */
1681 mapEnt->mapEntityNum = numMapEntities;
1687 /* get initial token */
1688 if ( !GetToken( qtrue ) ) {
1689 Sys_FPrintf( SYS_WRN, "WARNING: ParseEntity: EOF without closing brace\n"
1690 "Continuing to process map, but resulting BSP may be invalid.\n" );
1694 if ( !strcmp( token, "}" ) ) {
1698 if ( !strcmp( token, "{" ) ) {
1699 /* parse a brush or patch */
1700 if ( !GetToken( qtrue ) ) {
1705 if ( !strcmp( token, "patchDef2" ) ) {
1707 ParsePatch( onlyLights );
1709 else if ( !strcmp( token, "terrainDef" ) ) {
1711 Sys_FPrintf( SYS_WRN, "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1713 else if ( !strcmp( token, "brushDef" ) ) {
1714 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1715 Error( "Old brush format not allowed in new brush format map" );
1717 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1719 /* parse brush primitive */
1720 ParseBrush( onlyLights, noCollapseGroups );
1724 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1725 Error( "New brush format not allowed in old brush format map" );
1727 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1729 /* parse old brush format */
1731 ParseBrush( onlyLights, noCollapseGroups );
1733 entitySourceBrushes++;
1737 /* parse a key / value pair */
1740 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1741 if ( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' ) {
1742 ep->next = mapEnt->epairs;
1743 mapEnt->epairs = ep;
1748 /* ydnar: get classname */
1749 classname = ValueForKey( mapEnt, "classname" );
1751 /* ydnar: only lights? */
1752 if ( onlyLights && Q_strncasecmp( classname, "light", 5 ) ) {
1757 /* ydnar: determine if this is a func_group */
1758 if ( !Q_stricmp( "func_group", classname ) ) {
1765 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1766 if ( funcGroup || mapEnt->mapEntityNum == 0 ) {
1767 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1768 castShadows = WORLDSPAWN_CAST_SHADOWS;
1769 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1772 /* other entities don't cast any shadows, but recv worldspawn shadows */
1775 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1776 castShadows = ENTITY_CAST_SHADOWS;
1777 recvShadows = ENTITY_RECV_SHADOWS;
1780 /* get explicit shadow flags */
1781 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1783 /* vortex: added _ls key (short name of lightmapscale) */
1784 /* ydnar: get lightmap scaling value for this entity */
1785 lightmapScale = 0.0f;
1786 if ( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1787 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1788 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) ) {
1789 /* get lightmap scale from entity */
1790 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1791 if ( lightmapScale <= 0.0f ) {
1792 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1794 if ( lightmapScale <= 0.0f ) {
1795 lightmapScale = FloatForKey( mapEnt, "_ls" );
1797 if ( lightmapScale < 0.0f ) {
1798 lightmapScale = 0.0f;
1800 if ( lightmapScale > 0.0f ) {
1801 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1805 /* ydnar: get cel shader :) for this entity */
1806 value = ValueForKey( mapEnt, "_celshader" );
1807 if ( value[ 0 ] == '\0' ) {
1808 value = ValueForKey( &entities[ 0 ], "_celshader" );
1810 if ( value[ 0 ] != '\0' ) {
1811 if ( strcmp( value, "none" ) ) {
1812 sprintf( shader, "textures/%s", value );
1813 celShader = ShaderInfoForShader( shader );
1814 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1822 celShader = ( *globalCelShader ? ShaderInfoForShader( globalCelShader ) : NULL );
1825 /* jal : entity based _shadeangle */
1827 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) ) {
1828 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1830 /* vortex' aliases */
1831 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) ) {
1832 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1834 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) ) {
1835 shadeAngle = FloatForKey( mapEnt, "_sn" );
1837 else if ( strcmp( "", ValueForKey( mapEnt, "_sa" ) ) ) {
1838 shadeAngle = FloatForKey( mapEnt, "_sa" );
1840 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) ) {
1841 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1844 if ( shadeAngle < 0.0f ) {
1848 if ( shadeAngle > 0.0f ) {
1849 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1852 /* jal : entity based _samplesize */
1853 lightmapSampleSize = 0;
1854 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) ) {
1855 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1857 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) ) {
1858 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1860 else if ( strcmp( "", ValueForKey( mapEnt, "_ss" ) ) ) {
1861 lightmapSampleSize = IntForKey( mapEnt, "_ss" );
1864 if ( lightmapSampleSize < 0 ) {
1865 lightmapSampleSize = 0;
1868 if ( lightmapSampleSize > 0 ) {
1869 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1872 /* attach stuff to everything in the entity */
1873 for ( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1875 brush->entityNum = mapEnt->mapEntityNum;
1876 brush->castShadows = castShadows;
1877 brush->recvShadows = recvShadows;
1878 brush->lightmapSampleSize = lightmapSampleSize;
1879 brush->lightmapScale = lightmapScale;
1880 brush->celShader = celShader;
1881 brush->shadeAngleDegrees = shadeAngle;
1884 for ( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1886 patch->entityNum = mapEnt->mapEntityNum;
1887 patch->castShadows = castShadows;
1888 patch->recvShadows = recvShadows;
1889 patch->lightmapSampleSize = lightmapSampleSize;
1890 patch->lightmapScale = lightmapScale;
1891 patch->celShader = celShader;
1894 /* ydnar: gs mods: set entity bounds */
1895 SetEntityBounds( mapEnt );
1897 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1898 LoadEntityIndexMap( mapEnt );
1900 /* get entity origin and adjust brushes */
1901 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1902 if ( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] ) {
1903 AdjustBrushesForOrigin( mapEnt );
1906 /* group_info entities are just for editor grouping (fixme: leak!) */
1907 if ( !noCollapseGroups && !Q_stricmp( "group_info", classname ) ) {
1912 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1913 if ( !noCollapseGroups && funcGroup ) {
1914 MoveBrushesToWorld( mapEnt );
1927 loads a map file into a list of entities
1930 void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups ){
1933 int oldNumEntities = 0, numMapBrushes;
1937 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1938 Sys_Printf( "Loading %s\n", filename );
1941 file = SafeOpenRead( filename );
1944 /* load the map file */
1945 LoadScriptFile( filename, -1 );
1949 oldNumEntities = numEntities;
1956 numMapDrawSurfs = 0;
1958 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1960 /* allocate a very large temporary brush for building the brushes as they are loaded */
1961 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1963 /* parse the map file */
1964 while ( ParseMapEntity( onlyLights, noCollapseGroups ) ) ;
1968 /* emit some statistics */
1969 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1973 /* set map bounds */
1974 ClearBounds( mapMins, mapMaxs );
1975 for ( b = entities[ 0 ].brushes; b; b = b->next )
1977 AddPointToBounds( b->mins, mapMins, mapMaxs );
1978 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1981 /* get brush counts */
1982 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1983 if ( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) {
1984 Sys_FPrintf( SYS_WRN, "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1987 /* emit some statistics */
1988 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1989 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1990 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches );
1991 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels );
1992 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels );
1993 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1994 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes );
1995 Sys_Printf( "%9d areaportals\n", c_areaportals );
1996 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1997 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1998 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ] );
2000 /* write bogus map */
2002 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );