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
31 #include "tools/compilers/dmap/dmap.h"
32
33 //=================================================================================
34
35
36 #if 0
37
38 should we try and snap values very close to 0.5, 0.25, 0.125, etc?
39
40 do we write out normals, or just a "smooth shade" flag?
41 resolved: normals. otherwise adjacent facet shaded surfaces get their
42 vertexes merged, and they would have to be split apart before drawing
43
44 do we save out "wings" for shadow silhouette info?
45
46
47 #endif
48
49 static idFile *procFile;
50
51 #define AREANUM_DIFFERENT -2
52 /*
53 =============
54 PruneNodes_r
55
56 Any nodes that have all children with the same
57 area can be combined into a single leaf node
58
59 Returns the area number of all children, or
60 AREANUM_DIFFERENT if not the same.
61 =============
62 */
PruneNodes_r(node_t * node)63 int PruneNodes_r( node_t *node ) {
64 int a1, a2;
65
66 if ( node->planenum == PLANENUM_LEAF ) {
67 return node->area;
68 }
69
70 a1 = PruneNodes_r( node->children[0] );
71 a2 = PruneNodes_r( node->children[1] );
72
73 if ( a1 != a2 || a1 == AREANUM_DIFFERENT ) {
74 return AREANUM_DIFFERENT;
75 }
76
77 // free all the nodes below this point
78 FreeTreePortals_r( node->children[0] );
79 FreeTreePortals_r( node->children[1] );
80 FreeTree_r( node->children[0] );
81 FreeTree_r( node->children[1] );
82
83 // change this node to a leaf
84 node->planenum = PLANENUM_LEAF;
85 node->area = a1;
86
87 return a1;
88 }
89
WriteFloat(idFile * f,float v)90 static void WriteFloat( idFile *f, float v )
91 {
92 if ( idMath::Fabs(v - idMath::Rint(v)) < 0.001 ) {
93 f->WriteFloatString( "%i ", (int)idMath::Rint(v) );
94 }
95 else {
96 f->WriteFloatString( "%f ", v );
97 }
98 }
99
Write1DMatrix(idFile * f,int x,float * m)100 void Write1DMatrix( idFile *f, int x, float *m ) {
101 int i;
102
103 f->WriteFloatString( "( " );
104
105 for ( i = 0; i < x; i++ ) {
106 WriteFloat( f, m[i] );
107 }
108
109 f->WriteFloatString( ") " );
110 }
111
CountUniqueShaders(optimizeGroup_t * groups)112 static int CountUniqueShaders( optimizeGroup_t *groups ) {
113 optimizeGroup_t *a, *b;
114 int count;
115
116 count = 0;
117
118 for ( a = groups ; a ; a = a->nextGroup ) {
119 if ( !a->triList ) { // ignore groups with no tris
120 continue;
121 }
122 for ( b = groups ; b != a ; b = b->nextGroup ) {
123 if ( !b->triList ) {
124 continue;
125 }
126 if ( a->material != b->material ) {
127 continue;
128 }
129 if ( a->mergeGroup != b->mergeGroup ) {
130 continue;
131 }
132 break;
133 }
134 if ( a == b ) {
135 count++;
136 }
137 }
138
139 return count;
140 }
141
142
143 /*
144 ==============
145 MatchVert
146 ==============
147 */
148 #define XYZ_EPSILON 0.01
149 #define ST_EPSILON 0.001
150 #define COSINE_EPSILON 0.999
151
MatchVert(const idDrawVert * a,const idDrawVert * b)152 static bool MatchVert( const idDrawVert *a, const idDrawVert *b ) {
153 if ( idMath::Fabs( a->xyz[0] - b->xyz[0] ) > XYZ_EPSILON ) {
154 return false;
155 }
156 if ( idMath::Fabs( a->xyz[1] - b->xyz[1] ) > XYZ_EPSILON ) {
157 return false;
158 }
159 if ( idMath::Fabs( a->xyz[2] - b->xyz[2] ) > XYZ_EPSILON ) {
160 return false;
161 }
162 if ( idMath::Fabs( a->st[0] - b->st[0] ) > ST_EPSILON ) {
163 return false;
164 }
165 if ( idMath::Fabs( a->st[1] - b->st[1] ) > ST_EPSILON ) {
166 return false;
167 }
168
169 // if the normal is 0 (smoothed normals), consider it a match
170 if ( a->normal[0] == 0 && a->normal[1] == 0 && a->normal[2] == 0
171 && b->normal[0] == 0 && b->normal[1] == 0 && b->normal[2] == 0 ) {
172 return true;
173 }
174
175 // otherwise do a dot-product cosine check
176 if ( DotProduct( a->normal, b->normal ) < COSINE_EPSILON ) {
177 return false;
178 }
179
180 return true;
181 }
182
183 /*
184 ====================
185 ShareMapTriVerts
186
187 Converts independent triangles to shared vertex triangles
188 ====================
189 */
ShareMapTriVerts(const mapTri_t * tris)190 srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) {
191 const mapTri_t *step;
192 int count;
193 int i, j;
194 int numVerts;
195 int numIndexes;
196 srfTriangles_t *uTri;
197
198 // unique the vertexes
199 count = CountTriList( tris );
200
201 uTri = R_AllocStaticTriSurf();
202 R_AllocStaticTriSurfVerts( uTri, count * 3 );
203 R_AllocStaticTriSurfIndexes( uTri, count * 3 );
204
205 numVerts = 0;
206 numIndexes = 0;
207
208 for ( step = tris ; step ; step = step->next ) {
209 for ( i = 0 ; i < 3 ; i++ ) {
210 const idDrawVert *dv;
211
212 dv = &step->v[i];
213
214 // search for a match
215 for ( j = 0 ; j < numVerts ; j++ ) {
216 if ( MatchVert( &uTri->verts[j], dv ) ) {
217 break;
218 }
219 }
220 if ( j == numVerts ) {
221 numVerts++;
222 uTri->verts[j].xyz = dv->xyz;
223 uTri->verts[j].normal = dv->normal;
224 uTri->verts[j].st[0] = dv->st[0];
225 uTri->verts[j].st[1] = dv->st[1];
226 }
227
228 uTri->indexes[numIndexes++] = j;
229 }
230 }
231
232 uTri->numVerts = numVerts;
233 uTri->numIndexes = numIndexes;
234
235 return uTri;
236 }
237
238 /*
239 ==================
240 CleanupUTriangles
241 ==================
242 */
CleanupUTriangles(srfTriangles_t * tri)243 static void CleanupUTriangles( srfTriangles_t *tri ) {
244 // perform cleanup operations
245
246 R_RangeCheckIndexes( tri );
247 R_CreateSilIndexes( tri );
248 // R_RemoveDuplicatedTriangles( tri ); // this may remove valid overlapped transparent triangles
249 R_RemoveDegenerateTriangles( tri );
250 // R_RemoveUnusedVerts( tri );
251
252 R_FreeStaticTriSurfSilIndexes( tri );
253 }
254
255 /*
256 ====================
257 WriteUTriangles
258
259 Writes text verts and indexes to procfile
260 ====================
261 */
WriteUTriangles(const srfTriangles_t * uTris)262 static void WriteUTriangles( const srfTriangles_t *uTris ) {
263 int col;
264 int i;
265
266 // emit this chain
267 procFile->WriteFloatString( "/* numVerts = */ %i /* numIndexes = */ %i\n",
268 uTris->numVerts, uTris->numIndexes );
269
270 // verts
271 col = 0;
272 for ( i = 0 ; i < uTris->numVerts ; i++ ) {
273 float vec[8];
274 const idDrawVert *dv;
275
276 dv = &uTris->verts[i];
277
278 vec[0] = dv->xyz[0];
279 vec[1] = dv->xyz[1];
280 vec[2] = dv->xyz[2];
281 vec[3] = dv->st[0];
282 vec[4] = dv->st[1];
283 vec[5] = dv->normal[0];
284 vec[6] = dv->normal[1];
285 vec[7] = dv->normal[2];
286 Write1DMatrix( procFile, 8, vec );
287
288 if ( ++col == 3 ) {
289 col = 0;
290 procFile->WriteFloatString( "\n" );
291 }
292 }
293 if ( col != 0 ) {
294 procFile->WriteFloatString( "\n" );
295 }
296
297 // indexes
298 col = 0;
299 for ( i = 0 ; i < uTris->numIndexes ; i++ ) {
300 procFile->WriteFloatString( "%i ", uTris->indexes[i] );
301
302 if ( ++col == 18 ) {
303 col = 0;
304 procFile->WriteFloatString( "\n" );
305 }
306 }
307 if ( col != 0 ) {
308 procFile->WriteFloatString( "\n" );
309 }
310 }
311
312
313 /*
314 ====================
315 WriteShadowTriangles
316
317 Writes text verts and indexes to procfile
318 ====================
319 */
WriteShadowTriangles(const srfTriangles_t * tri)320 static void WriteShadowTriangles( const srfTriangles_t *tri ) {
321 int col;
322 int i;
323
324 // emit this chain
325 procFile->WriteFloatString( "/* numVerts = */ %i /* noCaps = */ %i /* noFrontCaps = */ %i /* numIndexes = */ %i /* planeBits = */ %i\n",
326 tri->numVerts, tri->numShadowIndexesNoCaps, tri->numShadowIndexesNoFrontCaps, tri->numIndexes, tri->shadowCapPlaneBits );
327
328 // verts
329 col = 0;
330 for ( i = 0 ; i < tri->numVerts ; i++ ) {
331 Write1DMatrix( procFile, 3, &tri->shadowVertexes[i].xyz[0] );
332
333 if ( ++col == 5 ) {
334 col = 0;
335 procFile->WriteFloatString( "\n" );
336 }
337 }
338 if ( col != 0 ) {
339 procFile->WriteFloatString( "\n" );
340 }
341
342 // indexes
343 col = 0;
344 for ( i = 0 ; i < tri->numIndexes ; i++ ) {
345 procFile->WriteFloatString( "%i ", tri->indexes[i] );
346
347 if ( ++col == 18 ) {
348 col = 0;
349 procFile->WriteFloatString( "\n" );
350 }
351 }
352 if ( col != 0 ) {
353 procFile->WriteFloatString( "\n" );
354 }
355 }
356
357
358 /*
359 =======================
360 GroupsAreSurfaceCompatible
361
362 Planes, texcoords, and groupLights can differ,
363 but the material and mergegroup must match
364 =======================
365 */
GroupsAreSurfaceCompatible(const optimizeGroup_t * a,const optimizeGroup_t * b)366 static bool GroupsAreSurfaceCompatible( const optimizeGroup_t *a, const optimizeGroup_t *b ) {
367 if ( a->material != b->material ) {
368 return false;
369 }
370 if ( a->mergeGroup != b->mergeGroup ) {
371 return false;
372 }
373 return true;
374 }
375
376 /*
377 ====================
378 WriteOutputSurfaces
379 ====================
380 */
WriteOutputSurfaces(int entityNum,int areaNum)381 static void WriteOutputSurfaces( int entityNum, int areaNum ) {
382 mapTri_t *ambient, *copy;
383 int surfaceNum;
384 int numSurfaces;
385 idMapEntity *entity;
386 uArea_t *area;
387 optimizeGroup_t *group, *groupStep;
388 int i; // , j;
389 // int col;
390 srfTriangles_t *uTri;
391 // mapTri_t *tri;
392 typedef struct interactionTris_s {
393 struct interactionTris_s *next;
394 mapTri_t *triList;
395 mapLight_t *light;
396 } interactionTris_t;
397
398 interactionTris_t *interactions, *checkInter; //, *nextInter;
399
400
401 area = &dmapGlobals.uEntities[entityNum].areas[areaNum];
402 entity = dmapGlobals.uEntities[entityNum].mapEntity;
403
404 numSurfaces = CountUniqueShaders( area->groups );
405
406
407 if ( entityNum == 0 ) {
408 procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n",
409 areaNum, numSurfaces );
410 } else {
411 const char *name;
412
413 entity->epairs.GetString( "name", "", &name );
414 if ( !name[0] ) {
415 common->Error( "Entity %i has surfaces, but no name key", entityNum );
416 }
417 procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n",
418 name, numSurfaces );
419 }
420
421 surfaceNum = 0;
422 for ( group = area->groups ; group ; group = group->nextGroup ) {
423 if ( group->surfaceEmited ) {
424 continue;
425 }
426
427 // combine all groups compatible with this one
428 // usually several optimizeGroup_t can be combined into a single
429 // surface, even though they couldn't be merged together to save
430 // vertexes because they had different planes, texture coordinates, or lights.
431 // Different mergeGroups will stay in separate surfaces.
432 ambient = NULL;
433
434 // each light that illuminates any of the groups in the surface will
435 // get its own list of indexes out of the original surface
436 interactions = NULL;
437
438 for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) {
439 if ( groupStep->surfaceEmited ) {
440 continue;
441 }
442 if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) {
443 continue;
444 }
445
446 // copy it out to the ambient list
447 copy = CopyTriList( groupStep->triList );
448 ambient = MergeTriLists( ambient, copy );
449 groupStep->surfaceEmited = true;
450
451 // duplicate it into an interaction for each groupLight
452 for ( i = 0 ; i < groupStep->numGroupLights ; i++ ) {
453 for ( checkInter = interactions ; checkInter ; checkInter = checkInter->next ) {
454 if ( checkInter->light == groupStep->groupLights[i] ) {
455 break;
456 }
457 }
458 if ( !checkInter ) {
459 // create a new interaction
460 checkInter = (interactionTris_t *)Mem_ClearedAlloc( sizeof( *checkInter ) );
461 checkInter->light = groupStep->groupLights[i];
462 checkInter->next = interactions;
463 interactions = checkInter;
464 }
465 copy = CopyTriList( groupStep->triList );
466 checkInter->triList = MergeTriLists( checkInter->triList, copy );
467 }
468 }
469
470 if ( !ambient ) {
471 continue;
472 }
473
474 if ( surfaceNum >= numSurfaces ) {
475 common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" );
476 }
477
478 procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum );
479 surfaceNum++;
480 procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() );
481
482 uTri = ShareMapTriVerts( ambient );
483 FreeTriList( ambient );
484
485 CleanupUTriangles( uTri );
486 WriteUTriangles( uTri );
487 R_FreeStaticTriSurf( uTri );
488
489 procFile->WriteFloatString( "}\n\n" );
490 }
491
492 procFile->WriteFloatString( "}\n\n" );
493 }
494
495 /*
496 ===============
497 WriteNode_r
498
499 ===============
500 */
WriteNode_r(node_t * node)501 static void WriteNode_r( node_t *node ) {
502 int child[2];
503 int i;
504 idPlane *plane;
505
506 if ( node->planenum == PLANENUM_LEAF ) {
507 // we shouldn't get here unless the entire world
508 // was a single leaf
509 procFile->WriteFloatString( "/* node 0 */ ( 0 0 0 0 ) -1 -1\n" );
510 return;
511 }
512
513 for ( i = 0 ; i < 2 ; i++ ) {
514 if ( node->children[i]->planenum == PLANENUM_LEAF ) {
515 child[i] = -1 - node->children[i]->area;
516 } else {
517 child[i] = node->children[i]->nodeNumber;
518 }
519 }
520
521 plane = &dmapGlobals.mapPlanes[node->planenum];
522
523 procFile->WriteFloatString( "/* node %i */ ", node->nodeNumber );
524 Write1DMatrix( procFile, 4, plane->ToFloatPtr() );
525 procFile->WriteFloatString( "%i %i\n", child[0], child[1] );
526
527 if ( child[0] > 0 ) {
528 WriteNode_r( node->children[0] );
529 }
530 if ( child[1] > 0 ) {
531 WriteNode_r( node->children[1] );
532 }
533 }
534
NumberNodes_r(node_t * node,int nextNumber)535 static int NumberNodes_r( node_t *node, int nextNumber ) {
536 if ( node->planenum == PLANENUM_LEAF ) {
537 return nextNumber;
538 }
539 node->nodeNumber = nextNumber;
540 nextNumber++;
541 nextNumber = NumberNodes_r( node->children[0], nextNumber );
542 nextNumber = NumberNodes_r( node->children[1], nextNumber );
543
544 return nextNumber;
545 }
546
547 /*
548 ====================
549 WriteOutputNodes
550 ====================
551 */
WriteOutputNodes(node_t * node)552 static void WriteOutputNodes( node_t *node ) {
553 int numNodes;
554
555 // prune unneeded nodes and count
556 PruneNodes_r( node );
557 numNodes = NumberNodes_r( node, 0 );
558
559 // output
560 procFile->WriteFloatString( "nodes { /* numNodes = */ %i\n\n", numNodes );
561 procFile->WriteFloatString( "/* node format is: ( planeVector ) positiveChild negativeChild */\n" );
562 procFile->WriteFloatString( "/* a child number of 0 is an opaque, solid area */\n" );
563 procFile->WriteFloatString( "/* negative child numbers are areas: (-1-child) */\n" );
564
565 WriteNode_r( node );
566
567 procFile->WriteFloatString( "}\n\n" );
568 }
569
570 /*
571 ====================
572 WriteOutputPortals
573 ====================
574 */
WriteOutputPortals(uEntity_t * e)575 static void WriteOutputPortals( uEntity_t *e ) {
576 int i, j;
577 interAreaPortal_t *iap;
578 idWinding *w;
579
580 procFile->WriteFloatString( "interAreaPortals { /* numAreas = */ %i /* numIAP = */ %i\n\n",
581 e->numAreas, numInterAreaPortals );
582 procFile->WriteFloatString( "/* interAreaPortal format is: numPoints positiveSideArea negativeSideArea ( point) ... */\n" );
583 for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
584 iap = &interAreaPortals[i];
585 w = iap->side->winding;
586 procFile->WriteFloatString("/* iap %i */ %i %i %i ", i, w->GetNumPoints(), iap->area0, iap->area1 );
587 for ( j = 0 ; j < w->GetNumPoints() ; j++ ) {
588 Write1DMatrix( procFile, 3, (*w)[j].ToFloatPtr() );
589 }
590 procFile->WriteFloatString("\n" );
591 }
592
593 procFile->WriteFloatString( "}\n\n" );
594 }
595
596
597 /*
598 ====================
599 WriteOutputEntity
600 ====================
601 */
WriteOutputEntity(int entityNum)602 static void WriteOutputEntity( int entityNum ) {
603 int i;
604 uEntity_t *e;
605
606 e = &dmapGlobals.uEntities[entityNum];
607
608 if ( entityNum != 0 ) {
609 // entities may have enclosed, empty areas that we don't need to write out
610 if ( e->numAreas > 1 ) {
611 e->numAreas = 1;
612 }
613 }
614
615 for ( i = 0 ; i < e->numAreas ; i++ ) {
616 WriteOutputSurfaces( entityNum, i );
617 }
618
619 // we will completely skip the portals and nodes if it is a single area
620 if ( entityNum == 0 && e->numAreas > 1 ) {
621 // output the area portals
622 WriteOutputPortals( e );
623
624 // output the nodes
625 WriteOutputNodes( e->tree->headnode );
626 }
627 }
628
629
630 /*
631 ====================
632 WriteOutputFile
633 ====================
634 */
WriteOutputFile(void)635 void WriteOutputFile( void ) {
636 int i;
637 uEntity_t *entity;
638 idStr qpath;
639
640 // write the file
641 common->Printf( "----- WriteOutputFile -----\n" );
642
643 sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase );
644
645 common->Printf( "writing %s\n", qpath.c_str() );
646 // _D3XP used fs_cdpath
647 procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
648 if ( !procFile ) {
649 common->Error( "Error opening %s", qpath.c_str() );
650 }
651
652 procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID );
653
654 // write the entity models and information, writing entities first
655 for ( i=dmapGlobals.num_entities - 1 ; i >= 0 ; i-- ) {
656 entity = &dmapGlobals.uEntities[i];
657
658 if ( !entity->primitives ) {
659 continue;
660 }
661
662 WriteOutputEntity( i );
663 }
664
665 // write the shadow volumes
666 for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
667 mapLight_t *light = dmapGlobals.mapLights[i];
668 if ( !light->shadowTris ) {
669 continue;
670 }
671
672 procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name );
673 WriteShadowTriangles( light->shadowTris );
674 procFile->WriteFloatString( "}\n\n" );
675
676 R_FreeStaticTriSurf( light->shadowTris );
677 light->shadowTris = NULL;
678 }
679
680 fileSystem->CloseFile( procFile );
681 }
682