]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/map.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / map.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 \r
21 ----------------------------------------------------------------------------------\r
22 \r
23 This code has been altered significantly from its original form, to support\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
25 \r
26 ------------------------------------------------------------------------------- */\r
27 \r
28 \r
29 \r
30 /* marker */\r
31 #define MAP_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 /* FIXME: remove these vars */\r
41 \r
42 /* undefine to make plane finding use linear sort (note: really slow) */\r
43 #define USE_HASHING\r
44 #define PLANE_HASHES    8192\r
45 \r
46 plane_t                                 *planehash[ PLANE_HASHES ];\r
47 \r
48 int                                             c_boxbevels;\r
49 int                                             c_edgebevels;\r
50 int                                             c_areaportals;\r
51 int                                             c_detail;\r
52 int                                             c_structural;\r
53 \r
54 \r
55 \r
56 /*\r
57 PlaneEqual()\r
58 ydnar: replaced with variable epsilon for djbob\r
59 */\r
60 \r
61 #define NORMAL_EPSILON  0.00001\r
62 #define DIST_EPSILON    0.01\r
63 \r
64 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )\r
65 {\r
66         float   ne, de;\r
67         \r
68         \r
69         /* get local copies */\r
70         ne = normalEpsilon;\r
71         de = distanceEpsilon;\r
72         \r
73         /* compare */\r
74         if( fabs( p->dist - dist ) <= de &&\r
75                 fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&\r
76                 fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&\r
77                 fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )\r
78                 return qtrue;\r
79         \r
80         /* different */\r
81         return qfalse;\r
82 }\r
83 \r
84 \r
85 \r
86 /*\r
87 AddPlaneToHash()\r
88 */\r
89 \r
90 void AddPlaneToHash( plane_t *p )\r
91 {\r
92         int             hash;\r
93 \r
94         \r
95         hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );\r
96 \r
97         p->hash_chain = planehash[hash];\r
98         planehash[hash] = p;\r
99 }\r
100 \r
101 /*\r
102 ================\r
103 CreateNewFloatPlane\r
104 ================\r
105 */\r
106 int CreateNewFloatPlane (vec3_t normal, vec_t dist)\r
107 {\r
108         plane_t *p, temp;\r
109 \r
110         if (VectorLength(normal) < 0.5)\r
111         {\r
112                 Sys_Printf( "FloatPlane: bad normal\n");\r
113                 return -1;\r
114         }\r
115 \r
116         // create a new plane\r
117         if (nummapplanes+2 > MAX_MAP_PLANES)\r
118                 Error ("MAX_MAP_PLANES");\r
119 \r
120         p = &mapplanes[nummapplanes];\r
121         VectorCopy (normal, p->normal);\r
122         p->dist = dist;\r
123         p->type = (p+1)->type = PlaneTypeForNormal (p->normal);\r
124 \r
125         VectorSubtract (vec3_origin, normal, (p+1)->normal);\r
126         (p+1)->dist = -dist;\r
127 \r
128         nummapplanes += 2;\r
129 \r
130         // allways put axial planes facing positive first\r
131         if (p->type < 3)\r
132         {\r
133                 if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)\r
134                 {\r
135                         // flip order\r
136                         temp = *p;\r
137                         *p = *(p+1);\r
138                         *(p+1) = temp;\r
139 \r
140                         AddPlaneToHash (p);\r
141                         AddPlaneToHash (p+1);\r
142                         return nummapplanes - 1;\r
143                 }\r
144         }\r
145 \r
146         AddPlaneToHash (p);\r
147         AddPlaneToHash (p+1);\r
148         return nummapplanes - 2;\r
149 }\r
150 \r
151 \r
152 \r
153 /*\r
154 SnapNormal()\r
155 snaps a near-axial normal vector\r
156 */\r
157 \r
158 void SnapNormal( vec3_t normal )\r
159 {\r
160         int             i;\r
161 \r
162         for( i = 0; i < 3; i++ )\r
163         {\r
164                 if( fabs( normal[ i ] - 1 ) < normalEpsilon )\r
165                 {\r
166                         VectorClear( normal );\r
167                         normal[ i ] = 1;\r
168                         break;\r
169                 }\r
170                 if( fabs( normal[ i ] - -1 ) < normalEpsilon )\r
171                 {\r
172                         VectorClear( normal );\r
173                         normal[ i ] = -1;\r
174                         break;\r
175                 }\r
176         }\r
177 }\r
178 \r
179 \r
180 \r
181 /*\r
182 SnapPlane()\r
183 snaps a plane to normal/distance epsilons\r
184 */\r
185 \r
186 void SnapPlane( vec3_t normal, vec_t *dist )\r
187 {\r
188         SnapNormal( normal );\r
189 \r
190         if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )\r
191                 *dist = Q_rint( *dist );\r
192 }\r
193 \r
194 \r
195 \r
196 /*\r
197 FindFloatPlane()\r
198 ydnar: changed to allow a number of test points to be supplied that\r
199 must be within an epsilon distance of the plane\r
200 */\r
201 \r
202 int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )\r
203 \r
204 #ifdef USE_HASHING\r
205 \r
206 {\r
207         int             i, j, hash, h;\r
208         plane_t *p;\r
209         vec_t   d;\r
210         \r
211         \r
212         /* hash the plane */\r
213         SnapPlane( normal, &dist );\r
214         hash = (PLANE_HASHES - 1) & (int) fabs( dist );\r
215         \r
216         /* search the border bins as well */\r
217         for( i = -1; i <= 1; i++ )\r
218         {\r
219                 h = (hash + i) & (PLANE_HASHES - 1);\r
220                 for( p = planehash[ h ]; p != NULL; p = p->hash_chain )\r
221                 {\r
222                         /* do standard plane compare */\r
223                         if( !PlaneEqual( p, normal, dist ) )\r
224                                 continue;\r
225                         \r
226                         /* ydnar: uncomment the following line for old-style plane finding */\r
227                         //%     return p - mapplanes;\r
228                         \r
229                         /* ydnar: test supplied points against this plane */\r
230                         for( j = 0; j < numPoints; j++ )\r
231                         {\r
232                                 d = DotProduct( points[ j ], normal ) - dist;\r
233                                 if( fabs( d ) > distanceEpsilon )\r
234                                         break;\r
235                         }\r
236                         \r
237                         /* found a matching plane */\r
238                         if( j >= numPoints )\r
239                                 return p - mapplanes;\r
240                 }\r
241         }\r
242         \r
243         /* none found, so create a new one */\r
244         return CreateNewFloatPlane( normal, dist );\r
245 }\r
246 \r
247 #else\r
248 \r
249 {\r
250         int             i;\r
251         plane_t *p;\r
252         \r
253 \r
254         SnapPlane( normal, &dist );\r
255         for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )\r
256         {\r
257                 if( PlaneEqual( p, normal, dist ) )\r
258                         return i;\r
259         }\r
260         \r
261         return CreateNewFloatPlane( normal, dist );\r
262 }\r
263 \r
264 #endif\r
265 \r
266 \r
267 \r
268 /*\r
269 MapPlaneFromPoints()\r
270 takes 3 points and finds the plane they lie in\r
271 */\r
272 \r
273 int MapPlaneFromPoints( vec3_t *p )\r
274 {\r
275         vec3_t  t1, t2, normal;\r
276         vec_t   dist;\r
277         \r
278         \r
279         /* calc plane normal */\r
280         VectorSubtract( p[ 0 ], p[ 1 ], t1 );\r
281         VectorSubtract( p[ 2 ], p[ 1 ], t2 );\r
282         CrossProduct( t1, t2, normal );\r
283         VectorNormalize( normal, normal );\r
284         \r
285         /* calc plane distance */\r
286         dist = DotProduct( p[ 0 ], normal );\r
287         \r
288         /* store the plane */\r
289         return FindFloatPlane( normal, dist, 3, p );\r
290 }\r
291 \r
292 \r
293 \r
294 /*\r
295 SetBrushContents()\r
296 the content flags and compile flags on all sides of a brush should be the same\r
297 */\r
298 \r
299 void SetBrushContents( brush_t *b )\r
300 {\r
301         int                     contentFlags, compileFlags;\r
302         side_t          *s;\r
303         int                     i;\r
304         qboolean        mixed;\r
305         \r
306         \r
307         /* get initial compile flags from first side */\r
308         s = &b->sides[ 0 ];\r
309         contentFlags = s->contentFlags;\r
310         compileFlags = s->compileFlags;\r
311         b->contentShader = s->shaderInfo;\r
312         mixed = qfalse;\r
313         \r
314         /* get the content/compile flags for every side in the brush */\r
315         for( i = 1; i < b->numsides; i++, s++ )\r
316         {\r
317                 s = &b->sides[ i ];\r
318                 if( s->shaderInfo == NULL )\r
319                         continue;\r
320                 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )\r
321                         mixed = qtrue;\r
322         }\r
323         \r
324         /* ydnar: getting rid of this stupid warning */\r
325         //%     if( mixed )\r
326         //%             Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );\r
327 \r
328         /* check for detail & structural */\r
329         if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )\r
330         {\r
331                 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );\r
332                 compileFlags &= ~C_DETAIL;\r
333         }\r
334         \r
335         /* the fulldetail flag will cause detail brushes to be treated like normal brushes */\r
336         if( fulldetail )\r
337                 compileFlags &= ~C_DETAIL;\r
338         \r
339         /* all translucent brushes that aren't specifically made structural will be detail */\r
340         if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )\r
341                 compileFlags |= C_DETAIL;\r
342         \r
343         /* detail? */\r
344         if( compileFlags & C_DETAIL )\r
345         {\r
346                 c_detail++;\r
347                 b->detail = qtrue;\r
348         }\r
349         else\r
350         {\r
351                 c_structural++;\r
352                 b->detail = qfalse;\r
353         }\r
354         \r
355         /* opaque? */\r
356         if( compileFlags & C_TRANSLUCENT )\r
357                 b->opaque = qfalse;\r
358         else\r
359                 b->opaque = qtrue;\r
360         \r
361         /* areaportal? */\r
362         if( compileFlags & C_AREAPORTAL )\r
363                 c_areaportals++;\r
364         \r
365         /* set brush flags */\r
366         b->contentFlags = contentFlags;\r
367         b->compileFlags = compileFlags;\r
368 }\r
369 \r
370 \r
371 \r
372 /*\r
373 AddBrushBevels()\r
374 adds any additional planes necessary to allow the brush being\r
375 built to be expanded against axial bounding boxes\r
376 ydnar 2003-01-20: added mrelusive fixes\r
377 */\r
378 \r
379 void AddBrushBevels( void )\r
380 {\r
381         int                     axis, dir;\r
382         int                     i, j, k, l, order;\r
383         side_t          sidetemp;\r
384         side_t          *s, *s2;\r
385         winding_t       *w, *w2;\r
386         vec3_t          normal;\r
387         float           dist;\r
388         vec3_t          vec, vec2;\r
389         float           d, minBack;\r
390 \r
391         //\r
392         // add the axial planes\r
393         //\r
394         order = 0;\r
395         for ( axis = 0; axis < 3; axis++ ) {\r
396                 for ( dir = -1; dir <= 1; dir += 2, order++ ) {\r
397                         // see if the plane is allready present\r
398                         for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )\r
399                         {\r
400                                 /* ydnar: testing disabling of mre code */\r
401                                 #if 0\r
402                                         if ( dir > 0 ) {\r
403                                                 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {\r
404                                                         break;\r
405                                                 }\r
406                                         }\r
407                                         else {\r
408                                                 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {\r
409                                                         break;\r
410                                                 }\r
411                                         }\r
412                                 #else\r
413                                         if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||\r
414                                                 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )\r
415                                                 break;\r
416                                 #endif\r
417                         }\r
418 \r
419                         if ( i == buildBrush->numsides ) {\r
420                                 // add a new side\r
421                                 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
422                                         xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
423                                 }\r
424                                 memset( s, 0, sizeof( *s ) );\r
425                                 buildBrush->numsides++;\r
426                                 VectorClear (normal);\r
427                                 normal[axis] = dir;\r
428 \r
429                                 if( dir == 1 )\r
430                                 {\r
431                                         /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
432                                         if( bevelSnap > 0 )\r
433                                                 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;\r
434                                         else\r
435                                                 dist = buildBrush->maxs[ axis ];\r
436                                 }\r
437                                 else\r
438                                 {\r
439                                         /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
440                                         if( bevelSnap > 0 )\r
441                                                 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;\r
442                                         else\r
443                                                 dist = -buildBrush->mins[ axis ];\r
444                                 }\r
445 \r
446                                 s->planenum = FindFloatPlane( normal, dist, 0, NULL );\r
447                                 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;\r
448                                 s->bevel = qtrue;\r
449                                 c_boxbevels++;\r
450                         }\r
451 \r
452                         // if the plane is not in it canonical order, swap it\r
453                         if ( i != order ) {\r
454                                 sidetemp = buildBrush->sides[order];\r
455                                 buildBrush->sides[order] = buildBrush->sides[i];\r
456                                 buildBrush->sides[i] = sidetemp;\r
457                         }\r
458                 }\r
459         }\r
460 \r
461         //\r
462         // add the edge bevels\r
463         //\r
464         if ( buildBrush->numsides == 6 ) {\r
465                 return;         // pure axial\r
466         }\r
467 \r
468         // test the non-axial plane edges\r
469         for ( i = 6; i < buildBrush->numsides; i++ ) {\r
470                 s = buildBrush->sides + i;\r
471                 w = s->winding;\r
472                 if ( !w ) {\r
473                         continue;\r
474                 }\r
475                 for ( j = 0; j < w->numpoints; j++) {\r
476                         k = (j+1)%w->numpoints;\r
477                         VectorSubtract( w->p[j], w->p[k], vec );\r
478                         if ( VectorNormalize( vec, vec ) < 0.5f ) {\r
479                                 continue;\r
480                         }\r
481                         SnapNormal( vec );\r
482                         for ( k = 0; k < 3; k++ ) {\r
483                                 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {\r
484                                         break;  // axial\r
485                                 }\r
486                         }\r
487                         if ( k != 3 ) {\r
488                                 continue;       // only test non-axial edges\r
489                         }\r
490 \r
491                         /* debug code */\r
492                         //%     Sys_Printf( "-------------\n" );\r
493 \r
494                         // try the six possible slanted axials from this edge\r
495                         for ( axis = 0; axis < 3; axis++ ) {\r
496                                 for ( dir = -1; dir <= 1; dir += 2 ) {\r
497                                         // construct a plane\r
498                                         VectorClear( vec2 );\r
499                                         vec2[axis] = dir;\r
500                                         CrossProduct( vec, vec2, normal );\r
501                                         if ( VectorNormalize( normal, normal ) < 0.5f ) {\r
502                                                 continue;\r
503                                         }\r
504                                         dist = DotProduct( w->p[j], normal );\r
505                                         \r
506                                         // if all the points on all the sides are\r
507                                         // behind this plane, it is a proper edge bevel\r
508                                         for ( k = 0; k < buildBrush->numsides; k++ ) {\r
509 \r
510                                                 // if this plane has allready been used, skip it\r
511                                                 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {\r
512                                                         break;\r
513                                                 }\r
514 \r
515                                                 w2 = buildBrush->sides[k].winding;\r
516                                                 if ( !w2 ) {\r
517                                                         continue;\r
518                                                 }\r
519                                                 minBack = 0.0f;\r
520                                                 for ( l = 0; l < w2->numpoints; l++ ) {\r
521                                                         d = DotProduct( w2->p[l], normal ) - dist;\r
522                                                         if ( d > 0.1f ) {\r
523                                                                 break;  // point in front\r
524                                                         }\r
525                                                         if ( d < minBack ) {\r
526                                                                 minBack = d;\r
527                                                         }\r
528                                                 }\r
529                                                 // if some point was at the front\r
530                                                 if ( l != w2->numpoints ) {\r
531                                                         break;\r
532                                                 }\r
533 \r
534                                                 // if no points at the back then the winding is on the bevel plane\r
535                                                 if ( minBack > -0.1f ) {\r
536                                                         //%     Sys_Printf( "On bevel plane\n" );\r
537                                                         break;\r
538                                                 }\r
539                                         }\r
540 \r
541                                         if ( k != buildBrush->numsides ) {\r
542                                                 continue;       // wasn't part of the outer hull\r
543                                         }\r
544                                         \r
545                                         /* debug code */\r
546                                         //%     Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );\r
547                                         \r
548                                         // add this plane\r
549                                         if( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
550                                                 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
551                                         }\r
552                                         s2 = &buildBrush->sides[buildBrush->numsides];\r
553                                         buildBrush->numsides++;\r
554                                         memset( s2, 0, sizeof( *s2 ) );\r
555 \r
556                                         s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );\r
557                                         s2->contentFlags = buildBrush->sides[0].contentFlags;\r
558                                         s2->bevel = qtrue;\r
559                                         c_edgebevels++;\r
560                                 }\r
561                         }\r
562                 }\r
563         }\r
564 }\r
565 \r
566 \r
567 \r
568 /*\r
569 FinishBrush()\r
570 produces a final brush based on the buildBrush->sides array\r
571 and links it to the current entity\r
572 */\r
573 \r
574 brush_t *FinishBrush( void )\r
575 {\r
576         brush_t *b;\r
577         \r
578         \r
579         /* create windings for sides and bounds for brush */\r
580         if ( !CreateBrushWindings( buildBrush ) )\r
581                 return NULL;\r
582 \r
583         /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.\r
584            after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */\r
585         if( buildBrush->compileFlags & C_ORIGIN )\r
586         {\r
587                 char    string[ 32 ];\r
588                 vec3_t  origin;\r
589 \r
590                 if( numEntities == 1 )\r
591                 {\r
592                         Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n", \r
593                                 mapEnt->mapEntityNum, entitySourceBrushes );\r
594                         return NULL;\r
595                 }\r
596                 \r
597                 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);\r
598                 VectorScale (origin, 0.5, origin);\r
599 \r
600                 sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );\r
601                 SetKeyValue( &entities[ numEntities - 1 ], "origin", string);\r
602 \r
603                 VectorCopy( origin, entities[ numEntities - 1 ].origin);\r
604 \r
605                 /* don't keep this brush */\r
606                 return NULL;\r
607         }\r
608         \r
609         /* determine if the brush is an area portal */\r
610         if( buildBrush->compileFlags & C_AREAPORTAL )\r
611         {\r
612                 if( numEntities != 1 )\r
613                 {\r
614                         Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );\r
615                         return NULL;\r
616                 }\r
617         }\r
618         \r
619         /* add bevel planes */\r
620         AddBrushBevels();\r
621         \r
622         /* keep it */\r
623         b = CopyBrush( buildBrush );\r
624         \r
625         /* set map entity and brush numbering */\r
626         b->entityNum = mapEnt->mapEntityNum;\r
627         b->brushNum = entitySourceBrushes;\r
628         \r
629         /* set original */\r
630         b->original = b;\r
631         \r
632         /* link opaque brushes to head of list, translucent brushes to end */\r
633         if( b->opaque || mapEnt->lastBrush == NULL )\r
634         {\r
635                 b->next = mapEnt->brushes;\r
636                 mapEnt->brushes = b;\r
637                 if( mapEnt->lastBrush == NULL )\r
638                         mapEnt->lastBrush = b;\r
639         }\r
640         else\r
641         {\r
642                 b->next = NULL;\r
643                 mapEnt->lastBrush->next = b;\r
644                 mapEnt->lastBrush = b;\r
645         }\r
646         \r
647         /* return to sender */\r
648         return b;\r
649 }\r
650 \r
651 \r
652 \r
653 /*\r
654 TextureAxisFromPlane()\r
655 determines best orthagonal axis to project a texture onto a wall\r
656 (must be identical in radiant!)\r
657 */\r
658 \r
659 vec3_t  baseaxis[18] =\r
660 {\r
661         {0,0,1}, {1,0,0}, {0,-1,0},                     // floor\r
662         {0,0,-1}, {1,0,0}, {0,-1,0},            // ceiling\r
663         {1,0,0}, {0,1,0}, {0,0,-1},                     // west wall\r
664         {-1,0,0}, {0,1,0}, {0,0,-1},            // east wall\r
665         {0,1,0}, {1,0,0}, {0,0,-1},                     // south wall\r
666         {0,-1,0}, {1,0,0}, {0,0,-1}                     // north wall\r
667 };\r
668 \r
669 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )\r
670 {\r
671         int             bestaxis;\r
672         vec_t   dot,best;\r
673         int             i;\r
674         \r
675         best = 0;\r
676         bestaxis = 0;\r
677         \r
678         for (i=0 ; i<6 ; i++)\r
679         {\r
680                 dot = DotProduct (pln->normal, baseaxis[i*3]);\r
681                 if( dot > best + 0.0001f )      /* ydnar: bug 637 fix, suggested by jmonroe */\r
682                 {\r
683                         best = dot;\r
684                         bestaxis = i;\r
685                 }\r
686         }\r
687         \r
688         VectorCopy (baseaxis[bestaxis*3+1], xv);\r
689         VectorCopy (baseaxis[bestaxis*3+2], yv);\r
690 }\r
691 \r
692 \r
693 \r
694 /*\r
695 QuakeTextureVecs()\r
696 creates world-to-texture mapping vecs for crappy quake plane arrangements\r
697 */\r
698 \r
699 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )\r
700 {\r
701         vec3_t  vecs[2];\r
702         int             sv, tv;\r
703         vec_t   ang, sinv, cosv;\r
704         vec_t   ns, nt;\r
705         int             i, j;\r
706         \r
707         \r
708         TextureAxisFromPlane(plane, vecs[0], vecs[1]);\r
709         \r
710         if (!scale[0])\r
711                 scale[0] = 1;\r
712         if (!scale[1])\r
713                 scale[1] = 1;\r
714 \r
715         // rotate axis\r
716         if (rotate == 0)\r
717                 { sinv = 0 ; cosv = 1; }\r
718         else if (rotate == 90)\r
719                 { sinv = 1 ; cosv = 0; }\r
720         else if (rotate == 180)\r
721                 { sinv = 0 ; cosv = -1; }\r
722         else if (rotate == 270)\r
723                 { sinv = -1 ; cosv = 0; }\r
724         else\r
725         {       \r
726                 ang = rotate / 180 * Q_PI;\r
727                 sinv = sin(ang);\r
728                 cosv = cos(ang);\r
729         }\r
730 \r
731         if (vecs[0][0])\r
732                 sv = 0;\r
733         else if (vecs[0][1])\r
734                 sv = 1;\r
735         else\r
736                 sv = 2;\r
737                                 \r
738         if (vecs[1][0])\r
739                 tv = 0;\r
740         else if (vecs[1][1])\r
741                 tv = 1;\r
742         else\r
743                 tv = 2;\r
744                                         \r
745         for (i=0 ; i<2 ; i++) {\r
746                 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];\r
747                 nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];\r
748                 vecs[i][sv] = ns;\r
749                 vecs[i][tv] = nt;\r
750         }\r
751 \r
752         for (i=0 ; i<2 ; i++)\r
753                 for (j=0 ; j<3 ; j++)\r
754                         mappingVecs[i][j] = vecs[i][j] / scale[i];\r
755 \r
756         mappingVecs[0][3] = shift[0];\r
757         mappingVecs[1][3] = shift[1];\r
758 }\r
759 \r
760 \r
761 \r
762 /*\r
763 ParseRawBrush()\r
764 parses the sides into buildBrush->sides[], nothing else.\r
765 no validation, back plane removal, etc.\r
766 \r
767 Timo - 08/26/99\r
768 added brush epairs parsing ( ignoring actually )\r
769 Timo - 08/04/99\r
770 added exclusive brush primitive parsing\r
771 Timo - 08/08/99\r
772 support for old brush format back in\r
773 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes\r
774 */\r
775 \r
776 static void ParseRawBrush( qboolean onlyLights )\r
777 {\r
778         side_t                  *side;\r
779         vec3_t                  planePoints[ 3 ];\r
780         int                             planenum;\r
781         shaderInfo_t    *si;\r
782         vec_t                   shift[ 2 ];\r
783         vec_t                   rotate;\r
784         vec_t                   scale[ 2 ];\r
785         char                    name[ MAX_QPATH ];\r
786         char                    shader[ MAX_QPATH ];\r
787         int                             flags;\r
788         \r
789         \r
790         /* initial setup */\r
791         buildBrush->numsides = 0;\r
792         buildBrush->detail = qfalse;\r
793         \r
794         /* bp */\r
795         if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
796                 MatchToken( "{" );\r
797         \r
798         /* parse sides */\r
799         while( 1 )\r
800         {\r
801                 if( !GetToken( qtrue ) )\r
802                         break;\r
803                 if( !strcmp( token, "}" ) )\r
804                         break;\r
805                  \r
806                 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */\r
807                 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
808                 {\r
809                         while( 1 )\r
810                         {\r
811                                 if( strcmp( token, "(" ) )\r
812                                         GetToken( qfalse );\r
813                                 else\r
814                                         break;\r
815                                 GetToken( qtrue );\r
816                         }\r
817                 }\r
818                 UnGetToken();\r
819                 \r
820                 /* test side count */\r
821                 if( buildBrush->numsides >= MAX_BUILD_SIDES )\r
822                         xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );\r
823                 \r
824                 /* add side */\r
825                 side = &buildBrush->sides[ buildBrush->numsides ];\r
826                 memset( side, 0, sizeof( *side ) );\r
827                 buildBrush->numsides++;\r
828                 \r
829                 /* read the three point plane definition */\r
830                 Parse1DMatrix( 3, planePoints[ 0 ] );\r
831                 Parse1DMatrix( 3, planePoints[ 1 ] );\r
832                 Parse1DMatrix( 3, planePoints[ 2 ] );\r
833                 \r
834                 /* bp: read the texture matrix */\r
835                 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
836                         Parse2DMatrix( 2, 3, (float*) side->texMat );\r
837                 \r
838                 /* read shader name */\r
839                 GetToken( qfalse );\r
840                 strcpy( name, token );\r
841                 \r
842                 /* bp */\r
843                 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
844                 {\r
845                         GetToken( qfalse );\r
846                         shift[ 0 ] = atof( token );\r
847                         GetToken( qfalse );\r
848                         shift[ 1 ] = atof( token );\r
849                         GetToken( qfalse );\r
850                         rotate = atof( token ); \r
851                         GetToken( qfalse );\r
852                         scale[ 0 ] = atof( token );\r
853                         GetToken( qfalse );\r
854                         scale[ 1 ] = atof( token );\r
855                 }\r
856                 \r
857                 /* set default flags and values */\r
858                 sprintf( shader, "textures/%s", name );\r
859                 if( onlyLights )\r
860                         si = &shaderInfo[ 0 ];\r
861                 else\r
862                         si = ShaderInfoForShader( shader );\r
863                 side->shaderInfo = si;\r
864                 side->surfaceFlags = si->surfaceFlags;\r
865                 side->contentFlags = si->contentFlags;\r
866                 side->compileFlags = si->compileFlags;\r
867                 side->value = si->value;\r
868                 \r
869                 /* ydnar: gs mods: bias texture shift */\r
870                 if( si->globalTexture == qfalse )\r
871                 {\r
872                         shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);\r
873                         shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);\r
874                 }\r
875                 \r
876                 /*\r
877                         historically, there are 3 integer values at the end of a brushside line in a .map file.\r
878                         in quake 3, the only thing that mattered was the first of these three values, which\r
879                         was previously the content flags. and only then did a single bit matter, the detail\r
880                         bit. because every game has its own special flags for specifying detail, the\r
881                         traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0\r
882                         by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but\r
883                         is stored in compileFlags, as opposed to contentFlags, for multiple-game\r
884                         portability. :sigh:\r
885                 */\r
886                 \r
887                 if( TokenAvailable() )\r
888                 {\r
889                         /* get detail bit from map content flags */\r
890                         GetToken( qfalse );\r
891                         flags = atoi( token );\r
892                         if( flags & C_DETAIL )\r
893                                 side->compileFlags |= C_DETAIL;\r
894                         \r
895                         /* historical */\r
896                         GetToken( qfalse );\r
897                         //% td.flags = atoi( token );\r
898                         GetToken( qfalse );\r
899                         //% td.value = atoi( token );\r
900                 }\r
901                 \r
902                 /* find the plane number */\r
903                 planenum = MapPlaneFromPoints( planePoints );\r
904                 side->planenum = planenum;\r
905                 \r
906                 /* bp: get the texture mapping for this texturedef / plane combination */\r
907                 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
908                         QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );\r
909         }\r
910         \r
911         /* bp */\r
912         if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
913         {\r
914                 UnGetToken();\r
915                 MatchToken( "}" );\r
916                 MatchToken( "}" );\r
917         }\r
918 }\r
919 \r
920 \r
921 \r
922 /*\r
923 RemoveDuplicateBrushPlanes\r
924 returns false if the brush has a mirrored set of planes,\r
925 meaning it encloses no volume.\r
926 also removes planes without any normal\r
927 */\r
928 \r
929 qboolean RemoveDuplicateBrushPlanes( brush_t *b )\r
930 {\r
931         int                     i, j, k;\r
932         side_t          *sides;\r
933 \r
934         sides = b->sides;\r
935 \r
936         for ( i = 1 ; i < b->numsides ; i++ ) {\r
937 \r
938                 // check for a degenerate plane\r
939                 if ( sides[i].planenum == -1) {\r
940                   xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );\r
941                         // remove it\r
942                         for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
943                                 sides[k-1] = sides[k];\r
944                         }\r
945                         b->numsides--;\r
946                         i--;\r
947                         continue;\r
948                 }\r
949 \r
950                 // check for duplication and mirroring\r
951                 for ( j = 0 ; j < i ; j++ ) {\r
952                         if ( sides[i].planenum == sides[j].planenum ) {\r
953                           xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );\r
954                                 // remove the second duplicate\r
955                                 for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
956                                         sides[k-1] = sides[k];\r
957                                 }\r
958                                 b->numsides--;\r
959                                 i--;\r
960                                 break;\r
961                         }\r
962 \r
963                         if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {\r
964                                 // mirror plane, brush is invalid\r
965                           xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );\r
966                                 return qfalse;\r
967                         }\r
968                 }\r
969         }\r
970         return qtrue;\r
971 }\r
972 \r
973 \r
974 \r
975 /*\r
976 ParseBrush()\r
977 parses a brush out of a map file and sets it up\r
978 */\r
979 \r
980 static void ParseBrush( qboolean onlyLights )\r
981 {\r
982         brush_t *b;\r
983         \r
984         \r
985         /* parse the brush out of the map */\r
986         ParseRawBrush( onlyLights );\r
987         \r
988         /* only go this far? */\r
989         if( onlyLights )\r
990                 return;\r
991         \r
992         /* set some defaults */\r
993         buildBrush->portalareas[ 0 ] = -1;\r
994         buildBrush->portalareas[ 1 ] = -1;\r
995         buildBrush->entityNum = numMapEntities - 1;\r
996         buildBrush->brushNum = entitySourceBrushes;\r
997         \r
998         /* if there are mirrored planes, the entire brush is invalid */\r
999         if( !RemoveDuplicateBrushPlanes( buildBrush ) )\r
1000                 return;\r
1001         \r
1002         /* get the content for the entire brush */\r
1003         SetBrushContents( buildBrush );\r
1004         \r
1005         /* allow detail brushes to be removed */\r
1006         if( nodetail && (buildBrush->compileFlags & C_DETAIL) )\r
1007         {\r
1008                 //%     FreeBrush( buildBrush );\r
1009                 return;\r
1010         }\r
1011         \r
1012         /* allow liquid brushes to be removed */\r
1013         if( nowater && (buildBrush->compileFlags & C_LIQUID ) )\r
1014         {\r
1015                 //%     FreeBrush( buildBrush );\r
1016                 return;\r
1017         }\r
1018         \r
1019         /* ydnar: allow hint brushes to be removed */\r
1020         if( noHint && (buildBrush->compileFlags & C_HINT) )\r
1021         {\r
1022                 //%     FreeBrush( buildBrush );\r
1023                 return;\r
1024         }\r
1025         \r
1026         /* finish the brush */\r
1027         b = FinishBrush();\r
1028 }\r
1029 \r
1030 \r
1031 \r
1032 /*\r
1033 MoveBrushesToWorld()\r
1034 takes all of the brushes from the current entity and\r
1035 adds them to the world's brush list\r
1036 (used by func_group)\r
1037 */\r
1038 \r
1039 void MoveBrushesToWorld( entity_t *ent )\r
1040 {\r
1041         brush_t         *b, *next;\r
1042         parseMesh_t     *pm;\r
1043 \r
1044         \r
1045         /* move brushes */\r
1046         for( b = ent->brushes; b != NULL; b = next )\r
1047         {\r
1048                 /* get next brush */\r
1049                 next = b->next;\r
1050                 \r
1051                 /* link opaque brushes to head of list, translucent brushes to end */\r
1052                 if( b->opaque || entities[ 0 ].lastBrush == NULL )\r
1053                 {\r
1054                         b->next = entities[ 0 ].brushes;\r
1055                         entities[ 0 ].brushes = b;\r
1056                         if( entities[ 0 ].lastBrush == NULL )\r
1057                                 entities[ 0 ].lastBrush = b;\r
1058                 }\r
1059                 else\r
1060                 {\r
1061                         b->next = NULL;\r
1062                         entities[ 0 ].lastBrush->next = b;\r
1063                         entities[ 0 ].lastBrush = b;\r
1064                 }\r
1065                 \r
1066                 //%     b->next = entities[ 0 ].brushes;\r
1067                 //%     entities[ 0 ].brushes = b;\r
1068         }\r
1069         ent->brushes = NULL;\r
1070         \r
1071         /* move patches */\r
1072         if( ent->patches != NULL )\r
1073         {\r
1074                 for( pm = ent->patches; pm->next; pm = pm->next );\r
1075                 \r
1076                 pm->next = entities[ 0 ].patches;\r
1077                 entities[ 0 ].patches = ent->patches;\r
1078                 \r
1079                 ent->patches = NULL;\r
1080         }\r
1081 }\r
1082 \r
1083 \r
1084 \r
1085 /*\r
1086 AdjustBrushesForOrigin()\r
1087 */\r
1088 \r
1089 void AdjustBrushesForOrigin( entity_t *ent )\r
1090 {\r
1091         \r
1092         int                     i;\r
1093         side_t          *s;\r
1094         vec_t           newdist;\r
1095         brush_t         *b;\r
1096         parseMesh_t     *p;\r
1097         \r
1098         \r
1099         /* walk brush list */\r
1100         for( b = ent->brushes; b != NULL; b = b->next )\r
1101         {\r
1102                 /* offset brush planes */\r
1103                 for( i = 0; i < b->numsides; i++)\r
1104                 {\r
1105                         /* get brush side */\r
1106                         s = &b->sides[ i ];\r
1107                         \r
1108                         /* offset side plane */\r
1109                         newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );\r
1110                         \r
1111                         /* find a new plane */\r
1112                         s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );\r
1113                 }\r
1114                 \r
1115                 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */\r
1116                 CreateBrushWindings( b );\r
1117         }\r
1118         \r
1119         /* walk patch list */\r
1120         for( p = ent->patches; p != NULL; p = p->next )\r
1121         {\r
1122                 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
1123                         VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );\r
1124         }\r
1125 }\r
1126 \r
1127 \r
1128 \r
1129 /*\r
1130 SetEntityBounds() - ydnar\r
1131 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)\r
1132 */\r
1133 \r
1134 void SetEntityBounds( entity_t *e )\r
1135 {\r
1136         int                     i;\r
1137         brush_t *b;\r
1138         parseMesh_t     *p;\r
1139         vec3_t          mins, maxs;\r
1140         const char      *value;\r
1141 \r
1142         \r
1143         \r
1144 \r
1145         /* walk the entity's brushes/patches and determine bounds */\r
1146         ClearBounds( mins, maxs );\r
1147         for( b = e->brushes; b; b = b->next )\r
1148         {\r
1149                 AddPointToBounds( b->mins, mins, maxs );\r
1150                 AddPointToBounds( b->maxs, mins, maxs );\r
1151         }\r
1152         for( p = e->patches; p; p = p->next )\r
1153         {\r
1154                 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
1155                         AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );\r
1156         }\r
1157         \r
1158         /* try to find explicit min/max key */\r
1159         value = ValueForKey( e, "min" ); \r
1160         if( value[ 0 ] != '\0' )\r
1161                 GetVectorForKey( e, "min", mins );\r
1162         value = ValueForKey( e, "max" ); \r
1163         if( value[ 0 ] != '\0' )\r
1164                 GetVectorForKey( e, "max", maxs );\r
1165         \r
1166         /* store the bounds */\r
1167         for( b = e->brushes; b; b = b->next )\r
1168         {\r
1169                 VectorCopy( mins, b->eMins );\r
1170                 VectorCopy( maxs, b->eMaxs );\r
1171         }\r
1172         for( p = e->patches; p; p = p->next )\r
1173         {\r
1174                 VectorCopy( mins, p->eMins );\r
1175                 VectorCopy( maxs, p->eMaxs );\r
1176         }\r
1177 }\r
1178 \r
1179 \r
1180 \r
1181 /*\r
1182 LoadEntityIndexMap() - ydnar\r
1183 based on LoadAlphaMap() from terrain.c, a little more generic\r
1184 */\r
1185 \r
1186 void LoadEntityIndexMap( entity_t *e )\r
1187 {\r
1188         int                             i, size, numLayers, w, h;\r
1189         const char              *value, *indexMapFilename, *shader;\r
1190         char                    ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;\r
1191         byte                    *pixels;\r
1192         unsigned int    *pixels32;\r
1193         indexMap_t              *im;\r
1194         brush_t                 *b;\r
1195         parseMesh_t             *p;\r
1196         \r
1197         \r
1198         /* this only works with bmodel ents */\r
1199         if( e->brushes == NULL && e->patches == NULL )\r
1200                 return;\r
1201         \r
1202         /* determine if there is an index map (support legacy "alphamap" key as well) */\r
1203         value = ValueForKey( e, "_indexmap" );\r
1204         if( value[ 0 ] == '\0' )\r
1205                 value = ValueForKey( e, "alphamap" );\r
1206         if( value[ 0 ] == '\0' )\r
1207                 return;\r
1208         indexMapFilename = value;\r
1209         \r
1210         /* get number of layers (support legacy "layers" key as well) */\r
1211         value = ValueForKey( e, "_layers" );\r
1212         if( value[ 0 ] == '\0' )\r
1213                 value = ValueForKey( e, "layers" );\r
1214         if( value[ 0 ] == '\0' )\r
1215         {\r
1216                 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );\r
1217                 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
1218                 return;\r
1219         }\r
1220         numLayers = atoi( value );\r
1221         if( numLayers < 1 )\r
1222         {\r
1223                 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );\r
1224                 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
1225                 return;\r
1226         }\r
1227         \r
1228         /* get base shader name (support legacy "shader" key as well) */\r
1229         value = ValueForKey( mapEnt, "_shader" );\r
1230         if( value[ 0 ] == '\0' )\r
1231                 value = ValueForKey( e, "shader" );\r
1232         if( value[ 0 ] == '\0' )\r
1233         {\r
1234                 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );\r
1235                 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
1236                 return;\r
1237         }\r
1238         shader = value;\r
1239         \r
1240         /* note it */\r
1241         Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n",  mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );\r
1242         \r
1243         /* get index map file extension */\r
1244         ExtractFileExtension( indexMapFilename, ext );\r
1245         \r
1246         /* handle tga image */\r
1247         if( !Q_stricmp( ext, "tga" ) )\r
1248         {\r
1249                 /* load it */\r
1250                 Load32BitImage( indexMapFilename, &pixels32, &w, &h );\r
1251                 \r
1252                 /* convert to bytes */\r
1253                 size = w * h;\r
1254                 pixels = safe_malloc( size );\r
1255                 for( i = 0; i < size; i++ )\r
1256                 {\r
1257                         pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;\r
1258                         if( pixels[ i ] >= numLayers )\r
1259                                 pixels[ i ] = numLayers - 1;\r
1260                 }\r
1261                 \r
1262                 /* free the 32 bit image */\r
1263                 free( pixels32 );\r
1264         }\r
1265         else\r
1266         {\r
1267                 /* load it */\r
1268                 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );\r
1269                 \r
1270                 /* debug code */\r
1271                 //%     Sys_Printf( "-------------------------------" );\r
1272                 \r
1273                 /* fix up out-of-range values */\r
1274                 size = w * h;\r
1275                 for( i = 0; i < size; i++ )\r
1276                 {\r
1277                         if( pixels[ i ] >= numLayers )\r
1278                                 pixels[ i ] = numLayers - 1;\r
1279                         \r
1280                         /* debug code */\r
1281                         //%     if( (i % w) == 0 )\r
1282                         //%             Sys_Printf( "\n" );\r
1283                         //%     Sys_Printf( "%c", pixels[ i ] + '0' );\r
1284                 }\r
1285                 \r
1286                 /* debug code */\r
1287                 //%     Sys_Printf( "\n-------------------------------\n" );\r
1288         }\r
1289         \r
1290         /* the index map must be at least 2x2 pixels */\r
1291         if( w < 2 || h < 2 )\r
1292         {\r
1293                 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );\r
1294                 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
1295                 free( pixels );\r
1296                 return;\r
1297         }\r
1298 \r
1299         /* create a new index map */\r
1300         im = safe_malloc( sizeof( *im ) );\r
1301         memset( im, 0, sizeof( *im ) );\r
1302         \r
1303         /* set it up */\r
1304         im->w = w;\r
1305         im->h = h;\r
1306         im->numLayers = numLayers;\r
1307         strcpy( im->name, indexMapFilename );\r
1308         strcpy( im->shader, shader );\r
1309         im->pixels = pixels;\r
1310         \r
1311         /* get height offsets */\r
1312         value = ValueForKey( mapEnt, "_offsets" );\r
1313         if( value[ 0 ] == '\0' )\r
1314                 value = ValueForKey( e, "offsets" );\r
1315         if( value[ 0 ] != '\0' )\r
1316         {\r
1317                 /* value is a space-seperated set of numbers */\r
1318                 strcpy( offset, value );\r
1319                 search = offset;\r
1320                 \r
1321                 /* get each value */\r
1322                 for( i = 0; i < 256 && *search != '\0'; i++ )\r
1323                 {\r
1324                         space = strstr( search, " " );\r
1325                         if( space != NULL )\r
1326                                 *space = '\0';\r
1327                         im->offsets[ i ] = atof( search );\r
1328                         if( space == NULL )\r
1329                                 break;\r
1330                         search = space + 1;\r
1331                 }\r
1332         }\r
1333         \r
1334         /* store the index map in every brush/patch in the entity */\r
1335         for( b = e->brushes; b != NULL; b = b->next )\r
1336                 b->im = im;\r
1337         for( p = e->patches; p != NULL; p = p->next )\r
1338                 p->im = im;\r
1339 }\r
1340 \r
1341 \r
1342 \r
1343 \r
1344 \r
1345 \r
1346 \r
1347 /*\r
1348 ParseMapEntity()\r
1349 parses a single entity out of a map file\r
1350 */\r
1351 \r
1352 static qboolean ParseMapEntity( qboolean onlyLights )\r
1353 {\r
1354         epair_t                 *ep;\r
1355         const char              *classname, *value;\r
1356         float                   lightmapScale;\r
1357         char                    shader[ MAX_QPATH ];\r
1358         shaderInfo_t    *celShader = NULL;\r
1359         brush_t                 *brush;\r
1360         parseMesh_t             *patch;\r
1361         qboolean                funcGroup;\r
1362         int                             castShadows, recvShadows;\r
1363         \r
1364         \r
1365         /* eof check */\r
1366         if( !GetToken( qtrue ) )\r
1367                 return qfalse;\r
1368         \r
1369         /* conformance check */\r
1370         if( strcmp( token, "{" ) )\r
1371         {\r
1372                 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"\r
1373                         "Continuing to process map, but resulting BSP may be invalid.\n",\r
1374                         token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );\r
1375                 return qfalse;\r
1376         }\r
1377         \r
1378         /* range check */\r
1379         if( numEntities >= MAX_MAP_ENTITIES )\r
1380                 Error( "numEntities == MAX_MAP_ENTITIES" );\r
1381         \r
1382         /* setup */\r
1383         entitySourceBrushes = 0;\r
1384         mapEnt = &entities[ numEntities ];\r
1385         numEntities++;\r
1386         memset( mapEnt, 0, sizeof( *mapEnt ) );\r
1387         \r
1388         /* ydnar: true entity numbering */\r
1389         mapEnt->mapEntityNum = numMapEntities;\r
1390         numMapEntities++;\r
1391         \r
1392         /* loop */\r
1393         while( 1 )\r
1394         {\r
1395                 /* get initial token */\r
1396                 if( !GetToken( qtrue ) )\r
1397                 {\r
1398                         Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"\r
1399                                 "Continuing to process map, but resulting BSP may be invalid.\n" );\r
1400                         return qfalse;\r
1401                 }\r
1402                 \r
1403                 if( !strcmp( token, "}" ) )\r
1404                         break;\r
1405                 \r
1406                 if( !strcmp( token, "{" ) )\r
1407                 {\r
1408                         /* parse a brush or patch */\r
1409                         if( !GetToken( qtrue ) )\r
1410                                 break;\r
1411                         \r
1412                         /* check */\r
1413                         if( !strcmp( token, "patchDef2" ) )\r
1414                         {\r
1415                                 numMapPatches++;\r
1416                                 ParsePatch( onlyLights );\r
1417                         }\r
1418                         else if( !strcmp( token, "terrainDef" ) )\r
1419                         {\r
1420                                 //% ParseTerrain();\r
1421                                 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */\r
1422                         }\r
1423                         else if( !strcmp( token, "brushDef" ) )\r
1424                         {\r
1425                                 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
1426                                         Error( "Old brush format not allowed in new brush format map" );\r
1427                                 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;\r
1428                                 \r
1429                                 /* parse brush primitive */\r
1430                                 ParseBrush( onlyLights );\r
1431                         }\r
1432                         else\r
1433                         {\r
1434                                 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
1435                                         Error( "New brush format not allowed in old brush format map" );\r
1436                                 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;\r
1437                                 \r
1438                                 /* parse old brush format */\r
1439                                 UnGetToken();\r
1440                                 ParseBrush( onlyLights );\r
1441                         }\r
1442                         entitySourceBrushes++;\r
1443                 }\r
1444                 else\r
1445                 {\r
1446                         /* parse a key / value pair */\r
1447                         ep = ParseEPair();\r
1448                         \r
1449                         /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */\r
1450                         if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )\r
1451                         {\r
1452                                 ep->next = mapEnt->epairs;\r
1453                                 mapEnt->epairs = ep;\r
1454                         }\r
1455                 }\r
1456         }\r
1457         \r
1458         /* ydnar: get classname */\r
1459         classname = ValueForKey( mapEnt, "classname" );\r
1460         \r
1461         /* ydnar: only lights? */\r
1462         if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )\r
1463         {\r
1464                 numEntities--;\r
1465                 return qtrue;\r
1466         }\r
1467         \r
1468         /* ydnar: determine if this is a func_group */\r
1469         if( !Q_stricmp( "func_group", classname ) )\r
1470                 funcGroup = qtrue;\r
1471         else\r
1472                 funcGroup = qfalse;\r
1473         \r
1474         /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */\r
1475         if( funcGroup || mapEnt->mapEntityNum == 0 )\r
1476         {\r
1477                 //%     Sys_Printf( "World:  %d\n", mapEnt->mapEntityNum );\r
1478                 castShadows = WORLDSPAWN_CAST_SHADOWS;\r
1479                 recvShadows = WORLDSPAWN_RECV_SHADOWS;\r
1480         }\r
1481         \r
1482         /* other entities don't cast any shadows, but recv worldspawn shadows */\r
1483         else\r
1484         {\r
1485                 //%     Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );\r
1486                 castShadows = ENTITY_CAST_SHADOWS;\r
1487                 recvShadows = ENTITY_RECV_SHADOWS;\r
1488         }\r
1489         \r
1490         /* get explicit shadow flags */\r
1491         GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );\r
1492         \r
1493         /* ydnar: get lightmap scaling value for this entity */\r
1494         if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||\r
1495                 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )\r
1496         {\r
1497                 /* get lightmap scale from entity */\r
1498                 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );\r
1499                 if( lightmapScale <= 0.0f )\r
1500                         lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );\r
1501                 if( lightmapScale > 0.0f )\r
1502                         Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );\r
1503         }\r
1504         else\r
1505                 lightmapScale = 0.0f;\r
1506         \r
1507         /* ydnar: get cel shader :) for this entity */\r
1508         value = ValueForKey( mapEnt, "_celshader" );\r
1509         if( value[ 0 ] == '\0' )        \r
1510                 value = ValueForKey( &entities[ 0 ], "_celshader" );\r
1511         if( value[ 0 ] != '\0' )\r
1512         {\r
1513                 sprintf( shader, "textures/%s", value );\r
1514                 celShader = ShaderInfoForShader( shader );\r
1515                 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );\r
1516         }\r
1517         else\r
1518                 celShader = NULL;\r
1519         \r
1520         /* attach stuff to everything in the entity */\r
1521         for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )\r
1522         {\r
1523                 brush->entityNum = mapEnt->mapEntityNum;\r
1524                 brush->castShadows = castShadows;\r
1525                 brush->recvShadows = recvShadows;\r
1526                 brush->lightmapScale = lightmapScale;\r
1527                 brush->celShader = celShader;\r
1528         }\r
1529         \r
1530         for( patch = mapEnt->patches; patch != NULL; patch = patch->next )\r
1531         {\r
1532                 patch->entityNum = mapEnt->mapEntityNum;\r
1533                 patch->castShadows = castShadows;\r
1534                 patch->recvShadows = recvShadows;\r
1535                 patch->lightmapScale = lightmapScale;\r
1536                 patch->celShader = celShader;\r
1537         }\r
1538         \r
1539         /* ydnar: gs mods: set entity bounds */\r
1540         SetEntityBounds( mapEnt );\r
1541         \r
1542         /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */\r
1543         LoadEntityIndexMap( mapEnt );\r
1544         \r
1545         /* get entity origin and adjust brushes */\r
1546         GetVectorForKey( mapEnt, "origin", mapEnt->origin );\r
1547         if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )\r
1548                 AdjustBrushesForOrigin( mapEnt );\r
1549 \r
1550         /* group_info entities are just for editor grouping (fixme: leak!) */\r
1551         if( !Q_stricmp( "group_info", classname ) )\r
1552         {\r
1553                 numEntities--;\r
1554                 return qtrue;\r
1555         }\r
1556         \r
1557         /* group entities are just for editor convenience, toss all brushes into worldspawn */\r
1558         if( funcGroup )\r
1559         {\r
1560                 MoveBrushesToWorld( mapEnt );\r
1561                 numEntities--;\r
1562                 return qtrue;\r
1563         }\r
1564         \r
1565         /* done */\r
1566         return qtrue;\r
1567 }\r
1568 \r
1569 \r
1570 \r
1571 /*\r
1572 LoadMapFile()\r
1573 loads a map file into a list of entities\r
1574 */\r
1575 \r
1576 void LoadMapFile( char *filename, qboolean onlyLights )\r
1577 {               \r
1578         FILE            *file;\r
1579         brush_t         *b;\r
1580         int                     oldNumEntities, numMapBrushes;\r
1581         \r
1582         \r
1583         /* note it */\r
1584         Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );\r
1585         Sys_Printf( "Loading %s\n", filename );\r
1586         \r
1587         /* hack */\r
1588         file = SafeOpenRead( filename );\r
1589         fclose( file );\r
1590         \r
1591         /* load the map file */\r
1592         LoadScriptFile( filename, -1 );\r
1593         \r
1594         /* setup */\r
1595         if( onlyLights )\r
1596                 oldNumEntities = numEntities;\r
1597         else\r
1598                 numEntities = 0;\r
1599         \r
1600         /* initial setup */\r
1601         numMapDrawSurfs = 0;\r
1602         c_detail = 0;\r
1603         g_bBrushPrimit = BPRIMIT_UNDEFINED;\r
1604         \r
1605         /* allocate a very large temporary brush for building the brushes as they are loaded */\r
1606         buildBrush = AllocBrush( MAX_BUILD_SIDES );\r
1607         \r
1608         /* parse the map file */\r
1609         while( ParseMapEntity( onlyLights ) );\r
1610         \r
1611         /* light loading */\r
1612         if( onlyLights )\r
1613         {\r
1614                 /* emit some statistics */\r
1615                 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );\r
1616         }\r
1617         else\r
1618         {\r
1619                 /* set map bounds */\r
1620                 ClearBounds( mapMins, mapMaxs );\r
1621                 for( b = entities[ 0 ].brushes; b; b = b->next )\r
1622                 {\r
1623                         AddPointToBounds( b->mins, mapMins, mapMaxs );\r
1624                         AddPointToBounds( b->maxs, mapMins, mapMaxs );\r
1625                 }\r
1626                 \r
1627                 /* get brush counts */\r
1628                 numMapBrushes = CountBrushList( entities[ 0 ].brushes );\r
1629                 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )\r
1630                         Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );\r
1631                 \r
1632                 /* emit some statistics */\r
1633                 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );\r
1634                 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );\r
1635                 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);\r
1636                 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);\r
1637                 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);\r
1638                 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );\r
1639                 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);\r
1640                 Sys_Printf( "%9d areaportals\n", c_areaportals);\r
1641                 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",\r
1642                         mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],\r
1643                         mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);\r
1644                 \r
1645                 /* write bogus map */\r
1646                 if( fakemap )\r
1647                         WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );\r
1648         }\r
1649 }\r