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