transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / writebsp.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 WRITEBSP_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 /*\r
41 EmitShader()\r
42 emits a bsp shader entry\r
43 */\r
44 \r
45 int     EmitShader( const char *shader, int *contentFlags, int *surfaceFlags )\r
46 {\r
47         int                             i;\r
48         shaderInfo_t    *si;\r
49         \r
50         \r
51         /* handle special cases */\r
52         if( shader == NULL )\r
53                 shader = "noshader";\r
54         \r
55         /* try to find an existing shader */\r
56         for( i = 0; i < numBSPShaders; i++ )\r
57         {\r
58                 /* ydnar: handle custom surface/content flags */\r
59                 if( surfaceFlags != NULL && bspShaders[ i ].surfaceFlags != *surfaceFlags )\r
60                         continue;\r
61                 if( contentFlags != NULL && bspShaders[ i ].contentFlags != *contentFlags )\r
62                         continue;\r
63                 \r
64                 /* compare name */\r
65                 if( !Q_stricmp( shader, bspShaders[ i ].shader ) )\r
66                         return i;\r
67         }\r
68         \r
69         /* get shaderinfo */\r
70         si = ShaderInfoForShader( shader );\r
71         \r
72         /* emit a new shader */\r
73         if( i == MAX_MAP_SHADERS )\r
74                 Error( "MAX_MAP_SHADERS" );\r
75         numBSPShaders++;\r
76         strcpy( bspShaders[ i ].shader, shader );\r
77         bspShaders[ i ].surfaceFlags = si->surfaceFlags;\r
78         bspShaders[ i ].contentFlags = si->contentFlags;\r
79         \r
80         /* handle custom content/surface flags */\r
81         if( surfaceFlags != NULL )\r
82                 bspShaders[ i ].surfaceFlags = *surfaceFlags;\r
83         if( contentFlags != NULL )\r
84                 bspShaders[ i ].contentFlags = *contentFlags;\r
85         \r
86         /* recursively emit any damage shaders */\r
87         if( si->damageShader[ 0 ] != '\0' )\r
88         {\r
89                 Sys_FPrintf( SYS_VRB, "Shader %s has damage shader %s\n", si->shader, si->damageShader );\r
90                 EmitShader( si->damageShader, NULL, NULL );\r
91         }\r
92         \r
93         /* return it */\r
94         return i;\r
95 }\r
96 \r
97 \r
98 \r
99 /*\r
100 EmitPlanes()\r
101 there is no oportunity to discard planes, because all of the original\r
102 brushes will be saved in the map\r
103 */\r
104 \r
105 void EmitPlanes( void )\r
106 {\r
107         int                     i;\r
108         bspPlane_t      *bp;\r
109         plane_t         *mp;\r
110         \r
111         \r
112         /* walk plane list */\r
113         mp = mapplanes;\r
114         for( i = 0; i < nummapplanes; i++, mp++ )\r
115         {\r
116                 bp = &bspPlanes[ numBSPPlanes ];\r
117                 VectorCopy( mp->normal, bp->normal );\r
118                 bp->dist = mp->dist;\r
119                 numBSPPlanes++;\r
120         }\r
121         \r
122         /* emit some statistics */\r
123         Sys_FPrintf( SYS_VRB, "%9d BSP planes\n", numBSPPlanes );\r
124 }\r
125 \r
126 \r
127 \r
128 /*\r
129 EmitLeaf()\r
130 emits a leafnode to the bsp file\r
131 */\r
132 \r
133 void EmitLeaf( node_t *node )\r
134 {\r
135         bspLeaf_t               *leaf_p;\r
136         brush_t                 *b;\r
137         drawSurfRef_t   *dsr;\r
138         int                             i = 0;\r
139 \r
140         \r
141         /* check limits */\r
142         if( numBSPLeafs >= MAX_MAP_LEAFS )\r
143                 Error( "MAX_MAP_LEAFS" );\r
144 \r
145         leaf_p = &bspLeafs[numBSPLeafs];\r
146         numBSPLeafs++;\r
147 \r
148         leaf_p->cluster = node->cluster;\r
149         leaf_p->area = node->area;\r
150 \r
151         /* emit bounding box */\r
152         VectorCopy( node->mins, leaf_p->mins );\r
153         VectorCopy( node->maxs, leaf_p->maxs );\r
154         \r
155         /* emit leaf brushes */\r
156         leaf_p->firstBSPLeafBrush = numBSPLeafBrushes;\r
157         for( b = node->brushlist; b; b = b->next )\r
158         {\r
159                 /* something is corrupting brushes */\r
160                 if( (int) b < 256 )\r
161                 {\r
162                         Sys_Printf( "WARNING: Node brush list corrupted (0x%08X)\n", b );\r
163                         break;\r
164                 }\r
165                 //%     if( b->guard != 0xDEADBEEF )\r
166                 //%             Sys_Printf( "Brush %6d: 0x%08X Guard: 0x%08X Next: 0x%08X Original: 0x%08X Sides: %d\n", b->brushNum, b, b, b->next, b->original, b->numsides );\r
167                 \r
168                 if( numBSPLeafBrushes >= MAX_MAP_LEAFBRUSHES )\r
169                         Error( "MAX_MAP_LEAFBRUSHES" );\r
170                 bspLeafBrushes[ numBSPLeafBrushes ] = b->original->outputNum;\r
171                 numBSPLeafBrushes++;\r
172         }\r
173         \r
174         leaf_p->numBSPLeafBrushes = numBSPLeafBrushes - leaf_p->firstBSPLeafBrush;\r
175         \r
176         /* emit leaf surfaces */\r
177         if( node->opaque )\r
178                 return;\r
179         \r
180         /* add the drawSurfRef_t drawsurfs */\r
181         leaf_p->firstBSPLeafSurface = numBSPLeafSurfaces;\r
182         for ( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )\r
183         {\r
184                 if( numBSPLeafSurfaces >= MAX_MAP_LEAFFACES )\r
185                         Error( "MAX_MAP_LEAFFACES" );\r
186                 bspLeafSurfaces[ numBSPLeafSurfaces ] = dsr->outputNum;\r
187                 numBSPLeafSurfaces++;                   \r
188         }\r
189         \r
190         leaf_p->numBSPLeafSurfaces = numBSPLeafSurfaces - leaf_p->firstBSPLeafSurface;\r
191 }\r
192 \r
193 \r
194 /*\r
195 EmitDrawNode_r()\r
196 recursively emit the bsp nodes\r
197 */\r
198 \r
199 int EmitDrawNode_r( node_t *node )\r
200 {\r
201         bspNode_t       *n;\r
202         int                     i;\r
203         \r
204         \r
205         /* check for leafnode */\r
206         if( node->planenum == PLANENUM_LEAF )\r
207         {\r
208                 EmitLeaf( node );\r
209                 return -numBSPLeafs;\r
210         }\r
211         \r
212         /* emit a node */\r
213         if( numBSPNodes == MAX_MAP_NODES )\r
214                 Error( "MAX_MAP_NODES" );\r
215         n = &bspNodes[ numBSPNodes ];\r
216         numBSPNodes++;\r
217         \r
218         VectorCopy (node->mins, n->mins);\r
219         VectorCopy (node->maxs, n->maxs);\r
220 \r
221         if (node->planenum & 1)\r
222                 Error ("WriteDrawNodes_r: odd planenum");\r
223         n->planeNum = node->planenum;\r
224 \r
225         //\r
226         // recursively output the other nodes\r
227         //      \r
228         for (i=0 ; i<2 ; i++)\r
229         {\r
230                 if (node->children[i]->planenum == PLANENUM_LEAF)\r
231                 {\r
232                         n->children[i] = -(numBSPLeafs + 1);\r
233                         EmitLeaf (node->children[i]);\r
234                 }\r
235                 else\r
236                 {\r
237                         n->children[i] = numBSPNodes;   \r
238                         EmitDrawNode_r (node->children[i]);\r
239                 }\r
240         }\r
241 \r
242         return n - bspNodes;\r
243 }\r
244 \r
245 \r
246 \r
247 /*\r
248 ============\r
249 SetModelNumbers\r
250 ============\r
251 */\r
252 void SetModelNumbers (void)\r
253 {\r
254         int             i;\r
255         int             models;\r
256         char    value[10];\r
257 \r
258         models = 1;\r
259         for ( i=1 ; i<numEntities ; i++ ) {\r
260                 if ( entities[i].brushes || entities[i].patches ) {\r
261                         sprintf ( value, "*%i", models );\r
262                         models++;\r
263                         SetKeyValue (&entities[i], "model", value);\r
264                 }\r
265         }\r
266 \r
267 }\r
268 \r
269 \r
270 \r
271 \r
272 /*\r
273 SetLightStyles()\r
274 sets style keys for entity lights\r
275 */\r
276 \r
277 void SetLightStyles( void )\r
278 {\r
279         int                     i, j, style, numStyles;\r
280         qboolean        keepLights;\r
281         const char      *t;\r
282         entity_t        *e;\r
283         epair_t         *ep, *next;\r
284         char            value[ 10 ];\r
285         char            lightTargets[ MAX_SWITCHED_LIGHTS ][ 64 ];\r
286         int                     lightStyles[ MAX_SWITCHED_LIGHTS ];\r
287         \r
288         \r
289         /* ydnar: determine if we keep lights in the bsp */\r
290         t = ValueForKey( &entities[ 0 ], "_keepLights" );\r
291         keepLights = (t[ 0 ] == '1') ? qtrue : qfalse;\r
292         \r
293         /* any light that is controlled (has a targetname) must have a unique style number generated for it */\r
294         numStyles = 0;\r
295         for( i = 1; i < numEntities; i++ )\r
296         {\r
297                 e = &entities[ i ];\r
298 \r
299                 t = ValueForKey( e, "classname" );\r
300                 if( Q_strncasecmp( t, "light", 5 ) )\r
301                         continue;\r
302                 t = ValueForKey( e, "targetname" );\r
303                 if( t[ 0 ] == '\0' )\r
304                 {\r
305                         /* ydnar: strip the light from the BSP file */\r
306                         if( keepLights == qfalse )\r
307                         {\r
308                                 ep = e->epairs;\r
309                                 while( ep != NULL )\r
310                                 {\r
311                                         next = ep->next;\r
312                                         free( ep->key );\r
313                                         free( ep->value );\r
314                                         free( ep );\r
315                                         ep = next;\r
316                                 }\r
317                                 e->epairs = NULL;\r
318                                 numStrippedLights++;\r
319                         }\r
320                         \r
321                         /* next light */\r
322                         continue;\r
323                 }\r
324                 \r
325                 /* get existing style */\r
326                 style = IntForKey( e, "style" );\r
327                 if( style < LS_NORMAL || style > LS_NONE )\r
328                         Error( "Invalid lightstyle (%d) on entity %d", style, i );\r
329                 \r
330                 /* find this targetname */\r
331                 for( j = 0; j < numStyles; j++ )\r
332                         if( lightStyles[ j ] == style && !strcmp( lightTargets[ j ], t ) )\r
333                                 break;\r
334                 \r
335                 /* add a new style */\r
336                 if( j >= numStyles )\r
337                 {\r
338                         if( numStyles == MAX_SWITCHED_LIGHTS )\r
339                                 Error( "MAX_SWITCHED_LIGHTS (%d) exceeded, reduce the number of lights with targetnames", MAX_SWITCHED_LIGHTS );\r
340                         strcpy( lightTargets[ j ], t );\r
341                         lightStyles[ j ] = style;\r
342                         numStyles++;\r
343                 }\r
344                 \r
345                 /* set explicit style */\r
346                 sprintf( value, "%d", 32 + j );\r
347                 SetKeyValue( e, "style", value );\r
348                 \r
349                 /* set old style */\r
350                 if( style != LS_NORMAL )\r
351                 {\r
352                         sprintf( value, "%d", style );\r
353                         SetKeyValue( e, "switch_style", value );\r
354                 }\r
355         }\r
356         \r
357         /* emit some statistics */\r
358         Sys_FPrintf( SYS_VRB, "%9d light entities stripped\n", numStrippedLights );\r
359 }\r
360 \r
361 \r
362 \r
363 /*\r
364 BeginBSPFile()\r
365 starts a new bsp file\r
366 */\r
367 \r
368 void BeginBSPFile( void )\r
369 {\r
370         /* these values may actually be initialized if the file existed when loaded, so clear them explicitly */\r
371         numBSPModels = 0;\r
372         numBSPNodes = 0;\r
373         numBSPBrushSides = 0;\r
374         numBSPLeafSurfaces = 0;\r
375         numBSPLeafBrushes = 0;\r
376         \r
377         /* leave leaf 0 as an error, because leafs are referenced as negative number nodes */\r
378         numBSPLeafs = 1;\r
379         \r
380         \r
381         /* ydnar: gs mods: set the first 6 drawindexes to 0 1 2 2 1 3 for triangles and quads */\r
382         numBSPDrawIndexes = 6;\r
383         bspDrawIndexes[ 0 ] = 0;\r
384         bspDrawIndexes[ 1 ] = 1;\r
385         bspDrawIndexes[ 2 ] = 2;\r
386         bspDrawIndexes[ 3 ] = 0;\r
387         bspDrawIndexes[ 4 ] = 2;\r
388         bspDrawIndexes[ 5 ] = 3;\r
389 }\r
390 \r
391 \r
392 \r
393 /*\r
394 EndBSPFile()\r
395 finishes a new bsp and writes to disk\r
396 */\r
397 \r
398 void EndBSPFile( void )\r
399 {\r
400         char    path[ 1024 ];\r
401         \r
402 \r
403         EmitPlanes();\r
404         \r
405         numBSPEntities = numEntities;\r
406         UnparseEntities();\r
407         \r
408         /* write the surface extra file */\r
409         WriteSurfaceExtraFile( source );\r
410         \r
411         /* write the bsp */\r
412         sprintf( path, "%s.bsp", source );\r
413         Sys_Printf( "Writing %s\n", path );\r
414         WriteBSPFile( path );\r
415 }\r
416 \r
417 \r
418 \r
419 /*\r
420 EmitBrushes()\r
421 writes the brush list to the bsp\r
422 */\r
423 \r
424 void EmitBrushes( brush_t *brushes, int *firstBrush, int *numBrushes )\r
425 {\r
426         int                             j;\r
427         brush_t                 *b;\r
428         bspBrush_t              *db;\r
429         bspBrushSide_t  *cp;\r
430         \r
431         \r
432         /* set initial brush */\r
433         if( firstBrush != NULL )\r
434                 *firstBrush = numBSPBrushes;\r
435         if( numBrushes != NULL )\r
436                 *numBrushes = 0;\r
437         \r
438         /* walk list of brushes */\r
439         for( b = brushes; b != NULL; b = b->next )\r
440         {\r
441                 /* check limits */\r
442                 if( numBSPBrushes == MAX_MAP_BRUSHES )\r
443                         Error( "MAX_MAP_BRUSHES (%d)", numBSPBrushes );\r
444                 \r
445                 /* get bsp brush */\r
446                 b->outputNum = numBSPBrushes;\r
447                 db = &bspBrushes[ numBSPBrushes ];\r
448                 numBSPBrushes++;\r
449                 if( numBrushes != NULL )\r
450                         (*numBrushes)++;\r
451                 \r
452                 db->shaderNum = EmitShader( b->contentShader->shader, &b->contentShader->contentFlags, &b->contentShader->surfaceFlags );\r
453                 db->firstSide = numBSPBrushSides;\r
454                 \r
455                 /* walk sides */\r
456                 db->numSides = 0;\r
457                 for( j = 0; j < b->numsides; j++ )\r
458                 {\r
459                         /* set output number to bogus initially */\r
460                         b->sides[ j ].outputNum = -1;\r
461                         \r
462                         /* don't emit generated backSide sides */\r
463                         if ( b->sides[ j ].backSide )\r
464                                 continue;\r
465                         \r
466                         /* check count */\r
467                         if( numBSPBrushSides == MAX_MAP_BRUSHSIDES )\r
468                                 Error( "MAX_MAP_BRUSHSIDES ");\r
469                         \r
470                         /* emit side */\r
471                         b->sides[ j ].outputNum = numBSPBrushSides;\r
472                         cp = &bspBrushSides[ numBSPBrushSides ];\r
473                         db->numSides++;\r
474                         numBSPBrushSides++;\r
475                         cp->planeNum = b->sides[ j ].planenum;\r
476                         \r
477                         /* emit shader */\r
478                         if( b->sides[ j ].shaderInfo )\r
479                                 cp->shaderNum = EmitShader( b->sides[ j ].shaderInfo->shader, &b->sides[ j ].shaderInfo->contentFlags, &b->sides[ j ].shaderInfo->surfaceFlags );\r
480                         else\r
481                                 cp->shaderNum = EmitShader( NULL, NULL, NULL );\r
482                 }\r
483         }\r
484 }\r
485 \r
486 \r
487 \r
488 /*\r
489 EmitFogs() - ydnar\r
490 turns map fogs into bsp fogs\r
491 */\r
492 \r
493 void EmitFogs( void )\r
494 {\r
495         int                     i, j;\r
496         \r
497         \r
498         /* setup */\r
499         numBSPFogs = numMapFogs;\r
500         \r
501         /* walk list */\r
502         for( i = 0; i < numMapFogs; i++ )\r
503         {\r
504                 /* set shader */\r
505                 strcpy( bspFogs[ i ].shader, mapFogs[ i ].si->shader );\r
506                 \r
507                 /* global fog doesn't have an associated brush */\r
508                 if( mapFogs[ i ].brush == NULL )\r
509                 {\r
510                         bspFogs[ i ].brushNum = -1;\r
511                         bspFogs[ i ].visibleSide = -1;\r
512                 }\r
513                 else\r
514                 {\r
515                         /* set brush */\r
516                         bspFogs[ i ].brushNum = mapFogs[ i ].brush->outputNum;\r
517                         \r
518                         /* try to use forced visible side */\r
519                         if( mapFogs[ i ].visibleSide >= 0 )\r
520                         {\r
521                                 bspFogs[ i ].visibleSide = mapFogs[ i ].visibleSide;\r
522                                 continue;\r
523                         }\r
524                         \r
525                         /* find visible side */\r
526                         for( j = 0; j < 6; j++ )\r
527                         {\r
528                                 if( mapFogs[ i ].brush->sides[ j ].visibleHull != NULL )\r
529                                 {\r
530                                         Sys_Printf( "Fog %d has visible side %d\n", i, j );\r
531                                         bspFogs[ i ].visibleSide = j;\r
532                                         break;\r
533                                 }\r
534                         }\r
535                 }\r
536         }\r
537 }\r
538 \r
539 \r
540 \r
541 /*\r
542 BeginModel()\r
543 sets up a new brush model\r
544 */\r
545 \r
546 void BeginModel( void )\r
547 {\r
548         bspModel_t      *mod;\r
549         brush_t         *b;\r
550         entity_t        *e;\r
551         vec3_t          mins, maxs;\r
552         vec3_t          lgMins, lgMaxs;         /* ydnar: lightgrid mins/maxs */\r
553         parseMesh_t     *p;\r
554         int                     i;\r
555         \r
556         \r
557         /* test limits */\r
558         if( numBSPModels == MAX_MAP_MODELS )\r
559                 Error( "MAX_MAP_MODELS" );\r
560         \r
561         /* get model and entity */\r
562         mod = &bspModels[ numBSPModels ];\r
563         e = &entities[ mapEntityNum ];\r
564         \r
565         /* ydnar: lightgrid mins/maxs */\r
566         ClearBounds( lgMins, lgMaxs );\r
567         \r
568         /* bound the brushes */\r
569         ClearBounds( mins, maxs );\r
570         for ( b = e->brushes; b; b = b->next )\r
571         {\r
572                 /* ignore non-real brushes (origin, etc) */\r
573                 if( b->numsides == 0 )\r
574                         continue;\r
575                 AddPointToBounds( b->mins, mins, maxs );\r
576                 AddPointToBounds( b->maxs, mins, maxs );\r
577                 \r
578                 /* ydnar: lightgrid bounds */\r
579                 if( b->compileFlags & C_LIGHTGRID )\r
580                 {\r
581                         AddPointToBounds( b->mins, lgMins, lgMaxs );\r
582                         AddPointToBounds( b->maxs, lgMins, lgMaxs );\r
583                 }\r
584         }\r
585         \r
586         /* bound patches */\r
587         for( p = e->patches; p; p = p->next )\r
588         {\r
589                 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
590                         AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs );\r
591         }\r
592         \r
593         /* ydnar: lightgrid mins/maxs */\r
594         if( lgMins[ 0 ] < 99999 )\r
595         {\r
596                 /* use lightgrid bounds */\r
597                 VectorCopy( lgMins, mod->mins );\r
598                 VectorCopy( lgMaxs, mod->maxs );\r
599         }\r
600         else\r
601         {\r
602                 /* use brush/patch bounds */\r
603                 VectorCopy( mins, mod->mins );\r
604                 VectorCopy( maxs, mod->maxs );\r
605         }\r
606         \r
607         /* note size */\r
608         Sys_FPrintf( SYS_VRB, "BSP bounds: { %f %f %f } { %f %f %f }\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );\r
609         Sys_FPrintf( SYS_VRB, "Lightgrid bounds: { %f %f %f } { %f %f %f }\n", lgMins[ 0 ], lgMins[ 1 ], lgMins[ 2 ], lgMaxs[ 0 ], lgMaxs[ 1 ], lgMaxs[ 2 ] );\r
610         \r
611         /* set firsts */\r
612         mod->firstBSPSurface = numBSPDrawSurfaces;\r
613         mod->firstBSPBrush = numBSPBrushes;\r
614 }\r
615 \r
616 \r
617 \r
618 \r
619 /*\r
620 EndModel()\r
621 finish a model's processing\r
622 */\r
623 \r
624 void EndModel( entity_t *e, node_t *headnode )\r
625 {\r
626         bspModel_t      *mod;\r
627         \r
628         \r
629         /* note it */\r
630         Sys_FPrintf( SYS_VRB, "--- EndModel ---\n" );\r
631         \r
632         /* emit the bsp */\r
633         mod = &bspModels[ numBSPModels ];\r
634         EmitDrawNode_r( headnode );\r
635         \r
636         /* set surfaces and brushes */\r
637         mod->numBSPSurfaces = numBSPDrawSurfaces - mod->firstBSPSurface;\r
638         mod->firstBSPBrush = e->firstBrush;\r
639         mod->numBSPBrushes = e->numBrushes;\r
640         \r
641         /* increment model count */\r
642         numBSPModels++;\r
643 }\r
644 \r
645 \r
646 \r