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