2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../../../idlib/precompiled.h"
34 //=================================================================================
39 should we try and snap values very close to 0.5, 0.25, 0.125, etc?
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
45 do we save out "wings" for shadow silhouette info?
50 static idFile *procFile;
52 #define AREANUM_DIFFERENT -2
57 Any nodes that have all children with the same
58 area can be combined into a single leaf node
60 Returns the area number of all children, or
61 AREANUM_DIFFERENT if not the same.
64 int PruneNodes_r( node_t *node ) {
67 if ( node->planenum == PLANENUM_LEAF ) {
71 a1 = PruneNodes_r( node->children[0] );
72 a2 = PruneNodes_r( node->children[1] );
74 if ( a1 != a2 || a1 == AREANUM_DIFFERENT ) {
75 return AREANUM_DIFFERENT;
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] );
84 // change this node to a leaf
85 node->planenum = PLANENUM_LEAF;
91 static void WriteFloat( idFile *f, float v )
93 if ( idMath::Fabs(v - idMath::Rint(v)) < 0.001 ) {
94 f->WriteFloatString( "%i ", (int)idMath::Rint(v) );
97 f->WriteFloatString( "%f ", v );
101 void Write1DMatrix( idFile *f, int x, float *m ) {
104 f->WriteFloatString( "( " );
106 for ( i = 0; i < x; i++ ) {
107 WriteFloat( f, m[i] );
110 f->WriteFloatString( ") " );
113 static int CountUniqueShaders( optimizeGroup_t *groups ) {
114 optimizeGroup_t *a, *b;
119 for ( a = groups ; a ; a = a->nextGroup ) {
120 if ( !a->triList ) { // ignore groups with no tris
123 for ( b = groups ; b != a ; b = b->nextGroup ) {
127 if ( a->material != b->material ) {
130 if ( a->mergeGroup != b->mergeGroup ) {
149 #define XYZ_EPSILON 0.01
150 #define ST_EPSILON 0.001
151 #define COSINE_EPSILON 0.999
153 static bool MatchVert( const idDrawVert *a, const idDrawVert *b ) {
154 if ( idMath::Fabs( a->xyz[0] - b->xyz[0] ) > XYZ_EPSILON ) {
157 if ( idMath::Fabs( a->xyz[1] - b->xyz[1] ) > XYZ_EPSILON ) {
160 if ( idMath::Fabs( a->xyz[2] - b->xyz[2] ) > XYZ_EPSILON ) {
163 if ( idMath::Fabs( a->st[0] - b->st[0] ) > ST_EPSILON ) {
166 if ( idMath::Fabs( a->st[1] - b->st[1] ) > ST_EPSILON ) {
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 ) {
176 // otherwise do a dot-product cosine check
177 if ( DotProduct( a->normal, b->normal ) < COSINE_EPSILON ) {
188 Converts independent triangles to shared vertex triangles
191 srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) {
192 const mapTri_t *step;
197 srfTriangles_t *uTri;
199 // unique the vertexes
200 count = CountTriList( tris );
202 uTri = R_AllocStaticTriSurf();
203 R_AllocStaticTriSurfVerts( uTri, count * 3 );
204 R_AllocStaticTriSurfIndexes( uTri, count * 3 );
209 for ( step = tris ; step ; step = step->next ) {
210 for ( i = 0 ; i < 3 ; i++ ) {
211 const idDrawVert *dv;
215 // search for a match
216 for ( j = 0 ; j < numVerts ; j++ ) {
217 if ( MatchVert( &uTri->verts[j], dv ) ) {
221 if ( j == 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];
229 uTri->indexes[numIndexes++] = j;
233 uTri->numVerts = numVerts;
234 uTri->numIndexes = numIndexes;
244 static void CleanupUTriangles( srfTriangles_t *tri ) {
245 // perform cleanup operations
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 );
253 R_FreeStaticTriSurfSilIndexes( tri );
260 Writes text verts and indexes to procfile
263 static void WriteUTriangles( const srfTriangles_t *uTris ) {
268 procFile->WriteFloatString( "/* numVerts = */ %i /* numIndexes = */ %i\n",
269 uTris->numVerts, uTris->numIndexes );
273 for ( i = 0 ; i < uTris->numVerts ; i++ ) {
275 const idDrawVert *dv;
277 dv = &uTris->verts[i];
284 vec[5] = dv->normal[0];
285 vec[6] = dv->normal[1];
286 vec[7] = dv->normal[2];
287 Write1DMatrix( procFile, 8, vec );
291 procFile->WriteFloatString( "\n" );
295 procFile->WriteFloatString( "\n" );
300 for ( i = 0 ; i < uTris->numIndexes ; i++ ) {
301 procFile->WriteFloatString( "%i ", uTris->indexes[i] );
305 procFile->WriteFloatString( "\n" );
309 procFile->WriteFloatString( "\n" );
318 Writes text verts and indexes to procfile
321 static void WriteShadowTriangles( const srfTriangles_t *tri ) {
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 );
331 for ( i = 0 ; i < tri->numVerts ; i++ ) {
332 Write1DMatrix( procFile, 3, &tri->shadowVertexes[i].xyz[0] );
336 procFile->WriteFloatString( "\n" );
340 procFile->WriteFloatString( "\n" );
345 for ( i = 0 ; i < tri->numIndexes ; i++ ) {
346 procFile->WriteFloatString( "%i ", tri->indexes[i] );
350 procFile->WriteFloatString( "\n" );
354 procFile->WriteFloatString( "\n" );
360 =======================
361 GroupsAreSurfaceCompatible
363 Planes, texcoords, and groupLights can differ,
364 but the material and mergegroup must match
365 =======================
367 static bool GroupsAreSurfaceCompatible( const optimizeGroup_t *a, const optimizeGroup_t *b ) {
368 if ( a->material != b->material ) {
371 if ( a->mergeGroup != b->mergeGroup ) {
382 static void WriteOutputSurfaces( int entityNum, int areaNum ) {
383 mapTri_t *ambient, *copy;
388 optimizeGroup_t *group, *groupStep;
391 srfTriangles_t *uTri;
393 typedef struct interactionTris_s {
394 struct interactionTris_s *next;
399 interactionTris_t *interactions, *checkInter; //, *nextInter;
402 area = &dmapGlobals.uEntities[entityNum].areas[areaNum];
403 entity = dmapGlobals.uEntities[entityNum].mapEntity;
405 numSurfaces = CountUniqueShaders( area->groups );
408 if ( entityNum == 0 ) {
409 procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n",
410 areaNum, numSurfaces );
414 entity->epairs.GetString( "name", "", &name );
416 common->Error( "Entity %i has surfaces, but no name key", entityNum );
418 procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n",
423 for ( group = area->groups ; group ; group = group->nextGroup ) {
424 if ( group->surfaceEmited ) {
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.
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
439 for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) {
440 if ( groupStep->surfaceEmited ) {
443 if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) {
447 // copy it out to the ambient list
448 copy = CopyTriList( groupStep->triList );
449 ambient = MergeTriLists( ambient, copy );
450 groupStep->surfaceEmited = true;
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] ) {
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;
466 copy = CopyTriList( groupStep->triList );
467 checkInter->triList = MergeTriLists( checkInter->triList, copy );
475 if ( surfaceNum >= numSurfaces ) {
476 common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" );
479 procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum );
481 procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() );
483 uTri = ShareMapTriVerts( ambient );
484 FreeTriList( ambient );
486 CleanupUTriangles( uTri );
487 WriteUTriangles( uTri );
488 R_FreeStaticTriSurf( uTri );
490 procFile->WriteFloatString( "}\n\n" );
493 procFile->WriteFloatString( "}\n\n" );
502 static void WriteNode_r( node_t *node ) {
507 if ( node->planenum == PLANENUM_LEAF ) {
508 // we shouldn't get here unless the entire world
510 procFile->WriteFloatString( "/* node 0 */ ( 0 0 0 0 ) -1 -1\n" );
514 for ( i = 0 ; i < 2 ; i++ ) {
515 if ( node->children[i]->planenum == PLANENUM_LEAF ) {
516 child[i] = -1 - node->children[i]->area;
518 child[i] = node->children[i]->nodeNumber;
522 plane = &dmapGlobals.mapPlanes[node->planenum];
524 procFile->WriteFloatString( "/* node %i */ ", node->nodeNumber );
525 Write1DMatrix( procFile, 4, plane->ToFloatPtr() );
526 procFile->WriteFloatString( "%i %i\n", child[0], child[1] );
528 if ( child[0] > 0 ) {
529 WriteNode_r( node->children[0] );
531 if ( child[1] > 0 ) {
532 WriteNode_r( node->children[1] );
536 static int NumberNodes_r( node_t *node, int nextNumber ) {
537 if ( node->planenum == PLANENUM_LEAF ) {
540 node->nodeNumber = nextNumber;
542 nextNumber = NumberNodes_r( node->children[0], nextNumber );
543 nextNumber = NumberNodes_r( node->children[1], nextNumber );
553 static void WriteOutputNodes( node_t *node ) {
556 // prune unneeded nodes and count
557 PruneNodes_r( node );
558 numNodes = NumberNodes_r( node, 0 );
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" );
568 procFile->WriteFloatString( "}\n\n" );
576 static void WriteOutputPortals( uEntity_t *e ) {
578 interAreaPortal_t *iap;
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() );
591 procFile->WriteFloatString("\n" );
594 procFile->WriteFloatString( "}\n\n" );
603 static void WriteOutputEntity( int entityNum ) {
607 e = &dmapGlobals.uEntities[entityNum];
609 if ( entityNum != 0 ) {
610 // entities may have enclosed, empty areas that we don't need to write out
611 if ( e->numAreas > 1 ) {
616 for ( i = 0 ; i < e->numAreas ; i++ ) {
617 WriteOutputSurfaces( entityNum, i );
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 );
626 WriteOutputNodes( e->tree->headnode );
636 void WriteOutputFile( void ) {
642 common->Printf( "----- WriteOutputFile -----\n" );
644 sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase );
646 common->Printf( "writing %s\n", qpath.c_str() );
647 // _D3XP used fs_cdpath
648 procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
650 common->Error( "Error opening %s", qpath.c_str() );
653 procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID );
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];
659 if ( !entity->primitives ) {
663 WriteOutputEntity( i );
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 ) {
673 procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name );
674 WriteShadowTriangles( light->shadowTris );
675 procFile->WriteFloatString( "}\n\n" );
677 R_FreeStaticTriSurf( light->shadowTris );
678 light->shadowTris = NULL;
681 fileSystem->CloseFile( procFile );