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 "tools/compilers/dmap/dmap.h"
31 
32 #include "tools/compilers/compiler_public.h"
33 
34 dmapGlobals_t	dmapGlobals;
35 
36 /*
37 ============
38 ProcessModel
39 ============
40 */
ProcessModel(uEntity_t * e,bool floodFill)41 bool ProcessModel( uEntity_t *e, bool floodFill ) {
42 	bspface_t	*faces;
43 
44 	// build a bsp tree using all of the sides
45 	// of all of the structural brushes
46 	faces = MakeStructuralBspFaceList ( e->primitives );
47 	e->tree = FaceBSP( faces );
48 
49 	// create portals at every leaf intersection
50 	// to allow flood filling
51 	MakeTreePortals( e->tree );
52 
53 	// classify the leafs as opaque or areaportal
54 	FilterBrushesIntoTree( e );
55 
56 	// see if the bsp is completely enclosed
57 	if ( floodFill && !dmapGlobals.noFlood ) {
58 		if ( FloodEntities( e->tree ) ) {
59 			// set the outside leafs to opaque
60 			FillOutside( e );
61 		} else {
62 			common->Printf ( "**********************\n" );
63 			common->Warning( "******* leaked *******" );
64 			common->Printf ( "**********************\n" );
65 			LeakFile( e->tree );
66 			// bail out here.  If someone really wants to
67 			// process a map that leaks, they should use
68 			// -noFlood
69 			return false;
70 		}
71 	}
72 
73 	// get minimum convex hulls for each visible side
74 	// this must be done before creating area portals,
75 	// because the visible hull is used as the portal
76 	ClipSidesByTree( e );
77 
78 	// determine areas before clipping tris into the
79 	// tree, so tris will never cross area boundaries
80 	FloodAreas( e );
81 
82 	// we now have a BSP tree with solid and non-solid leafs marked with areas
83 	// all primitives will now be clipped into this, throwing away
84 	// fragments in the solid areas
85 	PutPrimitivesInAreas( e );
86 
87 	// now build shadow volumes for the lights and split
88 	// the optimize lists by the light beam trees
89 	// so there won't be unneeded overdraw in the static
90 	// case
91 	Prelight( e );
92 
93 	// optimizing is a superset of fixing tjunctions
94 	if ( !dmapGlobals.noOptimize ) {
95 		OptimizeEntity( e );
96 	} else  if ( !dmapGlobals.noTJunc ) {
97 		FixEntityTjunctions( e );
98 	}
99 
100 	// now fix t junctions across areas
101 	FixGlobalTjunctions( e );
102 
103 	return true;
104 }
105 
106 /*
107 ============
108 ProcessModels
109 ============
110 */
ProcessModels(void)111 bool ProcessModels( void ) {
112 	bool	oldVerbose;
113 	uEntity_t	*entity;
114 
115 	oldVerbose = dmapGlobals.verbose;
116 
117 	for ( dmapGlobals.entityNum = 0 ; dmapGlobals.entityNum < dmapGlobals.num_entities ; dmapGlobals.entityNum++ ) {
118 
119 		entity = &dmapGlobals.uEntities[dmapGlobals.entityNum];
120 		if ( !entity->primitives ) {
121 			continue;
122 		}
123 
124 		common->Printf( "############### entity %i ###############\n", dmapGlobals.entityNum );
125 
126 		// if we leaked, stop without any more processing
127 		if ( !ProcessModel( entity, (bool)(dmapGlobals.entityNum == 0 ) ) ) {
128 			return false;
129 		}
130 
131 		// we usually don't want to see output for submodels unless
132 		// something strange is going on
133 		if ( !dmapGlobals.verboseentities ) {
134 			dmapGlobals.verbose = false;
135 		}
136 	}
137 
138 	dmapGlobals.verbose = oldVerbose;
139 
140 	return true;
141 }
142 
143 /*
144 ============
145 DmapHelp
146 ============
147 */
DmapHelp(void)148 void DmapHelp( void ) {
149 	common->Printf(
150 
151 	"Usage: dmap [options] mapfile\n"
152 	"Options:\n"
153 	"noCurves          = don't process curves\n"
154 	"noCM              = don't create collision map\n"
155 	"noAAS             = don't create AAS files\n"
156 
157 	);
158 }
159 
160 /*
161 ============
162 ResetDmapGlobals
163 ============
164 */
ResetDmapGlobals(void)165 void ResetDmapGlobals( void ) {
166 	dmapGlobals.mapFileBase[0] = '\0';
167 	dmapGlobals.dmapFile = NULL;
168 	dmapGlobals.mapPlanes.Clear();
169 	dmapGlobals.num_entities = 0;
170 	dmapGlobals.uEntities = NULL;
171 	dmapGlobals.entityNum = 0;
172 	dmapGlobals.mapLights.Clear();
173 	dmapGlobals.verbose = false;
174 	dmapGlobals.glview = false;
175 	dmapGlobals.noOptimize = false;
176 	dmapGlobals.verboseentities = false;
177 	dmapGlobals.noCurves = false;
178 	dmapGlobals.fullCarve = false;
179 	dmapGlobals.noModelBrushes = false;
180 	dmapGlobals.noTJunc = false;
181 	dmapGlobals.nomerge = false;
182 	dmapGlobals.noFlood = false;
183 	dmapGlobals.noClipSides = false;
184 	dmapGlobals.noLightCarve = false;
185 	dmapGlobals.noShadow = false;
186 	dmapGlobals.shadowOptLevel = SO_NONE;
187 	dmapGlobals.drawBounds.Clear();
188 	dmapGlobals.drawflag = false;
189 	dmapGlobals.totalShadowTriangles = 0;
190 	dmapGlobals.totalShadowVerts = 0;
191 }
192 
193 /*
194 ============
195 Dmap
196 ============
197 */
Dmap(const idCmdArgs & args)198 void Dmap( const idCmdArgs &args ) {
199 	int			i;
200 	int			start, end;
201 	char		path[1024];
202 	idStr		passedName;
203 	bool		leaked = false;
204 	bool		noCM = false;
205 	bool		noAAS = false;
206 
207 	ResetDmapGlobals();
208 
209 	if ( args.Argc() < 2 ) {
210 		DmapHelp();
211 		return;
212 	}
213 
214 	common->Printf("---- dmap ----\n");
215 
216 	dmapGlobals.fullCarve = true;
217 	dmapGlobals.shadowOptLevel = SO_MERGE_SURFACES;		// create shadows by merging all surfaces, but no super optimization
218 //	dmapGlobals.shadowOptLevel = SO_CLIP_OCCLUDERS;		// remove occluders that are completely covered
219 //	dmapGlobals.shadowOptLevel = SO_SIL_OPTIMIZE;
220 //	dmapGlobals.shadowOptLevel = SO_CULL_OCCLUDED;
221 
222 	dmapGlobals.noLightCarve = true;
223 
224 	for ( i = 1 ; i < args.Argc() ; i++ ) {
225 		const char *s;
226 
227 		s = args.Argv(i);
228 		if ( s[0] == '-' ) {
229 			s++;
230 			if ( s[0] == '\0' ) {
231 				continue;
232 			}
233 		}
234 
235 		if ( !idStr::Icmp( s,"glview" ) ) {
236 			dmapGlobals.glview = true;
237 		} else if ( !idStr::Icmp( s, "v" ) ) {
238 			common->Printf( "verbose = true\n" );
239 			dmapGlobals.verbose = true;
240 		} else if ( !idStr::Icmp( s, "draw" ) ) {
241 			common->Printf( "drawflag = true\n" );
242 			dmapGlobals.drawflag = true;
243 		} else if ( !idStr::Icmp( s, "noFlood" ) ) {
244 			common->Printf( "noFlood = true\n" );
245 			dmapGlobals.noFlood = true;
246 		} else if ( !idStr::Icmp( s, "noLightCarve" ) ) {
247 			common->Printf( "noLightCarve = true\n" );
248 			dmapGlobals.noLightCarve = true;
249 		} else if ( !idStr::Icmp( s, "lightCarve" ) ) {
250 			common->Printf( "noLightCarve = false\n" );
251 			dmapGlobals.noLightCarve = false;
252 		} else if ( !idStr::Icmp( s, "noOpt" ) ) {
253 			common->Printf( "noOptimize = true\n" );
254 			dmapGlobals.noOptimize = true;
255 		} else if ( !idStr::Icmp( s, "verboseentities" ) ) {
256 			common->Printf( "verboseentities = true\n");
257 			dmapGlobals.verboseentities = true;
258 		} else if ( !idStr::Icmp( s, "noCurves" ) ) {
259 			common->Printf( "noCurves = true\n");
260 			dmapGlobals.noCurves = true;
261 		} else if ( !idStr::Icmp( s, "noModels" ) ) {
262 			common->Printf( "noModels = true\n" );
263 			dmapGlobals.noModelBrushes = true;
264 		} else if ( !idStr::Icmp( s, "noClipSides" ) ) {
265 			common->Printf( "noClipSides = true\n" );
266 			dmapGlobals.noClipSides = true;
267 		} else if ( !idStr::Icmp( s, "noCarve" ) ) {
268 			common->Printf( "noCarve = true\n" );
269 			dmapGlobals.fullCarve = false;
270 		} else if ( !idStr::Icmp( s, "shadowOpt" ) ) {
271 			dmapGlobals.shadowOptLevel = (shadowOptLevel_t)atoi( args.Argv( i+1 ) );
272 			common->Printf( "shadowOpt = %i\n",dmapGlobals.shadowOptLevel );
273 			i += 1;
274 		} else if ( !idStr::Icmp( s, "noTjunc" ) ) {
275 			// triangle optimization won't work properly without tjunction fixing
276 			common->Printf ("noTJunc = true\n" );
277 			dmapGlobals.noTJunc = true;
278 			dmapGlobals.noOptimize = true;
279 			common->Printf ("forcing noOptimize = true\n" );
280 		} else if ( !idStr::Icmp( s, "noCM" ) ) {
281 			noCM = true;
282 			common->Printf( "noCM = true\n" );
283 		} else if ( !idStr::Icmp( s, "noAAS" ) ) {
284 			noAAS = true;
285 			common->Printf( "noAAS = true\n" );
286 		} else if ( !idStr::Icmp( s, "editorOutput" ) ) {
287 #ifdef _WIN32
288 			com_outputMsg = true;
289 #endif
290 		} else {
291 			break;
292 		}
293 	}
294 
295 	if ( i >= args.Argc() ) {
296 		common->Error( "usage: dmap [options] mapfile" );
297 	}
298 
299 	passedName = args.Argv(i);		// may have an extension
300 	passedName.BackSlashesToSlashes();
301 	if ( passedName.Icmpn( "maps/", 4 ) != 0 ) {
302 		passedName = "maps/" + passedName;
303 	}
304 
305 	idStr stripped = passedName;
306 	stripped.StripFileExtension();
307 	idStr::Copynz( dmapGlobals.mapFileBase, stripped, sizeof(dmapGlobals.mapFileBase) );
308 
309 	bool region = false;
310 	// if this isn't a regioned map, delete the last saved region map
311 	if ( passedName.Right( 4 ) != ".reg" ) {
312 		sprintf( path, "%s.reg", dmapGlobals.mapFileBase );
313 		fileSystem->RemoveFile( path );
314 	} else {
315 		region = true;
316 	}
317 
318 
319 	passedName = stripped;
320 
321 	// delete any old line leak files
322 	sprintf( path, "%s.lin", dmapGlobals.mapFileBase );
323 	fileSystem->RemoveFile( path );
324 
325 
326 	//
327 	// start from scratch
328 	//
329 	start = Sys_Milliseconds();
330 
331 	if ( !LoadDMapFile( passedName ) ) {
332 		return;
333 	}
334 
335 	if ( ProcessModels() ) {
336 		WriteOutputFile();
337 	} else {
338 		leaked = true;
339 	}
340 
341 	FreeDMapFile();
342 
343 	common->Printf( "%i total shadow triangles\n", dmapGlobals.totalShadowTriangles );
344 	common->Printf( "%i total shadow verts\n", dmapGlobals.totalShadowVerts );
345 
346 	end = Sys_Milliseconds();
347 	common->Printf( "-----------------------\n" );
348 	common->Printf( "%5.0f seconds for dmap\n", ( end - start ) * 0.001f );
349 
350 	if ( !leaked ) {
351 
352 		if ( !noCM ) {
353 
354 			// make sure the collision model manager is not used by the game
355 			cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
356 
357 			// create the collision map
358 			start = Sys_Milliseconds();
359 
360 			collisionModelManager->LoadMap( dmapGlobals.dmapFile );
361 			collisionModelManager->FreeMap();
362 
363 			end = Sys_Milliseconds();
364 			common->Printf( "-------------------------------------\n" );
365 			common->Printf( "%5.0f seconds to create collision map\n", ( end - start ) * 0.001f );
366 		}
367 
368 		if ( !noAAS && !region ) {
369 			// create AAS files
370 			RunAAS_f( args );
371 		}
372 	}
373 
374 	// free the common .map representation
375 	delete dmapGlobals.dmapFile;
376 
377 	// clear the map plane list
378 	dmapGlobals.mapPlanes.Clear();
379 
380 #ifdef _WIN32
381 	if ( com_outputMsg && com_hwndMsg != NULL ) {
382 		unsigned int msg = ::RegisterWindowMessage( DMAP_DONE );
383 		::PostMessage( com_hwndMsg, msg, 0, 0 );
384 	}
385 #endif
386 }
387 
388 /*
389 ============
390 Dmap_f
391 ============
392 */
Dmap_f(const idCmdArgs & args)393 void Dmap_f( const idCmdArgs &args ) {
394 
395 	common->ClearWarnings( "running dmap" );
396 
397 	// refresh the screen each time we print so it doesn't look
398 	// like it is hung
399 	common->SetRefreshOnPrint( true );
400 	Dmap( args );
401 	common->SetRefreshOnPrint( false );
402 
403 	common->PrintWarnings();
404 }
405