-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define MAP_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-/* FIXME: remove these vars */\r
-\r
-/* undefine to make plane finding use linear sort (note: really slow) */\r
-#define USE_HASHING\r
-#define PLANE_HASHES 8192\r
-\r
-plane_t *planehash[ PLANE_HASHES ];\r
-\r
-int c_boxbevels;\r
-int c_edgebevels;\r
-int c_areaportals;\r
-int c_detail;\r
-int c_structural;\r
-\r
-\r
-\r
-/*\r
-PlaneEqual()\r
-ydnar: replaced with variable epsilon for djbob\r
-*/\r
-\r
-#define NORMAL_EPSILON 0.00001\r
-#define DIST_EPSILON 0.01\r
-\r
-qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )\r
-{\r
- float ne, de;\r
- \r
- \r
- /* get local copies */\r
- ne = normalEpsilon;\r
- de = distanceEpsilon;\r
- \r
- /* compare */\r
- if( fabs( p->dist - dist ) <= de &&\r
- fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&\r
- fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&\r
- fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )\r
- return qtrue;\r
- \r
- /* different */\r
- return qfalse;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddPlaneToHash()\r
-*/\r
-\r
-void AddPlaneToHash( plane_t *p )\r
-{\r
- int hash;\r
-\r
- \r
- hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );\r
-\r
- p->hash_chain = planehash[hash];\r
- planehash[hash] = p;\r
-}\r
-\r
-/*\r
-================\r
-CreateNewFloatPlane\r
-================\r
-*/\r
-int CreateNewFloatPlane (vec3_t normal, vec_t dist)\r
-{\r
- plane_t *p, temp;\r
-\r
- if (VectorLength(normal) < 0.5)\r
- {\r
- Sys_Printf( "FloatPlane: bad normal\n");\r
- return -1;\r
- }\r
-\r
- // create a new plane\r
- if (nummapplanes+2 > MAX_MAP_PLANES)\r
- Error ("MAX_MAP_PLANES");\r
-\r
- p = &mapplanes[nummapplanes];\r
- VectorCopy (normal, p->normal);\r
- p->dist = dist;\r
- p->type = (p+1)->type = PlaneTypeForNormal (p->normal);\r
-\r
- VectorSubtract (vec3_origin, normal, (p+1)->normal);\r
- (p+1)->dist = -dist;\r
-\r
- nummapplanes += 2;\r
-\r
- // allways put axial planes facing positive first\r
- if (p->type < 3)\r
- {\r
- if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)\r
- {\r
- // flip order\r
- temp = *p;\r
- *p = *(p+1);\r
- *(p+1) = temp;\r
-\r
- AddPlaneToHash (p);\r
- AddPlaneToHash (p+1);\r
- return nummapplanes - 1;\r
- }\r
- }\r
-\r
- AddPlaneToHash (p);\r
- AddPlaneToHash (p+1);\r
- return nummapplanes - 2;\r
-}\r
-\r
-\r
-\r
-/*\r
-SnapNormal()\r
-snaps a near-axial normal vector\r
-*/\r
-\r
-void SnapNormal( vec3_t normal )\r
-{\r
- int i;\r
-\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( fabs( normal[ i ] - 1 ) < normalEpsilon )\r
- {\r
- VectorClear( normal );\r
- normal[ i ] = 1;\r
- break;\r
- }\r
- if( fabs( normal[ i ] - -1 ) < normalEpsilon )\r
- {\r
- VectorClear( normal );\r
- normal[ i ] = -1;\r
- break;\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-SnapPlane()\r
-snaps a plane to normal/distance epsilons\r
-*/\r
-\r
-void SnapPlane( vec3_t normal, vec_t *dist )\r
-{\r
- SnapNormal( normal );\r
-\r
- if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )\r
- *dist = Q_rint( *dist );\r
-}\r
-\r
-\r
-\r
-/*\r
-FindFloatPlane()\r
-ydnar: changed to allow a number of test points to be supplied that\r
-must be within an epsilon distance of the plane\r
-*/\r
-\r
-int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )\r
-\r
-#ifdef USE_HASHING\r
-\r
-{\r
- int i, j, hash, h;\r
- plane_t *p;\r
- vec_t d;\r
- \r
- \r
- /* hash the plane */\r
- SnapPlane( normal, &dist );\r
- hash = (PLANE_HASHES - 1) & (int) fabs( dist );\r
- \r
- /* search the border bins as well */\r
- for( i = -1; i <= 1; i++ )\r
- {\r
- h = (hash + i) & (PLANE_HASHES - 1);\r
- for( p = planehash[ h ]; p != NULL; p = p->hash_chain )\r
- {\r
- /* do standard plane compare */\r
- if( !PlaneEqual( p, normal, dist ) )\r
- continue;\r
- \r
- /* ydnar: uncomment the following line for old-style plane finding */\r
- //% return p - mapplanes;\r
- \r
- /* ydnar: test supplied points against this plane */\r
- for( j = 0; j < numPoints; j++ )\r
- {\r
- d = DotProduct( points[ j ], normal ) - dist;\r
- if( fabs( d ) > distanceEpsilon )\r
- break;\r
- }\r
- \r
- /* found a matching plane */\r
- if( j >= numPoints )\r
- return p - mapplanes;\r
- }\r
- }\r
- \r
- /* none found, so create a new one */\r
- return CreateNewFloatPlane( normal, dist );\r
-}\r
-\r
-#else\r
-\r
-{\r
- int i;\r
- plane_t *p;\r
- \r
-\r
- SnapPlane( normal, &dist );\r
- for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )\r
- {\r
- if( PlaneEqual( p, normal, dist ) )\r
- return i;\r
- }\r
- \r
- return CreateNewFloatPlane( normal, dist );\r
-}\r
-\r
-#endif\r
-\r
-\r
-\r
-/*\r
-MapPlaneFromPoints()\r
-takes 3 points and finds the plane they lie in\r
-*/\r
-\r
-int MapPlaneFromPoints( vec3_t *p )\r
-{\r
- vec3_t t1, t2, normal;\r
- vec_t dist;\r
- \r
- \r
- /* calc plane normal */\r
- VectorSubtract( p[ 0 ], p[ 1 ], t1 );\r
- VectorSubtract( p[ 2 ], p[ 1 ], t2 );\r
- CrossProduct( t1, t2, normal );\r
- VectorNormalize( normal, normal );\r
- \r
- /* calc plane distance */\r
- dist = DotProduct( p[ 0 ], normal );\r
- \r
- /* store the plane */\r
- return FindFloatPlane( normal, dist, 3, p );\r
-}\r
-\r
-\r
-\r
-/*\r
-SetBrushContents()\r
-the content flags and compile flags on all sides of a brush should be the same\r
-*/\r
-\r
-void SetBrushContents( brush_t *b )\r
-{\r
- int contentFlags, compileFlags;\r
- side_t *s;\r
- int i;\r
- qboolean mixed;\r
- \r
- \r
- /* get initial compile flags from first side */\r
- s = &b->sides[ 0 ];\r
- contentFlags = s->contentFlags;\r
- compileFlags = s->compileFlags;\r
- b->contentShader = s->shaderInfo;\r
- mixed = qfalse;\r
- \r
- /* get the content/compile flags for every side in the brush */\r
- for( i = 1; i < b->numsides; i++, s++ )\r
- {\r
- s = &b->sides[ i ];\r
- if( s->shaderInfo == NULL )\r
- continue;\r
- if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )\r
- mixed = qtrue;\r
- }\r
- \r
- /* ydnar: getting rid of this stupid warning */\r
- //% if( mixed )\r
- //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );\r
-\r
- /* check for detail & structural */\r
- if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )\r
- {\r
- xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );\r
- compileFlags &= ~C_DETAIL;\r
- }\r
- \r
- /* the fulldetail flag will cause detail brushes to be treated like normal brushes */\r
- if( fulldetail )\r
- compileFlags &= ~C_DETAIL;\r
- \r
- /* all translucent brushes that aren't specifically made structural will be detail */\r
- if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )\r
- compileFlags |= C_DETAIL;\r
- \r
- /* detail? */\r
- if( compileFlags & C_DETAIL )\r
- {\r
- c_detail++;\r
- b->detail = qtrue;\r
- }\r
- else\r
- {\r
- c_structural++;\r
- b->detail = qfalse;\r
- }\r
- \r
- /* opaque? */\r
- if( compileFlags & C_TRANSLUCENT )\r
- b->opaque = qfalse;\r
- else\r
- b->opaque = qtrue;\r
- \r
- /* areaportal? */\r
- if( compileFlags & C_AREAPORTAL )\r
- c_areaportals++;\r
- \r
- /* set brush flags */\r
- b->contentFlags = contentFlags;\r
- b->compileFlags = compileFlags;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddBrushBevels()\r
-adds any additional planes necessary to allow the brush being\r
-built to be expanded against axial bounding boxes\r
-ydnar 2003-01-20: added mrelusive fixes\r
-*/\r
-\r
-void AddBrushBevels( void )\r
-{\r
- int axis, dir;\r
- int i, j, k, l, order;\r
- side_t sidetemp;\r
- side_t *s, *s2;\r
- winding_t *w, *w2;\r
- vec3_t normal;\r
- float dist;\r
- vec3_t vec, vec2;\r
- float d, minBack;\r
-\r
- //\r
- // add the axial planes\r
- //\r
- order = 0;\r
- for ( axis = 0; axis < 3; axis++ ) {\r
- for ( dir = -1; dir <= 1; dir += 2, order++ ) {\r
- // see if the plane is allready present\r
- for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )\r
- {\r
- /* ydnar: testing disabling of mre code */\r
- #if 0\r
- if ( dir > 0 ) {\r
- if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {\r
- break;\r
- }\r
- }\r
- else {\r
- if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {\r
- break;\r
- }\r
- }\r
- #else\r
- if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||\r
- (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )\r
- break;\r
- #endif\r
- }\r
-\r
- if ( i == buildBrush->numsides ) {\r
- // add a new side\r
- if ( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
- xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
- }\r
- memset( s, 0, sizeof( *s ) );\r
- buildBrush->numsides++;\r
- VectorClear (normal);\r
- normal[axis] = dir;\r
-\r
- if( dir == 1 )\r
- {\r
- /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
- if( bevelSnap > 0 )\r
- dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;\r
- else\r
- dist = buildBrush->maxs[ axis ];\r
- }\r
- else\r
- {\r
- /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
- if( bevelSnap > 0 )\r
- dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;\r
- else\r
- dist = -buildBrush->mins[ axis ];\r
- }\r
-\r
- s->planenum = FindFloatPlane( normal, dist, 0, NULL );\r
- s->contentFlags = buildBrush->sides[ 0 ].contentFlags;\r
- s->bevel = qtrue;\r
- c_boxbevels++;\r
- }\r
-\r
- // if the plane is not in it canonical order, swap it\r
- if ( i != order ) {\r
- sidetemp = buildBrush->sides[order];\r
- buildBrush->sides[order] = buildBrush->sides[i];\r
- buildBrush->sides[i] = sidetemp;\r
- }\r
- }\r
- }\r
-\r
- //\r
- // add the edge bevels\r
- //\r
- if ( buildBrush->numsides == 6 ) {\r
- return; // pure axial\r
- }\r
-\r
- // test the non-axial plane edges\r
- for ( i = 6; i < buildBrush->numsides; i++ ) {\r
- s = buildBrush->sides + i;\r
- w = s->winding;\r
- if ( !w ) {\r
- continue;\r
- }\r
- for ( j = 0; j < w->numpoints; j++) {\r
- k = (j+1)%w->numpoints;\r
- VectorSubtract( w->p[j], w->p[k], vec );\r
- if ( VectorNormalize( vec, vec ) < 0.5f ) {\r
- continue;\r
- }\r
- SnapNormal( vec );\r
- for ( k = 0; k < 3; k++ ) {\r
- if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {\r
- break; // axial\r
- }\r
- }\r
- if ( k != 3 ) {\r
- continue; // only test non-axial edges\r
- }\r
-\r
- /* debug code */\r
- //% Sys_Printf( "-------------\n" );\r
-\r
- // try the six possible slanted axials from this edge\r
- for ( axis = 0; axis < 3; axis++ ) {\r
- for ( dir = -1; dir <= 1; dir += 2 ) {\r
- // construct a plane\r
- VectorClear( vec2 );\r
- vec2[axis] = dir;\r
- CrossProduct( vec, vec2, normal );\r
- if ( VectorNormalize( normal, normal ) < 0.5f ) {\r
- continue;\r
- }\r
- dist = DotProduct( w->p[j], normal );\r
- \r
- // if all the points on all the sides are\r
- // behind this plane, it is a proper edge bevel\r
- for ( k = 0; k < buildBrush->numsides; k++ ) {\r
-\r
- // if this plane has allready been used, skip it\r
- if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {\r
- break;\r
- }\r
-\r
- w2 = buildBrush->sides[k].winding;\r
- if ( !w2 ) {\r
- continue;\r
- }\r
- minBack = 0.0f;\r
- for ( l = 0; l < w2->numpoints; l++ ) {\r
- d = DotProduct( w2->p[l], normal ) - dist;\r
- if ( d > 0.1f ) {\r
- break; // point in front\r
- }\r
- if ( d < minBack ) {\r
- minBack = d;\r
- }\r
- }\r
- // if some point was at the front\r
- if ( l != w2->numpoints ) {\r
- break;\r
- }\r
-\r
- // if no points at the back then the winding is on the bevel plane\r
- if ( minBack > -0.1f ) {\r
- //% Sys_Printf( "On bevel plane\n" );\r
- break;\r
- }\r
- }\r
-\r
- if ( k != buildBrush->numsides ) {\r
- continue; // wasn't part of the outer hull\r
- }\r
- \r
- /* debug code */\r
- //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );\r
- \r
- // add this plane\r
- if( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
- xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
- }\r
- s2 = &buildBrush->sides[buildBrush->numsides];\r
- buildBrush->numsides++;\r
- memset( s2, 0, sizeof( *s2 ) );\r
-\r
- s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );\r
- s2->contentFlags = buildBrush->sides[0].contentFlags;\r
- s2->bevel = qtrue;\r
- c_edgebevels++;\r
- }\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-FinishBrush()\r
-produces a final brush based on the buildBrush->sides array\r
-and links it to the current entity\r
-*/\r
-\r
-brush_t *FinishBrush( void )\r
-{\r
- brush_t *b;\r
- \r
- \r
- /* create windings for sides and bounds for brush */\r
- if ( !CreateBrushWindings( buildBrush ) )\r
- return NULL;\r
-\r
- /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.\r
- after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */\r
- if( buildBrush->compileFlags & C_ORIGIN )\r
- {\r
- char string[ 32 ];\r
- vec3_t origin;\r
-\r
- if( numEntities == 1 )\r
- {\r
- Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n", \r
- mapEnt->mapEntityNum, entitySourceBrushes );\r
- return NULL;\r
- }\r
- \r
- VectorAdd (buildBrush->mins, buildBrush->maxs, origin);\r
- VectorScale (origin, 0.5, origin);\r
-\r
- sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );\r
- SetKeyValue( &entities[ numEntities - 1 ], "origin", string);\r
-\r
- VectorCopy( origin, entities[ numEntities - 1 ].origin);\r
-\r
- /* don't keep this brush */\r
- return NULL;\r
- }\r
- \r
- /* determine if the brush is an area portal */\r
- if( buildBrush->compileFlags & C_AREAPORTAL )\r
- {\r
- if( numEntities != 1 )\r
- {\r
- Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );\r
- return NULL;\r
- }\r
- }\r
- \r
- /* add bevel planes */\r
- AddBrushBevels();\r
- \r
- /* keep it */\r
- b = CopyBrush( buildBrush );\r
- \r
- /* set map entity and brush numbering */\r
- b->entityNum = mapEnt->mapEntityNum;\r
- b->brushNum = entitySourceBrushes;\r
- \r
- /* set original */\r
- b->original = b;\r
- \r
- /* link opaque brushes to head of list, translucent brushes to end */\r
- if( b->opaque || mapEnt->lastBrush == NULL )\r
- {\r
- b->next = mapEnt->brushes;\r
- mapEnt->brushes = b;\r
- if( mapEnt->lastBrush == NULL )\r
- mapEnt->lastBrush = b;\r
- }\r
- else\r
- {\r
- b->next = NULL;\r
- mapEnt->lastBrush->next = b;\r
- mapEnt->lastBrush = b;\r
- }\r
- \r
- /* return to sender */\r
- return b;\r
-}\r
-\r
-\r
-\r
-/*\r
-TextureAxisFromPlane()\r
-determines best orthagonal axis to project a texture onto a wall\r
-(must be identical in radiant!)\r
-*/\r
-\r
-vec3_t baseaxis[18] =\r
-{\r
- {0,0,1}, {1,0,0}, {0,-1,0}, // floor\r
- {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling\r
- {1,0,0}, {0,1,0}, {0,0,-1}, // west wall\r
- {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall\r
- {0,1,0}, {1,0,0}, {0,0,-1}, // south wall\r
- {0,-1,0}, {1,0,0}, {0,0,-1} // north wall\r
-};\r
-\r
-void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )\r
-{\r
- int bestaxis;\r
- vec_t dot,best;\r
- int i;\r
- \r
- best = 0;\r
- bestaxis = 0;\r
- \r
- for (i=0 ; i<6 ; i++)\r
- {\r
- dot = DotProduct (pln->normal, baseaxis[i*3]);\r
- if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */\r
- {\r
- best = dot;\r
- bestaxis = i;\r
- }\r
- }\r
- \r
- VectorCopy (baseaxis[bestaxis*3+1], xv);\r
- VectorCopy (baseaxis[bestaxis*3+2], yv);\r
-}\r
-\r
-\r
-\r
-/*\r
-QuakeTextureVecs()\r
-creates world-to-texture mapping vecs for crappy quake plane arrangements\r
-*/\r
-\r
-void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )\r
-{\r
- vec3_t vecs[2];\r
- int sv, tv;\r
- vec_t ang, sinv, cosv;\r
- vec_t ns, nt;\r
- int i, j;\r
- \r
- \r
- TextureAxisFromPlane(plane, vecs[0], vecs[1]);\r
- \r
- if (!scale[0])\r
- scale[0] = 1;\r
- if (!scale[1])\r
- scale[1] = 1;\r
-\r
- // rotate axis\r
- if (rotate == 0)\r
- { sinv = 0 ; cosv = 1; }\r
- else if (rotate == 90)\r
- { sinv = 1 ; cosv = 0; }\r
- else if (rotate == 180)\r
- { sinv = 0 ; cosv = -1; }\r
- else if (rotate == 270)\r
- { sinv = -1 ; cosv = 0; }\r
- else\r
- { \r
- ang = rotate / 180 * Q_PI;\r
- sinv = sin(ang);\r
- cosv = cos(ang);\r
- }\r
-\r
- if (vecs[0][0])\r
- sv = 0;\r
- else if (vecs[0][1])\r
- sv = 1;\r
- else\r
- sv = 2;\r
- \r
- if (vecs[1][0])\r
- tv = 0;\r
- else if (vecs[1][1])\r
- tv = 1;\r
- else\r
- tv = 2;\r
- \r
- for (i=0 ; i<2 ; i++) {\r
- ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];\r
- nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];\r
- vecs[i][sv] = ns;\r
- vecs[i][tv] = nt;\r
- }\r
-\r
- for (i=0 ; i<2 ; i++)\r
- for (j=0 ; j<3 ; j++)\r
- mappingVecs[i][j] = vecs[i][j] / scale[i];\r
-\r
- mappingVecs[0][3] = shift[0];\r
- mappingVecs[1][3] = shift[1];\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseRawBrush()\r
-parses the sides into buildBrush->sides[], nothing else.\r
-no validation, back plane removal, etc.\r
-\r
-Timo - 08/26/99\r
-added brush epairs parsing ( ignoring actually )\r
-Timo - 08/04/99\r
-added exclusive brush primitive parsing\r
-Timo - 08/08/99\r
-support for old brush format back in\r
-NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes\r
-*/\r
-\r
-static void ParseRawBrush( qboolean onlyLights )\r
-{\r
- side_t *side;\r
- vec3_t planePoints[ 3 ];\r
- int planenum;\r
- shaderInfo_t *si;\r
- vec_t shift[ 2 ];\r
- vec_t rotate;\r
- vec_t scale[ 2 ];\r
- char name[ MAX_QPATH ];\r
- char shader[ MAX_QPATH ];\r
- int flags;\r
- \r
- \r
- /* initial setup */\r
- buildBrush->numsides = 0;\r
- buildBrush->detail = qfalse;\r
- \r
- /* bp */\r
- if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
- MatchToken( "{" );\r
- \r
- /* parse sides */\r
- while( 1 )\r
- {\r
- if( !GetToken( qtrue ) )\r
- break;\r
- if( !strcmp( token, "}" ) )\r
- break;\r
- \r
- /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */\r
- if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
- {\r
- while( 1 )\r
- {\r
- if( strcmp( token, "(" ) )\r
- GetToken( qfalse );\r
- else\r
- break;\r
- GetToken( qtrue );\r
- }\r
- }\r
- UnGetToken();\r
- \r
- /* test side count */\r
- if( buildBrush->numsides >= MAX_BUILD_SIDES )\r
- xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );\r
- \r
- /* add side */\r
- side = &buildBrush->sides[ buildBrush->numsides ];\r
- memset( side, 0, sizeof( *side ) );\r
- buildBrush->numsides++;\r
- \r
- /* read the three point plane definition */\r
- Parse1DMatrix( 3, planePoints[ 0 ] );\r
- Parse1DMatrix( 3, planePoints[ 1 ] );\r
- Parse1DMatrix( 3, planePoints[ 2 ] );\r
- \r
- /* bp: read the texture matrix */\r
- if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
- Parse2DMatrix( 2, 3, (float*) side->texMat );\r
- \r
- /* read shader name */\r
- GetToken( qfalse );\r
- strcpy( name, token );\r
- \r
- /* bp */\r
- if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
- {\r
- GetToken( qfalse );\r
- shift[ 0 ] = atof( token );\r
- GetToken( qfalse );\r
- shift[ 1 ] = atof( token );\r
- GetToken( qfalse );\r
- rotate = atof( token ); \r
- GetToken( qfalse );\r
- scale[ 0 ] = atof( token );\r
- GetToken( qfalse );\r
- scale[ 1 ] = atof( token );\r
- }\r
- \r
- /* set default flags and values */\r
- sprintf( shader, "textures/%s", name );\r
- if( onlyLights )\r
- si = &shaderInfo[ 0 ];\r
- else\r
- si = ShaderInfoForShader( shader );\r
- side->shaderInfo = si;\r
- side->surfaceFlags = si->surfaceFlags;\r
- side->contentFlags = si->contentFlags;\r
- side->compileFlags = si->compileFlags;\r
- side->value = si->value;\r
- \r
- /* ydnar: gs mods: bias texture shift */\r
- if( si->globalTexture == qfalse )\r
- {\r
- shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);\r
- shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);\r
- }\r
- \r
- /*\r
- historically, there are 3 integer values at the end of a brushside line in a .map file.\r
- in quake 3, the only thing that mattered was the first of these three values, which\r
- was previously the content flags. and only then did a single bit matter, the detail\r
- bit. because every game has its own special flags for specifying detail, the\r
- traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0\r
- by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but\r
- is stored in compileFlags, as opposed to contentFlags, for multiple-game\r
- portability. :sigh:\r
- */\r
- \r
- if( TokenAvailable() )\r
- {\r
- /* get detail bit from map content flags */\r
- GetToken( qfalse );\r
- flags = atoi( token );\r
- if( flags & C_DETAIL )\r
- side->compileFlags |= C_DETAIL;\r
- \r
- /* historical */\r
- GetToken( qfalse );\r
- //% td.flags = atoi( token );\r
- GetToken( qfalse );\r
- //% td.value = atoi( token );\r
- }\r
- \r
- /* find the plane number */\r
- planenum = MapPlaneFromPoints( planePoints );\r
- side->planenum = planenum;\r
- \r
- /* bp: get the texture mapping for this texturedef / plane combination */\r
- if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
- QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );\r
- }\r
- \r
- /* bp */\r
- if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
- {\r
- UnGetToken();\r
- MatchToken( "}" );\r
- MatchToken( "}" );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-RemoveDuplicateBrushPlanes\r
-returns false if the brush has a mirrored set of planes,\r
-meaning it encloses no volume.\r
-also removes planes without any normal\r
-*/\r
-\r
-qboolean RemoveDuplicateBrushPlanes( brush_t *b )\r
-{\r
- int i, j, k;\r
- side_t *sides;\r
-\r
- sides = b->sides;\r
-\r
- for ( i = 1 ; i < b->numsides ; i++ ) {\r
-\r
- // check for a degenerate plane\r
- if ( sides[i].planenum == -1) {\r
- xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );\r
- // remove it\r
- for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
- sides[k-1] = sides[k];\r
- }\r
- b->numsides--;\r
- i--;\r
- continue;\r
- }\r
-\r
- // check for duplication and mirroring\r
- for ( j = 0 ; j < i ; j++ ) {\r
- if ( sides[i].planenum == sides[j].planenum ) {\r
- xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );\r
- // remove the second duplicate\r
- for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
- sides[k-1] = sides[k];\r
- }\r
- b->numsides--;\r
- i--;\r
- break;\r
- }\r
-\r
- if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {\r
- // mirror plane, brush is invalid\r
- xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );\r
- return qfalse;\r
- }\r
- }\r
- }\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseBrush()\r
-parses a brush out of a map file and sets it up\r
-*/\r
-\r
-static void ParseBrush( qboolean onlyLights )\r
-{\r
- brush_t *b;\r
- \r
- \r
- /* parse the brush out of the map */\r
- ParseRawBrush( onlyLights );\r
- \r
- /* only go this far? */\r
- if( onlyLights )\r
- return;\r
- \r
- /* set some defaults */\r
- buildBrush->portalareas[ 0 ] = -1;\r
- buildBrush->portalareas[ 1 ] = -1;\r
- buildBrush->entityNum = numMapEntities - 1;\r
- buildBrush->brushNum = entitySourceBrushes;\r
- \r
- /* if there are mirrored planes, the entire brush is invalid */\r
- if( !RemoveDuplicateBrushPlanes( buildBrush ) )\r
- return;\r
- \r
- /* get the content for the entire brush */\r
- SetBrushContents( buildBrush );\r
- \r
- /* allow detail brushes to be removed */\r
- if( nodetail && (buildBrush->compileFlags & C_DETAIL) )\r
- {\r
- //% FreeBrush( buildBrush );\r
- return;\r
- }\r
- \r
- /* allow liquid brushes to be removed */\r
- if( nowater && (buildBrush->compileFlags & C_LIQUID ) )\r
- {\r
- //% FreeBrush( buildBrush );\r
- return;\r
- }\r
- \r
- /* ydnar: allow hint brushes to be removed */\r
- if( noHint && (buildBrush->compileFlags & C_HINT) )\r
- {\r
- //% FreeBrush( buildBrush );\r
- return;\r
- }\r
- \r
- /* finish the brush */\r
- b = FinishBrush();\r
-}\r
-\r
-\r
-\r
-/*\r
-MoveBrushesToWorld()\r
-takes all of the brushes from the current entity and\r
-adds them to the world's brush list\r
-(used by func_group)\r
-*/\r
-\r
-void MoveBrushesToWorld( entity_t *ent )\r
-{\r
- brush_t *b, *next;\r
- parseMesh_t *pm;\r
-\r
- \r
- /* move brushes */\r
- for( b = ent->brushes; b != NULL; b = next )\r
- {\r
- /* get next brush */\r
- next = b->next;\r
- \r
- /* link opaque brushes to head of list, translucent brushes to end */\r
- if( b->opaque || entities[ 0 ].lastBrush == NULL )\r
- {\r
- b->next = entities[ 0 ].brushes;\r
- entities[ 0 ].brushes = b;\r
- if( entities[ 0 ].lastBrush == NULL )\r
- entities[ 0 ].lastBrush = b;\r
- }\r
- else\r
- {\r
- b->next = NULL;\r
- entities[ 0 ].lastBrush->next = b;\r
- entities[ 0 ].lastBrush = b;\r
- }\r
- \r
- //% b->next = entities[ 0 ].brushes;\r
- //% entities[ 0 ].brushes = b;\r
- }\r
- ent->brushes = NULL;\r
- \r
- /* move patches */\r
- if( ent->patches != NULL )\r
- {\r
- for( pm = ent->patches; pm->next; pm = pm->next );\r
- \r
- pm->next = entities[ 0 ].patches;\r
- entities[ 0 ].patches = ent->patches;\r
- \r
- ent->patches = NULL;\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-AdjustBrushesForOrigin()\r
-*/\r
-\r
-void AdjustBrushesForOrigin( entity_t *ent )\r
-{\r
- \r
- int i;\r
- side_t *s;\r
- vec_t newdist;\r
- brush_t *b;\r
- parseMesh_t *p;\r
- \r
- \r
- /* walk brush list */\r
- for( b = ent->brushes; b != NULL; b = b->next )\r
- {\r
- /* offset brush planes */\r
- for( i = 0; i < b->numsides; i++)\r
- {\r
- /* get brush side */\r
- s = &b->sides[ i ];\r
- \r
- /* offset side plane */\r
- newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );\r
- \r
- /* find a new plane */\r
- s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );\r
- }\r
- \r
- /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */\r
- CreateBrushWindings( b );\r
- }\r
- \r
- /* walk patch list */\r
- for( p = ent->patches; p != NULL; p = p->next )\r
- {\r
- for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
- VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-SetEntityBounds() - ydnar\r
-finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)\r
-*/\r
-\r
-void SetEntityBounds( entity_t *e )\r
-{\r
- int i;\r
- brush_t *b;\r
- parseMesh_t *p;\r
- vec3_t mins, maxs;\r
- const char *value;\r
-\r
- \r
- \r
-\r
- /* walk the entity's brushes/patches and determine bounds */\r
- ClearBounds( mins, maxs );\r
- for( b = e->brushes; b; b = b->next )\r
- {\r
- AddPointToBounds( b->mins, mins, maxs );\r
- AddPointToBounds( b->maxs, mins, maxs );\r
- }\r
- for( p = e->patches; p; p = p->next )\r
- {\r
- for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
- AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );\r
- }\r
- \r
- /* try to find explicit min/max key */\r
- value = ValueForKey( e, "min" ); \r
- if( value[ 0 ] != '\0' )\r
- GetVectorForKey( e, "min", mins );\r
- value = ValueForKey( e, "max" ); \r
- if( value[ 0 ] != '\0' )\r
- GetVectorForKey( e, "max", maxs );\r
- \r
- /* store the bounds */\r
- for( b = e->brushes; b; b = b->next )\r
- {\r
- VectorCopy( mins, b->eMins );\r
- VectorCopy( maxs, b->eMaxs );\r
- }\r
- for( p = e->patches; p; p = p->next )\r
- {\r
- VectorCopy( mins, p->eMins );\r
- VectorCopy( maxs, p->eMaxs );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadEntityIndexMap() - ydnar\r
-based on LoadAlphaMap() from terrain.c, a little more generic\r
-*/\r
-\r
-void LoadEntityIndexMap( entity_t *e )\r
-{\r
- int i, size, numLayers, w, h;\r
- const char *value, *indexMapFilename, *shader;\r
- char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;\r
- byte *pixels;\r
- unsigned int *pixels32;\r
- indexMap_t *im;\r
- brush_t *b;\r
- parseMesh_t *p;\r
- \r
- \r
- /* this only works with bmodel ents */\r
- if( e->brushes == NULL && e->patches == NULL )\r
- return;\r
- \r
- /* determine if there is an index map (support legacy "alphamap" key as well) */\r
- value = ValueForKey( e, "_indexmap" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( e, "alphamap" );\r
- if( value[ 0 ] == '\0' )\r
- return;\r
- indexMapFilename = value;\r
- \r
- /* get number of layers (support legacy "layers" key as well) */\r
- value = ValueForKey( e, "_layers" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( e, "layers" );\r
- if( value[ 0 ] == '\0' )\r
- {\r
- Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );\r
- Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
- return;\r
- }\r
- numLayers = atoi( value );\r
- if( numLayers < 1 )\r
- {\r
- Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );\r
- Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
- return;\r
- }\r
- \r
- /* get base shader name (support legacy "shader" key as well) */\r
- value = ValueForKey( mapEnt, "_shader" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( e, "shader" );\r
- if( value[ 0 ] == '\0' )\r
- {\r
- Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );\r
- Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
- return;\r
- }\r
- shader = value;\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );\r
- \r
- /* get index map file extension */\r
- ExtractFileExtension( indexMapFilename, ext );\r
- \r
- /* handle tga image */\r
- if( !Q_stricmp( ext, "tga" ) )\r
- {\r
- /* load it */\r
- Load32BitImage( indexMapFilename, &pixels32, &w, &h );\r
- \r
- /* convert to bytes */\r
- size = w * h;\r
- pixels = safe_malloc( size );\r
- for( i = 0; i < size; i++ )\r
- {\r
- pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;\r
- if( pixels[ i ] >= numLayers )\r
- pixels[ i ] = numLayers - 1;\r
- }\r
- \r
- /* free the 32 bit image */\r
- free( pixels32 );\r
- }\r
- else\r
- {\r
- /* load it */\r
- Load256Image( indexMapFilename, &pixels, NULL, &w, &h );\r
- \r
- /* debug code */\r
- //% Sys_Printf( "-------------------------------" );\r
- \r
- /* fix up out-of-range values */\r
- size = w * h;\r
- for( i = 0; i < size; i++ )\r
- {\r
- if( pixels[ i ] >= numLayers )\r
- pixels[ i ] = numLayers - 1;\r
- \r
- /* debug code */\r
- //% if( (i % w) == 0 )\r
- //% Sys_Printf( "\n" );\r
- //% Sys_Printf( "%c", pixels[ i ] + '0' );\r
- }\r
- \r
- /* debug code */\r
- //% Sys_Printf( "\n-------------------------------\n" );\r
- }\r
- \r
- /* the index map must be at least 2x2 pixels */\r
- if( w < 2 || h < 2 )\r
- {\r
- Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );\r
- Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
- free( pixels );\r
- return;\r
- }\r
-\r
- /* create a new index map */\r
- im = safe_malloc( sizeof( *im ) );\r
- memset( im, 0, sizeof( *im ) );\r
- \r
- /* set it up */\r
- im->w = w;\r
- im->h = h;\r
- im->numLayers = numLayers;\r
- strcpy( im->name, indexMapFilename );\r
- strcpy( im->shader, shader );\r
- im->pixels = pixels;\r
- \r
- /* get height offsets */\r
- value = ValueForKey( mapEnt, "_offsets" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( e, "offsets" );\r
- if( value[ 0 ] != '\0' )\r
- {\r
- /* value is a space-seperated set of numbers */\r
- strcpy( offset, value );\r
- search = offset;\r
- \r
- /* get each value */\r
- for( i = 0; i < 256 && *search != '\0'; i++ )\r
- {\r
- space = strstr( search, " " );\r
- if( space != NULL )\r
- *space = '\0';\r
- im->offsets[ i ] = atof( search );\r
- if( space == NULL )\r
- break;\r
- search = space + 1;\r
- }\r
- }\r
- \r
- /* store the index map in every brush/patch in the entity */\r
- for( b = e->brushes; b != NULL; b = b->next )\r
- b->im = im;\r
- for( p = e->patches; p != NULL; p = p->next )\r
- p->im = im;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-ParseMapEntity()\r
-parses a single entity out of a map file\r
-*/\r
-\r
-static qboolean ParseMapEntity( qboolean onlyLights )\r
-{\r
- epair_t *ep;\r
- const char *classname, *value;\r
- float lightmapScale;\r
- char shader[ MAX_QPATH ];\r
- shaderInfo_t *celShader = NULL;\r
- brush_t *brush;\r
- parseMesh_t *patch;\r
- qboolean funcGroup;\r
- int castShadows, recvShadows;\r
- \r
- \r
- /* eof check */\r
- if( !GetToken( qtrue ) )\r
- return qfalse;\r
- \r
- /* conformance check */\r
- if( strcmp( token, "{" ) )\r
- {\r
- Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"\r
- "Continuing to process map, but resulting BSP may be invalid.\n",\r
- token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );\r
- return qfalse;\r
- }\r
- \r
- /* range check */\r
- if( numEntities >= MAX_MAP_ENTITIES )\r
- Error( "numEntities == MAX_MAP_ENTITIES" );\r
- \r
- /* setup */\r
- entitySourceBrushes = 0;\r
- mapEnt = &entities[ numEntities ];\r
- numEntities++;\r
- memset( mapEnt, 0, sizeof( *mapEnt ) );\r
- \r
- /* ydnar: true entity numbering */\r
- mapEnt->mapEntityNum = numMapEntities;\r
- numMapEntities++;\r
- \r
- /* loop */\r
- while( 1 )\r
- {\r
- /* get initial token */\r
- if( !GetToken( qtrue ) )\r
- {\r
- Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"\r
- "Continuing to process map, but resulting BSP may be invalid.\n" );\r
- return qfalse;\r
- }\r
- \r
- if( !strcmp( token, "}" ) )\r
- break;\r
- \r
- if( !strcmp( token, "{" ) )\r
- {\r
- /* parse a brush or patch */\r
- if( !GetToken( qtrue ) )\r
- break;\r
- \r
- /* check */\r
- if( !strcmp( token, "patchDef2" ) )\r
- {\r
- numMapPatches++;\r
- ParsePatch( onlyLights );\r
- }\r
- else if( !strcmp( token, "terrainDef" ) )\r
- {\r
- //% ParseTerrain();\r
- Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */\r
- }\r
- else if( !strcmp( token, "brushDef" ) )\r
- {\r
- if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
- Error( "Old brush format not allowed in new brush format map" );\r
- g_bBrushPrimit = BPRIMIT_NEWBRUSHES;\r
- \r
- /* parse brush primitive */\r
- ParseBrush( onlyLights );\r
- }\r
- else\r
- {\r
- if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
- Error( "New brush format not allowed in old brush format map" );\r
- g_bBrushPrimit = BPRIMIT_OLDBRUSHES;\r
- \r
- /* parse old brush format */\r
- UnGetToken();\r
- ParseBrush( onlyLights );\r
- }\r
- entitySourceBrushes++;\r
- }\r
- else\r
- {\r
- /* parse a key / value pair */\r
- ep = ParseEPair();\r
- \r
- /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */\r
- if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )\r
- {\r
- ep->next = mapEnt->epairs;\r
- mapEnt->epairs = ep;\r
- }\r
- }\r
- }\r
- \r
- /* ydnar: get classname */\r
- classname = ValueForKey( mapEnt, "classname" );\r
- \r
- /* ydnar: only lights? */\r
- if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )\r
- {\r
- numEntities--;\r
- return qtrue;\r
- }\r
- \r
- /* ydnar: determine if this is a func_group */\r
- if( !Q_stricmp( "func_group", classname ) )\r
- funcGroup = qtrue;\r
- else\r
- funcGroup = qfalse;\r
- \r
- /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */\r
- if( funcGroup || mapEnt->mapEntityNum == 0 )\r
- {\r
- //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );\r
- castShadows = WORLDSPAWN_CAST_SHADOWS;\r
- recvShadows = WORLDSPAWN_RECV_SHADOWS;\r
- }\r
- \r
- /* other entities don't cast any shadows, but recv worldspawn shadows */\r
- else\r
- {\r
- //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );\r
- castShadows = ENTITY_CAST_SHADOWS;\r
- recvShadows = ENTITY_RECV_SHADOWS;\r
- }\r
- \r
- /* get explicit shadow flags */\r
- GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );\r
- \r
- /* ydnar: get lightmap scaling value for this entity */\r
- if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||\r
- strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )\r
- {\r
- /* get lightmap scale from entity */\r
- lightmapScale = FloatForKey( mapEnt, "lightmapscale" );\r
- if( lightmapScale <= 0.0f )\r
- lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );\r
- if( lightmapScale > 0.0f )\r
- Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );\r
- }\r
- else\r
- lightmapScale = 0.0f;\r
- \r
- /* ydnar: get cel shader :) for this entity */\r
- value = ValueForKey( mapEnt, "_celshader" );\r
- if( value[ 0 ] == '\0' ) \r
- value = ValueForKey( &entities[ 0 ], "_celshader" );\r
- if( value[ 0 ] != '\0' )\r
- {\r
- sprintf( shader, "textures/%s", value );\r
- celShader = ShaderInfoForShader( shader );\r
- Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );\r
- }\r
- else\r
- celShader = NULL;\r
- \r
- /* attach stuff to everything in the entity */\r
- for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )\r
- {\r
- brush->entityNum = mapEnt->mapEntityNum;\r
- brush->castShadows = castShadows;\r
- brush->recvShadows = recvShadows;\r
- brush->lightmapScale = lightmapScale;\r
- brush->celShader = celShader;\r
- }\r
- \r
- for( patch = mapEnt->patches; patch != NULL; patch = patch->next )\r
- {\r
- patch->entityNum = mapEnt->mapEntityNum;\r
- patch->castShadows = castShadows;\r
- patch->recvShadows = recvShadows;\r
- patch->lightmapScale = lightmapScale;\r
- patch->celShader = celShader;\r
- }\r
- \r
- /* ydnar: gs mods: set entity bounds */\r
- SetEntityBounds( mapEnt );\r
- \r
- /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */\r
- LoadEntityIndexMap( mapEnt );\r
- \r
- /* get entity origin and adjust brushes */\r
- GetVectorForKey( mapEnt, "origin", mapEnt->origin );\r
- if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )\r
- AdjustBrushesForOrigin( mapEnt );\r
-\r
- /* group_info entities are just for editor grouping (fixme: leak!) */\r
- if( !Q_stricmp( "group_info", classname ) )\r
- {\r
- numEntities--;\r
- return qtrue;\r
- }\r
- \r
- /* group entities are just for editor convenience, toss all brushes into worldspawn */\r
- if( funcGroup )\r
- {\r
- MoveBrushesToWorld( mapEnt );\r
- numEntities--;\r
- return qtrue;\r
- }\r
- \r
- /* done */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadMapFile()\r
-loads a map file into a list of entities\r
-*/\r
-\r
-void LoadMapFile( char *filename, qboolean onlyLights )\r
-{ \r
- FILE *file;\r
- brush_t *b;\r
- int oldNumEntities, numMapBrushes;\r
- \r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );\r
- Sys_Printf( "Loading %s\n", filename );\r
- \r
- /* hack */\r
- file = SafeOpenRead( filename );\r
- fclose( file );\r
- \r
- /* load the map file */\r
- LoadScriptFile( filename, -1 );\r
- \r
- /* setup */\r
- if( onlyLights )\r
- oldNumEntities = numEntities;\r
- else\r
- numEntities = 0;\r
- \r
- /* initial setup */\r
- numMapDrawSurfs = 0;\r
- c_detail = 0;\r
- g_bBrushPrimit = BPRIMIT_UNDEFINED;\r
- \r
- /* allocate a very large temporary brush for building the brushes as they are loaded */\r
- buildBrush = AllocBrush( MAX_BUILD_SIDES );\r
- \r
- /* parse the map file */\r
- while( ParseMapEntity( onlyLights ) );\r
- \r
- /* light loading */\r
- if( onlyLights )\r
- {\r
- /* emit some statistics */\r
- Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );\r
- }\r
- else\r
- {\r
- /* set map bounds */\r
- ClearBounds( mapMins, mapMaxs );\r
- for( b = entities[ 0 ].brushes; b; b = b->next )\r
- {\r
- AddPointToBounds( b->mins, mapMins, mapMaxs );\r
- AddPointToBounds( b->maxs, mapMins, mapMaxs );\r
- }\r
- \r
- /* get brush counts */\r
- numMapBrushes = CountBrushList( entities[ 0 ].brushes );\r
- if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )\r
- Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );\r
- \r
- /* emit some statistics */\r
- Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );\r
- Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );\r
- Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);\r
- Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);\r
- Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);\r
- Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );\r
- Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);\r
- Sys_Printf( "%9d areaportals\n", c_areaportals);\r
- Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",\r
- mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],\r
- mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);\r
- \r
- /* write bogus map */\r
- if( fakemap )\r
- WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );\r
- }\r
-}\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+----------------------------------------------------------------------------------
+
+This code has been altered significantly from its original form, to support
+several games based on the Quake III Arena engine, in the form of "Q3Map2."
+
+------------------------------------------------------------------------------- */
+
+
+
+/* marker */
+#define MAP_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+/* FIXME: remove these vars */
+
+/* undefine to make plane finding use linear sort (note: really slow) */
+#define USE_HASHING
+#define PLANE_HASHES 8192
+
+plane_t *planehash[ PLANE_HASHES ];
+
+int c_boxbevels;
+int c_edgebevels;
+int c_areaportals;
+int c_detail;
+int c_structural;
+
+
+
+/*
+PlaneEqual()
+ydnar: replaced with variable epsilon for djbob
+*/
+
+#define NORMAL_EPSILON 0.00001
+#define DIST_EPSILON 0.01
+
+qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )
+{
+ float ne, de;
+
+
+ /* get local copies */
+ ne = normalEpsilon;
+ de = distanceEpsilon;
+
+ /* compare */
+ if( fabs( p->dist - dist ) <= de &&
+ fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&
+ fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&
+ fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )
+ return qtrue;
+
+ /* different */
+ return qfalse;
+}
+
+
+
+/*
+AddPlaneToHash()
+*/
+
+void AddPlaneToHash( plane_t *p )
+{
+ int hash;
+
+
+ hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CreateNewFloatPlane (vec3_t normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ {
+ Sys_Printf( "FloatPlane: bad normal\n");
+ return -1;
+ }
+
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAP_PLANES)
+ Error ("MAX_MAP_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+}
+
+
+
+/*
+SnapNormal()
+snaps a near-axial normal vector
+*/
+
+void SnapNormal( vec3_t normal )
+{
+ int i;
+
+ for( i = 0; i < 3; i++ )
+ {
+ if( fabs( normal[ i ] - 1 ) < normalEpsilon )
+ {
+ VectorClear( normal );
+ normal[ i ] = 1;
+ break;
+ }
+ if( fabs( normal[ i ] - -1 ) < normalEpsilon )
+ {
+ VectorClear( normal );
+ normal[ i ] = -1;
+ break;
+ }
+ }
+}
+
+
+
+/*
+SnapPlane()
+snaps a plane to normal/distance epsilons
+*/
+
+void SnapPlane( vec3_t normal, vec_t *dist )
+{
+ SnapNormal( normal );
+
+ if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
+ *dist = Q_rint( *dist );
+}
+
+
+
+/*
+FindFloatPlane()
+ydnar: changed to allow a number of test points to be supplied that
+must be within an epsilon distance of the plane
+*/
+
+int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
+
+#ifdef USE_HASHING
+
+{
+ int i, j, hash, h;
+ plane_t *p;
+ vec_t d;
+
+
+ /* hash the plane */
+ SnapPlane( normal, &dist );
+ hash = (PLANE_HASHES - 1) & (int) fabs( dist );
+
+ /* search the border bins as well */
+ for( i = -1; i <= 1; i++ )
+ {
+ h = (hash + i) & (PLANE_HASHES - 1);
+ for( p = planehash[ h ]; p != NULL; p = p->hash_chain )
+ {
+ /* do standard plane compare */
+ if( !PlaneEqual( p, normal, dist ) )
+ continue;
+
+ /* ydnar: uncomment the following line for old-style plane finding */
+ //% return p - mapplanes;
+
+ /* ydnar: test supplied points against this plane */
+ for( j = 0; j < numPoints; j++ )
+ {
+ d = DotProduct( points[ j ], normal ) - dist;
+ if( fabs( d ) > distanceEpsilon )
+ break;
+ }
+
+ /* found a matching plane */
+ if( j >= numPoints )
+ return p - mapplanes;
+ }
+ }
+
+ /* none found, so create a new one */
+ return CreateNewFloatPlane( normal, dist );
+}
+
+#else
+
+{
+ int i;
+ plane_t *p;
+
+
+ SnapPlane( normal, &dist );
+ for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
+ {
+ if( PlaneEqual( p, normal, dist ) )
+ return i;
+ }
+
+ return CreateNewFloatPlane( normal, dist );
+}
+
+#endif
+
+
+
+/*
+MapPlaneFromPoints()
+takes 3 points and finds the plane they lie in
+*/
+
+int MapPlaneFromPoints( vec3_t *p )
+{
+ vec3_t t1, t2, normal;
+ vec_t dist;
+
+
+ /* calc plane normal */
+ VectorSubtract( p[ 0 ], p[ 1 ], t1 );
+ VectorSubtract( p[ 2 ], p[ 1 ], t2 );
+ CrossProduct( t1, t2, normal );
+ VectorNormalize( normal, normal );
+
+ /* calc plane distance */
+ dist = DotProduct( p[ 0 ], normal );
+
+ /* store the plane */
+ return FindFloatPlane( normal, dist, 3, p );
+}
+
+
+
+/*
+SetBrushContents()
+the content flags and compile flags on all sides of a brush should be the same
+*/
+
+void SetBrushContents( brush_t *b )
+{
+ int contentFlags, compileFlags;
+ side_t *s;
+ int i;
+ qboolean mixed;
+
+
+ /* get initial compile flags from first side */
+ s = &b->sides[ 0 ];
+ contentFlags = s->contentFlags;
+ compileFlags = s->compileFlags;
+ b->contentShader = s->shaderInfo;
+ mixed = qfalse;
+
+ /* get the content/compile flags for every side in the brush */
+ for( i = 1; i < b->numsides; i++, s++ )
+ {
+ s = &b->sides[ i ];
+ if( s->shaderInfo == NULL )
+ continue;
+ if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
+ mixed = qtrue;
+ }
+
+ /* ydnar: getting rid of this stupid warning */
+ //% if( mixed )
+ //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
+
+ /* check for detail & structural */
+ if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
+ {
+ xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
+ compileFlags &= ~C_DETAIL;
+ }
+
+ /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
+ if( fulldetail )
+ compileFlags &= ~C_DETAIL;
+
+ /* all translucent brushes that aren't specifically made structural will be detail */
+ if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
+ compileFlags |= C_DETAIL;
+
+ /* detail? */
+ if( compileFlags & C_DETAIL )
+ {
+ c_detail++;
+ b->detail = qtrue;
+ }
+ else
+ {
+ c_structural++;
+ b->detail = qfalse;
+ }
+
+ /* opaque? */
+ if( compileFlags & C_TRANSLUCENT )
+ b->opaque = qfalse;
+ else
+ b->opaque = qtrue;
+
+ /* areaportal? */
+ if( compileFlags & C_AREAPORTAL )
+ c_areaportals++;
+
+ /* set brush flags */
+ b->contentFlags = contentFlags;
+ b->compileFlags = compileFlags;
+}
+
+
+
+/*
+AddBrushBevels()
+adds any additional planes necessary to allow the brush being
+built to be expanded against axial bounding boxes
+ydnar 2003-01-20: added mrelusive fixes
+*/
+
+void AddBrushBevels( void )
+{
+ int axis, dir;
+ int i, j, k, l, order;
+ side_t sidetemp;
+ side_t *s, *s2;
+ winding_t *w, *w2;
+ vec3_t normal;
+ float dist;
+ vec3_t vec, vec2;
+ float d, minBack;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for ( axis = 0; axis < 3; axis++ ) {
+ for ( dir = -1; dir <= 1; dir += 2, order++ ) {
+ // see if the plane is allready present
+ for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
+ {
+ /* ydnar: testing disabling of mre code */
+ #if 0
+ if ( dir > 0 ) {
+ if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
+ break;
+ }
+ }
+ else {
+ if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
+ break;
+ }
+ }
+ #else
+ if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
+ (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
+ break;
+ #endif
+ }
+
+ if ( i == buildBrush->numsides ) {
+ // add a new side
+ if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+ xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
+ }
+ memset( s, 0, sizeof( *s ) );
+ buildBrush->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+
+ if( dir == 1 )
+ {
+ /* ydnar: adding bevel plane snapping for fewer bsp planes */
+ if( bevelSnap > 0 )
+ dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
+ else
+ dist = buildBrush->maxs[ axis ];
+ }
+ else
+ {
+ /* ydnar: adding bevel plane snapping for fewer bsp planes */
+ if( bevelSnap > 0 )
+ dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
+ else
+ dist = -buildBrush->mins[ axis ];
+ }
+
+ s->planenum = FindFloatPlane( normal, dist, 0, NULL );
+ s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
+ s->bevel = qtrue;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if ( i != order ) {
+ sidetemp = buildBrush->sides[order];
+ buildBrush->sides[order] = buildBrush->sides[i];
+ buildBrush->sides[i] = sidetemp;
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if ( buildBrush->numsides == 6 ) {
+ return; // pure axial
+ }
+
+ // test the non-axial plane edges
+ for ( i = 6; i < buildBrush->numsides; i++ ) {
+ s = buildBrush->sides + i;
+ w = s->winding;
+ if ( !w ) {
+ continue;
+ }
+ for ( j = 0; j < w->numpoints; j++) {
+ k = (j+1)%w->numpoints;
+ VectorSubtract( w->p[j], w->p[k], vec );
+ if ( VectorNormalize( vec, vec ) < 0.5f ) {
+ continue;
+ }
+ SnapNormal( vec );
+ for ( k = 0; k < 3; k++ ) {
+ if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
+ break; // axial
+ }
+ }
+ if ( k != 3 ) {
+ continue; // only test non-axial edges
+ }
+
+ /* debug code */
+ //% Sys_Printf( "-------------\n" );
+
+ // try the six possible slanted axials from this edge
+ for ( axis = 0; axis < 3; axis++ ) {
+ for ( dir = -1; dir <= 1; dir += 2 ) {
+ // construct a plane
+ VectorClear( vec2 );
+ vec2[axis] = dir;
+ CrossProduct( vec, vec2, normal );
+ if ( VectorNormalize( normal, normal ) < 0.5f ) {
+ continue;
+ }
+ dist = DotProduct( w->p[j], normal );
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for ( k = 0; k < buildBrush->numsides; k++ ) {
+
+ // if this plane has allready been used, skip it
+ if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
+ break;
+ }
+
+ w2 = buildBrush->sides[k].winding;
+ if ( !w2 ) {
+ continue;
+ }
+ minBack = 0.0f;
+ for ( l = 0; l < w2->numpoints; l++ ) {
+ d = DotProduct( w2->p[l], normal ) - dist;
+ if ( d > 0.1f ) {
+ break; // point in front
+ }
+ if ( d < minBack ) {
+ minBack = d;
+ }
+ }
+ // if some point was at the front
+ if ( l != w2->numpoints ) {
+ break;
+ }
+
+ // if no points at the back then the winding is on the bevel plane
+ if ( minBack > -0.1f ) {
+ //% Sys_Printf( "On bevel plane\n" );
+ break;
+ }
+ }
+
+ if ( k != buildBrush->numsides ) {
+ continue; // wasn't part of the outer hull
+ }
+
+ /* debug code */
+ //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
+
+ // add this plane
+ if( buildBrush->numsides == MAX_BUILD_SIDES ) {
+ xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
+ }
+ s2 = &buildBrush->sides[buildBrush->numsides];
+ buildBrush->numsides++;
+ memset( s2, 0, sizeof( *s2 ) );
+
+ s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
+ s2->contentFlags = buildBrush->sides[0].contentFlags;
+ s2->bevel = qtrue;
+ c_edgebevels++;
+ }
+ }
+ }
+ }
+}
+
+
+
+/*
+FinishBrush()
+produces a final brush based on the buildBrush->sides array
+and links it to the current entity
+*/
+
+brush_t *FinishBrush( void )
+{
+ brush_t *b;
+
+
+ /* create windings for sides and bounds for brush */
+ if ( !CreateBrushWindings( buildBrush ) )
+ return NULL;
+
+ /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
+ after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
+ if( buildBrush->compileFlags & C_ORIGIN )
+ {
+ char string[ 32 ];
+ vec3_t origin;
+
+ if( numEntities == 1 )
+ {
+ Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
+ mapEnt->mapEntityNum, entitySourceBrushes );
+ return NULL;
+ }
+
+ VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
+ VectorScale (origin, 0.5, origin);
+
+ sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
+ SetKeyValue( &entities[ numEntities - 1 ], "origin", string);
+
+ VectorCopy( origin, entities[ numEntities - 1 ].origin);
+
+ /* don't keep this brush */
+ return NULL;
+ }
+
+ /* determine if the brush is an area portal */
+ if( buildBrush->compileFlags & C_AREAPORTAL )
+ {
+ if( numEntities != 1 )
+ {
+ Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
+ return NULL;
+ }
+ }
+
+ /* add bevel planes */
+ AddBrushBevels();
+
+ /* keep it */
+ b = CopyBrush( buildBrush );
+
+ /* set map entity and brush numbering */
+ b->entityNum = mapEnt->mapEntityNum;
+ b->brushNum = entitySourceBrushes;
+
+ /* set original */
+ b->original = b;
+
+ /* link opaque brushes to head of list, translucent brushes to end */
+ if( b->opaque || mapEnt->lastBrush == NULL )
+ {
+ b->next = mapEnt->brushes;
+ mapEnt->brushes = b;
+ if( mapEnt->lastBrush == NULL )
+ mapEnt->lastBrush = b;
+ }
+ else
+ {
+ b->next = NULL;
+ mapEnt->lastBrush->next = b;
+ mapEnt->lastBrush = b;
+ }
+
+ /* return to sender */
+ return b;
+}
+
+
+
+/*
+TextureAxisFromPlane()
+determines best orthagonal axis to project a texture onto a wall
+(must be identical in radiant!)
+*/
+
+vec3_t baseaxis[18] =
+{
+ {0,0,1}, {1,0,0}, {0,-1,0}, // floor
+ {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+ {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+ {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+ {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+ {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
+{
+ int bestaxis;
+ vec_t dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+/*
+QuakeTextureVecs()
+creates world-to-texture mapping vecs for crappy quake plane arrangements
+*/
+
+void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
+{
+ vec3_t vecs[2];
+ int sv, tv;
+ vec_t ang, sinv, cosv;
+ vec_t ns, nt;
+ int i, j;
+
+
+ TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+
+ if (!scale[0])
+ scale[0] = 1;
+ if (!scale[1])
+ scale[1] = 1;
+
+ // rotate axis
+ if (rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++) {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ mappingVecs[i][j] = vecs[i][j] / scale[i];
+
+ mappingVecs[0][3] = shift[0];
+ mappingVecs[1][3] = shift[1];
+}
+
+
+
+/*
+ParseRawBrush()
+parses the sides into buildBrush->sides[], nothing else.
+no validation, back plane removal, etc.
+
+Timo - 08/26/99
+added brush epairs parsing ( ignoring actually )
+Timo - 08/04/99
+added exclusive brush primitive parsing
+Timo - 08/08/99
+support for old brush format back in
+NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
+*/
+
+static void ParseRawBrush( qboolean onlyLights )
+{
+ side_t *side;
+ vec3_t planePoints[ 3 ];
+ int planenum;
+ shaderInfo_t *si;
+ vec_t shift[ 2 ];
+ vec_t rotate;
+ vec_t scale[ 2 ];
+ char name[ MAX_QPATH ];
+ char shader[ MAX_QPATH ];
+ int flags;
+
+
+ /* initial setup */
+ buildBrush->numsides = 0;
+ buildBrush->detail = qfalse;
+
+ /* bp */
+ if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
+ MatchToken( "{" );
+
+ /* parse sides */
+ while( 1 )
+ {
+ if( !GetToken( qtrue ) )
+ break;
+ if( !strcmp( token, "}" ) )
+ break;
+
+ /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
+ if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
+ {
+ while( 1 )
+ {
+ if( strcmp( token, "(" ) )
+ GetToken( qfalse );
+ else
+ break;
+ GetToken( qtrue );
+ }
+ }
+ UnGetToken();
+
+ /* test side count */
+ if( buildBrush->numsides >= MAX_BUILD_SIDES )
+ xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
+
+ /* add side */
+ side = &buildBrush->sides[ buildBrush->numsides ];
+ memset( side, 0, sizeof( *side ) );
+ buildBrush->numsides++;
+
+ /* read the three point plane definition */
+ Parse1DMatrix( 3, planePoints[ 0 ] );
+ Parse1DMatrix( 3, planePoints[ 1 ] );
+ Parse1DMatrix( 3, planePoints[ 2 ] );
+
+ /* bp: read the texture matrix */
+ if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
+ Parse2DMatrix( 2, 3, (float*) side->texMat );
+
+ /* read shader name */
+ GetToken( qfalse );
+ strcpy( name, token );
+
+ /* bp */
+ if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
+ {
+ GetToken( qfalse );
+ shift[ 0 ] = atof( token );
+ GetToken( qfalse );
+ shift[ 1 ] = atof( token );
+ GetToken( qfalse );
+ rotate = atof( token );
+ GetToken( qfalse );
+ scale[ 0 ] = atof( token );
+ GetToken( qfalse );
+ scale[ 1 ] = atof( token );
+ }
+
+ /* set default flags and values */
+ sprintf( shader, "textures/%s", name );
+ if( onlyLights )
+ si = &shaderInfo[ 0 ];
+ else
+ si = ShaderInfoForShader( shader );
+ side->shaderInfo = si;
+ side->surfaceFlags = si->surfaceFlags;
+ side->contentFlags = si->contentFlags;
+ side->compileFlags = si->compileFlags;
+ side->value = si->value;
+
+ /* ydnar: gs mods: bias texture shift */
+ if( si->globalTexture == qfalse )
+ {
+ shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
+ shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
+ }
+
+ /*
+ historically, there are 3 integer values at the end of a brushside line in a .map file.
+ in quake 3, the only thing that mattered was the first of these three values, which
+ was previously the content flags. and only then did a single bit matter, the detail
+ bit. because every game has its own special flags for specifying detail, the
+ traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
+ by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
+ is stored in compileFlags, as opposed to contentFlags, for multiple-game
+ portability. :sigh:
+ */
+
+ if( TokenAvailable() )
+ {
+ /* get detail bit from map content flags */
+ GetToken( qfalse );
+ flags = atoi( token );
+ if( flags & C_DETAIL )
+ side->compileFlags |= C_DETAIL;
+
+ /* historical */
+ GetToken( qfalse );
+ //% td.flags = atoi( token );
+ GetToken( qfalse );
+ //% td.value = atoi( token );
+ }
+
+ /* find the plane number */
+ planenum = MapPlaneFromPoints( planePoints );
+ side->planenum = planenum;
+
+ /* bp: get the texture mapping for this texturedef / plane combination */
+ if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
+ QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
+ }
+
+ /* bp */
+ if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
+ {
+ UnGetToken();
+ MatchToken( "}" );
+ MatchToken( "}" );
+ }
+}
+
+
+
+/*
+RemoveDuplicateBrushPlanes
+returns false if the brush has a mirrored set of planes,
+meaning it encloses no volume.
+also removes planes without any normal
+*/
+
+qboolean RemoveDuplicateBrushPlanes( brush_t *b )
+{
+ int i, j, k;
+ side_t *sides;
+
+ sides = b->sides;
+
+ for ( i = 1 ; i < b->numsides ; i++ ) {
+
+ // check for a degenerate plane
+ if ( sides[i].planenum == -1) {
+ xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
+ // remove it
+ for ( k = i + 1 ; k < b->numsides ; k++ ) {
+ sides[k-1] = sides[k];
+ }
+ b->numsides--;
+ i--;
+ continue;
+ }
+
+ // check for duplication and mirroring
+ for ( j = 0 ; j < i ; j++ ) {
+ if ( sides[i].planenum == sides[j].planenum ) {
+ xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
+ // remove the second duplicate
+ for ( k = i + 1 ; k < b->numsides ; k++ ) {
+ sides[k-1] = sides[k];
+ }
+ b->numsides--;
+ i--;
+ break;
+ }
+
+ if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
+ // mirror plane, brush is invalid
+ xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
+ return qfalse;
+ }
+ }
+ }
+ return qtrue;
+}
+
+
+
+/*
+ParseBrush()
+parses a brush out of a map file and sets it up
+*/
+
+static void ParseBrush( qboolean onlyLights )
+{
+ brush_t *b;
+
+
+ /* parse the brush out of the map */
+ ParseRawBrush( onlyLights );
+
+ /* only go this far? */
+ if( onlyLights )
+ return;
+
+ /* set some defaults */
+ buildBrush->portalareas[ 0 ] = -1;
+ buildBrush->portalareas[ 1 ] = -1;
+ buildBrush->entityNum = numMapEntities - 1;
+ buildBrush->brushNum = entitySourceBrushes;
+
+ /* if there are mirrored planes, the entire brush is invalid */
+ if( !RemoveDuplicateBrushPlanes( buildBrush ) )
+ return;
+
+ /* get the content for the entire brush */
+ SetBrushContents( buildBrush );
+
+ /* allow detail brushes to be removed */
+ if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
+ {
+ //% FreeBrush( buildBrush );
+ return;
+ }
+
+ /* allow liquid brushes to be removed */
+ if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
+ {
+ //% FreeBrush( buildBrush );
+ return;
+ }
+
+ /* ydnar: allow hint brushes to be removed */
+ if( noHint && (buildBrush->compileFlags & C_HINT) )
+ {
+ //% FreeBrush( buildBrush );
+ return;
+ }
+
+ /* finish the brush */
+ b = FinishBrush();
+}
+
+
+
+/*
+MoveBrushesToWorld()
+takes all of the brushes from the current entity and
+adds them to the world's brush list
+(used by func_group)
+*/
+
+void MoveBrushesToWorld( entity_t *ent )
+{
+ brush_t *b, *next;
+ parseMesh_t *pm;
+
+
+ /* move brushes */
+ for( b = ent->brushes; b != NULL; b = next )
+ {
+ /* get next brush */
+ next = b->next;
+
+ /* link opaque brushes to head of list, translucent brushes to end */
+ if( b->opaque || entities[ 0 ].lastBrush == NULL )
+ {
+ b->next = entities[ 0 ].brushes;
+ entities[ 0 ].brushes = b;
+ if( entities[ 0 ].lastBrush == NULL )
+ entities[ 0 ].lastBrush = b;
+ }
+ else
+ {
+ b->next = NULL;
+ entities[ 0 ].lastBrush->next = b;
+ entities[ 0 ].lastBrush = b;
+ }
+
+ //% b->next = entities[ 0 ].brushes;
+ //% entities[ 0 ].brushes = b;
+ }
+ ent->brushes = NULL;
+
+ /* move patches */
+ if( ent->patches != NULL )
+ {
+ for( pm = ent->patches; pm->next; pm = pm->next );
+
+ pm->next = entities[ 0 ].patches;
+ entities[ 0 ].patches = ent->patches;
+
+ ent->patches = NULL;
+ }
+}
+
+
+
+/*
+AdjustBrushesForOrigin()
+*/
+
+void AdjustBrushesForOrigin( entity_t *ent )
+{
+
+ int i;
+ side_t *s;
+ vec_t newdist;
+ brush_t *b;
+ parseMesh_t *p;
+
+
+ /* walk brush list */
+ for( b = ent->brushes; b != NULL; b = b->next )
+ {
+ /* offset brush planes */
+ for( i = 0; i < b->numsides; i++)
+ {
+ /* get brush side */
+ s = &b->sides[ i ];
+
+ /* offset side plane */
+ newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
+
+ /* find a new plane */
+ s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
+ }
+
+ /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
+ CreateBrushWindings( b );
+ }
+
+ /* walk patch list */
+ for( p = ent->patches; p != NULL; p = p->next )
+ {
+ for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
+ VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
+ }
+}
+
+
+
+/*
+SetEntityBounds() - ydnar
+finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
+*/
+
+void SetEntityBounds( entity_t *e )
+{
+ int i;
+ brush_t *b;
+ parseMesh_t *p;
+ vec3_t mins, maxs;
+ const char *value;
+
+
+
+
+ /* walk the entity's brushes/patches and determine bounds */
+ ClearBounds( mins, maxs );
+ for( b = e->brushes; b; b = b->next )
+ {
+ AddPointToBounds( b->mins, mins, maxs );
+ AddPointToBounds( b->maxs, mins, maxs );
+ }
+ for( p = e->patches; p; p = p->next )
+ {
+ for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
+ AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
+ }
+
+ /* try to find explicit min/max key */
+ value = ValueForKey( e, "min" );
+ if( value[ 0 ] != '\0' )
+ GetVectorForKey( e, "min", mins );
+ value = ValueForKey( e, "max" );
+ if( value[ 0 ] != '\0' )
+ GetVectorForKey( e, "max", maxs );
+
+ /* store the bounds */
+ for( b = e->brushes; b; b = b->next )
+ {
+ VectorCopy( mins, b->eMins );
+ VectorCopy( maxs, b->eMaxs );
+ }
+ for( p = e->patches; p; p = p->next )
+ {
+ VectorCopy( mins, p->eMins );
+ VectorCopy( maxs, p->eMaxs );
+ }
+}
+
+
+
+/*
+LoadEntityIndexMap() - ydnar
+based on LoadAlphaMap() from terrain.c, a little more generic
+*/
+
+void LoadEntityIndexMap( entity_t *e )
+{
+ int i, size, numLayers, w, h;
+ const char *value, *indexMapFilename, *shader;
+ char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
+ byte *pixels;
+ unsigned int *pixels32;
+ indexMap_t *im;
+ brush_t *b;
+ parseMesh_t *p;
+
+
+ /* this only works with bmodel ents */
+ if( e->brushes == NULL && e->patches == NULL )
+ return;
+
+ /* determine if there is an index map (support legacy "alphamap" key as well) */
+ value = ValueForKey( e, "_indexmap" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( e, "alphamap" );
+ if( value[ 0 ] == '\0' )
+ return;
+ indexMapFilename = value;
+
+ /* get number of layers (support legacy "layers" key as well) */
+ value = ValueForKey( e, "_layers" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( e, "layers" );
+ if( value[ 0 ] == '\0' )
+ {
+ Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
+ Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+ return;
+ }
+ numLayers = atoi( value );
+ if( numLayers < 1 )
+ {
+ Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
+ Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+ return;
+ }
+
+ /* get base shader name (support legacy "shader" key as well) */
+ value = ValueForKey( mapEnt, "_shader" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( e, "shader" );
+ if( value[ 0 ] == '\0' )
+ {
+ Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
+ Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+ return;
+ }
+ shader = value;
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
+
+ /* get index map file extension */
+ ExtractFileExtension( indexMapFilename, ext );
+
+ /* handle tga image */
+ if( !Q_stricmp( ext, "tga" ) )
+ {
+ /* load it */
+ Load32BitImage( indexMapFilename, &pixels32, &w, &h );
+
+ /* convert to bytes */
+ size = w * h;
+ pixels = safe_malloc( size );
+ for( i = 0; i < size; i++ )
+ {
+ pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
+ if( pixels[ i ] >= numLayers )
+ pixels[ i ] = numLayers - 1;
+ }
+
+ /* free the 32 bit image */
+ free( pixels32 );
+ }
+ else
+ {
+ /* load it */
+ Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
+
+ /* debug code */
+ //% Sys_Printf( "-------------------------------" );
+
+ /* fix up out-of-range values */
+ size = w * h;
+ for( i = 0; i < size; i++ )
+ {
+ if( pixels[ i ] >= numLayers )
+ pixels[ i ] = numLayers - 1;
+
+ /* debug code */
+ //% if( (i % w) == 0 )
+ //% Sys_Printf( "\n" );
+ //% Sys_Printf( "%c", pixels[ i ] + '0' );
+ }
+
+ /* debug code */
+ //% Sys_Printf( "\n-------------------------------\n" );
+ }
+
+ /* the index map must be at least 2x2 pixels */
+ if( w < 2 || h < 2 )
+ {
+ Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
+ Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+ free( pixels );
+ return;
+ }
+
+ /* create a new index map */
+ im = safe_malloc( sizeof( *im ) );
+ memset( im, 0, sizeof( *im ) );
+
+ /* set it up */
+ im->w = w;
+ im->h = h;
+ im->numLayers = numLayers;
+ strcpy( im->name, indexMapFilename );
+ strcpy( im->shader, shader );
+ im->pixels = pixels;
+
+ /* get height offsets */
+ value = ValueForKey( mapEnt, "_offsets" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( e, "offsets" );
+ if( value[ 0 ] != '\0' )
+ {
+ /* value is a space-seperated set of numbers */
+ strcpy( offset, value );
+ search = offset;
+
+ /* get each value */
+ for( i = 0; i < 256 && *search != '\0'; i++ )
+ {
+ space = strstr( search, " " );
+ if( space != NULL )
+ *space = '\0';
+ im->offsets[ i ] = atof( search );
+ if( space == NULL )
+ break;
+ search = space + 1;
+ }
+ }
+
+ /* store the index map in every brush/patch in the entity */
+ for( b = e->brushes; b != NULL; b = b->next )
+ b->im = im;
+ for( p = e->patches; p != NULL; p = p->next )
+ p->im = im;
+}
+
+
+
+
+
+
+
+/*
+ParseMapEntity()
+parses a single entity out of a map file
+*/
+
+static qboolean ParseMapEntity( qboolean onlyLights )
+{
+ epair_t *ep;
+ const char *classname, *value;
+ float lightmapScale;
+ char shader[ MAX_QPATH ];
+ shaderInfo_t *celShader = NULL;
+ brush_t *brush;
+ parseMesh_t *patch;
+ qboolean funcGroup;
+ int castShadows, recvShadows;
+
+
+ /* eof check */
+ if( !GetToken( qtrue ) )
+ return qfalse;
+
+ /* conformance check */
+ if( strcmp( token, "{" ) )
+ {
+ Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
+ "Continuing to process map, but resulting BSP may be invalid.\n",
+ token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
+ return qfalse;
+ }
+
+ /* range check */
+ if( numEntities >= MAX_MAP_ENTITIES )
+ Error( "numEntities == MAX_MAP_ENTITIES" );
+
+ /* setup */
+ entitySourceBrushes = 0;
+ mapEnt = &entities[ numEntities ];
+ numEntities++;
+ memset( mapEnt, 0, sizeof( *mapEnt ) );
+
+ /* ydnar: true entity numbering */
+ mapEnt->mapEntityNum = numMapEntities;
+ numMapEntities++;
+
+ /* loop */
+ while( 1 )
+ {
+ /* get initial token */
+ if( !GetToken( qtrue ) )
+ {
+ Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
+ "Continuing to process map, but resulting BSP may be invalid.\n" );
+ return qfalse;
+ }
+
+ if( !strcmp( token, "}" ) )
+ break;
+
+ if( !strcmp( token, "{" ) )
+ {
+ /* parse a brush or patch */
+ if( !GetToken( qtrue ) )
+ break;
+
+ /* check */
+ if( !strcmp( token, "patchDef2" ) )
+ {
+ numMapPatches++;
+ ParsePatch( onlyLights );
+ }
+ else if( !strcmp( token, "terrainDef" ) )
+ {
+ //% ParseTerrain();
+ Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
+ }
+ else if( !strcmp( token, "brushDef" ) )
+ {
+ if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
+ Error( "Old brush format not allowed in new brush format map" );
+ g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
+
+ /* parse brush primitive */
+ ParseBrush( onlyLights );
+ }
+ else
+ {
+ if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
+ Error( "New brush format not allowed in old brush format map" );
+ g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
+
+ /* parse old brush format */
+ UnGetToken();
+ ParseBrush( onlyLights );
+ }
+ entitySourceBrushes++;
+ }
+ else
+ {
+ /* parse a key / value pair */
+ ep = ParseEPair();
+
+ /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
+ if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
+ {
+ ep->next = mapEnt->epairs;
+ mapEnt->epairs = ep;
+ }
+ }
+ }
+
+ /* ydnar: get classname */
+ classname = ValueForKey( mapEnt, "classname" );
+
+ /* ydnar: only lights? */
+ if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
+ {
+ numEntities--;
+ return qtrue;
+ }
+
+ /* ydnar: determine if this is a func_group */
+ if( !Q_stricmp( "func_group", classname ) )
+ funcGroup = qtrue;
+ else
+ funcGroup = qfalse;
+
+ /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
+ if( funcGroup || mapEnt->mapEntityNum == 0 )
+ {
+ //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
+ castShadows = WORLDSPAWN_CAST_SHADOWS;
+ recvShadows = WORLDSPAWN_RECV_SHADOWS;
+ }
+
+ /* other entities don't cast any shadows, but recv worldspawn shadows */
+ else
+ {
+ //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
+ castShadows = ENTITY_CAST_SHADOWS;
+ recvShadows = ENTITY_RECV_SHADOWS;
+ }
+
+ /* get explicit shadow flags */
+ GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
+
+ /* ydnar: get lightmap scaling value for this entity */
+ if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
+ strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
+ {
+ /* get lightmap scale from entity */
+ lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
+ if( lightmapScale <= 0.0f )
+ lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
+ if( lightmapScale > 0.0f )
+ Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
+ }
+ else
+ lightmapScale = 0.0f;
+
+ /* ydnar: get cel shader :) for this entity */
+ value = ValueForKey( mapEnt, "_celshader" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( &entities[ 0 ], "_celshader" );
+ if( value[ 0 ] != '\0' )
+ {
+ sprintf( shader, "textures/%s", value );
+ celShader = ShaderInfoForShader( shader );
+ Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
+ }
+ else
+ celShader = NULL;
+
+ /* attach stuff to everything in the entity */
+ for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
+ {
+ brush->entityNum = mapEnt->mapEntityNum;
+ brush->castShadows = castShadows;
+ brush->recvShadows = recvShadows;
+ brush->lightmapScale = lightmapScale;
+ brush->celShader = celShader;
+ }
+
+ for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
+ {
+ patch->entityNum = mapEnt->mapEntityNum;
+ patch->castShadows = castShadows;
+ patch->recvShadows = recvShadows;
+ patch->lightmapScale = lightmapScale;
+ patch->celShader = celShader;
+ }
+
+ /* ydnar: gs mods: set entity bounds */
+ SetEntityBounds( mapEnt );
+
+ /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
+ LoadEntityIndexMap( mapEnt );
+
+ /* get entity origin and adjust brushes */
+ GetVectorForKey( mapEnt, "origin", mapEnt->origin );
+ if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
+ AdjustBrushesForOrigin( mapEnt );
+
+ /* group_info entities are just for editor grouping (fixme: leak!) */
+ if( !Q_stricmp( "group_info", classname ) )
+ {
+ numEntities--;
+ return qtrue;
+ }
+
+ /* group entities are just for editor convenience, toss all brushes into worldspawn */
+ if( funcGroup )
+ {
+ MoveBrushesToWorld( mapEnt );
+ numEntities--;
+ return qtrue;
+ }
+
+ /* done */
+ return qtrue;
+}
+
+
+
+/*
+LoadMapFile()
+loads a map file into a list of entities
+*/
+
+void LoadMapFile( char *filename, qboolean onlyLights )
+{
+ FILE *file;
+ brush_t *b;
+ int oldNumEntities, numMapBrushes;
+
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
+ Sys_Printf( "Loading %s\n", filename );
+
+ /* hack */
+ file = SafeOpenRead( filename );
+ fclose( file );
+
+ /* load the map file */
+ LoadScriptFile( filename, -1 );
+
+ /* setup */
+ if( onlyLights )
+ oldNumEntities = numEntities;
+ else
+ numEntities = 0;
+
+ /* initial setup */
+ numMapDrawSurfs = 0;
+ c_detail = 0;
+ g_bBrushPrimit = BPRIMIT_UNDEFINED;
+
+ /* allocate a very large temporary brush for building the brushes as they are loaded */
+ buildBrush = AllocBrush( MAX_BUILD_SIDES );
+
+ /* parse the map file */
+ while( ParseMapEntity( onlyLights ) );
+
+ /* light loading */
+ if( onlyLights )
+ {
+ /* emit some statistics */
+ Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
+ }
+ else
+ {
+ /* set map bounds */
+ ClearBounds( mapMins, mapMaxs );
+ for( b = entities[ 0 ].brushes; b; b = b->next )
+ {
+ AddPointToBounds( b->mins, mapMins, mapMaxs );
+ AddPointToBounds( b->maxs, mapMins, mapMaxs );
+ }
+
+ /* get brush counts */
+ numMapBrushes = CountBrushList( entities[ 0 ].brushes );
+ if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
+ Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
+
+ /* emit some statistics */
+ Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
+ Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
+ Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
+ Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
+ Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
+ Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
+ Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
+ Sys_Printf( "%9d areaportals\n", c_areaportals);
+ Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
+ mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
+ mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
+
+ /* write bogus map */
+ if( fakemap )
+ WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );
+ }
+}