maya_main.cpp 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Maya5.0/maya.h"
  23. //#include "Maya6.0/maya.h" // must also change include directory in project from "MayaImport\Maya4.5\include" to "MayaImport\Maya6.0\include" (requires MSDev 7.1)
  24. #include "exporter.h"
  25. #include "maya_main.h"
  26. idStr errorMessage;
  27. bool initialized = false;
  28. #define DEFAULT_ANIM_EPSILON 0.125f
  29. #define DEFAULT_QUAT_EPSILON ( 1.0f / 8192.0f )
  30. #define SLOP_VERTEX 0.01f // merge xyz coordinates this far apart
  31. #define SLOP_TEXCOORD 0.001f // merge texture coordinates this far apart
  32. const char *componentNames[ 6 ] = { "Tx", "Ty", "Tz", "Qx", "Qy", "Qz" };
  33. idSys * sys = NULL;
  34. idCommon * common = NULL;
  35. idCVarSystem * cvarSystem = NULL;
  36. idCVar * idCVar::staticVars = NULL;
  37. /*
  38. =================
  39. MayaError
  40. =================
  41. */
  42. void MayaError( const char *fmt, ... ) {
  43. va_list argptr;
  44. char text[ 8192 ];
  45. va_start( argptr, fmt );
  46. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  47. va_end( argptr );
  48. throw idException( text );
  49. }
  50. /*
  51. =================
  52. FS_WriteFloatString
  53. =================
  54. */
  55. #define MAX_PRINT_MSG 4096
  56. static int WriteFloatString( FILE *file, const char *fmt, ... ) {
  57. long i;
  58. unsigned long u;
  59. double f;
  60. char *str;
  61. int index;
  62. idStr tmp, format;
  63. va_list argPtr;
  64. va_start( argPtr, fmt );
  65. index = 0;
  66. while( *fmt ) {
  67. switch( *fmt ) {
  68. case '%':
  69. format = "";
  70. format += *fmt++;
  71. while ( (*fmt >= '0' && *fmt <= '9') ||
  72. *fmt == '.' || *fmt == '-' || *fmt == '+' || *fmt == '#') {
  73. format += *fmt++;
  74. }
  75. format += *fmt;
  76. switch( *fmt ) {
  77. case 'f':
  78. case 'e':
  79. case 'E':
  80. case 'g':
  81. case 'G':
  82. f = va_arg( argPtr, double );
  83. if ( format.Length() <= 2 ) {
  84. // high precision floating point number without trailing zeros
  85. sprintf( tmp, "%1.10f", f );
  86. tmp.StripTrailing( '0' );
  87. tmp.StripTrailing( '.' );
  88. index += fprintf( file, "%s", tmp.c_str() );
  89. }
  90. else {
  91. index += fprintf( file, format.c_str(), f );
  92. }
  93. break;
  94. case 'd':
  95. case 'i':
  96. i = va_arg( argPtr, long );
  97. index += fprintf( file, format.c_str(), i );
  98. break;
  99. case 'u':
  100. u = va_arg( argPtr, unsigned long );
  101. index += fprintf( file, format.c_str(), u );
  102. break;
  103. case 'o':
  104. u = va_arg( argPtr, unsigned long );
  105. index += fprintf( file, format.c_str(), u );
  106. break;
  107. case 'x':
  108. u = va_arg( argPtr, unsigned long );
  109. index += fprintf( file, format.c_str(), u );
  110. break;
  111. case 'X':
  112. u = va_arg( argPtr, unsigned long );
  113. index += fprintf( file, format.c_str(), u );
  114. break;
  115. case 'c':
  116. i = va_arg( argPtr, long );
  117. index += fprintf( file, format.c_str(), (char) i );
  118. break;
  119. case 's':
  120. str = va_arg( argPtr, char * );
  121. index += fprintf( file, format.c_str(), str );
  122. break;
  123. case '%':
  124. index += fprintf( file, format.c_str() );
  125. break;
  126. default:
  127. MayaError( "WriteFloatString: invalid format %s", format.c_str() );
  128. break;
  129. }
  130. fmt++;
  131. break;
  132. case '\\':
  133. fmt++;
  134. switch( *fmt ) {
  135. case 't':
  136. index += fprintf( file, "\t" );
  137. break;
  138. case 'n':
  139. index += fprintf( file, "\n" );
  140. default:
  141. MayaError( "WriteFloatString: unknown escape character \'%c\'", *fmt );
  142. break;
  143. }
  144. fmt++;
  145. break;
  146. default:
  147. index += fprintf( file, "%c", *fmt );
  148. fmt++;
  149. break;
  150. }
  151. }
  152. va_end( argPtr );
  153. return index;
  154. }
  155. /*
  156. ================
  157. OSPathToRelativePath
  158. takes a full OS path, as might be found in data from a media creation
  159. program, and converts it to a qpath by stripping off directories
  160. Returns false if the osPath tree doesn't match any of the existing
  161. search paths.
  162. ================
  163. */
  164. bool OSPathToRelativePath( const char *osPath, idStr &qpath, const char *game ) {
  165. char *s, *base;
  166. // skip a drive letter?
  167. // search for anything with BASE_GAMEDIR in it
  168. // Ase files from max may have the form of:
  169. // "//Purgatory/purgatory/doom/base/models/mapobjects/bitch/hologirl.tga"
  170. // which won't match any of our drive letter based search paths
  171. base = (char *)strstr( osPath, BASE_GAMEDIR );
  172. // _D3XP added mod support
  173. if ( base == NULL && strlen(game) > 0 ) {
  174. base = s = (char *)strstr( osPath, game );
  175. while( s = strstr( s, game ) ) {
  176. s += strlen( game );
  177. if ( s[0] == '/' || s[0] == '\\' ) {
  178. base = s;
  179. }
  180. }
  181. }
  182. if ( base ) {
  183. s = strstr( base, "/" );
  184. if ( !s ) {
  185. s = strstr( base, "\\" );
  186. }
  187. if ( s ) {
  188. qpath = s + 1;
  189. return true;
  190. }
  191. }
  192. common->Printf( "OSPathToRelativePath failed on %s\n", osPath );
  193. qpath = osPath;
  194. return false;
  195. }
  196. /*
  197. ===============
  198. ConvertFromIdSpace
  199. ===============
  200. */
  201. idMat3 ConvertFromIdSpace( const idMat3 &idmat ) {
  202. idMat3 mat;
  203. mat[ 0 ][ 0 ] = idmat[ 0 ][ 0 ];
  204. mat[ 0 ][ 2 ] = -idmat[ 0 ][ 1 ];
  205. mat[ 0 ][ 1 ] = idmat[ 0 ][ 2 ];
  206. mat[ 1 ][ 0 ] = idmat[ 1 ][ 0 ];
  207. mat[ 1 ][ 2 ] = -idmat[ 1 ][ 1 ];
  208. mat[ 1 ][ 1 ] = idmat[ 1 ][ 2 ];
  209. mat[ 2 ][ 0 ] = idmat[ 2 ][ 0 ];
  210. mat[ 2 ][ 2 ] = -idmat[ 2 ][ 1 ];
  211. mat[ 2 ][ 1 ] = idmat[ 2 ][ 2 ];
  212. return mat;
  213. }
  214. /*
  215. ===============
  216. ConvertFromIdSpace
  217. ===============
  218. */
  219. idVec3 ConvertFromIdSpace( const idVec3 &idpos ) {
  220. idVec3 pos;
  221. pos.x = idpos.x;
  222. pos.z = -idpos.y;
  223. pos.y = idpos.z;
  224. return pos;
  225. }
  226. /*
  227. ===============
  228. ConvertToIdSpace
  229. ===============
  230. */
  231. idMat3 ConvertToIdSpace( const idMat3 &mat ) {
  232. idMat3 idmat;
  233. idmat[ 0 ][ 0 ] = mat[ 0 ][ 0 ];
  234. idmat[ 0 ][ 1 ] = -mat[ 0 ][ 2 ];
  235. idmat[ 0 ][ 2 ] = mat[ 0 ][ 1 ];
  236. idmat[ 1 ][ 0 ] = mat[ 1 ][ 0 ];
  237. idmat[ 1 ][ 1 ] = -mat[ 1 ][ 2 ];
  238. idmat[ 1 ][ 2 ] = mat[ 1 ][ 1 ];
  239. idmat[ 2 ][ 0 ] = mat[ 2 ][ 0 ];
  240. idmat[ 2 ][ 1 ] = -mat[ 2 ][ 2 ];
  241. idmat[ 2 ][ 2 ] = mat[ 2 ][ 1 ];
  242. return idmat;
  243. }
  244. /*
  245. ===============
  246. ConvertToIdSpace
  247. ===============
  248. */
  249. idVec3 ConvertToIdSpace( const idVec3 &pos ) {
  250. idVec3 idpos;
  251. idpos.x = pos.x;
  252. idpos.y = -pos.z;
  253. idpos.z = pos.y;
  254. return idpos;
  255. }
  256. /*
  257. ===============
  258. idVec
  259. ===============
  260. */
  261. idVec3 idVec( const MFloatPoint &point ) {
  262. return idVec3( point[ 0 ], point[ 1 ], point[ 2 ] );
  263. }
  264. /*
  265. ===============
  266. idVec
  267. ===============
  268. */
  269. idVec3 idVec( const MMatrix &matrix ) {
  270. return idVec3( matrix[ 3 ][ 0 ], matrix[ 3 ][ 1 ], matrix[ 3 ][ 2 ] );
  271. }
  272. /*
  273. ===============
  274. idMat
  275. ===============
  276. */
  277. idMat3 idMat( const MMatrix &matrix ) {
  278. int j, k;
  279. idMat3 mat;
  280. for( j = 0; j < 3; j++ ) {
  281. for( k = 0; k < 3; k++ ) {
  282. mat[ j ][ k ] = matrix[ j ][ k ];
  283. }
  284. }
  285. return mat;
  286. }
  287. /*
  288. ===============
  289. GetParent
  290. ===============
  291. */
  292. MFnDagNode *GetParent( MFnDagNode *joint ) {
  293. MStatus status;
  294. MObject parentObject;
  295. parentObject = joint->parent( 0, &status );
  296. if ( !status && status.statusCode() == MStatus::kInvalidParameter ) {
  297. return NULL;
  298. }
  299. while( !parentObject.hasFn( MFn::kTransform ) ) {
  300. MFnDagNode parentNode( parentObject, &status );
  301. if ( !status ) {
  302. return NULL;
  303. }
  304. parentObject = parentNode.parent( 0, &status );
  305. if ( !status && status.statusCode() == MStatus::kInvalidParameter ) {
  306. return NULL;
  307. }
  308. }
  309. MFnDagNode *parentNode;
  310. parentNode = new MFnDagNode( parentObject, &status );
  311. if ( !status ) {
  312. delete parentNode;
  313. return NULL;
  314. }
  315. return parentNode;
  316. }
  317. /*
  318. ==============================================================================================
  319. idTokenizer
  320. ==============================================================================================
  321. */
  322. /*
  323. ====================
  324. idTokenizer::SetTokens
  325. ====================
  326. */
  327. int idTokenizer::SetTokens( const char *buffer ) {
  328. const char *cmd;
  329. Clear();
  330. // tokenize commandline
  331. cmd = buffer;
  332. while ( *cmd ) {
  333. // skip whitespace
  334. while( *cmd && isspace( *cmd ) ) {
  335. cmd++;
  336. }
  337. if ( !*cmd ) {
  338. break;
  339. }
  340. idStr &current = tokens.Alloc();
  341. while( *cmd && !isspace( *cmd ) ) {
  342. current += *cmd;
  343. cmd++;
  344. }
  345. }
  346. return tokens.Num();
  347. }
  348. /*
  349. ====================
  350. idTokenizer::NextToken
  351. ====================
  352. */
  353. const char *idTokenizer::NextToken( const char *errorstring ) {
  354. if ( currentToken < tokens.Num() ) {
  355. return tokens[ currentToken++ ];
  356. }
  357. if ( errorstring ) {
  358. MayaError( "Error: %s", errorstring );
  359. }
  360. return NULL;
  361. }
  362. /*
  363. ==============================================================================================
  364. idExportOptions
  365. ==============================================================================================
  366. */
  367. /*
  368. ====================
  369. idExportOptions::Reset
  370. ====================
  371. */
  372. void idExportOptions::Reset( const char *commandline ) {
  373. scale = 1.0f;
  374. type = WRITE_MESH;
  375. startframe = -1;
  376. endframe = -1;
  377. ignoreMeshes = false;
  378. clearOrigin = false;
  379. clearOriginAxis = false;
  380. framerate = 24;
  381. align = "";
  382. rotate = 0.0f;
  383. commandLine = commandline;
  384. prefix = "";
  385. jointThreshold = 0.05f;
  386. ignoreScale = false;
  387. xyzPrecision = DEFAULT_ANIM_EPSILON;
  388. quatPrecision = DEFAULT_QUAT_EPSILON;
  389. cycleStart = -1;
  390. src.Clear();
  391. dest.Clear();
  392. tokens.SetTokens( commandline );
  393. keepjoints.Clear();
  394. renamejoints.Clear();
  395. remapjoints.Clear();
  396. exportgroups.Clear();
  397. skipmeshes.Clear();
  398. keepmeshes.Clear();
  399. groups.Clear();
  400. }
  401. /*
  402. ====================
  403. idExportOptions::idExportOptions
  404. ====================
  405. */
  406. idExportOptions::idExportOptions( const char *commandline, const char *ospath ) {
  407. idStr token;
  408. idNamePair joints;
  409. int i;
  410. idAnimGroup *group;
  411. idStr sourceDir;
  412. idStr destDir;
  413. Reset( commandline );
  414. token = tokens.NextToken( "Missing export command" );
  415. if ( token == "mesh" ) {
  416. type = WRITE_MESH;
  417. } else if ( token == "anim" ) {
  418. type = WRITE_ANIM;
  419. } else if ( token == "camera" ) {
  420. type = WRITE_CAMERA;
  421. } else {
  422. MayaError( "Unknown export command '%s'", token.c_str() );
  423. }
  424. src = tokens.NextToken( "Missing source filename" );
  425. dest = src;
  426. for( token = tokens.NextToken(); token != ""; token = tokens.NextToken() ) {
  427. if ( token == "-force" ) {
  428. // skip
  429. } else if ( token == "-game" ) {
  430. // parse game name
  431. game = tokens.NextToken( "Expecting game name after -game" );
  432. } else if ( token == "-rename" ) {
  433. // parse joint to rename
  434. joints.from = tokens.NextToken( "Missing joint name for -rename. Usage: -rename [joint name] [new name]" );
  435. joints.to = tokens.NextToken( "Missing new name for -rename. Usage: -rename [joint name] [new name]" );
  436. renamejoints.Append( joints );
  437. } else if ( token == "-prefix" ) {
  438. prefix = tokens.NextToken( "Missing name for -prefix. Usage: -prefix [joint prefix]" );
  439. } else if ( token == "-parent" ) {
  440. // parse joint to reparent
  441. joints.from = tokens.NextToken( "Missing joint name for -parent. Usage: -parent [joint name] [new parent]" );
  442. joints.to = tokens.NextToken( "Missing new parent for -parent. Usage: -parent [joint name] [new parent]" );
  443. remapjoints.Append( joints );
  444. } else if ( !token.Icmp( "-sourcedir" ) ) {
  445. // parse source directory
  446. sourceDir = tokens.NextToken( "Missing filename for -sourcedir. Usage: -sourcedir [directory]" );
  447. } else if ( !token.Icmp( "-destdir" ) ) {
  448. // parse destination directory
  449. destDir = tokens.NextToken( "Missing filename for -destdir. Usage: -destdir [directory]" );
  450. } else if ( token == "-dest" ) {
  451. // parse destination filename
  452. dest = tokens.NextToken( "Missing filename for -dest. Usage: -dest [filename]" );
  453. } else if ( token == "-range" ) {
  454. // parse frame range to export
  455. token = tokens.NextToken( "Missing start frame for -range. Usage: -range [start frame] [end frame]" );
  456. startframe = atoi( token );
  457. token = tokens.NextToken( "Missing end frame for -range. Usage: -range [start frame] [end frame]" );
  458. endframe = atoi( token );
  459. if ( startframe > endframe ) {
  460. MayaError( "Start frame is greater than end frame." );
  461. }
  462. } else if ( !token.Icmp( "-cycleStart" ) ) {
  463. // parse start frame of cycle
  464. token = tokens.NextToken( "Missing cycle start frame for -cycleStart. Usage: -cycleStart [first frame of cycle]" );
  465. cycleStart = atoi( token );
  466. } else if ( token == "-scale" ) {
  467. // parse scale
  468. token = tokens.NextToken( "Missing scale amount for -scale. Usage: -scale [scale amount]" );
  469. scale = atof( token );
  470. } else if ( token == "-align" ) {
  471. // parse align joint
  472. align = tokens.NextToken( "Missing joint name for -align. Usage: -align [joint name]" );
  473. } else if ( token == "-rotate" ) {
  474. // parse angle rotation
  475. token = tokens.NextToken( "Missing value for -rotate. Usage: -rotate [yaw]" );
  476. rotate = -atof( token );
  477. } else if ( token == "-nomesh" ) {
  478. ignoreMeshes = true;
  479. } else if ( token == "-clearorigin" ) {
  480. clearOrigin = true;
  481. clearOriginAxis = true;
  482. } else if ( token == "-clearoriginaxis" ) {
  483. clearOriginAxis = true;
  484. } else if ( token == "-ignorescale" ) {
  485. ignoreScale = true;
  486. } else if ( token == "-xyzprecision" ) {
  487. // parse quaternion precision
  488. token = tokens.NextToken( "Missing value for -xyzprecision. Usage: -xyzprecision [precision]" );
  489. xyzPrecision = atof( token );
  490. if ( xyzPrecision < 0.0f ) {
  491. MayaError( "Invalid value for -xyzprecision. Must be >= 0" );
  492. }
  493. } else if ( token == "-quatprecision" ) {
  494. // parse quaternion precision
  495. token = tokens.NextToken( "Missing value for -quatprecision. Usage: -quatprecision [precision]" );
  496. quatPrecision = atof( token );
  497. if ( quatPrecision < 0.0f ) {
  498. MayaError( "Invalid value for -quatprecision. Must be >= 0" );
  499. }
  500. } else if ( token == "-jointthreshold" ) {
  501. // parse joint threshold
  502. token = tokens.NextToken( "Missing weight for -jointthreshold. Usage: -jointthreshold [minimum joint weight]" );
  503. jointThreshold = atof( token );
  504. } else if ( token == "-skipmesh" ) {
  505. token = tokens.NextToken( "Missing name for -skipmesh. Usage: -skipmesh [name of mesh to skip]" );
  506. skipmeshes.AddUnique( token );
  507. } else if ( token == "-keepmesh" ) {
  508. token = tokens.NextToken( "Missing name for -keepmesh. Usage: -keepmesh [name of mesh to keep]" );
  509. keepmeshes.AddUnique( token );
  510. } else if ( token == "-jointgroup" ) {
  511. token = tokens.NextToken( "Missing name for -jointgroup. Usage: -jointgroup [group name] [joint1] [joint2]...[joint n]" );
  512. group = groups.Ptr();
  513. for( i = 0; i < groups.Num(); i++, group++ ) {
  514. if ( group->name == token ) {
  515. break;
  516. }
  517. }
  518. if ( i >= groups.Num() ) {
  519. // create a new group
  520. group = &groups.Alloc();
  521. group->name = token;
  522. }
  523. while( tokens.TokenAvailable() ) {
  524. token = tokens.NextToken();
  525. if ( token[ 0 ] == '-' ) {
  526. tokens.UnGetToken();
  527. break;
  528. }
  529. group->joints.AddUnique( token );
  530. }
  531. } else if ( token == "-group" ) {
  532. // add the list of groups to export (these don't affect the hierarchy)
  533. while( tokens.TokenAvailable() ) {
  534. token = tokens.NextToken();
  535. if ( token[ 0 ] == '-' ) {
  536. tokens.UnGetToken();
  537. break;
  538. }
  539. group = groups.Ptr();
  540. for( i = 0; i < groups.Num(); i++, group++ ) {
  541. if ( group->name == token ) {
  542. break;
  543. }
  544. }
  545. if ( i >= groups.Num() ) {
  546. MayaError( "Unknown group '%s'", token.c_str() );
  547. }
  548. exportgroups.AddUnique( group );
  549. }
  550. } else if ( token == "-keep" ) {
  551. // add joints that are kept whether they're used by a mesh or not
  552. while( tokens.TokenAvailable() ) {
  553. token = tokens.NextToken();
  554. if ( token[ 0 ] == '-' ) {
  555. tokens.UnGetToken();
  556. break;
  557. }
  558. keepjoints.AddUnique( token );
  559. }
  560. } else {
  561. MayaError( "Unknown option '%s'", token.c_str() );
  562. }
  563. }
  564. token = src;
  565. src = ospath;
  566. src.BackSlashesToSlashes();
  567. src.AppendPath( sourceDir );
  568. src.AppendPath( token );
  569. token = dest;
  570. dest = ospath;
  571. dest.BackSlashesToSlashes();
  572. dest.AppendPath( destDir );
  573. dest.AppendPath( token );
  574. // Maya only accepts unix style path separators
  575. src.BackSlashesToSlashes();
  576. dest.BackSlashesToSlashes();
  577. if ( skipmeshes.Num() && keepmeshes.Num() ) {
  578. MayaError( "Can't use -keepmesh and -skipmesh together." );
  579. }
  580. }
  581. /*
  582. ====================
  583. idExportOptions::jointInExportGroup
  584. ====================
  585. */
  586. bool idExportOptions::jointInExportGroup( const char *jointname ) {
  587. int i;
  588. int j;
  589. idAnimGroup *group;
  590. if ( !exportgroups.Num() ) {
  591. // if we don't have any groups specified as export then export every joint
  592. return true;
  593. }
  594. // search through all exported groups to see if this joint is exported
  595. for( i = 0; i < exportgroups.Num(); i++ ) {
  596. group = exportgroups[ i ];
  597. for( j = 0; j < group->joints.Num(); j++ ) {
  598. if ( group->joints[ j ] == jointname ) {
  599. return true;
  600. }
  601. }
  602. }
  603. return false;
  604. }
  605. /*
  606. ==============================================================================
  607. idExportJoint
  608. ==============================================================================
  609. */
  610. idExportJoint::idExportJoint() {
  611. index = 0;
  612. exportNum = 0;
  613. mayaNode.SetOwner( this );
  614. exportNode.SetOwner( this );
  615. dagnode = NULL;
  616. t = vec3_zero;
  617. wm = mat3_default;
  618. bindpos = vec3_zero;
  619. bindmat = mat3_default;
  620. keep = false;
  621. scale = 1.0f;
  622. invscale = 1.0f;
  623. animBits = 0;
  624. firstComponent = 0;
  625. baseFrame.q.Set( 0.0f, 0.0f, 0.0f );
  626. baseFrame.t.Zero();
  627. }
  628. idExportJoint &idExportJoint::operator=( const idExportJoint &other ) {
  629. name = other.name;
  630. realname = other.realname;
  631. longname = other.longname;
  632. index = other.index;
  633. exportNum = other.exportNum;
  634. keep = other.keep;
  635. scale = other.scale;
  636. invscale = other.invscale;
  637. dagnode = other.dagnode;
  638. mayaNode = other.mayaNode;
  639. exportNode = other.exportNode;
  640. t = other.t;
  641. idt = other.idt;
  642. wm = other.wm;
  643. idwm = other.idwm;
  644. bindpos = other.bindpos;
  645. bindmat = other.bindmat;
  646. animBits = other.animBits;
  647. firstComponent = other.firstComponent;
  648. baseFrame = other.baseFrame;
  649. mayaNode.SetOwner( this );
  650. exportNode.SetOwner( this );
  651. return *this;
  652. }
  653. /*
  654. ==============================================================================
  655. idExportMesh
  656. ==============================================================================
  657. */
  658. void idExportMesh::ShareVerts( void ) {
  659. int i, j, k;
  660. exportVertex_t vert;
  661. idList<exportVertex_t> v;
  662. v = verts;
  663. verts.Clear();
  664. for( i = 0; i < tris.Num(); i++ ) {
  665. for( j = 0; j < 3; j++ ) {
  666. vert = v[ tris[ i ].indexes[ j ] ];
  667. vert.texCoords[ 0 ] = uv[ i ].uv[ j ][ 0 ];
  668. vert.texCoords[ 1 ] = 1.0f - uv[ i ].uv[ j ][ 1 ];
  669. for( k = 0; k < verts.Num(); k++ ) {
  670. if ( vert.numWeights != verts[ k ].numWeights ) {
  671. continue;
  672. }
  673. if ( vert.startweight != verts[ k ].startweight ) {
  674. continue;
  675. }
  676. if ( !vert.pos.Compare( verts[ k ].pos, SLOP_VERTEX ) ) {
  677. continue;
  678. }
  679. if ( !vert.texCoords.Compare( verts[ k ].texCoords, SLOP_TEXCOORD ) ) {
  680. continue;
  681. }
  682. break;
  683. }
  684. if ( k < verts.Num() ) {
  685. tris[ i ].indexes[ j ] = k;
  686. } else {
  687. tris[ i ].indexes[ j ] = verts.Append( vert );
  688. }
  689. }
  690. }
  691. }
  692. void idExportMesh::Merge( idExportMesh *mesh ) {
  693. int i;
  694. int numverts;
  695. int numtris;
  696. int numweights;
  697. int numuvs;
  698. // merge name
  699. sprintf( name, "%s, %s", name.c_str(), mesh->name.c_str() );
  700. // merge verts
  701. numverts = verts.Num();
  702. verts.SetNum( numverts + mesh->verts.Num() );
  703. for( i = 0; i < mesh->verts.Num(); i++ ) {
  704. verts[ numverts + i ] = mesh->verts[ i ];
  705. verts[ numverts + i ].startweight += weights.Num();
  706. }
  707. // merge triangles
  708. numtris = tris.Num();
  709. tris.SetNum( numtris + mesh->tris.Num() );
  710. for( i = 0; i < mesh->tris.Num(); i++ ) {
  711. tris[ numtris + i ].indexes[ 0 ] = mesh->tris[ i ].indexes[ 0 ] + numverts;
  712. tris[ numtris + i ].indexes[ 1 ] = mesh->tris[ i ].indexes[ 1 ] + numverts;
  713. tris[ numtris + i ].indexes[ 2 ] = mesh->tris[ i ].indexes[ 2 ] + numverts;
  714. }
  715. // merge weights
  716. numweights = weights.Num();
  717. weights.SetNum( numweights + mesh->weights.Num() );
  718. for( i = 0; i < mesh->weights.Num(); i++ ) {
  719. weights[ numweights + i ] = mesh->weights[ i ];
  720. }
  721. // merge uvs
  722. numuvs = uv.Num();
  723. uv .SetNum( numuvs + mesh->uv.Num() );
  724. for( i = 0; i < mesh->uv.Num(); i++ ) {
  725. uv[ numuvs + i ] = mesh->uv[ i ];
  726. }
  727. }
  728. void idExportMesh::GetBounds( idBounds &bounds ) const {
  729. int i;
  730. int j;
  731. idVec3 pos;
  732. const exportWeight_t *weight;
  733. const exportVertex_t *vert;
  734. bounds.Clear();
  735. weight = weights.Ptr();
  736. vert = verts.Ptr();
  737. for( i = 0; i < verts.Num(); i++, vert++ ) {
  738. pos.Zero();
  739. weight = &weights[ vert->startweight ];
  740. for( j = 0; j < vert->numWeights; j++, weight++ ) {
  741. pos += weight->jointWeight * ( weight->joint->idwm * weight->offset + weight->joint->idt );
  742. }
  743. bounds.AddPoint( pos );
  744. }
  745. }
  746. /*
  747. ==============================================================================
  748. idExportModel
  749. ==============================================================================
  750. */
  751. /*
  752. ====================
  753. idExportModel::idExportModel
  754. ====================
  755. */
  756. ID_INLINE idExportModel::idExportModel() {
  757. export_joints = 0;
  758. skipjoints = 0;
  759. frameRate = 24;
  760. numFrames = 0;
  761. exportOrigin = NULL;
  762. }
  763. /*
  764. ====================
  765. idExportModel::~idExportModel
  766. ====================
  767. */
  768. ID_INLINE idExportModel::~idExportModel() {
  769. meshes.DeleteContents( true );
  770. }
  771. idExportJoint *idExportModel::FindJointReal( const char *name ) {
  772. idExportJoint *joint;
  773. int i;
  774. joint = joints.Ptr();
  775. for( i = 0; i < joints.Num(); i++, joint++ ) {
  776. if ( joint->realname == name ) {
  777. return joint;
  778. }
  779. }
  780. return NULL;
  781. }
  782. idExportJoint *idExportModel::FindJoint( const char *name ) {
  783. idExportJoint *joint;
  784. int i;
  785. joint = joints.Ptr();
  786. for( i = 0; i < joints.Num(); i++, joint++ ) {
  787. if ( joint->name == name ) {
  788. return joint;
  789. }
  790. }
  791. return NULL;
  792. }
  793. bool idExportModel::WriteMesh( const char *filename, idExportOptions &options ) {
  794. int i, j;
  795. int numMeshes;
  796. idExportMesh *mesh;
  797. idExportJoint *joint;
  798. idExportJoint *parent;
  799. idExportJoint *sibling;
  800. FILE *file;
  801. const char *parentName;
  802. int parentNum;
  803. idList<idExportJoint *> jointList;
  804. file = fopen( filename, "w" );
  805. if ( !file ) {
  806. return false;
  807. }
  808. for( joint = exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  809. jointList.Append( joint );
  810. }
  811. for( i = 0; i < jointList.Num(); i++ ) {
  812. joint = jointList[ i ];
  813. sibling = joint->exportNode.GetSibling();
  814. while( sibling ) {
  815. if ( idStr::Cmp( joint->name, sibling->name ) > 0 ) {
  816. joint->exportNode.MakeSiblingAfter( sibling->exportNode );
  817. sibling = joint->exportNode.GetSibling();
  818. } else {
  819. sibling = sibling->exportNode.GetSibling();
  820. }
  821. }
  822. }
  823. jointList.Clear();
  824. for( joint = exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  825. joint->exportNum = jointList.Append( joint );
  826. }
  827. numMeshes = 0;
  828. if ( !options.ignoreMeshes ) {
  829. for( i = 0; i < meshes.Num(); i++ ) {
  830. if ( meshes[ i ]->keep ) {
  831. numMeshes++;
  832. }
  833. }
  834. }
  835. // write version info
  836. WriteFloatString( file, MD5_VERSION_STRING " %d\n", MD5_VERSION );
  837. WriteFloatString( file, "commandline \"%s\"\n\n", options.commandLine.c_str() );
  838. // write joints
  839. WriteFloatString( file, "numJoints %d\n", jointList.Num() );
  840. WriteFloatString( file, "numMeshes %d\n\n", numMeshes );
  841. WriteFloatString( file, "joints {\n" );
  842. for( i = 0; i < jointList.Num(); i++ ) {
  843. joint = jointList[ i ];
  844. parent = joint->exportNode.GetParent();
  845. if ( parent ) {
  846. parentNum = parent->exportNum;
  847. parentName = parent->name.c_str();
  848. } else {
  849. parentNum = -1;
  850. parentName = "";
  851. }
  852. idCQuat bindQuat = joint->bindmat.ToQuat().ToCQuat();
  853. WriteFloatString( file, "\t\"%s\"\t%d ( %f %f %f ) ( %f %f %f )\t\t// %s\n", joint->name.c_str(), parentNum,
  854. joint->bindpos.x, joint->bindpos.y, joint->bindpos.z, bindQuat[ 0 ], bindQuat[ 1 ], bindQuat[ 2 ], parentName );
  855. }
  856. WriteFloatString( file, "}\n" );
  857. // write meshes
  858. for( i = 0; i < meshes.Num(); i++ ) {
  859. mesh = meshes[ i ];
  860. if ( !mesh->keep ) {
  861. continue;
  862. }
  863. WriteFloatString( file, "\nmesh {\n" );
  864. WriteFloatString( file, "\t// meshes: %s\n", mesh->name.c_str() );
  865. WriteFloatString( file, "\tshader \"%s\"\n", mesh->shader.c_str() );
  866. WriteFloatString( file, "\n\tnumverts %d\n", mesh->verts.Num() );
  867. for( j = 0; j < mesh->verts.Num(); j++ ) {
  868. WriteFloatString( file, "\tvert %d ( %f %f ) %d %d\n", j, mesh->verts[ j ].texCoords[ 0 ], mesh->verts[ j ].texCoords[ 1 ],
  869. mesh->verts[ j ].startweight, mesh->verts[ j ].numWeights );
  870. }
  871. WriteFloatString( file, "\n\tnumtris %d\n", mesh->tris.Num() );
  872. for( j = 0; j < mesh->tris.Num(); j++ ) {
  873. WriteFloatString( file, "\ttri %d %d %d %d\n", j, mesh->tris[ j ].indexes[ 2 ], mesh->tris[ j ].indexes[ 1 ], mesh->tris[ j ].indexes[ 0 ] );
  874. }
  875. WriteFloatString( file, "\n\tnumweights %d\n", mesh->weights.Num() );
  876. for( j = 0; j < mesh->weights.Num(); j++ ) {
  877. exportWeight_t *weight;
  878. weight = &mesh->weights[ j ];
  879. WriteFloatString( file, "\tweight %d %d %f ( %f %f %f )\n", j,
  880. weight->joint->exportNum, weight->jointWeight, weight->offset.x, weight->offset.y, weight->offset.z );
  881. }
  882. WriteFloatString( file, "}\n" );
  883. }
  884. fclose( file );
  885. return true;
  886. }
  887. bool idExportModel::WriteAnim( const char *filename, idExportOptions &options ) {
  888. int i, j;
  889. idExportJoint *joint;
  890. idExportJoint *parent;
  891. idExportJoint *sibling;
  892. jointFrame_t *frame;
  893. FILE *file;
  894. int numAnimatedComponents;
  895. idList<idExportJoint *> jointList;
  896. file = fopen( filename, "w" );
  897. if ( !file ) {
  898. return false;
  899. }
  900. for( joint = exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  901. jointList.Append( joint );
  902. }
  903. for( i = 0; i < jointList.Num(); i++ ) {
  904. joint = jointList[ i ];
  905. sibling = joint->exportNode.GetSibling();
  906. while( sibling ) {
  907. if ( idStr::Cmp( joint->name, sibling->name ) > 0 ) {
  908. joint->exportNode.MakeSiblingAfter( sibling->exportNode );
  909. sibling = joint->exportNode.GetSibling();
  910. } else {
  911. sibling = sibling->exportNode.GetSibling();
  912. }
  913. }
  914. }
  915. jointList.Clear();
  916. for( joint = exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  917. joint->exportNum = jointList.Append( joint );
  918. }
  919. numAnimatedComponents = 0;
  920. for( i = 0; i < jointList.Num(); i++ ) {
  921. joint = jointList[ i ];
  922. joint->exportNum = i;
  923. joint->baseFrame = frames[ 0 ][ joint->index ];
  924. joint->animBits = 0;
  925. for( j = 1; j < numFrames; j++ ) {
  926. frame = &frames[ j ][ joint->index ];
  927. if ( fabs( frame->t[ 0 ] - joint->baseFrame.t[ 0 ] ) > options.xyzPrecision ) {
  928. joint->animBits |= ANIM_TX;
  929. }
  930. if ( fabs( frame->t[ 1 ] - joint->baseFrame.t[ 1 ] ) > options.xyzPrecision ) {
  931. joint->animBits |= ANIM_TY;
  932. }
  933. if ( fabs( frame->t[ 2 ] - joint->baseFrame.t[ 2 ] ) > options.xyzPrecision ) {
  934. joint->animBits |= ANIM_TZ;
  935. }
  936. if ( fabs( frame->q[ 0 ] - joint->baseFrame.q[ 0 ] ) > options.quatPrecision ) {
  937. joint->animBits |= ANIM_QX;
  938. }
  939. if ( fabs( frame->q[ 1 ] - joint->baseFrame.q[ 1 ] ) > options.quatPrecision ) {
  940. joint->animBits |= ANIM_QY;
  941. }
  942. if ( fabs( frame->q[ 2 ] - joint->baseFrame.q[ 2 ] ) > options.quatPrecision ) {
  943. joint->animBits |= ANIM_QZ;
  944. }
  945. if ( ( joint->animBits & 63 ) == 63 ) {
  946. break;
  947. }
  948. }
  949. if ( joint->animBits ) {
  950. joint->firstComponent = numAnimatedComponents;
  951. for( j = 0; j < 6; j++ ) {
  952. if ( joint->animBits & BIT( j ) ) {
  953. numAnimatedComponents++;
  954. }
  955. }
  956. }
  957. }
  958. // write version info
  959. WriteFloatString( file, MD5_VERSION_STRING " %d\n", MD5_VERSION );
  960. WriteFloatString( file, "commandline \"%s\"\n\n", options.commandLine.c_str() );
  961. WriteFloatString( file, "numFrames %d\n", numFrames );
  962. WriteFloatString( file, "numJoints %d\n", jointList.Num() );
  963. WriteFloatString( file, "frameRate %d\n", frameRate );
  964. WriteFloatString( file, "numAnimatedComponents %d\n", numAnimatedComponents );
  965. // write out the hierarchy
  966. WriteFloatString( file, "\nhierarchy {\n" );
  967. for( i = 0; i < jointList.Num(); i++ ) {
  968. joint = jointList[ i ];
  969. parent = joint->exportNode.GetParent();
  970. if ( parent ) {
  971. WriteFloatString( file, "\t\"%s\"\t%d %d %d\t// %s", joint->name.c_str(), parent->exportNum, joint->animBits, joint->firstComponent, parent->name.c_str() );
  972. } else {
  973. WriteFloatString( file, "\t\"%s\"\t-1 %d %d\t//", joint->name.c_str(), joint->animBits, joint->firstComponent );
  974. }
  975. if ( !joint->animBits ) {
  976. WriteFloatString( file, "\n" );
  977. } else {
  978. WriteFloatString( file, " ( " );
  979. for( j = 0; j < 6; j++ ) {
  980. if ( joint->animBits & BIT( j ) ) {
  981. WriteFloatString( file, "%s ", componentNames[ j ] );
  982. }
  983. }
  984. WriteFloatString( file, ")\n" );
  985. }
  986. }
  987. WriteFloatString( file, "}\n" );
  988. // write the frame bounds
  989. WriteFloatString( file, "\nbounds {\n" );
  990. for( i = 0; i < numFrames; i++ ) {
  991. WriteFloatString( file, "\t( %f %f %f ) ( %f %f %f )\n", bounds[ i ][ 0 ].x, bounds[ i ][ 0 ].y, bounds[ i ][ 0 ].z, bounds[ i ][ 1 ].x, bounds[ i ][ 1 ].y, bounds[ i ][ 1 ].z );
  992. }
  993. WriteFloatString( file, "}\n" );
  994. // write the base frame
  995. WriteFloatString( file, "\nbaseframe {\n" );
  996. for( i = 0; i < jointList.Num(); i++ ) {
  997. joint = jointList[ i ];
  998. WriteFloatString( file, "\t( %f %f %f ) ( %f %f %f )\n", joint->baseFrame.t[ 0 ], joint->baseFrame.t[ 1 ], joint->baseFrame.t[ 2 ],
  999. joint->baseFrame.q[ 0 ], joint->baseFrame.q[ 1 ], joint->baseFrame.q[ 2 ] );
  1000. }
  1001. WriteFloatString( file, "}\n" );
  1002. // write the frames
  1003. for( i = 0; i < numFrames; i++ ) {
  1004. WriteFloatString( file, "\nframe %d {\n", i );
  1005. for( j = 0; j < jointList.Num(); j++ ) {
  1006. joint = jointList[ j ];
  1007. frame = &frames[ i ][ joint->index ];
  1008. if ( joint->animBits ) {
  1009. WriteFloatString( file, "\t" );
  1010. if ( joint->animBits & ANIM_TX ) {
  1011. WriteFloatString( file, " %f", frame->t[ 0 ] );
  1012. }
  1013. if ( joint->animBits & ANIM_TY ) {
  1014. WriteFloatString( file, " %f", frame->t[ 1 ] );
  1015. }
  1016. if ( joint->animBits & ANIM_TZ ) {
  1017. WriteFloatString( file, " %f", frame->t[ 2 ] );
  1018. }
  1019. if ( joint->animBits & ANIM_QX ) {
  1020. WriteFloatString( file, " %f", frame->q[ 0 ] );
  1021. }
  1022. if ( joint->animBits & ANIM_QY ) {
  1023. WriteFloatString( file, " %f", frame->q[ 1 ] );
  1024. }
  1025. if ( joint->animBits & ANIM_QZ ) {
  1026. WriteFloatString( file, " %f", frame->q[ 2 ] );
  1027. }
  1028. WriteFloatString( file, "\n" );
  1029. }
  1030. }
  1031. WriteFloatString( file, "}\n" );
  1032. }
  1033. fclose( file );
  1034. return true;
  1035. }
  1036. bool idExportModel::WriteCamera( const char *filename, idExportOptions &options ) {
  1037. int i;
  1038. FILE *file;
  1039. file = fopen( filename, "w" );
  1040. if ( !file ) {
  1041. return false;
  1042. }
  1043. // write version info
  1044. WriteFloatString( file, MD5_VERSION_STRING " %d\n", MD5_VERSION );
  1045. WriteFloatString( file, "commandline \"%s\"\n\n", options.commandLine.c_str() );
  1046. WriteFloatString( file, "numFrames %d\n", camera.Num() );
  1047. WriteFloatString( file, "frameRate %d\n", frameRate );
  1048. WriteFloatString( file, "numCuts %d\n", cameraCuts.Num() );
  1049. // write out the cuts
  1050. WriteFloatString( file, "\ncuts {\n" );
  1051. for( i = 0; i < cameraCuts.Num(); i++ ) {
  1052. WriteFloatString( file, "\t%d\n", cameraCuts[ i ] );
  1053. }
  1054. WriteFloatString( file, "}\n" );
  1055. // write out the frames
  1056. WriteFloatString( file, "\ncamera {\n" );
  1057. cameraFrame_t *frame = camera.Ptr();
  1058. for( i = 0; i < camera.Num(); i++, frame++ ) {
  1059. WriteFloatString( file, "\t( %f %f %f ) ( %f %f %f ) %f\n", frame->t.x, frame->t.y, frame->t.z, frame->q[ 0 ], frame->q[ 1 ], frame->q[ 2 ], frame->fov );
  1060. }
  1061. WriteFloatString( file, "}\n" );
  1062. fclose( file );
  1063. return true;
  1064. }
  1065. /*
  1066. ==============================================================================
  1067. Maya
  1068. ==============================================================================
  1069. */
  1070. /*
  1071. ===============
  1072. idMayaExport::~idMayaExport
  1073. ===============
  1074. */
  1075. idMayaExport::~idMayaExport() {
  1076. FreeDagNodes();
  1077. // free up the file in Maya
  1078. MFileIO::newFile( true );
  1079. }
  1080. /*
  1081. ===============
  1082. idMayaExport::TimeForFrame
  1083. ===============
  1084. */
  1085. float idMayaExport::TimeForFrame( int num ) const {
  1086. MTime time;
  1087. // set time unit to 24 frames per second
  1088. time.setUnit( MTime::kFilm );
  1089. time.setValue( num );
  1090. return time.as( MTime::kSeconds );
  1091. }
  1092. /*
  1093. ===============
  1094. idMayaExport::GetMayaFrameNum
  1095. ===============
  1096. */
  1097. int idMayaExport::GetMayaFrameNum( int num ) const {
  1098. int frameNum;
  1099. if ( options.cycleStart > options.startframe ) {
  1100. // in cycles, the last frame is a duplicate of the first frame, so with cycleStart we need to
  1101. // duplicate one of the interior frames instead and chop off the first frame.
  1102. frameNum = options.cycleStart + num;
  1103. if ( frameNum > options.endframe ) {
  1104. frameNum -= options.endframe - options.startframe;
  1105. }
  1106. if ( frameNum < options.startframe ) {
  1107. frameNum = options.startframe + 1;
  1108. }
  1109. } else {
  1110. frameNum = options.startframe + num;
  1111. if ( frameNum > options.endframe ) {
  1112. frameNum -= options.endframe + 1 - options.startframe;
  1113. }
  1114. if ( frameNum < options.startframe ) {
  1115. frameNum = options.startframe;
  1116. }
  1117. }
  1118. return frameNum;
  1119. }
  1120. /*
  1121. ===============
  1122. idMayaExport::SetFrame
  1123. ===============
  1124. */
  1125. void idMayaExport::SetFrame( int num ) {
  1126. MTime time;
  1127. int frameNum;
  1128. frameNum = GetMayaFrameNum( num );
  1129. // set time unit to 24 frames per second
  1130. time.setUnit( MTime::kFilm );
  1131. time.setValue( frameNum );
  1132. MGlobal::viewFrame( time );
  1133. }
  1134. /*
  1135. ===============
  1136. idMayaExport::PruneJoints
  1137. ===============
  1138. */
  1139. void idMayaExport::PruneJoints( idStrList &keepjoints, idStr &prefix ) {
  1140. int i;
  1141. int j;
  1142. idExportMesh *mesh;
  1143. idExportJoint *joint;
  1144. idExportJoint *joint2;
  1145. idExportJoint *parent;
  1146. int num_weights;
  1147. // if we don't have any joints specified to keep, mark the ones used by the meshes as keep
  1148. if ( !keepjoints.Num() && !prefix.Length() ) {
  1149. if ( !model.meshes.Num() || options.ignoreMeshes ) {
  1150. // export all joints
  1151. joint = model.joints.Ptr();
  1152. for( i = 0; i < model.joints.Num(); i++, joint++ ) {
  1153. joint->keep = true;
  1154. }
  1155. } else {
  1156. for( i = 0; i < model.meshes.Num(); i++, mesh++ ) {
  1157. mesh = model.meshes[ i ];
  1158. for( j = 0; j < mesh->weights.Num(); j++ ) {
  1159. mesh->weights[ j ].joint->keep = true;
  1160. }
  1161. }
  1162. }
  1163. } else {
  1164. // mark the joints to keep
  1165. for( i = 0; i < keepjoints.Num(); i++ ) {
  1166. joint = model.FindJoint( keepjoints[ i ] );
  1167. if ( joint ) {
  1168. joint->keep = true;
  1169. }
  1170. }
  1171. // count valid meshes
  1172. for( i = 0; i < model.meshes.Num(); i++ ) {
  1173. mesh = model.meshes[ i ];
  1174. num_weights = 0;
  1175. for( j = 0; j < mesh->weights.Num(); j++ ) {
  1176. if ( mesh->weights[ j ].joint->keep ) {
  1177. num_weights++;
  1178. } else if ( prefix.Length() && !mesh->weights[ j ].joint->realname.Cmpn( prefix, prefix.Length() ) ) {
  1179. // keep the joint if it's used by the mesh and it has the right prefix
  1180. mesh->weights[ j ].joint->keep = true;
  1181. num_weights++;
  1182. }
  1183. }
  1184. if ( num_weights != mesh->weights.Num() ) {
  1185. mesh->keep = false;
  1186. }
  1187. }
  1188. }
  1189. // find all joints aren't exported and reparent joint's children
  1190. model.export_joints = 0;
  1191. joint = model.joints.Ptr();
  1192. for( i = 0; i < model.joints.Num(); i++, joint++ ) {
  1193. if ( !joint->keep ) {
  1194. joint->exportNode.RemoveFromHierarchy();
  1195. } else {
  1196. joint->index = model.export_joints;
  1197. model.export_joints++;
  1198. // make sure we are parented to an exported joint
  1199. for( parent = joint->exportNode.GetParent(); parent != NULL; parent = parent->exportNode.GetParent() ) {
  1200. if ( parent->keep ) {
  1201. break;
  1202. }
  1203. }
  1204. if ( parent != NULL ) {
  1205. joint->exportNode.ParentTo( parent->exportNode );
  1206. } else {
  1207. joint->exportNode.ParentTo( model.exportHead );
  1208. }
  1209. }
  1210. }
  1211. // check if we have any duplicate joint names
  1212. for( joint = model.exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  1213. if ( !joint->keep ) {
  1214. MayaError( "Non-kept joint in export tree ('%s')", joint->name.c_str() );
  1215. }
  1216. for( joint2 = model.exportHead.GetNext(); joint2 != NULL; joint2 = joint2->exportNode.GetNext() ) {
  1217. if ( ( joint2 != joint ) && ( joint2->name == joint->name ) ) {
  1218. MayaError( "Two joints found with the same name ('%s')", joint->name.c_str() );
  1219. }
  1220. }
  1221. }
  1222. }
  1223. /*
  1224. ===============
  1225. idMayaExport::FreeDagNodes
  1226. ===============
  1227. */
  1228. void idMayaExport::FreeDagNodes( void ) {
  1229. int i;
  1230. for( i = 0; i < model.joints.Num(); i++ ) {
  1231. delete model.joints[ i ].dagnode;
  1232. model.joints[ i ].dagnode = NULL;
  1233. }
  1234. }
  1235. /*
  1236. ===============
  1237. idMayaExport::GetBindPose
  1238. ===============
  1239. */
  1240. void idMayaExport::GetBindPose( MObject &jointNode, idExportJoint *joint, float scale ) {
  1241. MStatus status;
  1242. MFnDependencyNode fnJoint( jointNode );
  1243. MObject aBindPose = fnJoint.attribute( "bindPose", &status );
  1244. joint->bindpos = vec3_zero;
  1245. joint->bindmat = mat3_default;
  1246. if ( MS::kSuccess == status ) {
  1247. unsigned ii;
  1248. unsigned jointIndex;
  1249. unsigned connLength;
  1250. MPlugArray connPlugs;
  1251. MPlug pBindPose( jointNode, aBindPose );
  1252. pBindPose.connectedTo( connPlugs, false, true );
  1253. connLength = connPlugs.length();
  1254. for( ii = 0; ii < connLength; ++ii ) {
  1255. if ( connPlugs[ ii ].node().apiType() == MFn::kDagPose ) {
  1256. MObject aMember = connPlugs[ ii ].attribute();
  1257. MFnAttribute fnAttr( aMember );
  1258. if ( fnAttr.name() == "worldMatrix" ) {
  1259. jointIndex = connPlugs[ ii ].logicalIndex();
  1260. MFnDependencyNode nDagPose( connPlugs[ ii ].node() );
  1261. // construct plugs for this joint's world matrix
  1262. MObject aWorldMatrix = nDagPose.attribute( "worldMatrix" );
  1263. MPlug pWorldMatrix( connPlugs[ ii ].node(), aWorldMatrix );
  1264. pWorldMatrix.selectAncestorLogicalIndex( jointIndex, aWorldMatrix );
  1265. // get the world matrix data
  1266. MObject worldMatrix;
  1267. MStatus status = pWorldMatrix.getValue( worldMatrix );
  1268. if ( MS::kSuccess != status ) {
  1269. // Problem retrieving world matrix
  1270. return;
  1271. }
  1272. MFnMatrixData dMatrix( worldMatrix );
  1273. MMatrix wMatrix = dMatrix.matrix( &status );
  1274. joint->bindmat = ConvertToIdSpace( idMat( wMatrix ) );
  1275. joint->bindpos = ConvertToIdSpace( idVec( wMatrix ) ) * scale;
  1276. if ( !options.ignoreScale ) {
  1277. joint->bindpos *= joint->scale;
  1278. } else {
  1279. joint->bindmat[ 0 ].Normalize();
  1280. joint->bindmat[ 1 ].Normalize();
  1281. joint->bindmat[ 2 ].Normalize();
  1282. }
  1283. return;
  1284. }
  1285. }
  1286. }
  1287. }
  1288. }
  1289. /*
  1290. ===============
  1291. idMayaExport::GetLocalTransform
  1292. ===============
  1293. */
  1294. void idMayaExport::GetLocalTransform( idExportJoint *joint, idVec3 &pos, idMat3 &mat ) {
  1295. MStatus status;
  1296. MDagPath dagPath;
  1297. pos.Zero();
  1298. mat.Identity();
  1299. if ( !joint->dagnode ) {
  1300. return;
  1301. }
  1302. status = joint->dagnode->getPath( dagPath );
  1303. if ( !status ) {
  1304. return;
  1305. }
  1306. MObject transformNode = dagPath.transform( &status );
  1307. if ( !status && ( status.statusCode () == MStatus::kInvalidParameter ) ) {
  1308. return;
  1309. }
  1310. MFnDagNode transform( transformNode, &status );
  1311. if ( !status ) {
  1312. return;
  1313. }
  1314. pos = idVec( transform.transformationMatrix() );
  1315. mat = idMat( transform.transformationMatrix() );
  1316. }
  1317. /*
  1318. ===============
  1319. idMayaExport::GetWorldTransform
  1320. ===============
  1321. */
  1322. void idMayaExport::GetWorldTransform( idExportJoint *joint, idVec3 &pos, idMat3 &mat, float scale ) {
  1323. idExportJoint *parent;
  1324. GetLocalTransform( joint, pos, mat );
  1325. mat.OrthoNormalizeSelf();
  1326. pos *= scale;
  1327. parent = joint->mayaNode.GetParent();
  1328. if ( parent ) {
  1329. idVec3 parentpos;
  1330. idMat3 parentmat;
  1331. GetWorldTransform( parent, parentpos, parentmat, scale );
  1332. pos = parentpos + ( parentmat * ( pos * parent->scale ) );
  1333. mat = mat * parentmat;
  1334. }
  1335. }
  1336. /*
  1337. ===============
  1338. idMayaExport::CreateJoints
  1339. ===============
  1340. */
  1341. void idMayaExport::CreateJoints( float scale ) {
  1342. int i;
  1343. int j;
  1344. idExportJoint *joint;
  1345. idExportJoint *parent;
  1346. MStatus status;
  1347. MDagPath dagPath;
  1348. MFnDagNode *parentNode;
  1349. SetFrame( 0 );
  1350. // create an initial list with all of the transformable nodes in the scene
  1351. MItDag dagIterator( MItDag::kDepthFirst, MFn::kTransform, &status );
  1352. for ( ; !dagIterator.isDone(); dagIterator.next() ) {
  1353. status = dagIterator.getPath( dagPath );
  1354. if ( !status ) {
  1355. MayaError( "CreateJoints: MItDag::getPath failed (%s)", status.errorString().asChar() );
  1356. continue;
  1357. }
  1358. joint = &model.joints.Alloc();
  1359. joint->index = model.joints.Num() - 1;
  1360. joint->dagnode = new MFnDagNode( dagPath, &status );
  1361. if ( !status ) {
  1362. MayaError( "CreateJoints: MFnDagNode constructor failed (%s)", status.errorString().asChar() );
  1363. continue;
  1364. }
  1365. joint->name = joint->dagnode->name().asChar();
  1366. joint->realname = joint->name;
  1367. }
  1368. // allocate an extra joint in case we need to add an origin later
  1369. model.exportOrigin = &model.joints.Alloc();
  1370. model.exportOrigin->index = model.joints.Num() - 1;
  1371. // create scene hierarchy
  1372. joint = model.joints.Ptr();
  1373. for( i = 0; i < model.joints.Num(); i++, joint++ ) {
  1374. if ( !joint->dagnode ) {
  1375. continue;
  1376. }
  1377. joint->mayaNode.ParentTo( model.mayaHead );
  1378. joint->exportNode.ParentTo( model.exportHead );
  1379. parentNode = GetParent( joint->dagnode );
  1380. if ( parentNode ) {
  1381. // find the parent joint in our jointlist
  1382. for( j = 0; j < model.joints.Num(); j++ ) {
  1383. if ( !model.joints[ j ].dagnode ) {
  1384. continue;
  1385. }
  1386. if ( model.joints[ j ].dagnode->name() == parentNode->name() ) {
  1387. joint->mayaNode.ParentTo( model.joints[ j ].mayaNode );
  1388. joint->exportNode.ParentTo( model.joints[ j ].exportNode );
  1389. break;
  1390. }
  1391. }
  1392. delete parentNode;
  1393. }
  1394. // create long name
  1395. parent = joint->mayaNode.GetParent();
  1396. if ( parent ) {
  1397. joint->longname = parent->longname + "/" + joint->name;
  1398. } else {
  1399. joint->longname = joint->name;
  1400. }
  1401. // get the joint's scale
  1402. GetLocalTransform( &model.joints[ i ], joint->t, joint->wm );
  1403. joint->scale = joint->wm[ 0 ].Length();
  1404. if ( parent ) {
  1405. joint->scale *= parent->scale;
  1406. if ( joint->scale != 0 ) {
  1407. joint->invscale = 1.0f / joint->scale;
  1408. } else {
  1409. joint->invscale = 0;
  1410. }
  1411. }
  1412. joint->dagnode->getPath( dagPath );
  1413. GetBindPose( dagPath.node( &status ), joint, scale );
  1414. }
  1415. }
  1416. /*
  1417. ===============
  1418. idMayaExport::RenameJoints
  1419. ===============
  1420. */
  1421. void idMayaExport::RenameJoints( idList<idNamePair> &renamejoints, idStr &prefix ) {
  1422. int i;
  1423. idExportJoint *joint;
  1424. // rename joints that match the prefix
  1425. if ( prefix.Length() ) {
  1426. joint = model.joints.Ptr();
  1427. for( i = 0; i < model.joints.Num(); i++, joint++ ) {
  1428. if ( !joint->name.Cmpn( prefix, prefix.Length() ) ) {
  1429. // remove the prefix from the name
  1430. joint->name = joint->name.Right( joint->name.Length() - prefix.Length() );
  1431. }
  1432. }
  1433. }
  1434. // rename joints if necessary
  1435. for( i = 0; i < renamejoints.Num(); i++ ) {
  1436. joint = model.FindJoint( renamejoints[ i ].from );
  1437. if ( joint ) {
  1438. joint->name = renamejoints[ i ].to;
  1439. }
  1440. }
  1441. }
  1442. /*
  1443. ===============
  1444. idMayaExport::RemapParents
  1445. ===============
  1446. */
  1447. bool idMayaExport::RemapParents( idList<idNamePair> &remapjoints ) {
  1448. int i;
  1449. idExportJoint *joint;
  1450. idExportJoint *parent;
  1451. idExportJoint *origin;
  1452. idExportJoint *sibling;
  1453. for( i = 0; i < remapjoints.Num(); i++ ) {
  1454. // find joint to reparent
  1455. joint = model.FindJoint( remapjoints[ i ].from );
  1456. if ( !joint ) {
  1457. // couldn't find joint, fail
  1458. MayaError( "Couldn't find joint '%s' to reparent\n", remapjoints[ i ].from.c_str() );
  1459. }
  1460. // find new parent joint
  1461. parent = model.FindJoint( remapjoints[ i ].to );
  1462. if ( !parent ) {
  1463. // couldn't find parent, fail
  1464. MayaError( "Couldn't find joint '%s' to be new parent for '%s'\n", remapjoints[ i ].to.c_str(), remapjoints[ i ].from.c_str() );
  1465. }
  1466. if ( parent->exportNode.ParentedBy( joint->exportNode ) ) {
  1467. MayaError( "Joint '%s' is a child of joint '%s' and can't become the parent.", joint->name.c_str(), parent->name.c_str() );
  1468. }
  1469. joint->exportNode.ParentTo( parent->exportNode );
  1470. }
  1471. // if we have an origin, make it the first node in the export list, otherwise add one
  1472. origin = model.FindJoint( "origin" );
  1473. if ( !origin ) {
  1474. origin = model.exportOrigin;
  1475. origin->dagnode = NULL;
  1476. origin->name = "origin";
  1477. origin->realname = "origin";
  1478. origin->bindmat.Identity();
  1479. origin->bindpos.Zero();
  1480. }
  1481. origin->exportNode.ParentTo( model.exportHead );
  1482. // force the joint to be kept
  1483. origin->keep = true;
  1484. // make all root joints children of the origin joint
  1485. joint = model.exportHead.GetChild();
  1486. while( joint ) {
  1487. sibling = joint->exportNode.GetSibling();
  1488. if ( joint != origin ) {
  1489. joint->exportNode.ParentTo( origin->exportNode );
  1490. }
  1491. joint = sibling;
  1492. }
  1493. return true;
  1494. }
  1495. /*
  1496. ===============
  1497. idMayaExport::FindShader
  1498. Find the shading node for the given shading group set node.
  1499. ===============
  1500. */
  1501. MObject idMayaExport::FindShader( MObject& setNode ) {
  1502. MStatus status;
  1503. MFnDependencyNode fnNode( setNode );
  1504. MPlug shaderPlug;
  1505. shaderPlug = fnNode.findPlug( "surfaceShader" );
  1506. if ( !shaderPlug.isNull() ) {
  1507. MPlugArray connectedPlugs;
  1508. bool asSrc = false;
  1509. bool asDst = true;
  1510. shaderPlug.connectedTo( connectedPlugs, asDst, asSrc, &status );
  1511. if ( connectedPlugs.length() != 1 ) {
  1512. MayaError( "FindShader: Error getting shader (%s)", status.errorString().asChar() );
  1513. } else {
  1514. return connectedPlugs[ 0 ].node();
  1515. }
  1516. }
  1517. return MObject::kNullObj;
  1518. }
  1519. /*
  1520. ===============
  1521. idMayaExport::GetTextureForMesh
  1522. Find the texture files that apply to the color of each polygon of
  1523. a selected shape if the shape has its polygons organized into sets.
  1524. ===============
  1525. */
  1526. void idMayaExport::GetTextureForMesh( idExportMesh *mesh, MFnDagNode &dagNode ) {
  1527. MStatus status;
  1528. MDagPath path;
  1529. int i;
  1530. int instanceNum;
  1531. status = dagNode.getPath( path );
  1532. if ( !status ) {
  1533. return;
  1534. }
  1535. path.extendToShape();
  1536. // If the shape is instanced then we need to determine which
  1537. // instance this path refers to.
  1538. //
  1539. instanceNum = 0;
  1540. if ( path.isInstanced() ) {
  1541. instanceNum = path.instanceNumber();
  1542. }
  1543. // Get a list of all sets pertaining to the selected shape and the
  1544. // members of those sets.
  1545. //
  1546. MFnMesh fnMesh( path );
  1547. MObjectArray sets;
  1548. MObjectArray comps;
  1549. status = fnMesh.getConnectedSetsAndMembers( instanceNum, sets, comps, true );
  1550. if ( !status ) {
  1551. MayaError( "GetTextureForMesh: MFnMesh::getConnectedSetsAndMembers failed (%s)", status.errorString().asChar() );
  1552. }
  1553. // Loop through all the sets. If the set is a polygonal set, find the
  1554. // shader attached to the and print out the texture file name for the
  1555. // set along with the polygons in the set.
  1556. //
  1557. for ( i = 0; i < ( int )sets.length(); i++ ) {
  1558. MObject set = sets[i];
  1559. MObject comp = comps[i];
  1560. MFnSet fnSet( set, &status );
  1561. if ( status == MS::kFailure ) {
  1562. MayaError( "GetTextureForMesh: MFnSet constructor failed (%s)", status.errorString().asChar() );
  1563. continue;
  1564. }
  1565. // Make sure the set is a polygonal set. If not, continue.
  1566. MItMeshPolygon piter(path, comp, &status);
  1567. if (status == MS::kFailure) {
  1568. continue;
  1569. }
  1570. // Find the texture that is applied to this set. First, get the
  1571. // shading node connected to the set. Then, if there is an input
  1572. // attribute called "color", search upstream from it for a texture
  1573. // file node.
  1574. //
  1575. MObject shaderNode = FindShader( set );
  1576. if ( shaderNode == MObject::kNullObj ) {
  1577. continue;
  1578. }
  1579. MPlug colorPlug = MFnDependencyNode(shaderNode).findPlug("color", &status);
  1580. if ( status == MS::kFailure ) {
  1581. continue;
  1582. }
  1583. MItDependencyGraph dgIt(colorPlug, MFn::kFileTexture,
  1584. MItDependencyGraph::kUpstream,
  1585. MItDependencyGraph::kBreadthFirst,
  1586. MItDependencyGraph::kNodeLevel,
  1587. &status);
  1588. if ( status == MS::kFailure ) {
  1589. continue;
  1590. }
  1591. dgIt.disablePruningOnFilter();
  1592. // If no texture file node was found, just continue.
  1593. //
  1594. if ( dgIt.isDone() ) {
  1595. continue;
  1596. }
  1597. // Print out the texture node name and texture file that it references.
  1598. //
  1599. MObject textureNode = dgIt.thisNode();
  1600. MPlug filenamePlug = MFnDependencyNode( textureNode ).findPlug( "fileTextureName" );
  1601. MString textureName;
  1602. filenamePlug.getValue( textureName );
  1603. // remove the OS path and save it in the mesh
  1604. OSPathToRelativePath( textureName.asChar(), mesh->shader, options.game );
  1605. mesh->shader.StripFileExtension();
  1606. return;
  1607. }
  1608. }
  1609. /*
  1610. ===============
  1611. idMayaExport::CopyMesh
  1612. ===============
  1613. */
  1614. idExportMesh *idMayaExport::CopyMesh( MFnSkinCluster &skinCluster, float scale ) {
  1615. MStatus status;
  1616. MObjectArray objarray;
  1617. MObjectArray outputarray;
  1618. int nGeom;
  1619. int i, j, k;
  1620. idExportMesh *mesh;
  1621. float uv_u, uv_v;
  1622. idStr name, altname;
  1623. int pos;
  1624. status = skinCluster.getInputGeometry( objarray );
  1625. if ( !status ) {
  1626. MayaError( "CopyMesh: Error getting input geometry (%s)", status.errorString().asChar() );
  1627. return NULL;
  1628. }
  1629. nGeom = objarray.length();
  1630. for( i = 0; i < nGeom; i++ ) {
  1631. MFnDagNode dagNode( objarray[ i ], &status );
  1632. if ( !status ) {
  1633. common->Printf( "CopyMesh: MFnDagNode Constructor failed (%s)", status.errorString().asChar() );
  1634. continue;
  1635. }
  1636. MFnMesh fnmesh( objarray[ i ], &status );
  1637. if ( !status ) {
  1638. // object isn't an MFnMesh
  1639. continue;
  1640. }
  1641. status = skinCluster.getOutputGeometry( outputarray );
  1642. if ( !status ) {
  1643. common->Printf( "CopyMesh: Error getting output geometry (%s)", status.errorString().asChar() );
  1644. return NULL;
  1645. }
  1646. if ( outputarray.length() < 1 ) {
  1647. return NULL;
  1648. }
  1649. name = fnmesh.name().asChar();
  1650. if ( options.prefix.Length() ) {
  1651. if ( !name.Cmpn( options.prefix, options.prefix.Length() ) ) {
  1652. // remove the prefix from the name
  1653. name = name.Right( name.Length() - options.prefix.Length() );
  1654. } else {
  1655. // name doesn't match prefix, so don't use this mesh
  1656. //return NULL;
  1657. }
  1658. }
  1659. pos = name.Find( "ShapeOrig" );
  1660. if ( pos >= 0 ) {
  1661. name.CapLength( pos );
  1662. }
  1663. MFnDagNode dagNode2( outputarray[ 0 ], &status );
  1664. if ( !status ) {
  1665. common->Printf( "CopyMesh: MFnDagNode Constructor failed (%s)", status.errorString().asChar() );
  1666. continue;
  1667. }
  1668. altname = name;
  1669. MObject parent = fnmesh.parent( 0, &status );
  1670. if ( status ) {
  1671. MFnDagNode parentNode( parent, &status );
  1672. if ( status ) {
  1673. altname = parentNode.name().asChar();
  1674. }
  1675. }
  1676. name.StripLeadingOnce( options.prefix );
  1677. altname.StripLeadingOnce( options.prefix );
  1678. if ( options.keepmeshes.Num() ) {
  1679. if ( !options.keepmeshes.Find( name ) && !options.keepmeshes.Find( altname ) ) {
  1680. if ( altname != name ) {
  1681. common->Printf( "Skipping mesh '%s' ('%s')\n", name.c_str(), altname.c_str() );
  1682. } else {
  1683. common->Printf( "Skipping mesh '%s'\n", name.c_str() );
  1684. }
  1685. return NULL;
  1686. }
  1687. }
  1688. if ( options.skipmeshes.Find( name ) || options.skipmeshes.Find( altname ) ) {
  1689. common->Printf( "Skipping mesh '%s' ('%s')\n", name.c_str(), altname.c_str() );
  1690. return NULL;
  1691. }
  1692. mesh = new idExportMesh();
  1693. model.meshes.Append( mesh );
  1694. if ( altname.Length() ) {
  1695. mesh->name = altname;
  1696. } else {
  1697. mesh->name = name;
  1698. }
  1699. GetTextureForMesh( mesh, dagNode2 );
  1700. int v = fnmesh.numVertices( &status );
  1701. mesh->verts.SetNum( v );
  1702. MFloatPointArray vertexArray;
  1703. fnmesh.getPoints( vertexArray, MSpace::kPreTransform );
  1704. for( j = 0; j < v; j++ ) {
  1705. memset( &mesh->verts[ j ], 0, sizeof( mesh->verts[ j ] ) );
  1706. mesh->verts[ j ].pos = ConvertToIdSpace( idVec( vertexArray[ j ] ) ) * scale;
  1707. }
  1708. MIntArray vertexList;
  1709. int p;
  1710. p = fnmesh.numPolygons( &status );
  1711. mesh->tris.SetNum( p );
  1712. mesh->uv.SetNum( p );
  1713. MString setName;
  1714. status = fnmesh.getCurrentUVSetName( setName );
  1715. if ( !status ) {
  1716. MayaError( "CopyMesh: MFnMesh::getCurrentUVSetName failed (%s)", status.errorString().asChar() );
  1717. }
  1718. for( j = 0; j < p; j++ ) {
  1719. fnmesh.getPolygonVertices( j, vertexList );
  1720. if ( vertexList.length() != 3 ) {
  1721. MayaError( "CopyMesh: Too many vertices on a face (%d)\n", vertexList.length() );
  1722. }
  1723. for( k = 0; k < 3; k++ ) {
  1724. mesh->tris[ j ].indexes[ k ] = vertexList[ k ];
  1725. status = fnmesh.getPolygonUV( j, k, uv_u, uv_v, &setName );
  1726. if ( !status ) {
  1727. MayaError( "CopyMesh: MFnMesh::getPolygonUV failed (%s)", status.errorString().asChar() );
  1728. }
  1729. mesh->uv[ j ].uv[ k ][ 0 ] = uv_u;
  1730. mesh->uv[ j ].uv[ k ][ 1 ] = uv_v;
  1731. }
  1732. }
  1733. return mesh;
  1734. }
  1735. return NULL;
  1736. }
  1737. /*
  1738. ===============
  1739. idMayaExport::CreateMesh
  1740. ===============
  1741. */
  1742. void idMayaExport::CreateMesh( float scale ) {
  1743. size_t count;
  1744. idExportMesh *mesh;
  1745. MStatus status;
  1746. exportWeight_t weight;
  1747. unsigned int nGeoms;
  1748. // Iterate through graph and search for skinCluster nodes
  1749. MItDependencyNodes iter( MFn::kSkinClusterFilter );
  1750. count = 0;
  1751. for ( ; !iter.isDone(); iter.next() ) {
  1752. MObject object = iter.item();
  1753. count++;
  1754. // For each skinCluster node, get the list of influence objects
  1755. MFnSkinCluster skinCluster( object, &status );
  1756. if ( !status ) {
  1757. MayaError( "%s: Error getting skin cluster (%s)", object.apiTypeStr(), status.errorString().asChar() );
  1758. }
  1759. mesh = CopyMesh( skinCluster, scale );
  1760. if ( !mesh ) {
  1761. continue;
  1762. }
  1763. MDagPathArray infs;
  1764. unsigned int nInfs = skinCluster.influenceObjects(infs, &status);
  1765. if ( !status ) {
  1766. MayaError( "Mesh '%s': Error getting influence objects (%s)", mesh->name.c_str(), status.errorString().asChar() );
  1767. }
  1768. if ( 0 == nInfs ) {
  1769. MayaError( "Mesh '%s': No influence objects found", mesh->name.c_str() );
  1770. }
  1771. // loop through the geometries affected by this cluster
  1772. nGeoms = skinCluster.numOutputConnections();
  1773. for (size_t ii = 0; ii < nGeoms; ++ii) {
  1774. unsigned int index = skinCluster.indexForOutputConnection(ii,&status);
  1775. if ( !status ) {
  1776. MayaError( "Mesh '%s': Error getting geometry index (%s)", mesh->name.c_str(), status.errorString().asChar() );
  1777. }
  1778. // get the dag path of the ii'th geometry
  1779. MDagPath skinPath;
  1780. status = skinCluster.getPathAtIndex(index,skinPath);
  1781. if ( !status ) {
  1782. MayaError( "Mesh '%s': Error getting geometry path (%s)", mesh->name.c_str(), status.errorString().asChar() );
  1783. }
  1784. // iterate through the components of this geometry
  1785. MItGeometry gIter( skinPath );
  1786. // print out the influence objects
  1787. idList<idExportJoint *> joints;
  1788. idExportJoint *joint;
  1789. exportVertex_t *vert;
  1790. joints.Resize( nInfs );
  1791. for (size_t kk = 0; kk < nInfs; ++kk) {
  1792. const char *c;
  1793. MString s;
  1794. s = infs[kk].partialPathName();
  1795. c = s.asChar();
  1796. joint = model.FindJointReal( c );
  1797. if ( !joint ) {
  1798. MayaError( "Mesh '%s': joint %s not found", mesh->name.c_str(), c );
  1799. }
  1800. joints.Append( joint );
  1801. }
  1802. for ( /* nothing */ ; !gIter.isDone(); gIter.next() ) {
  1803. MObject comp = gIter.component( &status );
  1804. if ( !status ) {
  1805. MayaError( "Mesh '%s': Error getting component (%s)", mesh->name.c_str(), status.errorString().asChar() );
  1806. }
  1807. // Get the weights for this vertex (one per influence object)
  1808. MFloatArray wts;
  1809. unsigned infCount;
  1810. status = skinCluster.getWeights(skinPath,comp,wts,infCount);
  1811. if ( !status ) {
  1812. MayaError( "Mesh '%s': Error getting weights (%s)", mesh->name.c_str(), status.errorString().asChar() );
  1813. }
  1814. if (0 == infCount) {
  1815. MayaError( "Mesh '%s': Error: 0 influence objects.", mesh->name.c_str() );
  1816. }
  1817. int num = gIter.index();
  1818. vert = &mesh->verts[ num ];
  1819. vert->startweight = mesh->weights.Num();
  1820. float totalweight = 0.0f;
  1821. // copy the weight data for this vertex
  1822. int numNonZeroWeights = 0;
  1823. int jj;
  1824. for ( jj = 0; jj < (int)infCount ; ++jj ) {
  1825. float w = ( float )wts[ jj ];
  1826. if ( w > 0.0f ) {
  1827. numNonZeroWeights++;
  1828. }
  1829. if ( w > options.jointThreshold ) {
  1830. weight.joint = joints[ jj ];
  1831. weight.jointWeight = wts[ jj ];
  1832. if ( !options.ignoreScale ) {
  1833. weight.joint->bindmat.ProjectVector( vert->pos - ( weight.joint->bindpos * weight.joint->invscale ), weight.offset );
  1834. weight.offset *= weight.joint->scale;
  1835. } else {
  1836. weight.joint->bindmat.ProjectVector( vert->pos - weight.joint->bindpos, weight.offset );
  1837. }
  1838. mesh->weights.Append( weight );
  1839. totalweight += weight.jointWeight;
  1840. }
  1841. }
  1842. vert->numWeights = mesh->weights.Num() - vert->startweight;
  1843. if ( !vert->numWeights ) {
  1844. if ( numNonZeroWeights ) {
  1845. MayaError( "Error on mesh '%s': Vertex %d doesn't have any joint weights exceeding jointThreshold (%f).", mesh->name.c_str(), num, options.jointThreshold );
  1846. } else {
  1847. MayaError( "Error on mesh '%s': Vertex %d doesn't have any joint weights.", mesh->name.c_str(), num );
  1848. }
  1849. } else if ( !totalweight ) {
  1850. MayaError( "Error on mesh '%s': Combined weight of 0 on vertex %d.", mesh->name.c_str(), num );
  1851. }
  1852. //if ( numNonZeroWeights ) {
  1853. // common->Printf( "Mesh '%s': skipped %d out of %d weights on vertex %d\n", mesh->name.c_str(), numNonZeroWeights, numNonZeroWeights + vert->numWeights, num );
  1854. //}
  1855. // normalize the joint weights
  1856. for( jj = 0; jj < vert->numWeights; jj++ ) {
  1857. mesh->weights[ vert->startweight + jj ].jointWeight /= totalweight;
  1858. }
  1859. }
  1860. break;
  1861. }
  1862. }
  1863. if ( !count && !options.ignoreMeshes ) {
  1864. MayaError( "CreateMesh: No skinClusters found in this scene.\n" );
  1865. }
  1866. }
  1867. /*
  1868. ===============
  1869. idMayaExport::CombineMeshes
  1870. combine surfaces with the same shader.
  1871. ===============
  1872. */
  1873. void idMayaExport::CombineMeshes( void ) {
  1874. int i, j;
  1875. int count;
  1876. idExportMesh *mesh;
  1877. idExportMesh *combine;
  1878. idList<idExportMesh *> oldmeshes;
  1879. oldmeshes = model.meshes;
  1880. model.meshes.Clear();
  1881. count = 0;
  1882. for( i = 0; i < oldmeshes.Num(); i++ ) {
  1883. mesh = oldmeshes[ i ];
  1884. if ( !mesh->keep ) {
  1885. delete mesh;
  1886. continue;
  1887. }
  1888. combine = NULL;
  1889. for( j = 0; j < model.meshes.Num(); j++ ) {
  1890. if ( model.meshes[ j ]->shader == mesh->shader ) {
  1891. combine = model.meshes[ j ];
  1892. break;
  1893. }
  1894. }
  1895. if ( combine ) {
  1896. combine->Merge( mesh );
  1897. delete mesh;
  1898. count++;
  1899. } else {
  1900. model.meshes.Append( mesh );
  1901. }
  1902. }
  1903. // share verts
  1904. for( i = 0; i < model.meshes.Num(); i++ ) {
  1905. model.meshes[ i ]->ShareVerts();
  1906. }
  1907. common->Printf( "Merged %d meshes\n", count );
  1908. }
  1909. /*
  1910. ===============
  1911. idMayaExport::GetAlignment
  1912. ===============
  1913. */
  1914. void idMayaExport::GetAlignment( idStr &alignName, idMat3 &align, float rotate, int startframe ) {
  1915. idVec3 pos;
  1916. idExportJoint *joint;
  1917. idAngles ang( 0, rotate, 0 );
  1918. idMat3 mat;
  1919. align.Identity();
  1920. if ( alignName.Length() ) {
  1921. SetFrame( 0 );
  1922. joint = model.FindJoint( alignName );
  1923. if ( !joint ) {
  1924. MayaError( "could not find joint '%s' to align model to.\n", alignName.c_str() );
  1925. }
  1926. // found it
  1927. GetWorldTransform( joint, pos, mat, 1.0f );
  1928. align[ 0 ][ 0 ] = mat[ 2 ][ 0 ];
  1929. align[ 0 ][ 1 ] = -mat[ 2 ][ 2 ];
  1930. align[ 0 ][ 2 ] = mat[ 2 ][ 1 ];
  1931. align[ 1 ][ 0 ] = mat[ 0 ][ 0 ];
  1932. align[ 1 ][ 1 ] = -mat[ 0 ][ 2 ];
  1933. align[ 1 ][ 2 ] = mat[ 0 ][ 1 ];
  1934. align[ 2 ][ 0 ] = mat[ 1 ][ 0 ];
  1935. align[ 2 ][ 1 ] = -mat[ 1 ][ 2 ];
  1936. align[ 2 ][ 2 ] = mat[ 1 ][ 1 ];
  1937. if ( rotate ) {
  1938. align *= ang.ToMat3();
  1939. }
  1940. } else if ( rotate ) {
  1941. align = ang.ToMat3();
  1942. }
  1943. align.TransposeSelf();
  1944. }
  1945. /*
  1946. ===============
  1947. idMayaExport::GetObjectType
  1948. return the type of the object
  1949. ===============
  1950. */
  1951. const char *idMayaExport::GetObjectType( MObject object ) {
  1952. if( object.isNull() ) {
  1953. return "(Null)";
  1954. }
  1955. MStatus stat;
  1956. MFnDependencyNode dgNode;
  1957. MString typeName;
  1958. stat = dgNode.setObject( object );
  1959. typeName = dgNode.typeName( &stat );
  1960. if( MS::kSuccess != stat ) {
  1961. // can not get the type name of this object
  1962. return "(Unknown)";
  1963. }
  1964. return typeName.asChar();
  1965. }
  1966. /*
  1967. ===============
  1968. idMayaExport::GetCameraFov
  1969. ===============
  1970. */
  1971. float idMayaExport::GetCameraFov( idExportJoint *joint ) {
  1972. int childCount;
  1973. int j;
  1974. double horiz;
  1975. double focal;
  1976. MStatus status;
  1977. const char *n1, *n2;
  1978. MFnDagNode *dagnode;
  1979. float fov;
  1980. dagnode = joint->dagnode;
  1981. MObject cameraNode = dagnode->object();
  1982. childCount = dagnode->childCount();
  1983. fov = 90.0f;
  1984. for( j = 0; j < childCount; j++ ) {
  1985. MObject childNode = dagnode->child( j );
  1986. n1 = GetObjectType( cameraNode );
  1987. n2 = GetObjectType( childNode );
  1988. if ( ( !strcmp( "transform", n1 ) ) && ( !strcmp( "camera", n2 ) ) ) {
  1989. MFnCamera camera( childNode );
  1990. focal = camera.focalLength();
  1991. horiz = camera.horizontalFilmAperture();
  1992. fov = RAD2DEG( 2 * atan( ( horiz * 0.5 ) / ( focal / 25.4 ) ) );
  1993. break;
  1994. }
  1995. }
  1996. return fov;
  1997. }
  1998. /*
  1999. ===============
  2000. idMayaExport::GetCameraFrame
  2001. ===============
  2002. */
  2003. void idMayaExport::GetCameraFrame( idExportJoint *camera, idMat3 &align, cameraFrame_t *cam ) {
  2004. idMat3 mat;
  2005. idMat3 axis;
  2006. idVec3 pos;
  2007. // get the worldspace positions of the joint
  2008. GetWorldTransform( camera, pos, axis, 1.0f );
  2009. // convert to id coordinates
  2010. cam->t = ConvertToIdSpace( pos ) * align;
  2011. // correct the orientation for the game
  2012. axis = ConvertToIdSpace( axis ) * align;
  2013. mat[ 0 ] = -axis[ 2 ];
  2014. mat[ 1 ] = -axis[ 0 ];
  2015. mat[ 2 ] = axis[ 1 ];
  2016. cam->q = mat.ToQuat().ToCQuat();
  2017. // get it's fov
  2018. cam->fov = GetCameraFov( camera );
  2019. }
  2020. /*
  2021. ===============
  2022. idMayaExport::CreateCameraAnim
  2023. ===============
  2024. */
  2025. void idMayaExport::CreateCameraAnim( idMat3 &align ) {
  2026. float start, end;
  2027. MDagPath dagPath;
  2028. int frameNum;
  2029. short v;
  2030. MStatus status;
  2031. cameraFrame_t cam;
  2032. idExportJoint *refCam;
  2033. idExportJoint *camJoint;
  2034. idStr currentCam;
  2035. idStr newCam;
  2036. MPlug plug;
  2037. MFnEnumAttribute cameraAttribute;
  2038. start = TimeForFrame( options.startframe );
  2039. end = TimeForFrame( options.endframe );
  2040. #if 0
  2041. options.framerate = 60;
  2042. model.numFrames = ( int )( ( end - start ) * ( float )options.framerate ) + 1;
  2043. model.frameRate = options.framerate;
  2044. #else
  2045. model.numFrames = options.endframe + 1 - options.startframe;
  2046. model.frameRate = options.framerate;
  2047. #endif
  2048. common->Printf( "start frame = %d\n end frame = %d\n", options.startframe, options.endframe );
  2049. common->Printf( " start time = %f\n end time = %f\n total time = %f\n", start, end, end - start );
  2050. if ( start > end ) {
  2051. MayaError( "Start frame is greater than end frame." );
  2052. }
  2053. refCam = model.FindJoint( "refcam" );
  2054. if ( refCam == NULL ) {
  2055. currentCam = MAYA_DEFAULT_CAMERA;
  2056. } else {
  2057. MObject cameraNode = refCam->dagnode->object();
  2058. MFnDependencyNode cameraDG( cameraNode, &status );
  2059. if( MS::kSuccess != status ) {
  2060. MayaError( "Can't find 'refcam' dependency node." );
  2061. return;
  2062. }
  2063. MObject attr = cameraDG.attribute( MString( "Camera" ), &status );
  2064. if( MS::kSuccess != status ) {
  2065. MayaError( "Can't find 'Camera' attribute on 'refcam'." );
  2066. return;
  2067. }
  2068. plug = MPlug( cameraNode, attr );
  2069. status = cameraAttribute.setObject( attr );
  2070. if( MS::kSuccess != status ) {
  2071. MayaError( "Bad 'Camera' attribute on 'refcam'." );
  2072. return;
  2073. }
  2074. model.camera.Clear();
  2075. model.cameraCuts.Clear();
  2076. SetFrame( 0 );
  2077. status = plug.getValue( v );
  2078. currentCam = cameraAttribute.fieldName( v, &status ).asChar();
  2079. if( MS::kSuccess != status ) {
  2080. MayaError( "Error getting camera name on frame %d", GetMayaFrameNum( 0 ) );
  2081. }
  2082. }
  2083. camJoint = model.FindJoint( currentCam );
  2084. if ( !camJoint ) {
  2085. MayaError( "Couldn't find camera '%s'", currentCam.c_str() );
  2086. }
  2087. for( frameNum = 0; frameNum < model.numFrames; frameNum++ ) {
  2088. common->Printf( "\rFrame %d/%d...", options.startframe + frameNum, options.endframe );
  2089. #if 0
  2090. MTime time;
  2091. time.setUnit( MTime::kSeconds );
  2092. time.setValue( start + ( ( float )frameNum / ( float )options.framerate ) );
  2093. MGlobal::viewFrame( time );
  2094. #else
  2095. SetFrame( frameNum );
  2096. #endif
  2097. // get the position for this frame
  2098. GetCameraFrame( camJoint, align, &model.camera.Alloc() );
  2099. if ( refCam != NULL ) {
  2100. status = plug.getValue( v );
  2101. newCam = cameraAttribute.fieldName( v, &status ).asChar();
  2102. if( MS::kSuccess != status ) {
  2103. MayaError( "Error getting camera name on frame %d", GetMayaFrameNum( frameNum ) );
  2104. }
  2105. if ( newCam != currentCam ) {
  2106. // place a cut at our last frame
  2107. model.cameraCuts.Append( model.camera.Num() - 1 );
  2108. currentCam = newCam;
  2109. camJoint = model.FindJoint( currentCam );
  2110. if ( !camJoint ) {
  2111. MayaError( "Couldn't find camera '%s'", currentCam.c_str() );
  2112. }
  2113. // get the position for this frame
  2114. GetCameraFrame( camJoint, align, &model.camera.Alloc() );
  2115. }
  2116. }
  2117. }
  2118. common->Printf( "\n" );
  2119. }
  2120. /*
  2121. ===============
  2122. idMayaExport::GetDefaultPose
  2123. ===============
  2124. */
  2125. void idMayaExport::GetDefaultPose( idMat3 &align ) {
  2126. float start;
  2127. MDagPath dagPath;
  2128. idMat3 jointaxis;
  2129. idVec3 jointpos;
  2130. idExportJoint *joint, *parent;
  2131. idBounds bnds;
  2132. idBounds meshBounds;
  2133. idList<jointFrame_t> frame;
  2134. start = TimeForFrame( options.startframe );
  2135. common->Printf( "default pose frame = %d\n", options.startframe );
  2136. common->Printf( " default pose time = %f\n", start );
  2137. frame.SetNum( model.joints.Num() );
  2138. SetFrame( 0 );
  2139. // convert joints into local coordinates and save in channels
  2140. for( joint = model.exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  2141. if ( !joint->dagnode ) {
  2142. // custom origin joint
  2143. joint->idwm.Identity();
  2144. joint->idt.Zero();
  2145. frame[ joint->index ].t.Zero();
  2146. frame[ joint->index ].q.Set( 0.0f, 0.0f, 0.0f );
  2147. continue;
  2148. }
  2149. // get the worldspace positions of the joint
  2150. GetWorldTransform( joint, jointpos, jointaxis, options.scale );
  2151. // convert to id coordinates
  2152. jointaxis = ConvertToIdSpace( jointaxis ) * align;
  2153. jointpos = ConvertToIdSpace( jointpos ) * align;
  2154. // save worldspace position of joint for children
  2155. joint->idwm = jointaxis;
  2156. joint->idt = jointpos;
  2157. parent = joint->exportNode.GetParent();
  2158. if ( parent ) {
  2159. // convert to local coordinates
  2160. jointpos = ( jointpos - parent->idt ) * parent->idwm.Transpose();
  2161. jointaxis = jointaxis * parent->idwm.Transpose();
  2162. } else if ( joint->name == "origin" ) {
  2163. if ( options.clearOrigin ) {
  2164. jointpos.Zero();
  2165. }
  2166. if ( options.clearOriginAxis ) {
  2167. jointaxis.Identity();
  2168. }
  2169. }
  2170. frame[ joint->index ].t = jointpos;
  2171. frame[ joint->index ].q = jointaxis.ToQuat().ToCQuat();
  2172. }
  2173. // relocate origin to start at 0, 0, 0 for first frame
  2174. joint = model.FindJoint( "origin" );
  2175. if ( joint ) {
  2176. frame[ joint->index ].t.Zero();
  2177. }
  2178. // transform the hierarchy
  2179. for( joint = model.exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  2180. jointpos = frame[ joint->index ].t;
  2181. jointaxis = frame[ joint->index ].q.ToQuat().ToMat3();
  2182. parent = joint->exportNode.GetParent();
  2183. if ( parent ) {
  2184. joint->idwm = jointaxis * parent->idwm;
  2185. joint->idt = parent->idt + jointpos * parent->idwm;
  2186. } else {
  2187. joint->idwm = jointaxis;
  2188. joint->idt = jointpos;
  2189. }
  2190. joint->bindmat = joint->idwm;
  2191. joint->bindpos = joint->idt;
  2192. }
  2193. common->Printf( "\n" );
  2194. }
  2195. /*
  2196. ===============
  2197. idMayaExport::CreateAnimation
  2198. ===============
  2199. */
  2200. void idMayaExport::CreateAnimation( idMat3 &align ) {
  2201. int i;
  2202. float start, end;
  2203. MDagPath dagPath;
  2204. idMat3 jointaxis;
  2205. idVec3 jointpos;
  2206. int frameNum;
  2207. idExportJoint *joint, *parent;
  2208. idBounds bnds;
  2209. idBounds meshBounds;
  2210. jointFrame_t *frame;
  2211. int cycleStart;
  2212. idVec3 totalDelta;
  2213. idList<jointFrame_t> copyFrames;
  2214. start = TimeForFrame( options.startframe );
  2215. end = TimeForFrame( options.endframe );
  2216. model.numFrames = options.endframe + 1 - options.startframe;
  2217. model.frameRate = options.framerate;
  2218. common->Printf( "start frame = %d\n end frame = %d\n", options.startframe, options.endframe );
  2219. common->Printf( " start time = %f\n end time = %f\n total time = %f\n", start, end, end - start );
  2220. if ( start > end ) {
  2221. MayaError( "Start frame is greater than end frame." );
  2222. }
  2223. model.bounds.SetNum( model.numFrames );
  2224. model.jointFrames.SetNum( model.numFrames * model.joints.Num() );
  2225. model.frames.SetNum( model.numFrames );
  2226. for( i = 0; i < model.numFrames; i++ ) {
  2227. model.frames[ i ] = &model.jointFrames[ model.joints.Num() * i ];
  2228. }
  2229. // *sigh*. cyclestart doesn't work nicely with the anims.
  2230. // may just want to not do it in SetTime anymore.
  2231. cycleStart = options.cycleStart;
  2232. options.cycleStart = options.startframe;
  2233. for( frameNum = 0; frameNum < model.numFrames; frameNum++ ) {
  2234. common->Printf( "\rFrame %d/%d...", options.startframe + frameNum, options.endframe );
  2235. frame = model.frames[ frameNum ];
  2236. SetFrame( frameNum );
  2237. // convert joints into local coordinates and save in channels
  2238. for( joint = model.exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  2239. if ( !joint->dagnode ) {
  2240. // custom origin joint
  2241. joint->idwm.Identity();
  2242. joint->idt.Zero();
  2243. frame[ joint->index ].t.Zero();
  2244. frame[ joint->index ].q.Set( 0.0f, 0.0f, 0.0f );
  2245. continue;
  2246. }
  2247. // get the worldspace positions of the joint
  2248. GetWorldTransform( joint, jointpos, jointaxis, options.scale );
  2249. // convert to id coordinates
  2250. jointaxis = ConvertToIdSpace( jointaxis ) * align;
  2251. jointpos = ConvertToIdSpace( jointpos ) * align;
  2252. // save worldspace position of joint for children
  2253. joint->idwm = jointaxis;
  2254. joint->idt = jointpos;
  2255. parent = joint->exportNode.GetParent();
  2256. if ( parent ) {
  2257. // convert to local coordinates
  2258. jointpos = ( jointpos - parent->idt ) * parent->idwm.Transpose();
  2259. jointaxis = jointaxis * parent->idwm.Transpose();
  2260. } else if ( joint->name == "origin" ) {
  2261. if ( options.clearOrigin ) {
  2262. jointpos.Zero();
  2263. }
  2264. if ( options.clearOriginAxis ) {
  2265. jointaxis.Identity();
  2266. }
  2267. }
  2268. frame[ joint->index ].t = jointpos;
  2269. frame[ joint->index ].q = jointaxis.ToQuat().ToCQuat();
  2270. }
  2271. }
  2272. options.cycleStart = cycleStart;
  2273. totalDelta.Zero();
  2274. joint = model.FindJoint( "origin" );
  2275. if ( joint ) {
  2276. frame = model.frames[ 0 ];
  2277. idVec3 origin = frame[ joint->index ].t;
  2278. frame = model.frames[ model.numFrames - 1 ];
  2279. totalDelta = frame[ joint->index ].t - origin;
  2280. }
  2281. // shift the frames when cycleStart is used
  2282. if ( options.cycleStart > options.startframe ) {
  2283. copyFrames = model.jointFrames;
  2284. for( i = 0; i < model.numFrames; i++ ) {
  2285. bool shiftorigin = false;
  2286. frameNum = i + ( options.cycleStart - options.startframe );
  2287. if ( frameNum >= model.numFrames ) {
  2288. // wrap around, skipping the first frame, since it's a dupe of the last frame
  2289. frameNum -= model.numFrames - 1;
  2290. shiftorigin = true;
  2291. }
  2292. memcpy( &model.jointFrames[ model.joints.Num() * i ], &copyFrames[ model.joints.Num() * frameNum ], model.joints.Num() * sizeof( copyFrames[ 0 ] ) );
  2293. if ( joint && shiftorigin ) {
  2294. model.frames[ i ][ joint->index ].t += totalDelta;
  2295. }
  2296. }
  2297. }
  2298. if ( joint ) {
  2299. // relocate origin to start at 0, 0, 0 for first frame
  2300. frame = model.frames[ 0 ];
  2301. idVec3 origin = frame[ joint->index ].t;
  2302. for( i = 0; i < model.numFrames; i++ ) {
  2303. frame = model.frames[ i ];
  2304. frame[ joint->index ].t -= origin;
  2305. }
  2306. }
  2307. // get the bounds for each frame
  2308. for( frameNum = 0; frameNum < model.numFrames; frameNum++ ) {
  2309. frame = model.frames[ frameNum ];
  2310. // transform the hierarchy
  2311. for( joint = model.exportHead.GetNext(); joint != NULL; joint = joint->exportNode.GetNext() ) {
  2312. jointpos = frame[ joint->index ].t;
  2313. jointaxis = frame[ joint->index ].q.ToQuat().ToMat3();
  2314. parent = joint->exportNode.GetParent();
  2315. if ( parent ) {
  2316. joint->idwm = jointaxis * parent->idwm;
  2317. joint->idt = parent->idt + jointpos * parent->idwm;
  2318. } else {
  2319. joint->idwm = jointaxis;
  2320. joint->idt = jointpos;
  2321. }
  2322. }
  2323. // get bounds for this frame
  2324. bnds.Clear();
  2325. for( i = 0; i < model.meshes.Num(); i++ ) {
  2326. if ( model.meshes[ i ]->keep ) {
  2327. model.meshes[ i ]->GetBounds( meshBounds );
  2328. bnds.AddBounds( meshBounds );
  2329. }
  2330. }
  2331. model.bounds[ frameNum ][ 0 ] = bnds[ 0 ];
  2332. model.bounds[ frameNum ][ 1 ] = bnds[ 1 ];
  2333. }
  2334. common->Printf( "\n" );
  2335. }
  2336. /*
  2337. ===============
  2338. idMayaExport::ConvertModel
  2339. ===============
  2340. */
  2341. void idMayaExport::ConvertModel( void ) {
  2342. MStatus status;
  2343. idMat3 align;
  2344. common->Printf( "Converting %s to %s...\n", options.src.c_str(), options.dest.c_str() );
  2345. // see if the destination file exists
  2346. FILE *file = fopen( options.dest, "r" );
  2347. if ( file ) {
  2348. fclose( file );
  2349. // make sure we can write to the destination
  2350. FILE *file = fopen( options.dest, "r+" );
  2351. if ( !file ) {
  2352. MayaError( "Unable to write to the file '%s'", options.dest.c_str() );
  2353. }
  2354. fclose( file );
  2355. }
  2356. MString filename( options.src );
  2357. MFileIO::newFile( true );
  2358. // Load the file into Maya
  2359. common->Printf( "Loading file...\n" );
  2360. status = MFileIO::open( filename, NULL, true );
  2361. if ( !status ) {
  2362. MayaError( "Error loading '%s': '%s'\n", filename.asChar(), status.errorString().asChar() );
  2363. }
  2364. // force Maya to update the frame. When using references, sometimes
  2365. // the model is posed the way it is in the source. Since Maya only
  2366. // updates it when the frame time changes to a value other than the
  2367. // current, just setting the time to the min time doesn't guarantee
  2368. // that the model gets updated.
  2369. MGlobal::viewFrame( MAnimControl::maxTime() );
  2370. MGlobal::viewFrame( MAnimControl::minTime() );
  2371. if ( options.startframe < 0 ) {
  2372. options.startframe = MAnimControl::minTime().as( MTime::kFilm );
  2373. }
  2374. if ( options.endframe < 0 ) {
  2375. options.endframe = MAnimControl::maxTime().as( MTime::kFilm );
  2376. }
  2377. if ( options.cycleStart < 0 ) {
  2378. options.cycleStart = options.startframe;
  2379. } else if ( ( options.cycleStart < options.startframe ) || ( options.cycleStart > options.endframe ) ) {
  2380. MayaError( "cycleStart (%d) out of frame range (%d to %d)\n", options.cycleStart, options.startframe, options.endframe );
  2381. } else if ( options.cycleStart == options.endframe ) {
  2382. // end frame is a duplicate of the first frame in cycles, so just disable cycleStart
  2383. options.cycleStart = options.startframe;
  2384. }
  2385. // create a list of the transform nodes that will make up our heirarchy
  2386. common->Printf( "Creating joints...\n" );
  2387. CreateJoints( options.scale );
  2388. if ( options.type != WRITE_CAMERA ) {
  2389. common->Printf( "Creating meshes...\n" );
  2390. CreateMesh( options.scale );
  2391. common->Printf( "Renaming joints...\n" );
  2392. RenameJoints( options.renamejoints, options.prefix );
  2393. common->Printf( "Remapping parents...\n" );
  2394. RemapParents( options.remapjoints );
  2395. common->Printf( "Pruning joints...\n" );
  2396. PruneJoints( options.keepjoints, options.prefix );
  2397. common->Printf( "Combining meshes...\n" );
  2398. CombineMeshes();
  2399. }
  2400. common->Printf( "Align model...\n" );
  2401. GetAlignment( options.align, align, options.rotate, 0 );
  2402. switch( options.type ) {
  2403. case WRITE_MESH :
  2404. common->Printf( "Grabbing default pose:\n" );
  2405. GetDefaultPose( align );
  2406. common->Printf( "Writing file...\n" );
  2407. if ( !model.WriteMesh( options.dest, options ) ) {
  2408. MayaError( "error writing to '%s'", options.dest.c_str() );
  2409. }
  2410. break;
  2411. case WRITE_ANIM :
  2412. common->Printf( "Creating animation frames:\n" );
  2413. CreateAnimation( align );
  2414. common->Printf( "Writing file...\n" );
  2415. if ( !model.WriteAnim( options.dest, options ) ) {
  2416. MayaError( "error writing to '%s'", options.dest.c_str() );
  2417. }
  2418. break;
  2419. case WRITE_CAMERA :
  2420. common->Printf( "Creating camera frames:\n" );
  2421. CreateCameraAnim( align );
  2422. common->Printf( "Writing file...\n" );
  2423. if ( !model.WriteCamera( options.dest, options ) ) {
  2424. MayaError( "error writing to '%s'", options.dest.c_str() );
  2425. }
  2426. break;
  2427. }
  2428. common->Printf( "done\n\n" );
  2429. }
  2430. /*
  2431. ===============
  2432. idMayaExport::ConvertToMD3
  2433. ===============
  2434. */
  2435. void idMayaExport::ConvertToMD3( void ) {
  2436. #if 0
  2437. int i, j;
  2438. md3Header_t *pinmodel;
  2439. md3Frame_t *frame;
  2440. md3Surface_t *surf;
  2441. md3Shader_t *shader;
  2442. md3Triangle_t *tri;
  2443. md3St_t *st;
  2444. md3XyzNormal_t *xyz;
  2445. md3Tag_t *tag;
  2446. int version;
  2447. int size;
  2448. //model_t *mod, int lod, void *buffer, const char *mod_name
  2449. pinmodel = (md3Header_t *)buffer;
  2450. version = LittleLong (pinmodel->version);
  2451. if (version != MD3_VERSION) {
  2452. common->Printf( "R_LoadMD3: %s has wrong version (%i should be %i)\n",
  2453. mod_name, version, MD3_VERSION);
  2454. return qfalse;
  2455. }
  2456. mod->type = MOD_MESH;
  2457. size = LittleLong(pinmodel->ofsEnd);
  2458. mod->dataSize += size;
  2459. mod->md3[lod] = ri.Hunk_Alloc( size );
  2460. memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) );
  2461. LL(mod->md3[lod]->ident);
  2462. LL(mod->md3[lod]->version);
  2463. LL(mod->md3[lod]->numFrames);
  2464. LL(mod->md3[lod]->numTags);
  2465. LL(mod->md3[lod]->numSurfaces);
  2466. LL(mod->md3[lod]->ofsFrames);
  2467. LL(mod->md3[lod]->ofsTags);
  2468. LL(mod->md3[lod]->ofsSurfaces);
  2469. LL(mod->md3[lod]->ofsEnd);
  2470. if ( mod->md3[lod]->numFrames < 1 ) {
  2471. common->Printf( "R_LoadMD3: %s has no frames\n", mod_name );
  2472. return qfalse;
  2473. }
  2474. // swap all the frames
  2475. frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames );
  2476. for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) {
  2477. frame->radius = LittleFloat( frame->radius );
  2478. for ( j = 0 ; j < 3 ; j++ ) {
  2479. frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
  2480. frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
  2481. frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
  2482. }
  2483. }
  2484. // swap all the tags
  2485. tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags );
  2486. for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) {
  2487. for ( j = 0 ; j < 3 ; j++ ) {
  2488. tag->origin[j] = LittleFloat( tag->origin[j] );
  2489. tag->axis[0][j] = LittleFloat( tag->axis[0][j] );
  2490. tag->axis[1][j] = LittleFloat( tag->axis[1][j] );
  2491. tag->axis[2][j] = LittleFloat( tag->axis[2][j] );
  2492. }
  2493. }
  2494. // swap all the surfaces
  2495. surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces );
  2496. for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) {
  2497. LL(surf->ident);
  2498. LL(surf->flags);
  2499. LL(surf->numFrames);
  2500. LL(surf->numShaders);
  2501. LL(surf->numTriangles);
  2502. LL(surf->ofsTriangles);
  2503. LL(surf->numVerts);
  2504. LL(surf->ofsShaders);
  2505. LL(surf->ofsSt);
  2506. LL(surf->ofsXyzNormals);
  2507. LL(surf->ofsEnd);
  2508. if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
  2509. ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)",
  2510. mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
  2511. }
  2512. if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
  2513. ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)",
  2514. mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
  2515. }
  2516. // change to surface identifier
  2517. surf->ident = SF_MD3;
  2518. // lowercase the surface name so skin compares are faster
  2519. Q_strlwr( surf->name );
  2520. // strip off a trailing _1 or _2
  2521. // this is a crutch for q3data being a mess
  2522. j = strlen( surf->name );
  2523. if ( j > 2 && surf->name[j-2] == '_' ) {
  2524. surf->name[j-2] = 0;
  2525. }
  2526. // register the shaders
  2527. shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
  2528. for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) {
  2529. shader_t *sh;
  2530. sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue );
  2531. if ( sh->defaultShader ) {
  2532. shader->shaderIndex = 0;
  2533. } else {
  2534. shader->shaderIndex = sh->index;
  2535. }
  2536. }
  2537. // swap all the triangles
  2538. tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
  2539. for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
  2540. LL(tri->indexes[0]);
  2541. LL(tri->indexes[1]);
  2542. LL(tri->indexes[2]);
  2543. }
  2544. // swap all the ST
  2545. st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
  2546. for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
  2547. st->st[0] = LittleFloat( st->st[0] );
  2548. st->st[1] = LittleFloat( st->st[1] );
  2549. }
  2550. // swap all the XyzNormals
  2551. xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
  2552. for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
  2553. {
  2554. xyz->xyz[0] = LittleShort( xyz->xyz[0] );
  2555. xyz->xyz[1] = LittleShort( xyz->xyz[1] );
  2556. xyz->xyz[2] = LittleShort( xyz->xyz[2] );
  2557. xyz->normal = LittleShort( xyz->normal );
  2558. }
  2559. // find the next surface
  2560. surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
  2561. }
  2562. return true;
  2563. #endif
  2564. }
  2565. /*
  2566. ==============================================================================
  2567. dll setup
  2568. ==============================================================================
  2569. */
  2570. /*
  2571. ===============
  2572. Maya_Shutdown
  2573. ===============
  2574. */
  2575. void Maya_Shutdown( void ) {
  2576. if ( initialized ) {
  2577. errorMessage.Clear();
  2578. initialized = false;
  2579. // This shuts down the entire app somehow, so just ignore it.
  2580. //MLibrary::cleanup();
  2581. }
  2582. }
  2583. /*
  2584. ===============
  2585. Maya_ConvertModel
  2586. ===============
  2587. */
  2588. const char *Maya_ConvertModel( const char *ospath, const char *commandline ) {
  2589. errorMessage = "Ok";
  2590. try {
  2591. idExportOptions options( commandline, ospath );
  2592. idMayaExport exportM( options );
  2593. exportM.ConvertModel();
  2594. }
  2595. catch( idException &exception ) {
  2596. errorMessage = exception.error;
  2597. }
  2598. return errorMessage;
  2599. }
  2600. /*
  2601. ===============
  2602. dllEntry
  2603. ===============
  2604. */
  2605. bool dllEntry( int version, idCommon *common, idSys *sys ) {
  2606. if ( !common || !sys ) {
  2607. return false;
  2608. }
  2609. ::common = common;
  2610. ::sys = sys;
  2611. ::cvarSystem = NULL;
  2612. idLib::sys = sys;
  2613. idLib::common = common;
  2614. idLib::cvarSystem = NULL;
  2615. idLib::fileSystem = NULL;
  2616. idLib::Init();
  2617. if ( version != MD5_VERSION ) {
  2618. common->Printf( "Error initializing Maya exporter: DLL version %d different from .exe version %d\n", MD5_VERSION, version );
  2619. return false;
  2620. }
  2621. if ( !initialized ) {
  2622. MStatus status;
  2623. status = MLibrary::initialize( GAME_NAME, true );
  2624. if ( !status ) {
  2625. common->Printf( "Error calling MLibrary::initialize (%s)\n", status.errorString().asChar() );
  2626. return false;
  2627. }
  2628. initialized = true;
  2629. }
  2630. return true;
  2631. }
  2632. // Force type checking on the interface functions to help ensure that they match the ones in the .exe
  2633. const exporterDLLEntry_t ValidateEntry = &dllEntry;
  2634. const exporterInterface_t ValidateConvert = &Maya_ConvertModel;
  2635. const exporterShutdown_t ValidateShutdown = &Maya_Shutdown;