1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30 #include "framework/Game.h"
31
32 #include "tools/compilers/dmap/dmap.h"
33
34 /*
35
36 After parsing, there will be a list of entities that each has
37 a list of primitives.
38
39 Primitives are either brushes, triangle soups, or model references.
40
41 Curves are tesselated to triangle soups at load time, but model
42 references are
43 Brushes will have
44
45 brushes, each of which has a side definition.
46
47 */
48
49 //
50 // private declarations
51 //
52
53 #define MAX_BUILD_SIDES 300
54
55 static int entityPrimitive; // to track editor brush numbers
56 static int c_numMapPatches;
57 static int c_areaportals;
58
59 static uEntity_t *uEntity;
60
61 // brushes are parsed into a temporary array of sides,
62 // which will have duplicates removed before the final brush is allocated
63 static uBrush_t *buildBrush;
64
65
66 #define NORMAL_EPSILON 0.00001f
67 #define DIST_EPSILON 0.01f
68
69
70 /*
71 ===========
72 FindFloatPlane
73 ===========
74 */
FindFloatPlane(const idPlane & plane,bool * fixedDegeneracies)75 int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies ) {
76 idPlane p = plane;
77 bool fixed = p.FixDegeneracies( DIST_EPSILON );
78 if ( fixed && fixedDegeneracies ) {
79 *fixedDegeneracies = true;
80 }
81 return dmapGlobals.mapPlanes.FindPlane( p, NORMAL_EPSILON, DIST_EPSILON );
82 }
83
84 /*
85 ===========
86 SetBrushContents
87
88 The contents on all sides of a brush should be the same
89 Sets contentsShader, contents, opaque
90 ===========
91 */
SetBrushContents(uBrush_t * b)92 static void SetBrushContents( uBrush_t *b ) {
93 int contents, c2;
94 side_t *s;
95 int i;
96
97 s = &b->sides[0];
98 contents = s->material->GetContentFlags();
99
100 b->contentShader = s->material;
101
102 // a brush is only opaque if all sides are opaque
103 b->opaque = true;
104
105 for ( i=1 ; i<b->numsides ; i++, s++ ) {
106 s = &b->sides[i];
107
108 if ( !s->material ) {
109 continue;
110 }
111
112 c2 = s->material->GetContentFlags();
113 if (c2 != contents) {
114 contents |= c2;
115 }
116
117 if ( s->material->Coverage() != MC_OPAQUE ) {
118 b->opaque = false;
119 }
120 }
121
122 if ( contents & CONTENTS_AREAPORTAL ) {
123 c_areaportals++;
124 }
125
126 b->contents = contents;
127 }
128
129
130 //============================================================================
131
132 /*
133 ===============
134 FreeBuildBrush
135 ===============
136 */
FreeBuildBrush(void)137 static void FreeBuildBrush( void ) {
138 int i;
139
140 for ( i = 0 ; i < buildBrush->numsides ; i++ ) {
141 if ( buildBrush->sides[i].winding ) {
142 delete buildBrush->sides[i].winding;
143 }
144 }
145 buildBrush->numsides = 0;
146 }
147
148 /*
149 ===============
150 FinishBrush
151
152 Produces a final brush based on the buildBrush->sides array
153 and links it to the current entity
154 ===============
155 */
FinishBrush(void)156 static uBrush_t *FinishBrush( void ) {
157 uBrush_t *b;
158 primitive_t *prim;
159
160 // create windings for sides and bounds for brush
161 if ( !CreateBrushWindings( buildBrush ) ) {
162 // don't keep this brush
163 FreeBuildBrush();
164 return NULL;
165 }
166
167 if ( buildBrush->contents & CONTENTS_AREAPORTAL ) {
168 if (dmapGlobals.num_entities != 1) {
169 common->Printf("Entity %i, Brush %i: areaportals only allowed in world\n"
170 , dmapGlobals.num_entities - 1, entityPrimitive);
171 FreeBuildBrush();
172 return NULL;
173 }
174 }
175
176 // keep it
177 b = CopyBrush( buildBrush );
178
179 FreeBuildBrush();
180
181 b->entitynum = dmapGlobals.num_entities-1;
182 b->brushnum = entityPrimitive;
183
184 b->original = b;
185
186 prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
187 memset( prim, 0, sizeof( *prim ) );
188 prim->next = uEntity->primitives;
189 uEntity->primitives = prim;
190
191 prim->brush = b;
192
193 return b;
194 }
195
196 /*
197 ================
198 AdjustEntityForOrigin
199 ================
200 */
201 #if 0
202 static void AdjustEntityForOrigin( uEntity_t *ent ) {
203 primitive_t *prim;
204 uBrush_t *b;
205 int i;
206 side_t *s;
207
208 for ( prim = ent->primitives ; prim ; prim = prim->next ) {
209 b = prim->brush;
210 if ( !b ) {
211 continue;
212 }
213 for ( i = 0; i < b->numsides; i++ ) {
214 idPlane plane;
215
216 s = &b->sides[i];
217
218 plane = dmapGlobals.mapPlanes[s->planenum];
219 plane[3] += plane.Normal() * ent->origin;
220
221 s->planenum = FindFloatPlane( plane );
222
223 s->texVec.v[0][3] += DotProduct( ent->origin, s->texVec.v[0] );
224 s->texVec.v[1][3] += DotProduct( ent->origin, s->texVec.v[1] );
225
226 // remove any integral shift
227 s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
228 s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
229 }
230 CreateBrushWindings(b);
231 }
232 }
233 #endif
234
235 /*
236 =================
237 RemoveDuplicateBrushPlanes
238
239 Returns false if the brush has a mirrored set of planes,
240 meaning it encloses no volume.
241 Also removes planes without any normal
242 =================
243 */
RemoveDuplicateBrushPlanes(uBrush_t * b)244 static bool RemoveDuplicateBrushPlanes( uBrush_t * b ) {
245 int i, j, k;
246 side_t *sides;
247
248 sides = b->sides;
249
250 for ( i = 1 ; i < b->numsides ; i++ ) {
251
252 // check for a degenerate plane
253 if ( sides[i].planenum == -1) {
254 common->Printf("Entity %i, Brush %i: degenerate plane\n"
255 , b->entitynum, b->brushnum);
256 // remove it
257 for ( k = i + 1 ; k < b->numsides ; k++ ) {
258 sides[k-1] = sides[k];
259 }
260 b->numsides--;
261 i--;
262 continue;
263 }
264
265 // check for duplication and mirroring
266 for ( j = 0 ; j < i ; j++ ) {
267 if ( sides[i].planenum == sides[j].planenum ) {
268 common->Printf("Entity %i, Brush %i: duplicate plane\n"
269 , b->entitynum, b->brushnum);
270 // remove the second duplicate
271 for ( k = i + 1 ; k < b->numsides ; k++ ) {
272 sides[k-1] = sides[k];
273 }
274 b->numsides--;
275 i--;
276 break;
277 }
278
279 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
280 // mirror plane, brush is invalid
281 common->Printf("Entity %i, Brush %i: mirrored plane\n"
282 , b->entitynum, b->brushnum);
283 return false;
284 }
285 }
286 }
287 return true;
288 }
289
290
291 /*
292 =================
293 ParseBrush
294 =================
295 */
ParseBrush(const idMapBrush * mapBrush,int primitiveNum)296 static void ParseBrush( const idMapBrush *mapBrush, int primitiveNum ) {
297 uBrush_t *b;
298 side_t *s;
299 const idMapBrushSide *ms;
300 int i;
301 bool fixedDegeneracies = false;
302
303 buildBrush->entitynum = dmapGlobals.num_entities-1;
304 buildBrush->brushnum = entityPrimitive;
305 buildBrush->numsides = mapBrush->GetNumSides();
306 for ( i = 0 ; i < mapBrush->GetNumSides() ; i++ ) {
307 s = &buildBrush->sides[i];
308 ms = mapBrush->GetSide(i);
309
310 memset( s, 0, sizeof( *s ) );
311 s->planenum = FindFloatPlane( ms->GetPlane(), &fixedDegeneracies );
312 s->material = declManager->FindMaterial( ms->GetMaterial() );
313 ms->GetTextureVectors( s->texVec.v );
314 // remove any integral shift, which will help with grouping
315 s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
316 s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
317 }
318
319 // if there are mirrored planes, the entire brush is invalid
320 if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
321 return;
322 }
323
324 // get the content for the entire brush
325 SetBrushContents( buildBrush );
326
327 b = FinishBrush();
328 if ( !b ) {
329 return;
330 }
331
332 if ( fixedDegeneracies && dmapGlobals.verboseentities ) {
333 common->Warning( "brush %d has degenerate plane equations", primitiveNum );
334 }
335 }
336
337 /*
338 ================
339 ParseSurface
340 ================
341 */
ParseSurface(const idMapPatch * patch,const idSurface * surface,const idMaterial * material)342 static void ParseSurface( const idMapPatch *patch, const idSurface *surface, const idMaterial *material ) {
343 int i;
344 mapTri_t *tri;
345 primitive_t *prim;
346
347 prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
348 memset( prim, 0, sizeof( *prim ) );
349 prim->next = uEntity->primitives;
350 uEntity->primitives = prim;
351
352 for ( i = 0; i < surface->GetNumIndexes(); i += 3 ) {
353 tri = AllocTri();
354 tri->v[2] = (*surface)[surface->GetIndexes()[i+0]];
355 tri->v[1] = (*surface)[surface->GetIndexes()[i+2]];
356 tri->v[0] = (*surface)[surface->GetIndexes()[i+1]];
357 tri->material = material;
358 tri->next = prim->tris;
359 prim->tris = tri;
360 }
361
362 // set merge groups if needed, to prevent multiple sides from being
363 // merged into a single surface in the case of gui shaders, mirrors, and autosprites
364 if ( material->IsDiscrete() ) {
365 for ( tri = prim->tris ; tri ; tri = tri->next ) {
366 tri->mergeGroup = (void *)patch;
367 }
368 }
369 }
370
371 /*
372 ================
373 ParsePatch
374 ================
375 */
ParsePatch(const idMapPatch * patch,int primitiveNum)376 static void ParsePatch( const idMapPatch *patch, int primitiveNum ) {
377 const idMaterial *mat;
378
379 if ( dmapGlobals.noCurves ) {
380 return;
381 }
382
383 c_numMapPatches++;
384
385 mat = declManager->FindMaterial( patch->GetMaterial() );
386
387 idSurface_Patch *cp = new idSurface_Patch( *patch );
388
389 if ( patch->GetExplicitlySubdivided() ) {
390 cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), true );
391 } else {
392 cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true );
393 }
394
395 ParseSurface( patch, cp, mat );
396
397 delete cp;
398 }
399
400 /*
401 ================
402 ProcessMapEntity
403 ================
404 */
ProcessMapEntity(idMapEntity * mapEnt)405 static bool ProcessMapEntity( idMapEntity *mapEnt ) {
406 idMapPrimitive *prim;
407
408 uEntity = &dmapGlobals.uEntities[dmapGlobals.num_entities];
409 memset( uEntity, 0, sizeof(*uEntity) );
410 uEntity->mapEntity = mapEnt;
411 dmapGlobals.num_entities++;
412
413 for ( entityPrimitive = 0; entityPrimitive < mapEnt->GetNumPrimitives(); entityPrimitive++ ) {
414 prim = mapEnt->GetPrimitive(entityPrimitive);
415
416 if ( prim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
417 ParseBrush( static_cast<idMapBrush*>(prim), entityPrimitive );
418 }
419 else if ( prim->GetType() == idMapPrimitive::TYPE_PATCH ) {
420 ParsePatch( static_cast<idMapPatch*>(prim), entityPrimitive );
421 }
422 }
423
424 // never put an origin on the world, even if the editor left one there
425 if ( dmapGlobals.num_entities != 1 ) {
426 uEntity->mapEntity->epairs.GetVector( "origin", "", uEntity->origin );
427 }
428
429 return true;
430 }
431
432 //===================================================================
433
434 /*
435 ==============
436 CreateMapLight
437
438 ==============
439 */
CreateMapLight(const idMapEntity * mapEnt)440 static void CreateMapLight( const idMapEntity *mapEnt ) {
441 mapLight_t *light;
442 bool dynamic;
443
444 // designers can add the "noPrelight" flag to signal that
445 // the lights will move around, so we don't want
446 // to bother chopping up the surfaces under it or creating
447 // shadow volumes
448 mapEnt->epairs.GetBool( "noPrelight", "0", dynamic );
449 if ( dynamic ) {
450 return;
451 }
452
453 light = new mapLight_t;
454 light->name[0] = '\0';
455 light->shadowTris = NULL;
456
457 // parse parms exactly as the game do
458 // use the game's epair parsing code so
459 // we can use the same renderLight generation
460 gameEdit->ParseSpawnArgsToRenderLight( &mapEnt->epairs, &light->def.parms );
461
462 R_DeriveLightData( &light->def );
463
464 // get the name for naming the shadow surfaces
465 const char *name;
466
467 mapEnt->epairs.GetString( "name", "", &name );
468
469 idStr::Copynz( light->name, name, sizeof( light->name ) );
470 if ( !light->name[0] ) {
471 common->Error( "Light at (%f,%f,%f) didn't have a name",
472 light->def.parms.origin[0], light->def.parms.origin[1], light->def.parms.origin[2] );
473 }
474 #if 0
475 // use the renderer code to get the bounding planes for the light
476 // based on all the parameters
477 R_RenderLightFrustum( light->parms, light->frustum );
478 light->lightShader = light->parms.shader;
479 #endif
480
481 dmapGlobals.mapLights.Append( light );
482
483 }
484
485 /*
486 ==============
487 CreateMapLights
488
489 ==============
490 */
CreateMapLights(const idMapFile * dmapFile)491 static void CreateMapLights( const idMapFile *dmapFile ) {
492 int i;
493 const idMapEntity *mapEnt;
494 const char *value;
495
496 for ( i = 0 ; i < dmapFile->GetNumEntities() ; i++ ) {
497 mapEnt = dmapFile->GetEntity(i);
498 mapEnt->epairs.GetString( "classname", "", &value);
499 if ( !idStr::Icmp( value, "light" ) ) {
500 CreateMapLight( mapEnt );
501 }
502
503 }
504
505 }
506
507 /*
508 ================
509 LoadDMapFile
510 ================
511 */
LoadDMapFile(const char * filename)512 bool LoadDMapFile( const char *filename ) {
513 primitive_t *prim;
514 idBounds mapBounds;
515 int brushes, triSurfs;
516 int i;
517 int size;
518
519 common->Printf( "--- LoadDMapFile ---\n" );
520 common->Printf( "loading %s\n", filename );
521
522 // load and parse the map file into canonical form
523 dmapGlobals.dmapFile = new idMapFile();
524 if ( !dmapGlobals.dmapFile->Parse(filename) ) {
525 delete dmapGlobals.dmapFile;
526 dmapGlobals.dmapFile = NULL;
527 common->Warning( "Couldn't load map file: '%s'", filename );
528 return false;
529 }
530
531 dmapGlobals.mapPlanes.Clear();
532 dmapGlobals.mapPlanes.SetGranularity( 1024 );
533
534 // process the canonical form into utility form
535 dmapGlobals.num_entities = 0;
536 c_numMapPatches = 0;
537 c_areaportals = 0;
538
539 size = dmapGlobals.dmapFile->GetNumEntities() * sizeof( dmapGlobals.uEntities[0] );
540 dmapGlobals.uEntities = (uEntity_t *)Mem_Alloc( size );
541 memset( dmapGlobals.uEntities, 0, size );
542
543 // allocate a very large temporary brush for building
544 // the brushes as they are loaded
545 buildBrush = AllocBrush( MAX_BUILD_SIDES );
546
547 for ( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ ) {
548 ProcessMapEntity( dmapGlobals.dmapFile->GetEntity(i) );
549 }
550
551 CreateMapLights( dmapGlobals.dmapFile );
552
553 brushes = 0;
554 triSurfs = 0;
555
556 mapBounds.Clear();
557 for ( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next ) {
558 if ( prim->brush ) {
559 brushes++;
560 mapBounds.AddBounds( prim->brush->bounds );
561 } else if ( prim->tris ) {
562 triSurfs++;
563 }
564 }
565
566 common->Printf( "%5i total world brushes\n", brushes );
567 common->Printf( "%5i total world triSurfs\n", triSurfs );
568 common->Printf( "%5i patches\n", c_numMapPatches );
569 common->Printf( "%5i entities\n", dmapGlobals.num_entities );
570 common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() );
571 common->Printf( "%5i areaportals\n", c_areaportals );
572 common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1],mapBounds[0][2],
573 mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] );
574
575 return true;
576 }
577
578 /*
579 ================
580 FreeOptimizeGroupList
581 ================
582 */
FreeOptimizeGroupList(optimizeGroup_t * groups)583 void FreeOptimizeGroupList( optimizeGroup_t *groups ) {
584 optimizeGroup_t *next;
585
586 for ( ; groups ; groups = next ) {
587 next = groups->nextGroup;
588 FreeTriList( groups->triList );
589 Mem_Free( groups );
590 }
591 }
592
593 /*
594 ================
595 FreeDMapFile
596 ================
597 */
FreeDMapFile(void)598 void FreeDMapFile( void ) {
599 int i, j;
600
601 FreeBrush( buildBrush );
602 buildBrush = NULL;
603
604 // free the entities and brushes
605 for ( i = 0 ; i < dmapGlobals.num_entities ; i++ ) {
606 uEntity_t *ent;
607 primitive_t *prim, *nextPrim;
608
609 ent = &dmapGlobals.uEntities[i];
610
611 FreeTree( ent->tree );
612
613 // free primitives
614 for ( prim = ent->primitives ; prim ; prim = nextPrim ) {
615 nextPrim = prim->next;
616 if ( prim->brush ) {
617 FreeBrush( prim->brush );
618 }
619 if ( prim->tris ) {
620 FreeTriList( prim->tris );
621 }
622 Mem_Free( prim );
623 }
624
625 // free area surfaces
626 if ( ent->areas ) {
627 for ( j = 0 ; j < ent->numAreas ; j++ ) {
628 uArea_t *area;
629
630 area = &ent->areas[j];
631 FreeOptimizeGroupList( area->groups );
632
633 }
634 Mem_Free( ent->areas );
635 }
636 }
637
638 Mem_Free( dmapGlobals.uEntities );
639
640 dmapGlobals.num_entities = 0;
641
642 // free the map lights
643 for ( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) {
644 R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def );
645 }
646 dmapGlobals.mapLights.DeleteContents( true );
647 }
648