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