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 "framework/Licensee.h"
31 #include "MayaImport/maya_main.h"
32
33 #include "gamesys/SysCvar.h"
34 #include "Game_local.h"
35
36 #include "anim/Anim.h"
37
38 /***********************************************************************
39
40 Maya conversion functions
41
42 ***********************************************************************/
43
44 static idStr Maya_Error;
45
46 static exporterInterface_t Maya_ConvertModel = NULL;
47 static exporterShutdown_t Maya_Shutdown = NULL;
48 static uintptr_t importDLL = 0;
49
50 bool idModelExport::initialized = false;
51
52 /*
53 ====================
54 idModelExport::idModelExport
55 ====================
56 */
idModelExport()57 idModelExport::idModelExport() {
58 Reset();
59 }
60
61 /*
62 ====================
63 idModelExport::Shutdown
64 ====================
65 */
Shutdown(void)66 void idModelExport::Shutdown( void ) {
67 if ( Maya_Shutdown ) {
68 Maya_Shutdown();
69 }
70
71 if ( importDLL ) {
72 sys->DLL_Unload( importDLL );
73 }
74
75 importDLL = 0;
76 Maya_Shutdown = NULL;
77 Maya_ConvertModel = NULL;
78 Maya_Error.Clear();
79 initialized = false;
80 }
81
82 /*
83 =====================
84 idModelExport::CheckMayaInstall
85
86 Determines if Maya is installed on the user's machine
87 =====================
88 */
CheckMayaInstall(void)89 bool idModelExport::CheckMayaInstall( void ) {
90 #ifndef _WIN32
91 return false;
92 #elif 0
93 HKEY hKey;
94 long lres, lType;
95
96 lres = RegOpenKey( HKEY_LOCAL_MACHINE, "SOFTWARE\\Alias|Wavefront\\Maya\\4.5\\Setup\\InstallPath", &hKey );
97
98 if ( lres != ERROR_SUCCESS ) {
99 return false;
100 }
101
102 lres = RegQueryValueEx( hKey, "MAYA_INSTALL_LOCATION", NULL, (unsigned long*)&lType, (unsigned char*)NULL, (unsigned long*)NULL );
103
104 RegCloseKey( hKey );
105
106 if ( lres != ERROR_SUCCESS ) {
107 return false;
108 }
109 return true;
110 #else
111 HKEY hKey;
112 long lres;
113
114 // only check the non-version specific key so that we only have to update the maya dll when new versions are released
115 lres = RegOpenKey( HKEY_LOCAL_MACHINE, "SOFTWARE\\Alias|Wavefront\\Maya", &hKey );
116 RegCloseKey( hKey );
117
118 if ( lres != ERROR_SUCCESS ) {
119 return false;
120 }
121 return true;
122 #endif
123 }
124
125 /*
126 =====================
127 idModelExport::LoadMayaDll
128
129 Checks to see if we can load the Maya export dll
130 =====================
131 */
LoadMayaDll(void)132 void idModelExport::LoadMayaDll( void ) {
133 exporterDLLEntry_t dllEntry;
134 char dllPath[ MAX_OSPATH ];
135
136 fileSystem->FindDLL( "MayaImport", dllPath );
137 if ( !dllPath[ 0 ] ) {
138 return;
139 }
140 importDLL = sys->DLL_Load( dllPath );
141 if ( !importDLL ) {
142 return;
143 }
144
145 // look up the dll interface functions
146 dllEntry = ( exporterDLLEntry_t )sys->DLL_GetProcAddress( importDLL, "dllEntry" );
147 Maya_ConvertModel = ( exporterInterface_t )sys->DLL_GetProcAddress( importDLL, "Maya_ConvertModel" );
148 Maya_Shutdown = ( exporterShutdown_t )sys->DLL_GetProcAddress( importDLL, "Maya_Shutdown" );
149 if ( !Maya_ConvertModel || !dllEntry || !Maya_Shutdown ) {
150 Maya_ConvertModel = NULL;
151 Maya_Shutdown = NULL;
152 sys->DLL_Unload( importDLL );
153 importDLL = 0;
154 gameLocal.Error( "Invalid interface on export DLL." );
155 return;
156 }
157
158 // initialize the DLL
159 if ( !dllEntry( MD5_VERSION, common, sys ) ) {
160 // init failed
161 Maya_ConvertModel = NULL;
162 Maya_Shutdown = NULL;
163 sys->DLL_Unload( importDLL );
164 importDLL = 0;
165 gameLocal.Error( "Export DLL init failed." );
166 return;
167 }
168 }
169
170 /*
171 =====================
172 idModelExport::ConvertMayaToMD5
173
174 Checks if a Maya model should be converted to an MD5, and converts if if the time/date or
175 version number has changed.
176 =====================
177 */
ConvertMayaToMD5(void)178 bool idModelExport::ConvertMayaToMD5( void ) {
179 ID_TIME_T sourceTime;
180 ID_TIME_T destTime;
181 int version;
182 idToken cmdLine;
183 idStr path;
184
185 // check if our DLL got loaded
186 if ( initialized && !Maya_ConvertModel ) {
187 Maya_Error = "MayaImport dll not loaded.";
188 return false;
189 }
190
191 // if idAnimManager::forceExport is set then we always reexport Maya models
192 if ( idAnimManager::forceExport ) {
193 force = true;
194 }
195
196 // get the source file's time
197 if ( fileSystem->ReadFile( src, NULL, &sourceTime ) < 0 ) {
198 // source file doesn't exist
199 return true;
200 }
201
202 // get the destination file's time
203 if ( !force && ( fileSystem->ReadFile( dest, NULL, &destTime ) >= 0 ) ) {
204 idParser parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
205
206 parser.LoadFile( dest );
207
208 // read the file version
209 if ( parser.CheckTokenString( MD5_VERSION_STRING ) ) {
210 version = parser.ParseInt();
211
212 // check the command line
213 if ( parser.CheckTokenString( "commandline" ) ) {
214 parser.ReadToken( &cmdLine );
215
216 // check the file time, scale, and version
217 if ( ( destTime >= sourceTime ) && ( version == MD5_VERSION ) && ( cmdLine == commandLine ) ) {
218 // don't convert it
219 return true;
220 }
221 }
222 }
223 }
224
225 // if this is the first time we've been run, check if Maya is installed and load our DLL
226 if ( !initialized ) {
227 initialized = true;
228
229 if ( !CheckMayaInstall() ) {
230 Maya_Error = "Maya not installed in registry.";
231 return false;
232 }
233
234 LoadMayaDll();
235
236 // check if our DLL got loaded
237 if ( !Maya_ConvertModel ) {
238 Maya_Error = "Could not load MayaImport dll.";
239 return false;
240 }
241 }
242
243 // we need to make sure we have a full path, so convert the filename to an OS path
244 // _D3XP :: we work out of the cdpath, at least until we get Alienbrain
245 src = fileSystem->RelativePathToOSPath( src, "fs_cdpath" );
246 dest = fileSystem->RelativePathToOSPath( dest, "fs_cdpath" );
247
248 dest.ExtractFilePath( path );
249 if ( path.Length() ) {
250 fileSystem->CreateOSPath( path );
251 }
252
253 // get the os path in case it needs to create one
254 path = fileSystem->RelativePathToOSPath( "", "fs_cdpath" /* _D3XP */ );
255
256 common->SetRefreshOnPrint( true );
257 Maya_Error = Maya_ConvertModel( path, commandLine );
258 common->SetRefreshOnPrint( false );
259 if ( Maya_Error != "Ok" ) {
260 return false;
261 }
262
263 // conversion succeded
264 return true;
265 }
266
267 /*
268 ====================
269 idModelExport::Reset
270 ====================
271 */
Reset(void)272 void idModelExport::Reset( void ) {
273 force = false;
274 commandLine = "";
275 src = "";
276 dest = "";
277 }
278
279 /*
280 ====================
281 idModelExport::ExportModel
282 ====================
283 */
ExportModel(const char * model)284 bool idModelExport::ExportModel( const char *model ) {
285 const char *game = cvarSystem->GetCVarString( "fs_game" );
286 if ( strlen(game) == 0 ) {
287 game = BASE_GAMEDIR;
288 }
289
290 Reset();
291 src = model;
292 dest = model;
293 dest.SetFileExtension( MD5_MESH_EXT );
294
295 sprintf( commandLine, "mesh %s -dest %s -game %s", src.c_str(), dest.c_str(), game );
296 if ( !ConvertMayaToMD5() ) {
297 gameLocal.Printf( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
298 return false;
299 }
300
301 return true;
302 }
303
304 /*
305 ====================
306 idModelExport::ExportAnim
307 ====================
308 */
ExportAnim(const char * anim)309 bool idModelExport::ExportAnim( const char *anim ) {
310 const char *game = cvarSystem->GetCVarString( "fs_game" );
311 if ( strlen(game) == 0 ) {
312 game = BASE_GAMEDIR;
313 }
314
315 Reset();
316 src = anim;
317 dest = anim;
318 dest.SetFileExtension( MD5_ANIM_EXT );
319
320 sprintf( commandLine, "anim %s -dest %s -game %s", src.c_str(), dest.c_str(), game );
321 if ( !ConvertMayaToMD5() ) {
322 gameLocal.Printf( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
323 return false;
324 }
325
326 return true;
327 }
328
329 /*
330 ====================
331 idModelExport::ParseOptions
332 ====================
333 */
ParseOptions(idLexer & lex)334 bool idModelExport::ParseOptions( idLexer &lex ) {
335 idToken token;
336 idStr destdir;
337 idStr sourcedir;
338
339 if ( !lex.ReadToken( &token ) ) {
340 lex.Error( "Expected filename" );
341 return false;
342 }
343
344 src = token;
345 dest = token;
346
347 while( lex.ReadToken( &token ) ) {
348 if ( token == "-" ) {
349 if ( !lex.ReadToken( &token ) ) {
350 lex.Error( "Expecting option" );
351 return false;
352 }
353 if ( token == "sourcedir" ) {
354 if ( !lex.ReadToken( &token ) ) {
355 lex.Error( "Missing pathname after -sourcedir" );
356 return false;
357 }
358 sourcedir = token;
359 } else if ( token == "destdir" ) {
360 if ( !lex.ReadToken( &token ) ) {
361 lex.Error( "Missing pathname after -destdir" );
362 return false;
363 }
364 destdir = token;
365 } else if ( token == "dest" ) {
366 if ( !lex.ReadToken( &token ) ) {
367 lex.Error( "Missing filename after -dest" );
368 return false;
369 }
370 dest = token;
371 } else {
372 commandLine += va( " -%s", token.c_str() );
373 }
374 } else {
375 commandLine += va( " %s", token.c_str() );
376 }
377 }
378
379 if ( sourcedir.Length() ) {
380 src.StripPath();
381 sourcedir.BackSlashesToSlashes();
382 sprintf( src, "%s/%s", sourcedir.c_str(), src.c_str() );
383 }
384
385 if ( destdir.Length() ) {
386 dest.StripPath();
387 destdir.BackSlashesToSlashes();
388 sprintf( dest, "%s/%s", destdir.c_str(), dest.c_str() );
389 }
390
391 return true;
392 }
393
394 /*
395 ====================
396 idModelExport::ParseExportSection
397 ====================
398 */
ParseExportSection(idParser & parser)399 int idModelExport::ParseExportSection( idParser &parser ) {
400 idToken command;
401 idToken token;
402 idStr defaultCommands;
403 idLexer lex;
404 idStr temp;
405 idStr parms;
406 int count;
407
408 const char *game = cvarSystem->GetCVarString( "fs_game" );
409
410 if ( strlen(game) == 0 ) {
411 game = BASE_GAMEDIR;
412 }
413
414 // only export sections that match our export mask
415 if ( g_exportMask.GetString()[ 0 ] ) {
416 if ( parser.CheckTokenString( "{" ) ) {
417 parser.SkipBracedSection( false );
418 return 0;
419 }
420
421 parser.ReadToken( &token );
422 if ( token.Icmp( g_exportMask.GetString() ) ) {
423 parser.SkipBracedSection();
424 return 0;
425 }
426 parser.ExpectTokenString( "{" );
427 } else if ( !parser.CheckTokenString( "{" ) ) {
428 // skip the export mask
429 parser.ReadToken( &token );
430 parser.ExpectTokenString( "{" );
431 }
432
433 count = 0;
434
435 lex.SetFlags( LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
436
437 while( 1 ) {
438
439 if ( !parser.ReadToken( &command ) ) {
440 parser.Error( "Unexpoected end-of-file" );
441 break;
442 }
443
444 if ( command == "}" ) {
445 break;
446 }
447
448 if ( command == "options" ) {
449 parser.ParseRestOfLine( defaultCommands );
450 } else if ( command == "addoptions" ) {
451 parser.ParseRestOfLine( temp );
452 defaultCommands += " ";
453 defaultCommands += temp;
454 } else if ( ( command == "mesh" ) || ( command == "anim" ) || ( command == "camera" ) ) {
455 if ( !parser.ReadToken( &token ) ) {
456 parser.Error( "Expected filename" );
457 }
458
459 temp = token;
460 parser.ParseRestOfLine( parms );
461
462 if ( defaultCommands.Length() ) {
463 sprintf( temp, "%s %s", temp.c_str(), defaultCommands.c_str() );
464 }
465
466 if ( parms.Length() ) {
467 sprintf( temp, "%s %s", temp.c_str(), parms.c_str() );
468 }
469
470 lex.LoadMemory( temp, temp.Length(), parser.GetFileName() );
471
472 Reset();
473 if ( ParseOptions( lex ) ) {
474 const char *game = cvarSystem->GetCVarString( "fs_game" );
475 if ( strlen(game) == 0 ) {
476 game = BASE_GAMEDIR;
477 }
478
479 if ( command == "mesh" ) {
480 dest.SetFileExtension( MD5_MESH_EXT );
481 } else if ( command == "anim" ) {
482 dest.SetFileExtension( MD5_ANIM_EXT );
483 } else if ( command == "camera" ) {
484 dest.SetFileExtension( MD5_CAMERA_EXT );
485 } else {
486 dest.SetFileExtension( command );
487 }
488 idStr back = commandLine;
489 sprintf( commandLine, "%s %s -dest %s -game %s%s", command.c_str(), src.c_str(), dest.c_str(), game, commandLine.c_str() );
490 if ( ConvertMayaToMD5() ) {
491 count++;
492 } else {
493 parser.Warning( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
494 }
495 }
496 lex.FreeSource();
497 } else {
498 parser.Error( "Unknown token: %s", command.c_str() );
499 parser.SkipBracedSection( false );
500 break;
501 }
502 }
503
504 return count;
505 }
506
507 /*
508 ================
509 idModelExport::ExportDefFile
510 ================
511 */
ExportDefFile(const char * filename)512 int idModelExport::ExportDefFile( const char *filename ) {
513 idParser parser( LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
514 idToken token;
515 int count;
516
517 count = 0;
518
519 if ( !parser.LoadFile( filename ) ) {
520 gameLocal.Printf( "Could not load '%s'\n", filename );
521 return 0;
522 }
523
524 while( parser.ReadToken( &token ) ) {
525 if ( token == "export" ) {
526 count += ParseExportSection( parser );
527 } else {
528 parser.ReadToken( &token );
529 parser.SkipBracedSection();
530 }
531 }
532
533 return count;
534 }
535
536 /*
537 ================
538 idModelExport::ExportModels
539 ================
540 */
ExportModels(const char * pathname,const char * extension)541 int idModelExport::ExportModels( const char *pathname, const char *extension ) {
542 int count;
543
544 count = 0;
545
546 idFileList *files;
547 int i;
548
549 if ( !CheckMayaInstall() ) {
550 // if Maya isn't installed, don't bother checking if we have anims to export
551 return 0;
552 }
553
554 gameLocal.Printf( "----- Exporting models -----\n" );
555 if ( !g_exportMask.GetString()[ 0 ] ) {
556 gameLocal.Printf( " Export mask: '%s'\n", g_exportMask.GetString() );
557 }
558
559 count = 0;
560
561 files = fileSystem->ListFiles( pathname, extension );
562 for( i = 0; i < files->GetNumFiles(); i++ ) {
563 count += ExportDefFile( va( "%s/%s", pathname, files->GetFile( i ) ) );
564 }
565 fileSystem->FreeFileList( files );
566
567 gameLocal.Printf( "...%d models exported.\n", count );
568
569 return count;
570 }
571