W3DModelDraw.cpp 136 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: W3DModelDraw.cpp ///////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, November 2001
  25. // Desc: Default w3d draw module
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #define DEFINE_W3DANIMMODE_NAMES
  29. #define DEFINE_WEAPONSLOTTYPE_NAMES
  30. #define NO_DEBUG_CRC
  31. #include "Common/CRC.h"
  32. #include "Common/CRCDebug.h"
  33. #include "Common/GameState.h"
  34. #include "Common/GlobalData.h"
  35. #include "Common/PerfTimer.h"
  36. #include "Common/RandomValue.h"
  37. #include "Common/ThingTemplate.h"
  38. #include "Common/GameLOD.h"
  39. #include "Common/Xfer.h"
  40. #include "Common/GameState.h"
  41. #include "Common/QuickTrig.h"
  42. #include "GameClient/Drawable.h"
  43. #include "GameClient/FXList.h"
  44. #include "GameClient/Shadow.h"
  45. #include "GameLogic/GameLogic.h" // for real-time frame
  46. #include "GameLogic/Object.h"
  47. #include "GameLogic/WeaponSet.h"
  48. #include "GameLogic/FPUControl.h"
  49. #include "GameLogic/Module/AIUpdate.h"
  50. #include "GameLogic/Module/PhysicsUpdate.h"
  51. #include "W3DDevice/GameClient/Module/W3DModelDraw.h"
  52. #include "W3DDevice/GameClient/W3DAssetManager.h"
  53. #include "W3DDevice/GameClient/W3DDisplay.h"
  54. #include "W3DDevice/GameClient/W3DScene.h"
  55. #include "W3DDevice/GameClient/W3DShadow.h"
  56. #include "W3DDevice/GameClient/W3DTerrainTracks.h"
  57. #include "W3DDevice/GameClient/WorldHeightMap.h"
  58. #include "WW3D2/HAnim.h"
  59. #include "WW3D2/HLod.h"
  60. #include "WW3D2/RendObj.h"
  61. #include "WW3D2/Mesh.h"
  62. #include "WW3D2/MeshMdl.h"
  63. #include "Common/BitFlagsIO.h"
  64. #ifdef _INTERNAL
  65. // for occasional debugging...
  66. //#pragma optimize("", off)
  67. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  68. #endif
  69. //-------------------------------------------------------------------------------------------------
  70. static inline Bool isValidTimeToCalcLogicStuff()
  71. {
  72. return (TheGameLogic && TheGameLogic->isInGameLogicUpdate()) ||
  73. (TheGameState && TheGameState->isInLoadGame());
  74. }
  75. //-------------------------------------------------------------------------------------------------
  76. //-------------------------------------------------------------------------------------------------
  77. //-------------------------------------------------------------------------------------------------
  78. #if defined(DEBUG_CRC) && (defined(_DEBUG) || defined(_INTERNAL))
  79. #include <cstdarg>
  80. class LogClass
  81. {
  82. public:
  83. LogClass(const char *fname);
  84. ~LogClass();
  85. void log(const char *fmt, ...);
  86. void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line);
  87. void dumpReal(Real r, AsciiString name, AsciiString fname, Int line);
  88. protected:
  89. FILE *m_fp;
  90. };
  91. LogClass::LogClass(const char *fname)
  92. {
  93. char buffer[ _MAX_PATH ];
  94. GetModuleFileName( NULL, buffer, sizeof( buffer ) );
  95. char *pEnd = buffer + strlen( buffer );
  96. while( pEnd != buffer )
  97. {
  98. if( *pEnd == '\\' )
  99. {
  100. *pEnd = 0;
  101. break;
  102. }
  103. pEnd--;
  104. }
  105. AsciiString fullPath;
  106. fullPath.format("%s\\%s", buffer, fname);
  107. m_fp = fopen(fullPath.str(), "wt");
  108. }
  109. LogClass::~LogClass()
  110. {
  111. if (m_fp)
  112. {
  113. fclose(m_fp);
  114. }
  115. }
  116. void LogClass::log(const char *fmt, ...)
  117. {
  118. DEBUG_ASSERTCRASH(isValidTimeToCalcLogicStuff(), ("Calc'ing logic bone pos in client!!!"));
  119. if (!m_fp /*|| !isValidTimeToCalcLogicStuff()*/)
  120. return;
  121. static char buf[1024];
  122. static Int lastFrame = 0;
  123. static Int lastIndex = 0;
  124. if (lastFrame != TheGameLogic->getFrame())
  125. {
  126. lastFrame = TheGameLogic->getFrame();
  127. lastIndex = 0;
  128. }
  129. va_list va;
  130. va_start( va, fmt );
  131. _vsnprintf(buf, 1024, fmt, va );
  132. buf[1023] = 0;
  133. va_end( va );
  134. char *tmp = buf;
  135. while (tmp && *tmp)
  136. {
  137. if (*tmp == '\r' || *tmp == '\n')
  138. {
  139. *tmp = ' ';
  140. }
  141. ++tmp;
  142. }
  143. fprintf(m_fp, "%d:%d %s\n", lastFrame, lastIndex++, buf);
  144. fflush(m_fp);
  145. }
  146. void LogClass::dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line)
  147. {
  148. fname.toLower();
  149. fname = fname.reverseFind('\\') + 1;
  150. const Real *matrix = (const Real *)m;
  151. log("dumpMatrix3D() %s:%d %s\n",
  152. fname.str(), line, name.str());
  153. for (Int i=0; i<3; ++i)
  154. log(" 0x%08X 0x%08X 0x%08X 0x%08X\n",
  155. AS_INT(matrix[(i<<2)+0]), AS_INT(matrix[(i<<2)+1]), AS_INT(matrix[(i<<2)+2]), AS_INT(matrix[(i<<2)+3]));
  156. }
  157. void LogClass::dumpReal(Real r, AsciiString name, AsciiString fname, Int line)
  158. {
  159. if (!m_fp || !isValidTimeToCalcLogicStuff())
  160. return;
  161. fname.toLower();
  162. fname = fname.reverseFind('\\') + 1;
  163. log("dumpReal() %s:%d %s %8.8X (%f)\n",
  164. fname.str(), line, name.str(), AS_INT(r), r);
  165. }
  166. LogClass BonePosLog("bonePositions.txt");
  167. #define BONEPOS_LOG(x) BonePosLog.log x
  168. #define BONEPOS_DUMPMATRIX3D(x) BONEPOS_DUMPMATRIX3DNAMED(x, #x)
  169. #define BONEPOS_DUMPMATRIX3DNAMED(x, y) BonePosLog.dumpMatrix3D(x, y, __FILE__, __LINE__)
  170. #define BONEPOS_DUMPREAL(x) BONEPOS_DUMPREALNAMED(x, #x)
  171. #define BONEPOS_DUMPREALNAMED(x, y) BonePosLog.dumpReal(x, y, __FILE__, __LINE__)
  172. #else // DEBUG_CRC
  173. #define BONEPOS_LOG(x) {}
  174. #define BONEPOS_DUMPMATRIX3D(x) {}
  175. #define BONEPOS_DUMPMATRIX3DNAMED(x, y) {}
  176. #define BONEPOS_DUMPREAL(x) {}
  177. #define BONEPOS_DUMPREALNAMED(x, y) {}
  178. #endif // DEBUG_CRC
  179. //-------------------------------------------------------------------------------------------------
  180. //-------------------------------------------------------------------------------------------------
  181. //-------------------------------------------------------------------------------------------------
  182. #if defined(_DEBUG) || defined(_INTERNAL)
  183. extern AsciiString TheThingTemplateBeingParsedName;
  184. extern Real TheSkateDistOverride;
  185. #endif
  186. // flags that aren't read directly from INI, but set in response to various other situations
  187. enum INIReadFlagsType
  188. {
  189. ANIMS_COPIED_FROM_DEFAULT_STATE = 0,
  190. GOT_NONIDLE_ANIMS,
  191. GOT_IDLE_ANIMS,
  192. };
  193. enum ACBits
  194. {
  195. RANDOMIZE_START_FRAME = 0,
  196. START_FRAME_FIRST,
  197. START_FRAME_LAST,
  198. ADJUST_HEIGHT_BY_CONSTRUCTION_PERCENT,
  199. PRISTINE_BONE_POS_IN_FINAL_FRAME,
  200. MAINTAIN_FRAME_ACROSS_STATES,
  201. RESTART_ANIM_WHEN_COMPLETE,
  202. MAINTAIN_FRAME_ACROSS_STATES2,
  203. MAINTAIN_FRAME_ACROSS_STATES3,
  204. MAINTAIN_FRAME_ACROSS_STATES4,
  205. };
  206. static const char *ACBitsNames[] =
  207. {
  208. "RANDOMSTART",
  209. "START_FRAME_FIRST",
  210. "START_FRAME_LAST",
  211. "ADJUST_HEIGHT_BY_CONSTRUCTION_PERCENT",
  212. "PRISTINE_BONE_POS_IN_FINAL_FRAME",
  213. "MAINTAIN_FRAME_ACROSS_STATES",
  214. "RESTART_ANIM_WHEN_COMPLETE",
  215. "MAINTAIN_FRAME_ACROSS_STATES2",
  216. "MAINTAIN_FRAME_ACROSS_STATES3",
  217. "MAINTAIN_FRAME_ACROSS_STATES4",
  218. NULL
  219. };
  220. static const Int ALL_MAINTAIN_FRAME_FLAGS =
  221. (1<<MAINTAIN_FRAME_ACROSS_STATES) |
  222. (1<<MAINTAIN_FRAME_ACROSS_STATES2) |
  223. (1<<MAINTAIN_FRAME_ACROSS_STATES3) |
  224. (1<<MAINTAIN_FRAME_ACROSS_STATES4);
  225. inline Bool isAnyMaintainFrameFlagSet(Int flags)
  226. {
  227. return (flags & ALL_MAINTAIN_FRAME_FLAGS) != 0;
  228. }
  229. inline Bool isCommonMaintainFrameFlagSet(Int a, Int b)
  230. {
  231. a &= ALL_MAINTAIN_FRAME_FLAGS;
  232. b &= ALL_MAINTAIN_FRAME_FLAGS;
  233. return (a & b) != 0;
  234. }
  235. /** @todo: Move this to some kind of INI file*/
  236. //
  237. // Note: these values are saved in save files, so you MUST NOT REMOVE OR CHANGE
  238. // existing values!
  239. //
  240. static char *TerrainDecalTextureName[TERRAIN_DECAL_MAX]=
  241. {
  242. #ifdef ALLOW_DEMORALIZE
  243. "DM_RING",//demoralized
  244. #else
  245. "TERRAIN_DECAL_DEMORALIZED_OBSOLETE",
  246. #endif
  247. "EXHorde",//enthusiastic
  248. "EXHorde_UP", //enthusiastic with nationalism
  249. "EXHordeB",//enthusiastic vehicle
  250. "EXHordeB_UP", //enthusiastic vehicle with nationalism
  251. "EXJunkCrate",//Marks a crate as special
  252. "EXHordeC_UP", //enthusiastic with fanaticism
  253. "EXChemSuit", //Marks a unit as having chemical suit on
  254. "", //dummy entry for TERRAIN_DECAL_NONE
  255. "" //dummy entry for TERRAIN_DECAL_SHADOW_TEXTURE
  256. };
  257. const UnsignedInt NO_NEXT_DURATION = 0xffffffff;
  258. //-------------------------------------------------------------------------------------------------
  259. W3DAnimationInfo::W3DAnimationInfo(const AsciiString& name, Bool isIdle, Real distanceCovered) :
  260. #ifdef RETAIN_ANIM_HANDLES
  261. m_handle(NULL),
  262. m_naturalDurationInMsec(0),
  263. #else
  264. m_naturalDurationInMsec(-1),
  265. #endif
  266. m_name(name),
  267. m_isIdleAnim(isIdle),
  268. m_distanceCovered(distanceCovered)
  269. {
  270. }
  271. //-------------------------------------------------------------------------------------------------
  272. W3DAnimationInfo::W3DAnimationInfo( const W3DAnimationInfo &r ) :
  273. m_name(r.m_name),
  274. #ifdef RETAIN_ANIM_HANDLES
  275. m_handle(r.m_handle),
  276. #endif
  277. m_distanceCovered(r.m_distanceCovered),
  278. m_isIdleAnim(r.m_isIdleAnim),
  279. m_naturalDurationInMsec(r.m_naturalDurationInMsec)
  280. {
  281. #ifdef RETAIN_ANIM_HANDLES
  282. if (m_handle)
  283. m_handle->Add_Ref();
  284. #endif
  285. }
  286. //-------------------------------------------------------------------------------------------------
  287. W3DAnimationInfo& W3DAnimationInfo::operator=(const W3DAnimationInfo &r)
  288. {
  289. m_name = r.m_name;
  290. m_distanceCovered = r.m_distanceCovered;
  291. m_naturalDurationInMsec = r.m_naturalDurationInMsec;
  292. m_isIdleAnim = r.m_isIdleAnim;
  293. #ifdef RETAIN_ANIM_HANDLES
  294. REF_PTR_RELEASE(m_handle);
  295. m_handle = r.m_handle;
  296. if (m_handle)
  297. m_handle->Add_Ref();
  298. #endif
  299. return (*this);
  300. }
  301. //-------------------------------------------------------------------------------------------------
  302. // note that this now returns an ADDREFED handle, which must be released by the caller!
  303. HAnimClass* W3DAnimationInfo::getAnimHandle() const
  304. {
  305. #ifdef RETAIN_ANIM_HANDLES
  306. if (m_handle == NULL)
  307. {
  308. // Get_HAnim addrefs it, so we'll have to release it in our dtor.
  309. m_handle = W3DDisplay::m_assetManager->Get_HAnim(m_name.str());
  310. DEBUG_ASSERTCRASH(m_handle, ("*** ASSET ERROR: animation %s not found\n",m_name.str()));
  311. if (m_handle)
  312. {
  313. m_naturalDurationInMsec = m_handle->Get_Num_Frames() * 1000.0f / m_handle->Get_Frame_Rate();
  314. }
  315. }
  316. // since we have it locally, must addref.
  317. if (m_handle)
  318. m_handle->Add_Ref();
  319. return m_handle;
  320. #else
  321. HAnimClass* handle = W3DDisplay::m_assetManager->Get_HAnim(m_name.str());
  322. DEBUG_ASSERTCRASH(handle, ("*** ASSET ERROR: animation %s not found\n",m_name.str()));
  323. if (handle != NULL && m_naturalDurationInMsec < 0)
  324. {
  325. m_naturalDurationInMsec = handle->Get_Num_Frames() * 1000.0f / handle->Get_Frame_Rate();
  326. }
  327. // since Get_HAnim() returns an addrefed handle, we must NOT addref here.
  328. //if (handle)
  329. // handle->Add_Ref();
  330. return handle;
  331. #endif
  332. }
  333. //-------------------------------------------------------------------------------------------------
  334. W3DAnimationInfo::~W3DAnimationInfo()
  335. {
  336. #ifdef RETAIN_ANIM_HANDLES
  337. REF_PTR_RELEASE(m_handle);
  338. m_handle = NULL;
  339. #endif
  340. }
  341. //-------------------------------------------------------------------------------------------------
  342. void ModelConditionInfo::preloadAssets( TimeOfDay timeOfDay, Real scale )
  343. {
  344. // load this asset
  345. if( m_modelName.isEmpty() == FALSE )
  346. {
  347. TheDisplay->preloadModelAssets( m_modelName );
  348. }
  349. // this can be called from the client, which is problematic
  350. // validateStuff(NULL, getDrawable()->getScale());
  351. //validateCachedBones(NULL, scale);
  352. //validateTurretInfo();
  353. //validateWeaponBarrelInfo();
  354. }
  355. //-------------------------------------------------------------------------------------------------
  356. void ModelConditionInfo::addPublicBone(const AsciiString& boneName) const
  357. {
  358. if (boneName.isEmpty() || boneName.isNone())
  359. return;
  360. AsciiString tmp = boneName;
  361. tmp.toLower();
  362. if (std::find(m_publicBones.begin(), m_publicBones.end(), tmp) == m_publicBones.end())
  363. {
  364. m_publicBones.push_back(tmp);
  365. }
  366. }
  367. //-------------------------------------------------------------------------------------------------
  368. Bool ModelConditionInfo::matchesMode(Bool night, Bool snowy) const
  369. {
  370. for (std::vector<ModelConditionFlags>::const_iterator it = m_conditionsYesVec.begin();
  371. it != m_conditionsYesVec.end();
  372. ++it)
  373. {
  374. if (it->test(MODELCONDITION_NIGHT) == (night) &&
  375. it->test(MODELCONDITION_SNOW) == (snowy))
  376. {
  377. return true;
  378. }
  379. }
  380. return false;
  381. }
  382. //-------------------------------------------------------------------------------------------------
  383. inline Bool testFlagBit(Int flags, Int bit)
  384. {
  385. return (flags & (1<<bit)) != 0;
  386. }
  387. //-------------------------------------------------------------------------------------------------
  388. static Bool findSingleBone(RenderObjClass* robj, const AsciiString& boneName, Matrix3D& mtx, Int& boneIndex)
  389. {
  390. if (boneName.isNone() || boneName.isEmpty())
  391. return false;
  392. boneIndex = robj->Get_Bone_Index(boneName.str());
  393. if (boneIndex != 0)
  394. {
  395. mtx = robj->Get_Bone_Transform(boneIndex);
  396. return true;
  397. }
  398. else
  399. {
  400. return false;
  401. }
  402. }
  403. //-------------------------------------------------------------------------------------------------
  404. static Bool findSingleSubObj(RenderObjClass* robj, const AsciiString& boneName, Matrix3D& mtx, Int& boneIndex)
  405. {
  406. if (boneName.isNone() || boneName.isEmpty())
  407. return false;
  408. RenderObjClass* childObject = robj->Get_Sub_Object_By_Name(boneName.str());
  409. if (childObject)
  410. {
  411. mtx = childObject->Get_Transform();
  412. // you'd think this would work, but, it does not. do it the hard way.
  413. // boneIndex = childObject->Get_Sub_Object_Bone_Index(childObject);
  414. for (Int subObj = 0; subObj < robj->Get_Num_Sub_Objects(); subObj++)
  415. {
  416. RenderObjClass* test = robj->Get_Sub_Object(subObj);
  417. if (test == childObject)
  418. {
  419. boneIndex = robj->Get_Sub_Object_Bone_Index(0, subObj);
  420. #if defined(_DEBUG) || defined(_INTERNAL)
  421. test->Release_Ref();
  422. test = robj->Get_Sub_Object_On_Bone(0, boneIndex);
  423. DEBUG_ASSERTCRASH(test != NULL && test == childObject, ("*** ASSET ERROR: Hmm, bone problem"));
  424. #endif
  425. }
  426. if (test) test->Release_Ref();
  427. }
  428. childObject->Release_Ref();
  429. return true;
  430. }
  431. else
  432. {
  433. return false;
  434. }
  435. }
  436. //-------------------------------------------------------------------------------------------------
  437. static Bool doSingleBoneName(RenderObjClass* robj, const AsciiString& boneName, PristineBoneInfoMap& map)
  438. {
  439. Bool foundAsBone = false;
  440. Bool foundAsSubObj = false;
  441. PristineBoneInfo info;
  442. AsciiString tmp;
  443. AsciiString boneNameTmp = boneName;
  444. boneNameTmp.toLower(); // convert to all-lowercase to avoid case sens issues later
  445. setFPMode();
  446. if (findSingleBone(robj, boneNameTmp, info.mtx, info.boneIndex))
  447. {
  448. //DEBUG_LOG(("added bone %s\n",boneNameTmp.str()));
  449. BONEPOS_LOG(("Caching bone %s (index %d)\n", boneNameTmp.str(), info.boneIndex));
  450. BONEPOS_DUMPMATRIX3D(&(info.mtx));
  451. map[NAMEKEY(boneNameTmp)] = info;
  452. foundAsBone = true;
  453. }
  454. for (Int i = 1; i <= 99; ++i)
  455. {
  456. tmp.format("%s%02d", boneNameTmp.str(), i);
  457. if (findSingleBone(robj, tmp, info.mtx, info.boneIndex))
  458. {
  459. //DEBUG_LOG(("added bone %s\n",tmp.str()));
  460. BONEPOS_LOG(("Caching bone %s (index %d)\n", tmp.str(), info.boneIndex));
  461. BONEPOS_DUMPMATRIX3D(&(info.mtx));
  462. map[NAMEKEY(tmp)] = info;
  463. foundAsBone = true;
  464. }
  465. else
  466. {
  467. break;
  468. }
  469. }
  470. if (!foundAsBone)
  471. {
  472. if (findSingleSubObj(robj, boneNameTmp, info.mtx, info.boneIndex))
  473. {
  474. //DEBUG_LOG(("added subobj %s\n",boneNameTmp.str()));
  475. BONEPOS_LOG(("Caching bone from subobject %s (index %d)\n", boneNameTmp.str(), info.boneIndex));
  476. BONEPOS_DUMPMATRIX3D(&(info.mtx));
  477. map[NAMEKEY(boneNameTmp)] = info;
  478. foundAsSubObj = true;
  479. }
  480. for (Int i = 1; i <= 99; ++i)
  481. {
  482. tmp.format("%s%02d", boneNameTmp.str(), i);
  483. if (findSingleSubObj(robj, tmp, info.mtx, info.boneIndex))
  484. {
  485. //DEBUG_LOG(("added subobj %s\n",tmp.str()));
  486. BONEPOS_LOG(("Caching bone from subobject %s (index %d)\n", tmp.str(), info.boneIndex));
  487. BONEPOS_DUMPMATRIX3D(&(info.mtx));
  488. map[NAMEKEY(tmp)] = info;
  489. foundAsSubObj = true;
  490. }
  491. else
  492. {
  493. break;
  494. }
  495. }
  496. }
  497. return foundAsBone || foundAsSubObj;
  498. }
  499. //-------------------------------------------------------------------------------------------------
  500. void ModelConditionInfo::validateStuff(RenderObjClass* robj, Real scale, const std::vector<AsciiString>& extraPublicBones) const
  501. {
  502. // srj sez: hm, this doesn't make sense; I think we really do need to validate transition states.
  503. // if (m_transition != NO_TRANSITION)
  504. // return;
  505. loadAnimations();
  506. if (!(m_validStuff & PUBLIC_BONES_VALID) && isValidTimeToCalcLogicStuff())
  507. {
  508. for (std::vector<AsciiString>::const_iterator bone_it = extraPublicBones.begin(); bone_it != extraPublicBones.end(); ++bone_it)
  509. {
  510. addPublicBone(*bone_it);
  511. }
  512. m_validStuff |= PUBLIC_BONES_VALID;
  513. }
  514. validateCachedBones(robj, scale);
  515. validateTurretInfo();
  516. validateWeaponBarrelInfo();
  517. }
  518. //-------------------------------------------------------------------------------------------------
  519. void ModelConditionInfo::validateCachedBones(RenderObjClass* robj, Real scale) const
  520. {
  521. //DEBUG_ASSERTCRASH(isValidTimeToCalcLogicStuff(), ("calling validateCachedBones() from in GameClient!"));
  522. if (m_validStuff & PRISTINE_BONES_VALID)
  523. return;
  524. if (!isValidTimeToCalcLogicStuff())
  525. {
  526. m_pristineBones.clear();
  527. m_validStuff &= ~PRISTINE_BONES_VALID;
  528. return;
  529. }
  530. setFPMode();
  531. BONEPOS_LOG(("Validating bones for %s: %s", m_modelName.str(), getDescription().str()));
  532. //BONEPOS_LOG(("Passing in valid render obj: %d\n", (robj != 0)));
  533. BONEPOS_DUMPREAL(scale);
  534. m_pristineBones.clear();
  535. // go ahead and set this here, in case we return early.
  536. m_validStuff |= PRISTINE_BONES_VALID;
  537. Bool tossRobj = false;
  538. if (robj == NULL)
  539. {
  540. if (m_modelName.isEmpty())
  541. {
  542. //BONEPOS_LOG(("Bailing: model name is empty\n"));
  543. return;
  544. }
  545. robj = W3DDisplay::m_assetManager->Create_Render_Obj(m_modelName.str(), scale, 0);
  546. DEBUG_ASSERTCRASH(robj, ("*** ASSET ERROR: Model %s not found!\n",m_modelName.str()));
  547. if (!robj)
  548. {
  549. //BONEPOS_LOG(("Bailing: could not load render object\n"));
  550. return;
  551. }
  552. tossRobj = true;
  553. }
  554. Matrix3D originalTransform = robj->Get_Transform(); // save the transform
  555. HLodClass* hlod = NULL;
  556. HAnimClass* curAnim = NULL;
  557. int numFrames = 0;
  558. float frame = 0.0f;
  559. int mode = 0;
  560. float mult = 1.0f;
  561. if (robj->Class_ID() == RenderObjClass::CLASSID_HLOD)
  562. {
  563. hlod = (HLodClass*)robj;
  564. curAnim = hlod->Peek_Animation_And_Info(frame, numFrames, mode, mult);
  565. }
  566. // if we have any animations in this state, always choose the first, since the animations
  567. // vary on a per-client basis.
  568. HAnimClass* animToUse;
  569. if (m_animations.size() > 0)
  570. {
  571. animToUse = m_animations.front().getAnimHandle(); // return an AddRef'ed handle
  572. }
  573. else
  574. {
  575. animToUse = curAnim; // Peek_Animation_And_Info does not addref, so we must do so here
  576. if (animToUse)
  577. animToUse->Add_Ref();
  578. }
  579. if (animToUse != NULL)
  580. {
  581. // make sure we're in frame zero.
  582. Int whichFrame = testFlagBit(m_flags, PRISTINE_BONE_POS_IN_FINAL_FRAME) ? animToUse->Get_Num_Frames()-1 : 0;
  583. robj->Set_Animation(animToUse, whichFrame, RenderObjClass::ANIM_MODE_MANUAL);
  584. // must balance the addref, above
  585. REF_PTR_RELEASE(animToUse);
  586. animToUse = NULL;
  587. }
  588. Matrix3D tmp(true);
  589. tmp.Scale(scale);
  590. robj->Set_Transform(tmp); // set to identity transform
  591. if (TheGlobalData)
  592. {
  593. for (std::vector<AsciiString>::const_iterator it = TheGlobalData->m_standardPublicBones.begin(); it != TheGlobalData->m_standardPublicBones.end(); ++it)
  594. {
  595. if (!doSingleBoneName(robj, *it, m_pristineBones))
  596. {
  597. // don't crash here, since these are catch-all global bones and won't be present in most models.
  598. //DEBUG_CRASH(("public bone %s (and variations thereof) not found in model %s!\n",it->str(),m_modelName.str()));
  599. }
  600. //else
  601. //{
  602. // DEBUG_LOG(("global bone %s (or variations thereof) found in model %s\n",it->str(),m_modelName.str()));
  603. //}
  604. }
  605. }
  606. for (std::vector<AsciiString>::const_iterator it = m_publicBones.begin(); it != m_publicBones.end(); ++it)
  607. {
  608. if (!doSingleBoneName(robj, *it, m_pristineBones))
  609. {
  610. // DO crash here, since we specifically requested this bone for this model
  611. DEBUG_CRASH(("*** ASSET ERROR: public bone '%s' (and variations thereof) not found in model %s!\n",it->str(),m_modelName.str()));
  612. }
  613. //else
  614. //{
  615. // DEBUG_LOG(("extra bone %s (or variations thereof) found in model %s\n",it->str(),m_modelName.str()));
  616. //}
  617. }
  618. robj->Set_Transform(originalTransform); // restore previous transform
  619. if (curAnim != NULL)
  620. {
  621. robj->Set_Animation(curAnim, frame, mode);
  622. hlod->Set_Animation_Frame_Rate_Multiplier(mult);
  623. }
  624. if (tossRobj)
  625. {
  626. REF_PTR_RELEASE(robj);
  627. }
  628. }
  629. //-------------------------------------------------------------------------------------------------
  630. void ModelConditionInfo::validateWeaponBarrelInfo() const
  631. {
  632. //DEBUG_ASSERTCRASH(isValidTimeToCalcLogicStuff(), ("calling validateWeaponBarrelInfo() from in GameClient!"));
  633. if (m_validStuff & BARRELS_VALID)
  634. return;
  635. if (!isValidTimeToCalcLogicStuff())
  636. {
  637. return;
  638. }
  639. setFPMode();
  640. for (int wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  641. {
  642. m_weaponBarrelInfoVec[wslot].clear();
  643. m_hasRecoilBonesOrMuzzleFlashes[wslot] = false;
  644. const AsciiString& fxBoneName = m_weaponFireFXBoneName[wslot];
  645. const AsciiString& recoilBoneName = m_weaponRecoilBoneName[wslot];
  646. const AsciiString& mfName = m_weaponMuzzleFlashName[wslot];
  647. const AsciiString& plbName = m_weaponProjectileLaunchBoneName[wslot];
  648. // a useful tool for finding missing INI entries in the weapon-bone block
  649. // since this class has no idea which object it refers to, it must assume that the projectilelaunchbonename
  650. // is required if the fxBoneName is specified
  651. // this is not always true, since some weapons have no launched projectiles, hence the caveat in the assert message
  652. //#if defined(_DEBUG) || defined(_INTERNAL)
  653. // if (
  654. // ( m_modelName.startsWith("UV") || m_modelName.startsWith("NV") || m_modelName.startsWith("AV") ||
  655. // m_modelName.startsWith("uv") || m_modelName.startsWith("nv") || m_modelName.startsWith("av")
  656. // )
  657. //
  658. // && fxBoneName.isNotEmpty()
  659. //
  660. // )
  661. // DEBUG_ASSERTCRASH( plbName.isNotEmpty(), ("You appear to have a missing projectilelaunchbonename. \n Promptly ignore this assert if this model is used by a non-projectile-weapon-bearing unit\nModel name = %s.", m_modelName.str()) );
  662. //#endif
  663. if (fxBoneName.isNotEmpty() || recoilBoneName.isNotEmpty() || mfName.isNotEmpty() || plbName.isNotEmpty())
  664. {
  665. Int prevFxBone = 0;
  666. char buffer[256];
  667. for (Int i = 1; i <= 99; ++i)
  668. {
  669. WeaponBarrelInfo info;
  670. info.m_projectileOffsetMtx.Make_Identity();
  671. if (!recoilBoneName.isEmpty())
  672. {
  673. sprintf(buffer, "%s%02d", recoilBoneName.str(), i);
  674. findPristineBone(NAMEKEY(buffer), &info.m_recoilBone);
  675. }
  676. if (!mfName.isEmpty())
  677. {
  678. sprintf(buffer, "%s%02d", mfName.str(), i);
  679. findPristineBone(NAMEKEY(buffer), &info.m_muzzleFlashBone);
  680. #if defined(_DEBUG) || defined(_INTERNAL)
  681. if (info.m_muzzleFlashBone)
  682. info.m_muzzleFlashBoneName = buffer;
  683. #endif
  684. }
  685. if (!fxBoneName.isEmpty())
  686. {
  687. sprintf(buffer, "%s%02d", fxBoneName.str(), i);
  688. findPristineBone(NAMEKEY(buffer), &info.m_fxBone);
  689. // special case: if we have multiple muzzleflashes, but only one fxbone, use that fxbone for everything.
  690. if (info.m_fxBone == 0 && info.m_muzzleFlashBone != 0)
  691. info.m_fxBone = prevFxBone;
  692. }
  693. Int plbBoneIndex = 0;
  694. if (!plbName.isEmpty())
  695. {
  696. sprintf(buffer, "%s%02d", plbName.str(), i);
  697. const Matrix3D* mtx = findPristineBone(NAMEKEY(buffer), &plbBoneIndex);
  698. if (mtx != NULL)
  699. info.m_projectileOffsetMtx = *mtx;
  700. }
  701. if (info.m_fxBone == 0 && info.m_recoilBone == 0 && info.m_muzzleFlashBone == 0 && plbBoneIndex == 0)
  702. break;
  703. CRCDEBUG_LOG(("validateWeaponBarrelInfo() - model name %s wslot %d\n", m_modelName.str(), wslot));
  704. DUMPMATRIX3D(&(info.m_projectileOffsetMtx));
  705. BONEPOS_LOG(("validateWeaponBarrelInfo() - model name %s wslot %d\n", m_modelName.str(), wslot));
  706. BONEPOS_DUMPMATRIX3D(&(info.m_projectileOffsetMtx));
  707. m_weaponBarrelInfoVec[wslot].push_back(info);
  708. if (info.m_recoilBone != 0 || info.m_muzzleFlashBone != 0)
  709. m_hasRecoilBonesOrMuzzleFlashes[wslot] = true;
  710. prevFxBone = info.m_fxBone;
  711. }
  712. if (m_weaponBarrelInfoVec[wslot].empty())
  713. {
  714. // try the unadorned names
  715. WeaponBarrelInfo info;
  716. if (!recoilBoneName.isEmpty())
  717. findPristineBone(NAMEKEY(recoilBoneName), &info.m_recoilBone);
  718. if (!mfName.isEmpty())
  719. findPristineBone(NAMEKEY(mfName), &info.m_muzzleFlashBone);
  720. #if defined(_DEBUG) || defined(_INTERNAL)
  721. if (info.m_muzzleFlashBone)
  722. info.m_muzzleFlashBoneName = mfName;
  723. #endif
  724. const Matrix3D* plbMtx = plbName.isEmpty() ? NULL : findPristineBone(NAMEKEY(plbName), NULL);
  725. if (plbMtx != NULL)
  726. info.m_projectileOffsetMtx = *plbMtx;
  727. else
  728. info.m_projectileOffsetMtx.Make_Identity();
  729. if (!fxBoneName.isEmpty())
  730. findPristineBone(NAMEKEY(fxBoneName), &info.m_fxBone);
  731. if (info.m_fxBone != 0 || info.m_recoilBone != 0 || info.m_muzzleFlashBone != 0 || plbMtx != NULL)
  732. {
  733. CRCDEBUG_LOG(("validateWeaponBarrelInfo() - model name %s (unadorned) wslot %d\n", m_modelName.str(), wslot));
  734. DUMPMATRIX3D(&(info.m_projectileOffsetMtx));
  735. BONEPOS_LOG(("validateWeaponBarrelInfo() - model name %s (unadorned) wslot %d\n", m_modelName.str(), wslot));
  736. BONEPOS_DUMPMATRIX3D(&(info.m_projectileOffsetMtx));
  737. m_weaponBarrelInfoVec[wslot].push_back(info);
  738. if (info.m_recoilBone != 0 || info.m_muzzleFlashBone != 0)
  739. m_hasRecoilBonesOrMuzzleFlashes[wslot] = true;
  740. }
  741. else
  742. {
  743. CRCDEBUG_LOG(("validateWeaponBarrelInfo() - model name %s (unadorned) found nothing\n", m_modelName.str()));
  744. BONEPOS_LOG(("validateWeaponBarrelInfo() - model name %s (unadorned) found nothing\n", m_modelName.str()));
  745. }
  746. } // if empty
  747. DEBUG_ASSERTCRASH(!(m_modelName.isNotEmpty() && m_weaponBarrelInfoVec[wslot].empty()), ("*** ASSET ERROR: No fx bone named '%s' found in model %s!\n",fxBoneName.str(),m_modelName.str()));
  748. }
  749. }
  750. m_validStuff |= BARRELS_VALID;
  751. }
  752. //-------------------------------------------------------------------------------------------------
  753. void ModelConditionInfo::validateTurretInfo() const
  754. {
  755. //DEBUG_ASSERTCRASH(isValidTimeToCalcLogicStuff(), ("calling validateTurretInfo() from in GameClient!"));
  756. if (m_validStuff & TURRETS_VALID)
  757. return;
  758. setFPMode();
  759. for (int tslot = 0; tslot < MAX_TURRETS; ++tslot)
  760. {
  761. TurretInfo& tur = m_turrets[tslot];
  762. if (!isValidTimeToCalcLogicStuff() || m_modelName.isEmpty())
  763. {
  764. tur.m_turretAngleBone = 0;
  765. tur.m_turretPitchBone = 0;
  766. continue;
  767. }
  768. if (tur.m_turretAngleNameKey != NAMEKEY_INVALID)
  769. {
  770. if (findPristineBone(tur.m_turretAngleNameKey, &tur.m_turretAngleBone) == NULL)
  771. {
  772. DEBUG_CRASH(("*** ASSET ERROR: TurretBone %s not found! (%s)\n",KEYNAME(tur.m_turretAngleNameKey).str(),m_modelName.str()));
  773. tur.m_turretAngleBone = 0;
  774. }
  775. }
  776. else
  777. {
  778. tur.m_turretAngleBone = 0;
  779. }
  780. if (tur.m_turretPitchNameKey != NAMEKEY_INVALID)
  781. {
  782. if (findPristineBone(tur.m_turretPitchNameKey, &tur.m_turretPitchBone) == NULL)
  783. {
  784. DEBUG_CRASH(("*** ASSET ERROR: TurretBone %s not found! (%s)\n",KEYNAME(tur.m_turretPitchNameKey).str(),m_modelName.str()));
  785. tur.m_turretPitchBone = 0;
  786. }
  787. }
  788. else
  789. {
  790. tur.m_turretPitchBone = 0;
  791. }
  792. }
  793. if (isValidTimeToCalcLogicStuff())
  794. {
  795. m_validStuff |= TURRETS_VALID;
  796. }
  797. }
  798. //-------------------------------------------------------------------------------------------------
  799. const Matrix3D* ModelConditionInfo::findPristineBone(NameKeyType boneName, Int* boneIndex) const
  800. {
  801. DEBUG_ASSERTCRASH((m_validStuff & PRISTINE_BONES_VALID), ("*** ASSET ERROR: bones are not valid"));
  802. if (!(m_validStuff & PRISTINE_BONES_VALID))
  803. {
  804. // set it to zero, some callers rely on this
  805. if (boneIndex)
  806. *boneIndex = 0;
  807. return NULL;
  808. }
  809. if (boneName == NAMEKEY_INVALID)
  810. {
  811. // set it to zero, some callers rely on this
  812. if (boneIndex)
  813. *boneIndex = 0;
  814. return NULL;
  815. }
  816. PristineBoneInfoMap::const_iterator it = m_pristineBones.find(boneName);
  817. if (it != m_pristineBones.end())
  818. {
  819. if (boneIndex)
  820. *boneIndex = it->second.boneIndex;
  821. return &it->second.mtx;
  822. }
  823. else
  824. {
  825. // set it to zero -- some callers rely on this!
  826. if (boneIndex)
  827. *boneIndex = 0;
  828. return NULL;
  829. }
  830. }
  831. //-------------------------------------------------------------------------------------------------
  832. Bool ModelConditionInfo::findPristineBonePos(NameKeyType boneName, Coord3D& pos) const
  833. {
  834. const Matrix3D* mtx = findPristineBone(boneName, NULL);
  835. if (mtx)
  836. {
  837. Vector3 v = mtx->Get_Translation();
  838. pos.x = v.X;
  839. pos.y = v.Y;
  840. pos.z = v.Z;
  841. return true;
  842. }
  843. else
  844. {
  845. pos.zero();
  846. return false;
  847. }
  848. }
  849. //-------------------------------------------------------------------------------------------------
  850. void ModelConditionInfo::loadAnimations() const
  851. {
  852. #ifdef RETAIN_ANIM_HANDLES
  853. for (W3DAnimationVector::const_iterator it2 = m_animations.begin(); it2 != m_animations.end(); ++it2)
  854. {
  855. HAnimClass* h = it2->getAnimHandle(); // just force it to get loaded
  856. REF_PTR_RELEASE(h);
  857. h = NULL;
  858. }
  859. #else
  860. // srj sez: I think there is no real reason to preload these all anymore. things that need the anims
  861. // (for bones) will force an implicit load anyway, but there's no point in forcibly loading the anims
  862. // that don't contain interesting logical bones.
  863. #endif
  864. }
  865. //-------------------------------------------------------------------------------------------------
  866. void ModelConditionInfo::clear()
  867. {
  868. int i;
  869. #if defined(_DEBUG) || defined(_INTERNAL)
  870. m_description.clear();
  871. #endif
  872. m_conditionsYesVec.clear();
  873. m_modelName.clear();
  874. for (i = 0; i < MAX_TURRETS; ++i)
  875. {
  876. m_turrets[i].clear();
  877. }
  878. m_hideShowVec.clear();
  879. for (i = 0; i < WEAPONSLOT_COUNT; ++i)
  880. {
  881. m_weaponFireFXBoneName[i].clear();
  882. m_weaponRecoilBoneName[i].clear();
  883. m_weaponMuzzleFlashName[i].clear();
  884. m_weaponProjectileLaunchBoneName[i].clear();
  885. m_weaponBarrelInfoVec[i].clear();
  886. m_hasRecoilBonesOrMuzzleFlashes[i] = false;
  887. }
  888. m_particleSysBones.clear();
  889. m_animations.clear();
  890. m_flags = 0;
  891. m_transitionKey = NAMEKEY_INVALID;
  892. m_allowToFinishKey = NAMEKEY_INVALID;
  893. m_iniReadFlags = 0;
  894. m_mode = RenderObjClass::ANIM_MODE_ONCE;
  895. m_transitionSig = NO_TRANSITION;
  896. m_animMinSpeedFactor = 1.0f;
  897. m_animMaxSpeedFactor = 1.0f;
  898. m_pristineBones.clear();
  899. m_validStuff = 0;
  900. }
  901. //-------------------------------------------------------------------------------------------------
  902. W3DModelDrawModuleData::W3DModelDrawModuleData() :
  903. m_validated(0),
  904. m_okToChangeModelColor(false),
  905. m_particlesAttachedToAnimatedBones(false),
  906. m_animationsRequirePower(true),
  907. #ifdef CACHE_ATTACH_BONE
  908. m_attachToDrawableBoneOffsetValid(false),
  909. #endif
  910. m_minLODRequired(STATIC_GAME_LOD_LOW),
  911. m_defaultState(-1)
  912. {
  913. const Real MAX_SHIFT = 3.0f;
  914. const Real INITIAL_RECOIL_RATE = 2.0f;
  915. const Real RECOIL_DAMPING = 0.4f;
  916. const Real SETTLE_RATE = 0.065f;
  917. m_projectileBoneFeedbackEnabledSlots = 0;
  918. m_initialRecoil = INITIAL_RECOIL_RATE;
  919. m_maxRecoil = MAX_SHIFT;
  920. m_recoilDamping = RECOIL_DAMPING;
  921. m_recoilSettle = SETTLE_RATE;
  922. m_receivesDynamicLights = TRUE;
  923. // m_ignoreConditionStates defaults to all zero, which is what we want
  924. }
  925. //-------------------------------------------------------------------------------------------------
  926. void W3DModelDrawModuleData::validateStuffForTimeAndWeather(const Drawable* draw, Bool night, Bool snowy) const
  927. {
  928. if (!isValidTimeToCalcLogicStuff())
  929. return;
  930. enum
  931. {
  932. NORMAL = 0x0001,
  933. NIGHT = 0x0002,
  934. SNOWY = 0x0004,
  935. NIGHT_SNOWY = 0x0008
  936. };
  937. Int mode;
  938. if (night)
  939. {
  940. mode = (snowy) ? NIGHT_SNOWY : NIGHT;
  941. }
  942. else
  943. {
  944. mode = (snowy) ? SNOWY : NORMAL;
  945. }
  946. if (m_validated & mode)
  947. return;
  948. m_validated |= mode;
  949. for (ModelConditionVector::iterator c_it = m_conditionStates.begin(); c_it != m_conditionStates.end(); ++c_it)
  950. {
  951. if (!c_it->matchesMode(false, false) && !c_it->matchesMode(night, snowy))
  952. continue;
  953. c_it->validateStuff(NULL, draw->getScale(), m_extraPublicBones);
  954. }
  955. for (TransitionMap::iterator t_it = m_transitionMap.begin(); t_it != m_transitionMap.end(); ++t_it)
  956. {
  957. // here's the tricky part: only want to load the anims for this one if there is at least one
  958. // source AND at least one dest state that matches the current mode
  959. NameKeyType src = recoverSrcState(t_it->first);
  960. NameKeyType dst = recoverDstState(t_it->first);
  961. Bool a = false;
  962. Bool b = false;
  963. for (c_it = m_conditionStates.begin(); c_it != m_conditionStates.end(); ++c_it)
  964. {
  965. if (!a && c_it->m_transitionKey == src && c_it->matchesMode(night, snowy))
  966. a = true;
  967. if (!b && c_it->m_transitionKey == dst && c_it->matchesMode(night, snowy))
  968. b = true;
  969. }
  970. if (a && b)
  971. {
  972. t_it->second.loadAnimations();
  973. // nope -- transition states don't get public bones.
  974. //it->addPublicBone(m_extraPublicBones);
  975. // srj sez: hm, this doesn't make sense; I think we really do need to validate transition states.
  976. t_it->second.validateStuff(NULL, draw->getScale(), m_extraPublicBones);
  977. }
  978. }
  979. }
  980. //-------------------------------------------------------------------------------------------------
  981. W3DModelDrawModuleData::~W3DModelDrawModuleData()
  982. {
  983. m_conditionStateMap.clear();
  984. }
  985. //-------------------------------------------------------------------------------------------------
  986. void W3DModelDrawModuleData::preloadAssets( TimeOfDay timeOfDay, Real scale ) const
  987. {
  988. for( ModelConditionVector::iterator it = m_conditionStates.begin();
  989. it != m_conditionStates.end();
  990. ++it )
  991. {
  992. it->preloadAssets( timeOfDay, scale );
  993. }
  994. }
  995. //-------------------------------------------------------------------------------------------------
  996. AsciiString W3DModelDrawModuleData::getBestModelNameForWB(const ModelConditionFlags& c) const
  997. {
  998. const ModelConditionInfo* info = findBestInfo(c);
  999. if (info)
  1000. return info->m_modelName;
  1001. return AsciiString::TheEmptyString;
  1002. }
  1003. #ifdef CACHE_ATTACH_BONE
  1004. //-------------------------------------------------------------------------------------------------
  1005. const Vector3* W3DModelDrawModuleData::getAttachToDrawableBoneOffset(const Drawable* draw) const
  1006. {
  1007. if (m_attachToDrawableBone.isEmpty())
  1008. {
  1009. return NULL;
  1010. }
  1011. else
  1012. {
  1013. if (!m_attachToDrawableBoneOffsetValid)
  1014. {
  1015. // must use pristine bone here since the result is used by logic
  1016. Matrix3D boneMtx;
  1017. if (draw->getPristineBonePositions(m_attachToDrawableBone.str(), 0, NULL, &boneMtx, 1) == 1)
  1018. {
  1019. m_attachToDrawableBoneOffset = boneMtx.Get_Translation();
  1020. }
  1021. else
  1022. {
  1023. m_attachToDrawableBoneOffset.X = 0;
  1024. m_attachToDrawableBoneOffset.Y = 0;
  1025. m_attachToDrawableBoneOffset.Z = 0;
  1026. }
  1027. m_attachToDrawableBoneOffsetValid = true;
  1028. }
  1029. return &m_attachToDrawableBoneOffset;
  1030. }
  1031. }
  1032. #endif
  1033. //-------------------------------------------------------------------------------------------------
  1034. enum ParseCondStateType
  1035. {
  1036. PARSE_NORMAL,
  1037. PARSE_DEFAULT,
  1038. PARSE_TRANSITION,
  1039. PARSE_ALIAS
  1040. };
  1041. //-------------------------------------------------------------------------------------------------
  1042. static void parseAsciiStringLC( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1043. {
  1044. AsciiString* asciiString = (AsciiString *)store;
  1045. *asciiString = ini->getNextAsciiString();
  1046. asciiString->toLower();
  1047. }
  1048. //-------------------------------------------------------------------------------------------------
  1049. void W3DModelDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
  1050. {
  1051. ModuleData::buildFieldParse(p);
  1052. static const FieldParse dataFieldParse[] =
  1053. {
  1054. { "InitialRecoilSpeed", INI::parseVelocityReal, NULL, offsetof(W3DModelDrawModuleData, m_initialRecoil) },
  1055. { "MaxRecoilDistance", INI::parseReal, NULL, offsetof(W3DModelDrawModuleData, m_maxRecoil) },
  1056. { "RecoilDamping", INI::parseReal, NULL, offsetof(W3DModelDrawModuleData, m_recoilDamping) },
  1057. { "RecoilSettleSpeed", INI::parseVelocityReal, NULL, offsetof(W3DModelDrawModuleData, m_recoilSettle) },
  1058. { "OkToChangeModelColor", INI::parseBool, NULL, offsetof(W3DModelDrawModuleData, m_okToChangeModelColor) },
  1059. { "AnimationsRequirePower", INI::parseBool, NULL, offsetof(W3DModelDrawModuleData, m_animationsRequirePower) },
  1060. { "ParticlesAttachedToAnimatedBones", INI::parseBool, NULL, offsetof(W3DModelDrawModuleData, m_particlesAttachedToAnimatedBones) },
  1061. { "MinLODRequired", INI::parseStaticGameLODLevel, NULL, offsetof(W3DModelDrawModuleData, m_minLODRequired) },
  1062. { "ProjectileBoneFeedbackEnabledSlots", INI::parseBitString32, TheWeaponSlotTypeNames, offsetof(W3DModelDrawModuleData, m_projectileBoneFeedbackEnabledSlots) },
  1063. { "DefaultConditionState", W3DModelDrawModuleData::parseConditionState, (void*)PARSE_DEFAULT, 0 },
  1064. { "ConditionState", W3DModelDrawModuleData::parseConditionState, (void*)PARSE_NORMAL, 0 },
  1065. { "AliasConditionState", W3DModelDrawModuleData::parseConditionState, (void*)PARSE_ALIAS, 0 },
  1066. { "TransitionState", W3DModelDrawModuleData::parseConditionState, (void*)PARSE_TRANSITION, 0 },
  1067. { "TrackMarks", parseAsciiStringLC, NULL, offsetof(W3DModelDrawModuleData, m_trackFile) },
  1068. { "ExtraPublicBone", INI::parseAsciiStringVectorAppend, NULL, offsetof(W3DModelDrawModuleData, m_extraPublicBones) },
  1069. { "AttachToBoneInAnotherModule", parseAsciiStringLC, NULL, offsetof(W3DModelDrawModuleData, m_attachToDrawableBone) },
  1070. { "IgnoreConditionStates", ModelConditionFlags::parseFromINI, NULL, offsetof(W3DModelDrawModuleData, m_ignoreConditionStates) },
  1071. { "ReceivesDynamicLights", INI::parseBool, NULL, offsetof(W3DModelDrawModuleData, m_receivesDynamicLights) },
  1072. { 0, 0, 0, 0 }
  1073. };
  1074. p.add(dataFieldParse);
  1075. }
  1076. //-------------------------------------------------------------------------------------------------
  1077. enum AnimParseType
  1078. {
  1079. ANIM_NORMAL,
  1080. ANIM_IDLE
  1081. };
  1082. //-------------------------------------------------------------------------------------------------
  1083. static void parseAnimation(INI* ini, void *instance, void * /*store*/, const void* userData)
  1084. {
  1085. AnimParseType animType = (AnimParseType)(UnsignedInt)userData;
  1086. AsciiString animName = ini->getNextAsciiString();
  1087. animName.toLower();
  1088. const char* distanceCoveredToken = ini->getNextTokenOrNull();
  1089. Real distanceCovered = distanceCoveredToken ? INI::scanReal(distanceCoveredToken) : 0;
  1090. DEBUG_ASSERTCRASH(!(animType == ANIM_IDLE && distanceCovered != 0), ("You should not specify nonzero DistanceCovered values for Idle Anims"));
  1091. const char* timesToRepeatToken = ini->getNextTokenOrNull();
  1092. Int timesToRepeat = timesToRepeatToken ? INI::scanInt(timesToRepeatToken) : 1;
  1093. if (timesToRepeat < 1) timesToRepeat = 1;
  1094. W3DAnimationInfo animInfo(animName, (animType == ANIM_IDLE), distanceCovered);
  1095. ModelConditionInfo* self = (ModelConditionInfo*)instance;
  1096. if (self->m_iniReadFlags & (1<<ANIMS_COPIED_FROM_DEFAULT_STATE))
  1097. {
  1098. self->m_iniReadFlags &= ~((1<<ANIMS_COPIED_FROM_DEFAULT_STATE)|(1<<GOT_IDLE_ANIMS)|(1<<GOT_NONIDLE_ANIMS));
  1099. self->m_animations.clear();
  1100. }
  1101. if (animInfo.isIdleAnim())
  1102. self->m_iniReadFlags |= (1<<GOT_IDLE_ANIMS);
  1103. else
  1104. self->m_iniReadFlags |= (1<<GOT_NONIDLE_ANIMS);
  1105. if (!animName.isEmpty() && !animName.isNone())
  1106. {
  1107. while (timesToRepeat--)
  1108. self->m_animations.push_back(animInfo);
  1109. }
  1110. }
  1111. //-------------------------------------------------------------------------------------------------
  1112. static void parseShowHideSubObject(INI* ini, void *instance, void *store, const void* userData)
  1113. {
  1114. std::vector<ModelConditionInfo::HideShowSubObjInfo>* vec = (std::vector<ModelConditionInfo::HideShowSubObjInfo>*)store;
  1115. AsciiString subObjName = ini->getNextAsciiString();
  1116. subObjName.toLower();
  1117. // handle "None"
  1118. if (subObjName.isNone())
  1119. {
  1120. vec->clear();
  1121. return;
  1122. }
  1123. while (subObjName.isNotEmpty())
  1124. {
  1125. Bool found = false;
  1126. for (std::vector<ModelConditionInfo::HideShowSubObjInfo>::iterator it = vec->begin(); it != vec->end(); ++it)
  1127. {
  1128. if (stricmp(it->subObjName.str(), subObjName.str()) == 0)
  1129. {
  1130. it->hide = (userData != NULL);
  1131. found = true;
  1132. }
  1133. }
  1134. if (!found)
  1135. {
  1136. ModelConditionInfo::HideShowSubObjInfo info;
  1137. info.subObjName = subObjName;
  1138. info.hide = (userData != NULL);
  1139. vec->push_back(info);
  1140. }
  1141. subObjName = ini->getNextAsciiString();
  1142. subObjName.toLower();
  1143. }
  1144. }
  1145. //-------------------------------------------------------------------------------------------------
  1146. void W3DModelDraw::showSubObject( const AsciiString& name, Bool show )
  1147. {
  1148. if( name.isNotEmpty() )
  1149. {
  1150. Bool found = false;
  1151. for( std::vector<ModelConditionInfo::HideShowSubObjInfo>::iterator it = m_subObjectVec.begin(); it != m_subObjectVec.end(); ++it )
  1152. {
  1153. if( stricmp( it->subObjName.str(), name.str() ) == 0 )
  1154. {
  1155. it->hide = !show;
  1156. found = true;
  1157. }
  1158. }
  1159. if( !found )
  1160. {
  1161. ModelConditionInfo::HideShowSubObjInfo info;
  1162. info.subObjName = name;
  1163. info.hide = !show;
  1164. m_subObjectVec.push_back( info );
  1165. }
  1166. }
  1167. }
  1168. //-------------------------------------------------------------------------------------------------
  1169. static void parseWeaponBoneName(INI* ini, void *instance, void * store, const void* /*userData*/)
  1170. {
  1171. ModelConditionInfo* self = (ModelConditionInfo*)instance;
  1172. AsciiString* arr = (AsciiString*)store;
  1173. WeaponSlotType wslot = (WeaponSlotType)INI::scanIndexList(ini->getNextToken(), TheWeaponSlotTypeNames);
  1174. arr[wslot] = ini->getNextAsciiString();
  1175. arr[wslot].toLower();
  1176. if (arr[wslot].isNone())
  1177. arr[wslot].clear();
  1178. if (self)
  1179. self->addPublicBone(arr[wslot]);
  1180. }
  1181. //-------------------------------------------------------------------------------------------------
  1182. static void parseParticleSysBone(INI* ini, void *instance, void * store, const void * /*userData*/)
  1183. {
  1184. ParticleSysBoneInfo info;
  1185. info.boneName = ini->getNextAsciiString();
  1186. info.boneName.toLower();
  1187. ini->parseParticleSystemTemplate(ini, instance, &(info.particleSystemTemplate), NULL);
  1188. ModelConditionInfo *self = (ModelConditionInfo *)instance;
  1189. self->m_particleSysBones.push_back(info);
  1190. }
  1191. //-------------------------------------------------------------------------------------------------
  1192. static void parseRealRange( INI *ini, void *instance, void *store, const void* /*userData*/ )
  1193. {
  1194. ModelConditionInfo *self = (ModelConditionInfo *)instance;
  1195. const char *token = ini->getNextToken();
  1196. self->m_animMinSpeedFactor = ini->scanReal( token );
  1197. token = ini->getNextToken();
  1198. self->m_animMaxSpeedFactor = ini->scanReal( token );
  1199. }
  1200. //-------------------------------------------------------------------------------------------------
  1201. static void parseLowercaseNameKey(INI* ini, void *instance, void * store, const void * /*userData*/)
  1202. {
  1203. NameKeyType* key = (NameKeyType*)store;
  1204. AsciiString tmp = ini->getNextToken();
  1205. tmp.toLower();
  1206. *key = NAMEKEY(tmp.str());
  1207. }
  1208. //-------------------------------------------------------------------------------------------------
  1209. static void parseBoneNameKey(INI* ini, void *instance, void * store, const void * /*userData*/)
  1210. {
  1211. ModelConditionInfo* self = (ModelConditionInfo*)instance;
  1212. NameKeyType* key = (NameKeyType*)store;
  1213. AsciiString tmp = ini->getNextToken();
  1214. tmp.toLower();
  1215. if (self)
  1216. self->addPublicBone(tmp);
  1217. if (tmp.isEmpty() || tmp.isNone())
  1218. *key = NAMEKEY_INVALID;
  1219. else
  1220. *key = NAMEKEY(tmp.str());
  1221. }
  1222. //-------------------------------------------------------------------------------------------------
  1223. static Bool doesStateExist(const ModelConditionVector& v, const ModelConditionFlags& f)
  1224. {
  1225. for (ModelConditionVector::const_iterator it = v.begin(); it != v.end(); ++it)
  1226. {
  1227. for (Int i = it->getConditionsYesCount()-1; i >= 0; --i)
  1228. {
  1229. if (f == it->getNthConditionsYes(i))
  1230. return true;
  1231. }
  1232. }
  1233. return false;
  1234. }
  1235. //-------------------------------------------------------------------------------------------------
  1236. void W3DModelDrawModuleData::parseConditionState(INI* ini, void *instance, void * /*store*/, const void* userData)
  1237. {
  1238. static const FieldParse myFieldParse[] =
  1239. {
  1240. { "Model", parseAsciiStringLC, NULL, offsetof(ModelConditionInfo, m_modelName) },
  1241. { "Turret", parseBoneNameKey, NULL, offsetof(ModelConditionInfo, m_turrets[0].m_turretAngleNameKey) },
  1242. { "TurretArtAngle", INI::parseAngleReal, NULL, offsetof(ModelConditionInfo, m_turrets[0].m_turretArtAngle) },
  1243. { "TurretPitch", parseBoneNameKey, NULL, offsetof(ModelConditionInfo, m_turrets[0].m_turretPitchNameKey) },
  1244. { "TurretArtPitch", INI::parseAngleReal, NULL, offsetof(ModelConditionInfo, m_turrets[0].m_turretArtPitch) },
  1245. { "AltTurret", parseBoneNameKey, NULL, offsetof(ModelConditionInfo, m_turrets[1].m_turretAngleNameKey) },
  1246. { "AltTurretArtAngle", INI::parseAngleReal, NULL, offsetof(ModelConditionInfo, m_turrets[1].m_turretArtAngle) },
  1247. { "AltTurretPitch", parseBoneNameKey, NULL, offsetof(ModelConditionInfo, m_turrets[1].m_turretPitchNameKey) },
  1248. { "AltTurretArtPitch", INI::parseAngleReal, NULL, offsetof(ModelConditionInfo, m_turrets[1].m_turretArtPitch) },
  1249. { "ShowSubObject", parseShowHideSubObject, (void*)0, offsetof(ModelConditionInfo, m_hideShowVec) },
  1250. { "HideSubObject", parseShowHideSubObject, (void*)1, offsetof(ModelConditionInfo, m_hideShowVec) },
  1251. { "WeaponFireFXBone", parseWeaponBoneName, NULL, offsetof(ModelConditionInfo, m_weaponFireFXBoneName[0]) },
  1252. { "WeaponRecoilBone", parseWeaponBoneName, NULL, offsetof(ModelConditionInfo, m_weaponRecoilBoneName[0]) },
  1253. { "WeaponMuzzleFlash", parseWeaponBoneName, NULL, offsetof(ModelConditionInfo, m_weaponMuzzleFlashName[0]) },
  1254. { "WeaponLaunchBone", parseWeaponBoneName, NULL, offsetof(ModelConditionInfo, m_weaponProjectileLaunchBoneName[0]) },
  1255. { "WeaponHideShowBone", parseWeaponBoneName, NULL, offsetof(ModelConditionInfo, m_weaponProjectileHideShowName[0]) },
  1256. { "Animation", parseAnimation, (void*)ANIM_NORMAL, offsetof(ModelConditionInfo, m_animations) },
  1257. { "IdleAnimation", parseAnimation, (void*)ANIM_IDLE, offsetof(ModelConditionInfo, m_animations) },
  1258. { "AnimationMode", INI::parseIndexList, TheAnimModeNames, offsetof(ModelConditionInfo, m_mode) },
  1259. { "TransitionKey", parseLowercaseNameKey, NULL, offsetof(ModelConditionInfo, m_transitionKey) },
  1260. { "WaitForStateToFinishIfPossible", parseLowercaseNameKey, NULL, offsetof(ModelConditionInfo, m_allowToFinishKey) },
  1261. { "Flags", INI::parseBitString32, ACBitsNames, offsetof(ModelConditionInfo, m_flags) },
  1262. { "ParticleSysBone", parseParticleSysBone, NULL, 0 },
  1263. { "AnimationSpeedFactorRange", parseRealRange, NULL, 0 },
  1264. { 0, 0, 0, 0 }
  1265. };
  1266. ModelConditionInfo info;
  1267. W3DModelDrawModuleData* self = (W3DModelDrawModuleData*)instance;
  1268. ParseCondStateType cst = (ParseCondStateType)(UnsignedInt)userData;
  1269. switch (cst)
  1270. {
  1271. case PARSE_DEFAULT:
  1272. {
  1273. if (self->m_defaultState >= 0)
  1274. {
  1275. DEBUG_CRASH(("*** ASSET ERROR: you may have only one default state!\n"));
  1276. throw INI_INVALID_DATA;
  1277. }
  1278. else if (ini->getNextTokenOrNull())
  1279. {
  1280. DEBUG_CRASH(("*** ASSET ERROR: unknown keyword\n"));
  1281. throw INI_INVALID_DATA;
  1282. }
  1283. else
  1284. {
  1285. if (!self->m_conditionStates.empty())
  1286. {
  1287. DEBUG_CRASH(("*** ASSET ERROR: when using DefaultConditionState, it must be the first state listed (%s)\n",TheThingTemplateBeingParsedName.str()));
  1288. throw INI_INVALID_DATA;
  1289. }
  1290. // note, this is size(), not size()-1, since we haven't actually modified the list yet
  1291. self->m_defaultState = self->m_conditionStates.size();
  1292. //DEBUG_LOG(("set default state to %d\n",self->m_defaultState));
  1293. // add an empty conditionstateflag set
  1294. ModelConditionFlags blankConditions;
  1295. info.m_conditionsYesVec.clear();
  1296. info.m_conditionsYesVec.push_back(blankConditions);
  1297. #if defined(_DEBUG) || defined(_INTERNAL)
  1298. info.m_description.clear();
  1299. info.m_description.concat(TheThingTemplateBeingParsedName);
  1300. info.m_description.concat(" DEFAULT");
  1301. #endif
  1302. }
  1303. }
  1304. break;
  1305. case PARSE_TRANSITION:
  1306. {
  1307. AsciiString firstNm = ini->getNextToken(); firstNm.toLower();
  1308. AsciiString secondNm = ini->getNextToken(); secondNm.toLower();
  1309. NameKeyType firstKey = NAMEKEY(firstNm);
  1310. NameKeyType secondKey = NAMEKEY(secondNm);
  1311. if (firstKey == secondKey)
  1312. {
  1313. DEBUG_CRASH(("*** ASSET ERROR: You may not declare a transition between two identical states\n"));
  1314. throw INI_INVALID_DATA;
  1315. }
  1316. if (self->m_defaultState >= 0)
  1317. {
  1318. info = self->m_conditionStates.at(self->m_defaultState);
  1319. info.m_iniReadFlags |= (1<<ANIMS_COPIED_FROM_DEFAULT_STATE);
  1320. info.m_transitionKey = NAMEKEY_INVALID;
  1321. info.m_allowToFinishKey = NAMEKEY_INVALID;
  1322. }
  1323. info.m_transitionSig = buildTransitionSig(firstKey, secondKey);
  1324. #if defined(_DEBUG) || defined(_INTERNAL)
  1325. info.m_description.clear();
  1326. info.m_description.concat(TheThingTemplateBeingParsedName);
  1327. info.m_description.concat(" TRANSITION: ");
  1328. info.m_description.concat(firstNm);
  1329. info.m_description.concat(" ");
  1330. info.m_description.concat(secondNm);
  1331. #endif
  1332. }
  1333. break;
  1334. case PARSE_ALIAS:
  1335. {
  1336. if (self->m_conditionStates.empty())
  1337. {
  1338. DEBUG_CRASH(("*** ASSET ERROR: AliasConditionState must refer to the previous state!\n"));
  1339. throw INI_INVALID_DATA;
  1340. }
  1341. ModelConditionInfo& prevState = self->m_conditionStates.at(self->m_conditionStates.size()-1);
  1342. ModelConditionFlags conditionsYes;
  1343. #if defined(_DEBUG) || defined(_INTERNAL)
  1344. AsciiString description;
  1345. conditionsYes.parse(ini, &description);
  1346. prevState.m_description.concat("\nAKA: ");
  1347. prevState.m_description.concat(description);
  1348. #else
  1349. conditionsYes.parse(ini, NULL);
  1350. #endif
  1351. if (conditionsYes.anyIntersectionWith(self->m_ignoreConditionStates))
  1352. {
  1353. DEBUG_CRASH(("You should not specify bits in a state once they are used in IgnoreConditionStates (%s)\n", TheThingTemplateBeingParsedName.str()));
  1354. throw INI_INVALID_DATA;
  1355. }
  1356. if (doesStateExist(self->m_conditionStates, conditionsYes))
  1357. {
  1358. DEBUG_CRASH(("*** ASSET ERROR: duplicate condition states are not currently allowed"));
  1359. throw INI_INVALID_DATA;
  1360. }
  1361. if (!conditionsYes.any() && self->m_defaultState >= 0)
  1362. {
  1363. DEBUG_CRASH(("*** ASSET ERROR: you may not specify both a Default state and a Conditions=None state"));
  1364. throw INI_INVALID_DATA;
  1365. }
  1366. prevState.m_conditionsYesVec.push_back(conditionsYes);
  1367. // yes, return, NOT break!
  1368. return;
  1369. }
  1370. case PARSE_NORMAL:
  1371. {
  1372. if (self->m_defaultState >= 0 && cst != PARSE_ALIAS)
  1373. {
  1374. info = self->m_conditionStates.at(self->m_defaultState);
  1375. info.m_iniReadFlags |= (1<<ANIMS_COPIED_FROM_DEFAULT_STATE);
  1376. info.m_conditionsYesVec.clear();
  1377. }
  1378. // no, we do not currently require a default state, cuz it would break the exiting INI
  1379. // files too badly. maybe someday.
  1380. // else
  1381. // {
  1382. // DEBUG_CRASH(("*** ASSET ERROR: you must specify a default state\n"));
  1383. // throw INI_INVALID_DATA;
  1384. // }
  1385. ModelConditionFlags conditionsYes;
  1386. #if defined(_DEBUG) || defined(_INTERNAL)
  1387. AsciiString description;
  1388. conditionsYes.parse(ini, &description);
  1389. info.m_description.clear();
  1390. info.m_description.concat(TheThingTemplateBeingParsedName);
  1391. info.m_description.concat("\n ");
  1392. info.m_description.concat(description);
  1393. #else
  1394. conditionsYes.parse(ini, NULL);
  1395. #endif
  1396. if (conditionsYes.anyIntersectionWith(self->m_ignoreConditionStates))
  1397. {
  1398. DEBUG_CRASH(("You should not specify bits in a state once they are used in IgnoreConditionStates (%s)\n", TheThingTemplateBeingParsedName.str()));
  1399. throw INI_INVALID_DATA;
  1400. }
  1401. if (self->m_defaultState < 0 && self->m_conditionStates.empty() && conditionsYes.any())
  1402. {
  1403. // it doesn't actually NEED to be first, but it does need to be present, and this is the simplest way to enforce...
  1404. DEBUG_CRASH(("*** ASSET ERROR: when not using DefaultConditionState, the first ConditionState must be for NONE (%s)\n",TheThingTemplateBeingParsedName.str()));
  1405. throw INI_INVALID_DATA;
  1406. }
  1407. if (!conditionsYes.any() && self->m_defaultState >= 0)
  1408. {
  1409. DEBUG_CRASH(("*** ASSET ERROR: you may not specify both a Default state and a Conditions=None state"));
  1410. throw INI_INVALID_DATA;
  1411. }
  1412. if (doesStateExist(self->m_conditionStates, conditionsYes))
  1413. {
  1414. DEBUG_CRASH(("*** ASSET ERROR: duplicate condition states are not currently allowed (%s)",info.m_description.str()));
  1415. throw INI_INVALID_DATA;
  1416. }
  1417. DEBUG_ASSERTCRASH(info.m_conditionsYesVec.size() == 0, ("*** ASSET ERROR: nonempty m_conditionsYesVec.size(), see srj"));
  1418. info.m_conditionsYesVec.clear();
  1419. info.m_conditionsYesVec.push_back(conditionsYes);
  1420. }
  1421. break;
  1422. }
  1423. ini->initFromINI(&info, myFieldParse);
  1424. if (info.m_modelName.isEmpty())
  1425. {
  1426. DEBUG_CRASH(("*** ASSET ERROR: you must specify a model name"));
  1427. throw INI_INVALID_DATA;
  1428. }
  1429. else if (info.m_modelName.isNone())
  1430. {
  1431. info.m_modelName.clear();
  1432. }
  1433. if ((info.m_iniReadFlags & (1<<GOT_IDLE_ANIMS)) && (info.m_iniReadFlags & (1<<GOT_NONIDLE_ANIMS)))
  1434. {
  1435. DEBUG_CRASH(("*** ASSET ERROR: you should not specify both Animations and IdleAnimations for the same state"));
  1436. throw INI_INVALID_DATA;
  1437. }
  1438. if ((info.m_iniReadFlags & (1<<GOT_IDLE_ANIMS)) && (info.m_mode != RenderObjClass::ANIM_MODE_ONCE && info.m_mode != RenderObjClass::ANIM_MODE_ONCE_BACKWARDS))
  1439. {
  1440. DEBUG_CRASH(("*** ASSET ERROR: Idle Anims should always use ONCE or ONCE_BACKWARDS (%s)\n",TheThingTemplateBeingParsedName.str()));
  1441. throw INI_INVALID_DATA;
  1442. }
  1443. info.m_validStuff &= ~ModelConditionInfo::HAS_PROJECTILE_BONES;
  1444. for (int wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  1445. {
  1446. if (info.m_weaponProjectileLaunchBoneName[wslot].isNotEmpty())
  1447. {
  1448. info.m_validStuff |= ModelConditionInfo::HAS_PROJECTILE_BONES;
  1449. break;
  1450. }
  1451. }
  1452. if (cst == PARSE_TRANSITION)
  1453. {
  1454. if (info.m_iniReadFlags & (1<<GOT_IDLE_ANIMS))
  1455. {
  1456. DEBUG_CRASH(("*** ASSET ERROR: Transition States should not specify Idle anims"));
  1457. throw INI_INVALID_DATA;
  1458. }
  1459. if (info.m_mode != RenderObjClass::ANIM_MODE_ONCE && info.m_mode != RenderObjClass::ANIM_MODE_ONCE_BACKWARDS)
  1460. {
  1461. DEBUG_CRASH(("*** ASSET ERROR: Transition States should always use ONCE or ONCE_BACKWARDS"));
  1462. throw INI_INVALID_DATA;
  1463. }
  1464. if (info.m_transitionKey != NAMEKEY_INVALID || info.m_allowToFinishKey != NAMEKEY_INVALID)
  1465. {
  1466. DEBUG_CRASH(("*** ASSET ERROR: Transition States must not have transition keys or m_allowToFinishKey"));
  1467. throw INI_INVALID_DATA;
  1468. }
  1469. self->m_transitionMap[info.m_transitionSig] = info;
  1470. }
  1471. else
  1472. {
  1473. self->m_conditionStates.push_back(info);
  1474. }
  1475. }
  1476. //-------------------------------------------------------------------------------------------------
  1477. static Int countOnBits(UnsignedInt val)
  1478. {
  1479. Int count = 0;
  1480. for (Int i = 0; i < 32; ++i)
  1481. {
  1482. if (val & 1)
  1483. ++count;
  1484. val >>= 1;
  1485. }
  1486. return count;
  1487. }
  1488. //-------------------------------------------------------------------------------------------------
  1489. const ModelConditionInfo* W3DModelDrawModuleData::findBestInfo(const ModelConditionFlags& c) const
  1490. {
  1491. ModelConditionFlags bits = c;
  1492. bits.clear(m_ignoreConditionStates);
  1493. return m_conditionStateMap.findBestInfo(m_conditionStates, bits);
  1494. }
  1495. //-------------------------------------------------------------------------------------------------
  1496. //-------------------------------------------------------------------------------------------------
  1497. //-------------------------------------------------------------------------------------------------
  1498. //-------------------------------------------------------------------------------------------------
  1499. W3DModelDraw::W3DModelDraw(Thing *thing, const ModuleData* moduleData) : DrawModule(thing, moduleData)
  1500. {
  1501. int i;
  1502. m_animationMode = RenderObjClass::ANIM_MODE_LOOP;
  1503. m_hideHeadlights = true;
  1504. m_pauseAnimation = false;
  1505. m_curState = NULL;
  1506. m_hexColor = 0;
  1507. m_renderObject = NULL;
  1508. m_shadow = NULL;
  1509. m_shadowEnabled = TRUE;
  1510. m_terrainDecal = NULL;
  1511. m_trackRenderObject = NULL;
  1512. m_whichAnimInCurState = -1;
  1513. m_nextState = NULL;
  1514. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  1515. for (i = 0; i < WEAPONSLOT_COUNT; ++i)
  1516. {
  1517. m_weaponRecoilInfoVec[i].clear();
  1518. }
  1519. m_needRecalcBoneParticleSystems = false;
  1520. m_fullyObscuredByShroud = false;
  1521. // only validate the current time-of-day and weather conditions by default.
  1522. getW3DModelDrawModuleData()->validateStuffForTimeAndWeather(getDrawable(),
  1523. TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT,
  1524. TheGlobalData->m_weather == WEATHER_SNOWY);
  1525. ModelConditionFlags emptyFlags;
  1526. const ModelConditionInfo* info = findBestInfo(emptyFlags);
  1527. if (!info)
  1528. {
  1529. DEBUG_CRASH(("*** ASSET ERROR: all draw modules must have an IDLE state\n"));
  1530. throw INI_INVALID_DATA;
  1531. }
  1532. Drawable* draw = getDrawable();
  1533. if ( draw )
  1534. {
  1535. Object* obj = draw->getObject();
  1536. if (obj)
  1537. {
  1538. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  1539. m_hexColor = obj->getNightIndicatorColor();
  1540. else
  1541. m_hexColor = obj->getIndicatorColor();
  1542. }
  1543. // THE VAST MAJORITY OF THESE SHOULD BE TRUE
  1544. if ( ! getW3DModelDrawModuleData()->m_receivesDynamicLights)
  1545. {
  1546. draw->setReceivesDynamicLights( FALSE );
  1547. DEBUG_LOG(("setReceivesDynamicLights = FALSE: %s\n", draw->getTemplate()->getName().str()));
  1548. }
  1549. }
  1550. setModelState(info);
  1551. }
  1552. //-------------------------------------------------------------------------------------------------
  1553. //-------------------------------------------------------------------------------------------------
  1554. void W3DModelDraw::onDrawableBoundToObject(void)
  1555. {
  1556. getW3DModelDrawModuleData()->validateStuffForTimeAndWeather(getDrawable(),
  1557. TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT,
  1558. TheGlobalData->m_weather == WEATHER_SNOWY);
  1559. }
  1560. //-------------------------------------------------------------------------------------------------
  1561. //-------------------------------------------------------------------------------------------------
  1562. W3DModelDraw::~W3DModelDraw(void)
  1563. {
  1564. if (m_trackRenderObject && TheTerrainTracksRenderObjClassSystem)
  1565. {
  1566. TheTerrainTracksRenderObjClassSystem->unbindTrack(m_trackRenderObject);
  1567. m_trackRenderObject = NULL;
  1568. }
  1569. nukeCurrentRender(NULL);
  1570. }
  1571. //-------------------------------------------------------------------------------------------------
  1572. void W3DModelDraw::doStartOrStopParticleSys()
  1573. {
  1574. Bool hidden = getDrawable()->isDrawableEffectivelyHidden() || m_fullyObscuredByShroud;
  1575. for (std::vector<ParticleSysTrackerType>::const_iterator it = m_particleSystemIDs.begin(); it != m_particleSystemIDs.end(); ++it)
  1576. //for (std::vector<ParticleSystemID>::const_iterator it = m_particleSystemIDs.begin(); it != m_particleSystemIDs.end(); ++it)
  1577. {
  1578. ParticleSystem *sys = TheParticleSystemManager->findParticleSystem((*it).id);
  1579. if (sys != NULL) {
  1580. // this can be NULL
  1581. if (hidden) {
  1582. sys->stop();
  1583. } else {
  1584. sys->start();
  1585. }
  1586. }
  1587. }
  1588. }
  1589. //-------------------------------------------------------------------------------------------------
  1590. void W3DModelDraw::setHidden(Bool hidden)
  1591. {
  1592. if (m_renderObject)
  1593. m_renderObject->Set_Hidden(hidden);
  1594. if (m_shadow)
  1595. m_shadow->enableShadowRender(!hidden);
  1596. m_shadowEnabled = hidden;
  1597. if (m_terrainDecal)
  1598. m_terrainDecal->enableShadowRender(!hidden);
  1599. if (m_trackRenderObject && hidden)
  1600. { const Coord3D* pos = getDrawable()->getPosition();
  1601. m_trackRenderObject->addCapEdgeToTrack(pos->x,pos->y);
  1602. }
  1603. doStartOrStopParticleSys();
  1604. }
  1605. /**Free all data used by this model's shadow. This is used to dynamically enable/disable shadows by the options screen*/
  1606. void W3DModelDraw::releaseShadows(void) ///< frees all shadow resources used by this module - used by Options screen.
  1607. {
  1608. if (m_shadow)
  1609. m_shadow->release();
  1610. m_shadow = NULL;
  1611. }
  1612. /** Create shadow resources if not already present. This is used to dynamically enable/disable shadows by the options screen*/
  1613. void W3DModelDraw::allocateShadows(void)
  1614. {
  1615. const ThingTemplate *tmplate=getDrawable()->getTemplate();
  1616. //Check if we don't already have a shadow but need one for this type of model.
  1617. if (m_shadow == NULL && m_renderObject && TheW3DShadowManager && tmplate->getShadowType() != SHADOW_NONE)
  1618. {
  1619. Shadow::ShadowTypeInfo shadowInfo;
  1620. strcpy(shadowInfo.m_ShadowName, tmplate->getShadowTextureName().str());
  1621. DEBUG_ASSERTCRASH(shadowInfo.m_ShadowName[0] != '\0', ("this should be validated in ThingTemplate now"));
  1622. shadowInfo.allowUpdates = FALSE; //shadow image will never update
  1623. shadowInfo.allowWorldAlign = TRUE; //shadow image will wrap around world objects
  1624. shadowInfo.m_type = (ShadowType)tmplate->getShadowType();
  1625. shadowInfo.m_sizeX = tmplate->getShadowSizeX();
  1626. shadowInfo.m_sizeY = tmplate->getShadowSizeY();
  1627. shadowInfo.m_offsetX = tmplate->getShadowOffsetX();
  1628. shadowInfo.m_offsetY = tmplate->getShadowOffsetY();
  1629. m_shadow = TheW3DShadowManager->addShadow(m_renderObject, &shadowInfo);
  1630. if (m_shadow)
  1631. { m_shadow->enableShadowInvisible(m_fullyObscuredByShroud);
  1632. if (m_renderObject->Is_Hidden() || !m_shadowEnabled)
  1633. m_shadow->enableShadowRender(FALSE);
  1634. }
  1635. }
  1636. }
  1637. //-------------------------------------------------------------------------------------------------
  1638. void W3DModelDraw::setShadowsEnabled(Bool enable)
  1639. {
  1640. if (m_shadow)
  1641. m_shadow->enableShadowRender(enable);
  1642. m_shadowEnabled = enable;
  1643. }
  1644. /**collect some stats about the rendering cost of this draw module */
  1645. #if defined(_DEBUG) || defined(_INTERNAL)
  1646. void W3DModelDraw::getRenderCost(RenderCost & rc) const
  1647. {
  1648. getRenderCostRecursive(rc,m_renderObject);
  1649. if (m_shadow)
  1650. m_shadow->getRenderCost(rc);
  1651. }
  1652. #endif //_DEBUG || _INTERNAL
  1653. /**recurse through sub-objs to collect stats about the rendering cost of this draw module */
  1654. #if defined(_DEBUG) || defined(_INTERNAL)
  1655. void W3DModelDraw::getRenderCostRecursive(RenderCost & rc,RenderObjClass * robj) const
  1656. {
  1657. if (robj == NULL) return;
  1658. // recurse through sub-objects
  1659. for (int i=0; i<robj->Get_Num_Sub_Objects(); i++) {
  1660. RenderObjClass * sub_obj = robj->Get_Sub_Object(i);
  1661. getRenderCostRecursive(rc,sub_obj);
  1662. REF_PTR_RELEASE(sub_obj);
  1663. }
  1664. // Only consider visible sub-objects. Some vehicles have hidden headlight cones for example
  1665. // that are not normally rendered.
  1666. if (robj->Is_Not_Hidden_At_All()) {
  1667. // collect stats from meshes
  1668. if (robj->Class_ID() == RenderObjClass::CLASSID_MESH) {
  1669. MeshClass * mesh = (MeshClass*)robj;
  1670. MeshModelClass * model = mesh->Peek_Model();
  1671. if (model != NULL)
  1672. { if (model->Get_Flag(MeshGeometryClass::SORT))
  1673. rc.addSortedMeshes(1);
  1674. if (model->Get_Flag(MeshGeometryClass::SKIN))
  1675. rc.addSkinMeshes(1);
  1676. }
  1677. rc.addDrawCalls(mesh->Get_Draw_Call_Count());
  1678. }
  1679. // collect bone stats.
  1680. const HTreeClass * htree = robj->Get_HTree();
  1681. if (htree != NULL) {
  1682. rc.addBones(htree->Num_Pivots());
  1683. }
  1684. }
  1685. }
  1686. #endif //_DEBUG || _INTERNAL
  1687. //-------------------------------------------------------------------------------------------------
  1688. void W3DModelDraw::setFullyObscuredByShroud(Bool fullyObscured)
  1689. {
  1690. if (m_fullyObscuredByShroud != fullyObscured)
  1691. {
  1692. m_fullyObscuredByShroud = fullyObscured;
  1693. if (m_shadow)
  1694. m_shadow->enableShadowInvisible(m_fullyObscuredByShroud);
  1695. if (m_terrainDecal)
  1696. m_terrainDecal->enableShadowInvisible(m_fullyObscuredByShroud);
  1697. doStartOrStopParticleSys();
  1698. }
  1699. }
  1700. //-------------------------------------------------------------------------------------------------
  1701. static Bool isAnimationComplete(RenderObjClass* r)
  1702. {
  1703. if (r && r->Class_ID() == RenderObjClass::CLASSID_HLOD)
  1704. {
  1705. HLodClass *hlod = (HLodClass*)r;
  1706. return hlod->Is_Animation_Complete();
  1707. }
  1708. return true;
  1709. }
  1710. //-------------------------------------------------------------------------------------------------
  1711. void W3DModelDraw::adjustAnimSpeedToMovementSpeed()
  1712. {
  1713. // if the cur animation has a "distance covered" number, try to throttle
  1714. // the animation rate to match our actual speed.
  1715. Real dist = getCurAnimDistanceCovered();
  1716. if (dist > 0.0f)
  1717. {
  1718. const Object *obj = getDrawable()->getObject();
  1719. if (obj)
  1720. {
  1721. const PhysicsBehavior* physics = obj->getPhysics();
  1722. if (physics)
  1723. {
  1724. Real speed = physics->getVelocityMagnitude(); // in dist/frame
  1725. if (speed > 0.0f)
  1726. {
  1727. // if distance-covered is specified for this anim, adjust the frame rate
  1728. // so it appears to sync with our unit speed
  1729. Real desiredDurationInMsec = dist / speed * MSEC_PER_LOGICFRAME_REAL;
  1730. setCurAnimDurationInMsec(desiredDurationInMsec);
  1731. }
  1732. }
  1733. }
  1734. }
  1735. }
  1736. //-------------------------------------------------------------------------------------------------
  1737. void W3DModelDraw::adjustTransformMtx(Matrix3D& mtx) const
  1738. {
  1739. const W3DModelDrawModuleData* d = getW3DModelDrawModuleData();
  1740. #ifdef CACHE_ATTACH_BONE
  1741. const Vector3* offset = d->getAttachToDrawableBoneOffset(getDrawable());
  1742. if (offset)
  1743. {
  1744. Vector3 tmp = mtx.Rotate_Vector(*offset);
  1745. mtx.Adjust_X_Translation(tmp.X);
  1746. mtx.Adjust_Y_Translation(tmp.Y);
  1747. mtx.Adjust_Z_Translation(tmp.Z);
  1748. }
  1749. #else
  1750. if (d->m_attachToDrawableBone.isNotEmpty())
  1751. {
  1752. // override the mtx in this case. yes, call drawable, since the bone in question is
  1753. // likely to be in another module!
  1754. Matrix3D boneMtx;
  1755. if (getDrawable()->getCurrentWorldspaceClientBonePositions(d->m_attachToDrawableBone.str(), boneMtx))
  1756. {
  1757. mtx = boneMtx;
  1758. }
  1759. else
  1760. {
  1761. DEBUG_LOG(("m_attachToDrawableBone %s not found\n",getW3DModelDrawModuleData()->m_attachToDrawableBone.str()));
  1762. }
  1763. }
  1764. #endif
  1765. if (m_curState->m_flags & (1<<ADJUST_HEIGHT_BY_CONSTRUCTION_PERCENT))
  1766. {
  1767. const Object *obj = getDrawable()->getObject();
  1768. if (obj)
  1769. {
  1770. Real pct = obj->getConstructionPercent();
  1771. if (pct >= 0.0f)
  1772. {
  1773. Real height = obj->getGeometryInfo().getMaxHeightAbovePosition();
  1774. mtx.Translate_Z(-height + (height * pct / 100.0f));
  1775. }
  1776. }
  1777. }
  1778. }
  1779. //-------------------------------------------------------------------------------------------------
  1780. void W3DModelDraw::doDrawModule(const Matrix3D* transformMtx)
  1781. {
  1782. // update whether or not we should be animating.
  1783. setPauseAnimation( !getDrawable()->getShouldAnimate(getW3DModelDrawModuleData()->m_animationsRequirePower) );
  1784. Matrix3D scaledTransform;
  1785. if (getDrawable()->getInstanceScale() != 1.0f)
  1786. {
  1787. // do custom scaling of the W3D model.
  1788. scaledTransform = *transformMtx;
  1789. scaledTransform.Scale(getDrawable()->getInstanceScale());
  1790. transformMtx = &scaledTransform;
  1791. if (m_renderObject)
  1792. m_renderObject->Set_ObjectScale(getDrawable()->getInstanceScale());
  1793. }
  1794. if (isAnimationComplete(m_renderObject))
  1795. {
  1796. if (m_curState != NULL && m_nextState != NULL)
  1797. {
  1798. //DEBUG_LOG(("transition %s is complete\n",m_curState->m_description.str()));
  1799. const ModelConditionInfo* nextState = m_nextState;
  1800. UnsignedInt nextDuration = m_nextStateAnimLoopDuration;
  1801. m_nextState = NULL;
  1802. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  1803. setModelState(nextState);
  1804. if (nextDuration != NO_NEXT_DURATION)
  1805. {
  1806. //DEBUG_LOG(("restoring pending duration of %d frames\n",nextDuration));
  1807. setAnimationLoopDuration(nextDuration);
  1808. }
  1809. }
  1810. if (m_renderObject &&
  1811. m_curState != NULL &&
  1812. m_whichAnimInCurState != -1)
  1813. {
  1814. if (m_curState->m_animations[m_whichAnimInCurState].isIdleAnim())
  1815. {
  1816. //DEBUG_LOG(("randomly switching to new idle state!\n"));
  1817. // state hasn't changed, if it's been awhile, switch the idle anim
  1818. // (yes, that's right: pass curState for prevState)
  1819. adjustAnimation(m_curState, -1.0);
  1820. }
  1821. else if (testFlagBit(m_curState->m_flags, RESTART_ANIM_WHEN_COMPLETE))
  1822. {
  1823. adjustAnimation(m_curState, -1.0);
  1824. }
  1825. }
  1826. }
  1827. adjustAnimSpeedToMovementSpeed();
  1828. // set our position in the render object to our position of the drawable
  1829. if (m_renderObject)
  1830. {
  1831. Matrix3D mtx = *transformMtx;
  1832. adjustTransformMtx(mtx);
  1833. m_renderObject->Set_Transform(mtx);
  1834. }
  1835. handleClientTurretPositioning();
  1836. recalcBonesForClientParticleSystems();
  1837. const W3DModelDrawModuleData *modData = getW3DModelDrawModuleData();
  1838. if ( modData->m_particlesAttachedToAnimatedBones )
  1839. updateBonesForClientParticleSystems();// LORENZEN ADDED THIS
  1840. // IT REPOSITIONS PARTICLESYSTEMS TO TSTAY IN SYNC WITH ANIMATED BONES
  1841. handleClientRecoil();
  1842. }
  1843. //-------------------------------------------------------------------------------------------------
  1844. const ModelConditionInfo* W3DModelDraw::findTransitionForSig(TransitionSig sig) const
  1845. {
  1846. const TransitionMap& transitionMap = getW3DModelDrawModuleData()->m_transitionMap;
  1847. TransitionMap::const_iterator it = transitionMap.find(sig);
  1848. if (it != transitionMap.end())
  1849. {
  1850. return &(*it).second;
  1851. }
  1852. return NULL;
  1853. }
  1854. //-------------------------------------------------------------------------------------------------
  1855. Real W3DModelDraw::getCurrentAnimFraction() const
  1856. {
  1857. if (m_curState != NULL
  1858. && isAnyMaintainFrameFlagSet(m_curState->m_flags)
  1859. && m_renderObject != NULL
  1860. && m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD)
  1861. {
  1862. float framenum, dummy;
  1863. int mode, numFrames;
  1864. HLodClass* hlod = (HLodClass*)m_renderObject;
  1865. /*HAnimClass* anim =*/ hlod->Peek_Animation_And_Info(framenum, numFrames, mode, dummy);
  1866. if (framenum < 0.0)
  1867. return 0.0;
  1868. else if (framenum >= numFrames)
  1869. return 1.0;
  1870. else
  1871. return (Real)framenum / ((Real)numFrames - 1);
  1872. }
  1873. else
  1874. {
  1875. return -1.0;
  1876. }
  1877. }
  1878. //-------------------------------------------------------------------------------------------------
  1879. void W3DModelDraw::adjustAnimation(const ModelConditionInfo* prevState, Real prevAnimFraction)
  1880. {
  1881. if (!m_curState)
  1882. return;
  1883. // if the current state has m_animations associated, do the right thing
  1884. Int numAnims = m_curState->m_animations.size();
  1885. if (numAnims > 0)
  1886. {
  1887. if (numAnims == 1)
  1888. {
  1889. m_whichAnimInCurState = 0;
  1890. }
  1891. else if (prevState == m_curState)
  1892. {
  1893. // select an idle anim different than the currently playing one
  1894. Int animToAvoid = m_whichAnimInCurState;
  1895. while (m_whichAnimInCurState == animToAvoid)
  1896. m_whichAnimInCurState = GameClientRandomValue(0, numAnims-1);
  1897. }
  1898. else
  1899. {
  1900. m_whichAnimInCurState = GameClientRandomValue(0, numAnims-1);
  1901. }
  1902. const W3DAnimationInfo& animInfo = m_curState->m_animations[m_whichAnimInCurState];
  1903. HAnimClass* animHandle = animInfo.getAnimHandle(); // note that this now returns an ADDREFED handle, which must be released by the caller!
  1904. if (m_renderObject && animHandle)
  1905. {
  1906. Int startFrame = 0;
  1907. if (m_curState->m_mode == RenderObjClass::ANIM_MODE_ONCE_BACKWARDS ||
  1908. m_curState->m_mode == RenderObjClass::ANIM_MODE_LOOP_BACKWARDS)
  1909. {
  1910. startFrame = animHandle->Get_Num_Frames()-1;
  1911. }
  1912. if (testFlagBit(m_curState->m_flags, RANDOMIZE_START_FRAME))
  1913. {
  1914. startFrame = GameClientRandomValue(0, animHandle->Get_Num_Frames()-1);
  1915. }
  1916. else if (testFlagBit(m_curState->m_flags, START_FRAME_FIRST))
  1917. {
  1918. startFrame = 0;
  1919. }
  1920. else if (testFlagBit(m_curState->m_flags, START_FRAME_LAST))
  1921. {
  1922. startFrame = animHandle->Get_Num_Frames()-1;
  1923. }
  1924. // order is important here: MAINTAIN_FRAME_ACROSS_STATES is overridden by the other bits, above.
  1925. else if (isAnyMaintainFrameFlagSet(m_curState->m_flags) &&
  1926. prevState &&
  1927. prevState != m_curState &&
  1928. isAnyMaintainFrameFlagSet(prevState->m_flags) &&
  1929. isCommonMaintainFrameFlagSet(m_curState->m_flags, prevState->m_flags) &&
  1930. prevAnimFraction >= 0.0)
  1931. {
  1932. startFrame = REAL_TO_INT(prevAnimFraction * animHandle->Get_Num_Frames()-1);
  1933. }
  1934. m_renderObject->Set_Animation(animHandle, startFrame, m_curState->m_mode);
  1935. REF_PTR_RELEASE(animHandle);
  1936. animHandle = NULL;
  1937. if (m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD)
  1938. {
  1939. HLodClass *hlod = (HLodClass*)m_renderObject;
  1940. Real factor = GameClientRandomValueReal( m_curState->m_animMinSpeedFactor, m_curState->m_animMaxSpeedFactor );
  1941. hlod->Set_Animation_Frame_Rate_Multiplier( factor );
  1942. }
  1943. }
  1944. }
  1945. else
  1946. {
  1947. m_whichAnimInCurState = -1;
  1948. }
  1949. }
  1950. //-------------------------------------------------------------------------------------------------
  1951. Bool W3DModelDraw::setCurAnimDurationInMsec(Real desiredDurationInMsec)
  1952. {
  1953. if (m_renderObject && m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD)
  1954. {
  1955. HLodClass* hlod = (HLodClass*)m_renderObject;
  1956. HAnimClass* anim = hlod->Peek_Animation();
  1957. if (anim)
  1958. {
  1959. Real naturalDurationInMsec = anim->Get_Num_Frames() * 1000.0f / anim->Get_Frame_Rate();
  1960. if (naturalDurationInMsec > 0.0f && desiredDurationInMsec > 0.0f)
  1961. {
  1962. Real multiplier = naturalDurationInMsec / desiredDurationInMsec;
  1963. hlod->Set_Animation_Frame_Rate_Multiplier(multiplier);
  1964. return true;
  1965. }
  1966. }
  1967. }
  1968. return false;
  1969. }
  1970. //-------------------------------------------------------------------------------------------------
  1971. Real W3DModelDraw::getCurAnimDistanceCovered() const
  1972. {
  1973. if (m_curState != NULL && m_whichAnimInCurState >= 0)
  1974. {
  1975. const W3DAnimationInfo& animInfo = m_curState->m_animations[m_whichAnimInCurState];
  1976. #if defined(_DEBUG) || defined(_INTERNAL)
  1977. if (TheSkateDistOverride != 0.0f)
  1978. return TheSkateDistOverride;
  1979. #endif
  1980. return animInfo.getDistanceCovered();
  1981. }
  1982. return 0.0f;
  1983. }
  1984. //-------------------------------------------------------------------------------------------------
  1985. /**
  1986. Utility function to make it easier to recursively hide all objects connected to a certain bone.
  1987. We will hide all objects connected to bones which are children of boneIdx
  1988. */
  1989. static void doHideShowBoneSubObjs(Bool state, Int numSubObjects, Int boneIdx, RenderObjClass *fullObject, const HTreeClass *htree)
  1990. {
  1991. #if 1 //(gth) fixed and tested this version
  1992. for (Int i=0; i < numSubObjects; i++)
  1993. {
  1994. bool is_child = false;
  1995. Int parentBoneIndex = fullObject->Get_Sub_Object_Bone_Index(0, i);
  1996. while (parentBoneIndex != 0)
  1997. {
  1998. parentBoneIndex = htree->Get_Parent_Index(parentBoneIndex);
  1999. if (parentBoneIndex == boneIdx)
  2000. {
  2001. is_child = true;
  2002. break;
  2003. }
  2004. }
  2005. if (is_child)
  2006. {
  2007. RenderObjClass* childObject = fullObject->Get_Sub_Object(i);
  2008. childObject->Set_Hidden(state);
  2009. childObject->Release_Ref();
  2010. }
  2011. }
  2012. #endif
  2013. #if 0 //old slow version
  2014. for (Int i=0; i < numSubObjects; i++)
  2015. {
  2016. Int childBoneIndex = fullObject->Get_Sub_Object_Bone_Index(0, i);
  2017. Int parentIndex = htree->Get_Parent_Index(childBoneIndex);
  2018. if (childBoneIndex == parentIndex)
  2019. continue;
  2020. if (parentIndex == boneIdx) // this object has our subobject as parent so copy hide state
  2021. {
  2022. RenderObjClass* childObject = fullObject->Get_Sub_Object(i);
  2023. // recurse down the hierarchy to hide all sub-children
  2024. doHideShowBoneSubObjs(state, numSubObjects, childBoneIndex, fullObject, htree);
  2025. childObject->Set_Hidden(state);
  2026. childObject->Release_Ref();
  2027. }
  2028. }
  2029. #endif
  2030. }
  2031. //-------------------------------------------------------------------------------------------------
  2032. void ModelConditionInfo::WeaponBarrelInfo::setMuzzleFlashHidden(RenderObjClass *fullObject, Bool hide) const
  2033. {
  2034. if (fullObject)
  2035. {
  2036. RenderObjClass* childObject = fullObject->Get_Sub_Object_On_Bone(0, m_muzzleFlashBone);
  2037. if (childObject)
  2038. {
  2039. childObject->Set_Hidden(hide);
  2040. childObject->Release_Ref();
  2041. }
  2042. else
  2043. {
  2044. DEBUG_CRASH(("*** ASSET ERROR: childObject %s not found in setMuzzleFlashHidden()\n",m_muzzleFlashBoneName.str()));
  2045. }
  2046. }
  2047. }
  2048. //-------------------------------------------------------------------------------------------------
  2049. void W3DModelDraw::doHideShowSubObjs(const std::vector<ModelConditionInfo::HideShowSubObjInfo>* vec)
  2050. {
  2051. if (!m_renderObject)
  2052. return;
  2053. if (!vec->empty())
  2054. {
  2055. for (std::vector<ModelConditionInfo::HideShowSubObjInfo>::const_iterator it = vec->begin(); it != vec->end(); ++it)
  2056. {
  2057. Int objIndex;
  2058. RenderObjClass* subObj;
  2059. if ((subObj = m_renderObject->Get_Sub_Object_By_Name(it->subObjName.str(), &objIndex)) != NULL)
  2060. {
  2061. subObj->Set_Hidden(it->hide);
  2062. const HTreeClass *htree = m_renderObject->Get_HTree();
  2063. if (htree)
  2064. {
  2065. //get the bone of this subobject so we can hide all other child objects that use this bone
  2066. //as a parent.
  2067. Int boneIdx = m_renderObject->Get_Sub_Object_Bone_Index(0, objIndex);
  2068. doHideShowBoneSubObjs(it->hide, m_renderObject->Get_Num_Sub_Objects(), boneIdx, m_renderObject, htree);
  2069. }
  2070. subObj->Release_Ref();
  2071. }
  2072. else
  2073. {
  2074. DEBUG_CRASH(("*** ASSET ERROR: SubObject %s not found (%s)!\n",it->subObjName.str(),getDrawable()->getTemplate()->getName().str()));
  2075. }
  2076. }
  2077. }
  2078. //Kris (added Aug 2002)
  2079. //This is really important as it allows a person to override modelcondition show/hide objects. Used by the A10 strike
  2080. //September 2002 -- now used by the UpgradeSubObject system.
  2081. if( !m_subObjectVec.empty() )
  2082. {
  2083. updateSubObjects();
  2084. }
  2085. }
  2086. //-------------------------------------------------------------------------------------------------
  2087. void W3DModelDraw::stopClientParticleSystems()
  2088. {
  2089. for (std::vector<ParticleSysTrackerType>::const_iterator it = m_particleSystemIDs.begin(); it != m_particleSystemIDs.end(); ++it)
  2090. //for (std::vector<ParticleSystemID>::const_iterator it = m_particleSystemIDs.begin(); it != m_particleSystemIDs.end(); ++it)
  2091. {
  2092. ParticleSystem *sys = TheParticleSystemManager->findParticleSystem((*it).id);
  2093. if (sys != NULL)
  2094. {
  2095. // this can be NULL
  2096. sys->destroy();
  2097. }
  2098. }
  2099. m_particleSystemIDs.clear();
  2100. }
  2101. //-------------------------------------------------------------------------------------------------
  2102. /*
  2103. DANGER WARNING READ ME
  2104. DANGER WARNING READ ME
  2105. DANGER WARNING READ ME
  2106. This function must not EVER do ANYTHING which can in any way, shape, or form
  2107. affect GameLogic in any way; if it does, net desyncs will occur and we
  2108. will all lose our jobs. This must remain pure-client-only, and ensure
  2109. that nothing it does can be detected, even in read-only form, by GameLogic!
  2110. DANGER WARNING READ ME
  2111. DANGER WARNING READ ME
  2112. DANGER WARNING READ ME
  2113. */
  2114. void W3DModelDraw::handleClientTurretPositioning()
  2115. {
  2116. if (!m_curState || !(m_curState->m_validStuff & ModelConditionInfo::TURRETS_VALID))
  2117. return;
  2118. for (int tslot = 0; tslot < MAX_TURRETS; ++tslot)
  2119. {
  2120. const ModelConditionInfo::TurretInfo& tur = m_curState->m_turrets[tslot];
  2121. Real turretAngle = 0;
  2122. Real turretPitch = 0;
  2123. if (tur.m_turretAngleBone || tur.m_turretPitchBone)
  2124. {
  2125. const Object *obj = getDrawable()->getObject();
  2126. if (obj)
  2127. {
  2128. const AIUpdateInterface* ai = obj->getAIUpdateInterface();
  2129. if (ai)
  2130. ai->getTurretRotAndPitch((WhichTurretType)tslot, &turretAngle, &turretPitch);
  2131. }
  2132. // do turret, if any
  2133. if (tur.m_turretAngleBone != 0)
  2134. {
  2135. if (m_curState)
  2136. turretAngle += tur.m_turretArtAngle;
  2137. Matrix3D turretXfrm(1);
  2138. turretXfrm.Rotate_Z(turretAngle);
  2139. if (m_renderObject)
  2140. {
  2141. m_renderObject->Capture_Bone( tur.m_turretAngleBone );
  2142. m_renderObject->Control_Bone( tur.m_turretAngleBone, turretXfrm );
  2143. }
  2144. }
  2145. // do turret pitch, if any
  2146. if (tur.m_turretPitchBone != 0)
  2147. {
  2148. if (m_curState)
  2149. turretPitch += tur.m_turretArtPitch;
  2150. Matrix3D turretPitchXfrm(1);
  2151. turretPitchXfrm.Rotate_Y(-turretPitch);
  2152. if (m_renderObject)
  2153. {
  2154. m_renderObject->Capture_Bone( tur.m_turretPitchBone );
  2155. m_renderObject->Control_Bone( tur.m_turretPitchBone, turretPitchXfrm );
  2156. }
  2157. }
  2158. }
  2159. } // next tslot
  2160. }
  2161. //void W3DModelDraw::handleClientFlagPositioning()
  2162. //{
  2163. // const ModelConditionInfo::TurretInfo& tur = m_curState->m_turrets[tslot];
  2164. // Real turretAngle = TheGlobalData->m_downwindAngle;
  2165. // if (tur.m_flagAngleBone )
  2166. // {
  2167. // Matrix3D turretXfrm(1);
  2168. // turretXfrm.Rotate_Z(turretAngle);
  2169. // if (m_renderObject)
  2170. // {
  2171. // m_renderObject->Capture_Bone( tur.m_turretAngleBone );
  2172. // m_renderObject->Control_Bone( tur.m_turretAngleBone, turretXfrm );
  2173. // }
  2174. // }
  2175. //}
  2176. //-------------------------------------------------------------------------------------------------
  2177. /*
  2178. Note that, strictly speaking this code is WRONG, since it assumes it will get called every frame,
  2179. but in fact, will only get called every frame that it is visible. In practice, this isn't a big problem,
  2180. but...
  2181. @todo fix me someday (srj)
  2182. */
  2183. void W3DModelDraw::handleClientRecoil()
  2184. {
  2185. const W3DModelDrawModuleData* d = getW3DModelDrawModuleData();
  2186. if (!(m_curState->m_validStuff & ModelConditionInfo::BARRELS_VALID))
  2187. {
  2188. return;
  2189. }
  2190. // do recoil, if any
  2191. for (int wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  2192. {
  2193. if (!m_curState->m_hasRecoilBonesOrMuzzleFlashes[wslot])
  2194. continue;
  2195. const ModelConditionInfo::WeaponBarrelInfoVec& barrels = m_curState->m_weaponBarrelInfoVec[wslot];
  2196. WeaponRecoilInfoVec& recoils = m_weaponRecoilInfoVec[wslot];
  2197. Int count = barrels.size();
  2198. Int recoilCount = recoils.size();
  2199. DEBUG_ASSERTCRASH(count == recoilCount, ("Barrel count != recoil count!"));
  2200. count = (count>recoilCount)?recoilCount:count;
  2201. for (Int i = 0; i < count; ++i)
  2202. {
  2203. if (barrels[i].m_muzzleFlashBone != 0)
  2204. {
  2205. Bool hidden = recoils[i].m_state != WeaponRecoilInfo::RECOIL_START;
  2206. //DEBUG_LOG(("adjust muzzleflash %08lx for Draw %08lx state %s to %d at frame %d\n",subObjToHide,this,m_curState->m_description.str(),hidden?1:0,TheGameLogic->getFrame()));
  2207. barrels[i].setMuzzleFlashHidden(m_renderObject, hidden);
  2208. }
  2209. const Real TINY_RECOIL = 0.01f;
  2210. if (barrels[i].m_recoilBone != 0)
  2211. {
  2212. switch (recoils[i].m_state )
  2213. {
  2214. case WeaponRecoilInfo::IDLE:
  2215. // nothing
  2216. break;
  2217. case WeaponRecoilInfo::RECOIL_START:
  2218. case WeaponRecoilInfo::RECOIL:
  2219. recoils[i].m_shift += recoils[i].m_recoilRate;
  2220. recoils[i].m_recoilRate *= d->m_recoilDamping; // recoil decelerates
  2221. if (recoils[i].m_shift >= d->m_maxRecoil)
  2222. {
  2223. recoils[i].m_shift = d->m_maxRecoil;
  2224. recoils[i].m_state = WeaponRecoilInfo::SETTLE;
  2225. }
  2226. else if (fabs(recoils[i].m_recoilRate) < TINY_RECOIL)
  2227. {
  2228. recoils[i].m_state = WeaponRecoilInfo::SETTLE;
  2229. }
  2230. break;
  2231. case WeaponRecoilInfo::SETTLE:
  2232. recoils[i].m_shift -= d->m_recoilSettle;
  2233. if (recoils[i].m_shift <= 0.0f)
  2234. {
  2235. recoils[i].m_shift = 0.0f;
  2236. recoils[i].m_state = WeaponRecoilInfo::IDLE;
  2237. }
  2238. break;
  2239. }
  2240. Matrix3D gunXfrm;
  2241. gunXfrm.Make_Identity();
  2242. gunXfrm.Translate_X( -recoils[i].m_shift );
  2243. //DEBUG_ASSERTLOG(recoils[i].m_shift==0.0f,("adjust bone %d by %f\n",recoils[i].m_recoilBone,recoils[i].m_shift));
  2244. if (m_renderObject)
  2245. {
  2246. m_renderObject->Capture_Bone( barrels[i].m_recoilBone );
  2247. m_renderObject->Control_Bone( barrels[i].m_recoilBone, gunXfrm );
  2248. }
  2249. }
  2250. else
  2251. {
  2252. recoils[i].m_state = WeaponRecoilInfo::IDLE;
  2253. //DEBUG_LOG(("reset Draw %08lx state %08lx\n",this,m_curState));
  2254. }
  2255. }
  2256. }
  2257. }
  2258. //-------------------------------------------------------------------------------------------------
  2259. /*
  2260. DANGER WARNING READ ME
  2261. DANGER WARNING READ ME
  2262. DANGER WARNING READ ME
  2263. This function must not EVER do ANYTHING which can in any way, shape, or form
  2264. affect GameLogic in any way; if it does, net desyncs will occur and we
  2265. will all lose our jobs. This must remain pure-client-only, and ensure
  2266. that nothing it does can be detected, even in read-only form, by GameLogic!
  2267. DANGER WARNING READ ME
  2268. DANGER WARNING READ ME
  2269. DANGER WARNING READ ME
  2270. */
  2271. void W3DModelDraw::recalcBonesForClientParticleSystems()
  2272. {
  2273. if (m_needRecalcBoneParticleSystems)
  2274. {
  2275. const Drawable* drawable = getDrawable();
  2276. if (drawable != NULL )
  2277. {
  2278. if( m_curState != NULL && drawable->testDrawableStatus( DRAWABLE_STATUS_NO_STATE_PARTICLES ) == FALSE )
  2279. {
  2280. for (std::vector<ParticleSysBoneInfo>::const_iterator it = m_curState->m_particleSysBones.begin(); it != m_curState->m_particleSysBones.end(); ++it)
  2281. {
  2282. ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(it->particleSystemTemplate);
  2283. if (sys != NULL)
  2284. {
  2285. Coord3D pos;
  2286. pos.zero();
  2287. Real rotation = 0.0f;
  2288. Int boneIndex = m_renderObject ? m_renderObject->Get_Bone_Index(it->boneName.str()) : 0;
  2289. if (boneIndex != 0)
  2290. {
  2291. // ugh... kill the mtx so we get it in modelspace, not world space
  2292. Matrix3D originalTransform = m_renderObject->Get_Transform(); // save the transform
  2293. Matrix3D tmp(true);
  2294. tmp.Scale(getDrawable()->getScale());
  2295. m_renderObject->Set_Transform(tmp); // set to identity transform
  2296. const Matrix3D boneTransform = m_renderObject->Get_Bone_Transform(boneIndex);
  2297. Vector3 vpos = boneTransform.Get_Translation();
  2298. rotation = boneTransform.Get_Z_Rotation();
  2299. m_renderObject->Set_Transform(originalTransform); // restore it
  2300. pos.x = vpos.X;
  2301. pos.y = vpos.Y;
  2302. pos.z = vpos.Z;
  2303. }
  2304. // got the bone position...
  2305. sys->setPosition(&pos);
  2306. // and the direction, so that the system is oriented like the bone is
  2307. sys->rotateLocalTransformZ(rotation);
  2308. // Attatch it to the object...
  2309. sys->attachToDrawable(drawable);
  2310. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  2311. sys->setSaveable(FALSE);
  2312. if (drawable->isDrawableEffectivelyHidden() || m_fullyObscuredByShroud)
  2313. {
  2314. sys->stop(); // don't start the systems for drawables that are hidden.
  2315. }
  2316. // store the particle system id so we can kill it later.
  2317. ParticleSysTrackerType tracker;
  2318. tracker.id = sys->getSystemID();
  2319. tracker.boneIndex = boneIndex;
  2320. m_particleSystemIDs.push_back( tracker );
  2321. }
  2322. }
  2323. }
  2324. }
  2325. m_needRecalcBoneParticleSystems = false;
  2326. }
  2327. }
  2328. //-------------------------------------------------------------------------------------------------
  2329. /*
  2330. DANGER WARNING READ ME
  2331. DANGER WARNING READ ME
  2332. DANGER WARNING READ ME
  2333. This function must not EVER do ANYTHING which can in any way, shape, or form
  2334. affect GameLogic in any way; if it does, net desyncs will occur and we
  2335. will all lose our jobs. This must remain pure-client-only, and ensure
  2336. that nothing it does can be detected, even in read-only form, by GameLogic!
  2337. DANGER WARNING READ ME
  2338. DANGER WARNING READ ME
  2339. DANGER WARNING READ ME
  2340. */
  2341. Bool W3DModelDraw::updateBonesForClientParticleSystems()
  2342. {
  2343. const Drawable* drawable = getDrawable();
  2344. if (drawable != NULL && m_curState != NULL && m_renderObject != NULL )
  2345. {
  2346. // Matrix3D originalTransform = m_renderObject->Get_Transform();
  2347. // Matrix3D tmp = originalTransform;
  2348. // Vector3 zeroTranslation(0,0,0);
  2349. // tmp.Set_Translation( zeroTranslation );
  2350. // tmp.Scale(drawable->getScale());
  2351. // m_renderObject->Set_Transform(tmp);
  2352. for (std::vector<ParticleSysTrackerType>::const_iterator it = m_particleSystemIDs.begin(); it != m_particleSystemIDs.end(); ++it)
  2353. {
  2354. ParticleSystem *sys = TheParticleSystemManager->findParticleSystem((*it).id);
  2355. Int boneIndex = (*it).boneIndex;
  2356. if ( (sys != NULL) && (boneIndex != 0) )
  2357. {
  2358. const Matrix3D boneTransform = m_renderObject->Get_Bone_Transform(boneIndex);// just a little worried about state changes
  2359. Vector3 vpos = boneTransform.Get_Translation();
  2360. Coord3D pos;
  2361. pos.x = vpos.X;
  2362. pos.y = vpos.Y;
  2363. pos.z = vpos.Z;
  2364. sys->setPosition(&pos);
  2365. Real orientation = boneTransform.Get_Z_Rotation();
  2366. sys->rotateLocalTransformZ(orientation);
  2367. sys->setLocalTransform(&boneTransform);
  2368. sys->setSkipParentXfrm(true);
  2369. }
  2370. }// next praticle system
  2371. // m_renderObject->Set_Transform(originalTransform);
  2372. }// end if Drawable
  2373. return TRUE;
  2374. }
  2375. //-------------------------------------------------------------------------------------------------
  2376. void W3DModelDraw::setTerrainDecal(TerrainDecalType type)
  2377. {
  2378. if (m_terrainDecal)
  2379. m_terrainDecal->release();
  2380. m_terrainDecal = NULL;
  2381. if (type == TERRAIN_DECAL_NONE || type >= TERRAIN_DECAL_MAX)
  2382. //turning off decals on this object. (or bad value.)
  2383. return;
  2384. const ThingTemplate *tmplate=getDrawable()->getTemplate();
  2385. //create a new terrain decal
  2386. Shadow::ShadowTypeInfo decalInfo;
  2387. decalInfo.allowUpdates = FALSE; //shadow image will never update
  2388. decalInfo.allowWorldAlign = TRUE; //shadow image will wrap around world objects
  2389. decalInfo.m_type = SHADOW_ALPHA_DECAL;
  2390. //decalInfo.m_type = SHADOW_ADDITIVE_DECAL;//temporary kluge to test graphics
  2391. if (type == TERRAIN_DECAL_SHADOW_TEXTURE)
  2392. strcpy(decalInfo.m_ShadowName,tmplate->getShadowTextureName().str());
  2393. else
  2394. strcpy(decalInfo.m_ShadowName,TerrainDecalTextureName[type]);
  2395. decalInfo.m_sizeX = tmplate->getShadowSizeX();
  2396. decalInfo.m_sizeY = tmplate->getShadowSizeY();
  2397. decalInfo.m_offsetX = tmplate->getShadowOffsetX();
  2398. decalInfo.m_offsetY = tmplate->getShadowOffsetY();
  2399. if (TheProjectedShadowManager)
  2400. m_terrainDecal = TheProjectedShadowManager->addDecal(m_renderObject,&decalInfo);
  2401. if (m_terrainDecal)
  2402. { m_terrainDecal->enableShadowInvisible(m_fullyObscuredByShroud);
  2403. m_terrainDecal->enableShadowRender(m_shadowEnabled);
  2404. }
  2405. }
  2406. //-------------------------------------------------------------------------------------------------
  2407. void W3DModelDraw::setTerrainDecalSize(Real x, Real y)
  2408. {
  2409. if (m_terrainDecal)
  2410. {
  2411. m_terrainDecal->setSize(x,y);
  2412. }
  2413. }
  2414. //-------------------------------------------------------------------------------------------------
  2415. void W3DModelDraw::setTerrainDecalOpacity(Real o)
  2416. {
  2417. if (m_terrainDecal)
  2418. {
  2419. m_terrainDecal->setOpacity((Int)(255.0f * o));
  2420. }
  2421. }
  2422. //-------------------------------------------------------------------------------------------------
  2423. void W3DModelDraw::nukeCurrentRender(Matrix3D* xform)
  2424. {
  2425. // this needs to be "dirtied" so that the new pausing of the animation will be triggered.
  2426. m_pauseAnimation = false;
  2427. // changing geometry, so we need to remove shadow if present
  2428. if (m_shadow)
  2429. m_shadow->release();
  2430. m_shadow = NULL;
  2431. if(m_terrainDecal)
  2432. m_terrainDecal->release();
  2433. m_terrainDecal = NULL;
  2434. // remove existing render object from the scene
  2435. if (m_renderObject)
  2436. {
  2437. // save the transform for the new model
  2438. if (xform)
  2439. *xform = m_renderObject->Get_Transform();
  2440. W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
  2441. REF_PTR_RELEASE(m_renderObject);
  2442. m_renderObject = NULL;
  2443. }
  2444. else
  2445. {
  2446. if (xform)
  2447. {
  2448. *xform = *getDrawable()->getTransformMatrix();
  2449. }
  2450. }
  2451. }
  2452. //-------------------------------------------------------------------------------------------------
  2453. #if defined(_DEBUG) || defined(_INTERNAL) //art wants to see buildings without flags as a test.
  2454. void W3DModelDraw::hideGarrisonFlags(Bool hide)
  2455. {
  2456. if (!m_renderObject)
  2457. return;
  2458. Int objIndex;
  2459. RenderObjClass* subObj;
  2460. if ((subObj = m_renderObject->Get_Sub_Object_By_Name("POLE", &objIndex)) != NULL)
  2461. {
  2462. subObj->Set_Hidden(hide);
  2463. const HTreeClass *htree = m_renderObject->Get_HTree();
  2464. if (htree)
  2465. {
  2466. //get the bone of this subobject so we can hide all other child objects that use this bone
  2467. //as a parent.
  2468. Int boneIdx = m_renderObject->Get_Sub_Object_Bone_Index(0, objIndex);
  2469. doHideShowBoneSubObjs(hide, m_renderObject->Get_Num_Sub_Objects(), boneIdx, m_renderObject, htree);
  2470. }
  2471. subObj->Release_Ref();
  2472. }
  2473. }
  2474. #endif
  2475. //-------------------------------------------------------------------------------------------------
  2476. /** Hides all subobjects which are headlights. Used to disable lights on models during the day.*/
  2477. void W3DModelDraw::hideAllHeadlights(Bool hide)
  2478. {
  2479. if (m_renderObject)
  2480. {
  2481. for (Int subObj = 0; subObj < m_renderObject->Get_Num_Sub_Objects(); subObj++)
  2482. {
  2483. RenderObjClass* test = m_renderObject->Get_Sub_Object(subObj);
  2484. if (strstr(test->Get_Name(),"HEADLIGHT"))
  2485. {
  2486. test->Set_Hidden(hide);
  2487. }
  2488. test->Release_Ref();
  2489. }
  2490. }
  2491. }
  2492. //-------------------------------------------------------------------------------------------------
  2493. void W3DModelDraw::hideAllMuzzleFlashes(const ModelConditionInfo* state, RenderObjClass* renderObject)
  2494. {
  2495. if (!state || !renderObject)
  2496. return;
  2497. if (!(state->m_validStuff & ModelConditionInfo::BARRELS_VALID))
  2498. {
  2499. return;
  2500. }
  2501. for (int wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  2502. {
  2503. if (!state->m_hasRecoilBonesOrMuzzleFlashes[wslot])
  2504. continue;
  2505. const ModelConditionInfo::WeaponBarrelInfoVec& barrels = state->m_weaponBarrelInfoVec[wslot];
  2506. for (ModelConditionInfo::WeaponBarrelInfoVec::const_iterator it = barrels.begin(); it != barrels.end(); ++it)
  2507. {
  2508. if (it->m_muzzleFlashBone != 0)
  2509. {
  2510. it->setMuzzleFlashHidden(renderObject, true);
  2511. }
  2512. }
  2513. }
  2514. }
  2515. //-------------------------------------------------------------------------------------------------
  2516. static Bool turretNamesDiffer(const ModelConditionInfo* a, const ModelConditionInfo* b)
  2517. {
  2518. if (!(a->m_validStuff & ModelConditionInfo::TURRETS_VALID) || !(b->m_validStuff & ModelConditionInfo::TURRETS_VALID))
  2519. {
  2520. return true;
  2521. }
  2522. for (int i = 0; i < MAX_TURRETS; ++i)
  2523. if (a->m_turrets[i].m_turretAngleNameKey != b->m_turrets[i].m_turretAngleNameKey ||
  2524. a->m_turrets[i].m_turretPitchNameKey != b->m_turrets[i].m_turretPitchNameKey)
  2525. return true;
  2526. return false;
  2527. }
  2528. //-------------------------------------------------------------------------------------------------
  2529. /** Change the model for this drawable. If color is non-zero, it will also apply team color to the
  2530. model */
  2531. //-------------------------------------------------------------------------------------------------
  2532. void W3DModelDraw::setModelState(const ModelConditionInfo* newState)
  2533. {
  2534. DEBUG_ASSERTCRASH(newState, ("invalid state in W3DModelDraw::setModelState\n"));
  2535. #ifdef DEBUG_OBJECT_ID_EXISTS
  2536. if (getDrawable() && getDrawable()->getObject() && getDrawable()->getObject()->getID() == TheObjectIDToDebug)
  2537. {
  2538. DEBUG_LOG(("REQUEST switching to state %s for obj %s %d\n",newState->m_description.str(),getDrawable()->getObject()->getTemplate()->getName().str(),getDrawable()->getObject()->getID()));
  2539. }
  2540. #endif
  2541. const ModelConditionInfo* nextState = NULL;
  2542. if (m_curState != NULL && newState != NULL)
  2543. {
  2544. // if the requested state is the current state (and nothing is pending),
  2545. // or if the requested state is pending, just punt.
  2546. //
  2547. // note that if the requested state matches the current state AND
  2548. // we also have a pending state, then we nuke both of 'em. This sounds
  2549. // weird, but is deliberate: nextState and newState will always be "real" states (or null),
  2550. // leaving us these possibilities:
  2551. //
  2552. // -- curState is a transition state
  2553. // --in this case, new state should take precedence (unless there's a wait-to-finish marking,
  2554. // which would be handled by the else clause)
  2555. //
  2556. // -- curState is a real state, nextState is a real state with a wait-to-finish clause
  2557. // -- if curState == newState, presume that we should restart curState, thus punting thepending nextState
  2558. // -- if curState != newState, presume we should switch to it, thus punting curState and nextState
  2559. //
  2560. // -- curState is a real state, nextState is a real state without a wait-to-finish clause
  2561. // -- should be impossible!
  2562. if ((m_curState == newState && m_nextState == NULL) ||
  2563. (m_curState != NULL && m_nextState == newState))
  2564. {
  2565. #ifdef DEBUG_OBJECT_ID_EXISTS
  2566. if (getDrawable() && getDrawable()->getObject() && getDrawable()->getObject()->getID() == TheObjectIDToDebug)
  2567. {
  2568. DEBUG_LOG(("IGNORE duplicate state %s for obj %s %d\n",newState->m_description.str(),getDrawable()->getObject()->getTemplate()->getName().str(),getDrawable()->getObject()->getID()));
  2569. }
  2570. #endif
  2571. // I don't think he'll be interested...
  2572. // he's already got one, you see
  2573. return;
  2574. }
  2575. // check for allow-to-finish implicit transitions
  2576. else if (newState != m_curState &&
  2577. newState->m_allowToFinishKey != NAMEKEY_INVALID &&
  2578. newState->m_allowToFinishKey == m_curState->m_transitionKey &&
  2579. m_renderObject &&
  2580. !isAnimationComplete(m_renderObject))
  2581. {
  2582. #ifdef DEBUG_OBJECT_ID_EXISTS
  2583. if (getDrawable() && getDrawable()->getObject() && getDrawable()->getObject()->getID() == TheObjectIDToDebug)
  2584. {
  2585. DEBUG_LOG(("ALLOW_TO_FINISH state %s for obj %s %d\n",newState->m_description.str(),getDrawable()->getObject()->getTemplate()->getName().str(),getDrawable()->getObject()->getID()));
  2586. }
  2587. #endif
  2588. m_nextState = newState;
  2589. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  2590. return;
  2591. }
  2592. // check for a normal->normal state transition
  2593. else if (newState != m_curState &&
  2594. m_curState->m_transitionKey != NAMEKEY_INVALID &&
  2595. newState->m_transitionKey != NAMEKEY_INVALID)
  2596. {
  2597. TransitionSig sig = buildTransitionSig(m_curState->m_transitionKey, newState->m_transitionKey);
  2598. const ModelConditionInfo* transState = findTransitionForSig(sig);
  2599. if (transState != NULL)
  2600. {
  2601. #ifdef DEBUG_OBJECT_ID_EXISTS
  2602. if (getDrawable() && getDrawable()->getObject() && getDrawable()->getObject()->getID() == TheObjectIDToDebug)
  2603. {
  2604. DEBUG_LOG(("using TRANSITION state %s before requested state %s for obj %s %d\n",transState->m_description.str(),newState->m_description.str(),getDrawable()->getObject()->getTemplate()->getName().str(),getDrawable()->getObject()->getID()));
  2605. }
  2606. #endif
  2607. nextState = newState;
  2608. newState = transState;
  2609. }
  2610. }
  2611. }
  2612. // get this here, before we change anything... we'll need it to pass to adjustAnimation (srj)
  2613. Real prevAnimFraction = getCurrentAnimFraction();
  2614. //
  2615. // Set up any particle effects for the new model. Also stop the old ones, note that we
  2616. // will never allow the placement of new ones if the status bit is set that tells us
  2617. // we should not automatically create particle systems for the model condition states
  2618. //
  2619. if( getDrawable()->testDrawableStatus( DRAWABLE_STATUS_NO_STATE_PARTICLES ) == FALSE )
  2620. m_needRecalcBoneParticleSystems = true;
  2621. // always stop particle systems now
  2622. stopClientParticleSystems();
  2623. // and always hide muzzle flashes, in case modelstate and model have crossed at the same time
  2624. hideAllMuzzleFlashes(newState, m_renderObject);
  2625. // note that different states might use the same model; for these, don't go thru the
  2626. // expense of creating a new render-object. (exception: if color is changing, or subobjs are changing,
  2627. // or a few other things...)
  2628. if (m_curState == NULL ||
  2629. newState->m_modelName != m_curState->m_modelName ||
  2630. turretNamesDiffer(newState, m_curState)
  2631. // srj sez: I'm not sure why we want to do the "hard stuff" if we have projectile bones; I think
  2632. // it is a holdover from days gone by when bones were handled quite differently, rather than being cached.
  2633. // but doing this hard stuff is a lot more work, and I think it's unnecessary, so let's remove this.
  2634. //|| (newState->m_validStuff & ModelConditionInfo::HAS_PROJECTILE_BONES)
  2635. )
  2636. {
  2637. Matrix3D transform;
  2638. nukeCurrentRender(&transform);
  2639. Drawable* draw = getDrawable();
  2640. // create a new render object and set into drawable
  2641. if (newState->m_modelName.isEmpty())
  2642. {
  2643. m_renderObject = NULL;
  2644. }
  2645. else
  2646. {
  2647. m_renderObject = W3DDisplay::m_assetManager->Create_Render_Obj(newState->m_modelName.str(), draw->getScale(), m_hexColor);
  2648. DEBUG_ASSERTCRASH(m_renderObject, ("*** ASSET ERROR: Model %s not found!\n",newState->m_modelName.str()));
  2649. }
  2650. //BONEPOS_LOG(("validateStuff() from within W3DModelDraw::setModelState()\n"));
  2651. //BONEPOS_DUMPREAL(draw->getScale());
  2652. newState->validateStuff(m_renderObject, draw->getScale(), getW3DModelDrawModuleData()->m_extraPublicBones);
  2653. // ensure that any muzzle flashes from the *new* state, start out hidden...
  2654. // hideAllMuzzleFlashes(newState, m_renderObject);//moved to above
  2655. rebuildWeaponRecoilInfo(newState);
  2656. doHideShowSubObjs(&newState->m_hideShowVec);
  2657. #if defined(_DEBUG) || defined(_INTERNAL) //art wants to see buildings without flags as a test.
  2658. if (TheGlobalData->m_hideGarrisonFlags && draw->isKindOf(KINDOF_STRUCTURE))
  2659. hideGarrisonFlags(TRUE);
  2660. #endif
  2661. const ThingTemplate *tmplate = draw->getTemplate();
  2662. // set up tracks, if not already set.
  2663. if (m_renderObject &&
  2664. TheGlobalData->m_makeTrackMarks &&
  2665. !m_trackRenderObject &&
  2666. TheTerrainTracksRenderObjClassSystem != NULL &&
  2667. !getW3DModelDrawModuleData()->m_trackFile.isEmpty())
  2668. {
  2669. m_trackRenderObject = TheTerrainTracksRenderObjClassSystem->bindTrack(m_renderObject, 1.0f*MAP_XY_FACTOR, getW3DModelDrawModuleData()->m_trackFile.str());
  2670. if (draw && m_trackRenderObject)
  2671. m_trackRenderObject->setOwnerDrawable(draw);
  2672. }
  2673. // set up shadows
  2674. if (m_renderObject && TheW3DShadowManager && tmplate->getShadowType() != SHADOW_NONE)
  2675. {
  2676. Shadow::ShadowTypeInfo shadowInfo;
  2677. strcpy(shadowInfo.m_ShadowName, tmplate->getShadowTextureName().str());
  2678. DEBUG_ASSERTCRASH(shadowInfo.m_ShadowName[0] != '\0', ("this should be validated in ThingTemplate now"));
  2679. shadowInfo.allowUpdates = FALSE; //shadow image will never update
  2680. shadowInfo.allowWorldAlign = TRUE; //shadow image will wrap around world objects
  2681. shadowInfo.m_type = (ShadowType)tmplate->getShadowType();
  2682. shadowInfo.m_sizeX = tmplate->getShadowSizeX();
  2683. shadowInfo.m_sizeY = tmplate->getShadowSizeY();
  2684. shadowInfo.m_offsetX = tmplate->getShadowOffsetX();
  2685. shadowInfo.m_offsetY = tmplate->getShadowOffsetY();
  2686. m_shadow = TheW3DShadowManager->addShadow(m_renderObject, &shadowInfo, draw);
  2687. if (m_shadow)
  2688. { m_shadow->enableShadowInvisible(m_fullyObscuredByShroud);
  2689. m_shadow->enableShadowRender(m_shadowEnabled);
  2690. }
  2691. }
  2692. if( m_renderObject )
  2693. {
  2694. // set collision type for render object. Used by WW3D2 collision code.
  2695. if (tmplate->isKindOf(KINDOF_SELECTABLE))
  2696. {
  2697. m_renderObject->Set_Collision_Type( PICK_TYPE_SELECTABLE );
  2698. }
  2699. if( tmplate->isKindOf( KINDOF_SHRUBBERY ))
  2700. {
  2701. m_renderObject->Set_Collision_Type( PICK_TYPE_SHRUBBERY );
  2702. }
  2703. if( tmplate->isKindOf( KINDOF_MINE ))
  2704. {
  2705. m_renderObject->Set_Collision_Type( PICK_TYPE_MINES );
  2706. }
  2707. if( tmplate->isKindOf( KINDOF_FORCEATTACKABLE ))
  2708. {
  2709. m_renderObject->Set_Collision_Type( PICK_TYPE_FORCEATTACKABLE );
  2710. }
  2711. if( tmplate->isKindOf( KINDOF_CLICK_THROUGH ))
  2712. {
  2713. m_renderObject->Set_Collision_Type( 0 );
  2714. }
  2715. Object *obj = draw->getObject();
  2716. if( obj )
  2717. {
  2718. // for non bridge objects we adjust some collision types
  2719. if( obj->isKindOf( KINDOF_BRIDGE ) == FALSE &&
  2720. obj->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE )
  2721. {
  2722. if( obj->isKindOf( KINDOF_STRUCTURE ) && draw->getModelConditionFlags().test( MODELCONDITION_RUBBLE ) )
  2723. {
  2724. //A dead building, -- don't allow the user to click on rubble! Treat it as a location instead.
  2725. m_renderObject->Set_Collision_Type( 0 );
  2726. }
  2727. else if( obj->isEffectivelyDead() )
  2728. {
  2729. //A dead object, -- don't allow the user to click on rubble/hulks! Treat it as a location instead.
  2730. m_renderObject->Set_Collision_Type( 0 );
  2731. }
  2732. }
  2733. }
  2734. // add render object to our scene
  2735. W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
  2736. // tie in our drawable as the user data pointer in the render object
  2737. m_renderObject->Set_User_Data(draw->getDrawableInfo());
  2738. setTerrainDecal(draw->getTerrainDecalType());
  2739. //We created a new render object so we need to preserve the visibility state
  2740. //of the previous render object.
  2741. if (draw->isDrawableEffectivelyHidden())
  2742. {
  2743. m_renderObject->Set_Hidden(TRUE);
  2744. if (m_shadow)
  2745. m_shadow->enableShadowRender(FALSE);
  2746. m_shadowEnabled = FALSE;
  2747. }
  2748. //
  2749. // set the transform for the new model to that we saved before, we do this so that the
  2750. // model transition is smooth and will immediately appear at the same orientation and location
  2751. // as the previous one
  2752. //
  2753. m_renderObject->Set_Transform(transform);
  2754. onRenderObjRecreated();
  2755. }
  2756. }
  2757. else
  2758. {
  2759. //BONEPOS_LOG(("validateStuff() from within W3DModelDraw::setModelState()\n"));
  2760. //BONEPOS_DUMPREAL(getDrawable()->getScale());
  2761. newState->validateStuff(m_renderObject, getDrawable()->getScale(), getW3DModelDrawModuleData()->m_extraPublicBones);
  2762. rebuildWeaponRecoilInfo(newState);
  2763. // ensure that any muzzle flashes from the *previous* state, are hidden...
  2764. // hideAllMuzzleFlashes(m_curState, m_renderObject);// moved to above
  2765. doHideShowSubObjs(&newState->m_hideShowVec);
  2766. }
  2767. hideAllHeadlights(m_hideHeadlights);
  2768. const ModelConditionInfo* prevState = m_curState; // save, to pass to adjustAnimation (could be null)
  2769. m_curState = newState;
  2770. m_nextState = nextState;
  2771. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  2772. adjustAnimation(prevState, prevAnimFraction);
  2773. }
  2774. //-------------------------------------------------------------------------------------------------
  2775. void W3DModelDraw::replaceModelConditionState(const ModelConditionFlags& c)
  2776. {
  2777. m_hideHeadlights = c.test(MODELCONDITION_NIGHT) ? false : true;
  2778. const ModelConditionInfo* info = findBestInfo(c);
  2779. if (info)
  2780. setModelState(info);
  2781. hideAllHeadlights(m_hideHeadlights);
  2782. }
  2783. //-------------------------------------------------------------------------------------------------
  2784. void W3DModelDraw::setSelectable(Bool selectable)
  2785. {
  2786. // set collision type for render object. Used by WW3D2 collision code.
  2787. if( m_renderObject )
  2788. {
  2789. int current = m_renderObject->Get_Collision_Type();
  2790. if (selectable) {
  2791. current |= PICK_TYPE_SELECTABLE;
  2792. } else {
  2793. current &= ~PICK_TYPE_SELECTABLE;
  2794. }
  2795. m_renderObject->Set_Collision_Type(current);
  2796. } // end if
  2797. }
  2798. //-------------------------------------------------------------------------------------------------
  2799. void W3DModelDraw::replaceIndicatorColor(Color color)
  2800. {
  2801. if (!getW3DModelDrawModuleData()->m_okToChangeModelColor)
  2802. return;
  2803. if (getRenderObject())
  2804. {
  2805. Int newColor = (color == 0) ? 0 : (color | 0xFF000000);
  2806. if (newColor != m_hexColor)
  2807. {
  2808. m_hexColor = newColor;
  2809. // set these to NULL to force a regen of everything in setModelState.
  2810. const ModelConditionInfo* tmp = m_curState;
  2811. m_curState = NULL;
  2812. m_nextState = NULL;
  2813. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  2814. setModelState(tmp);
  2815. }
  2816. }
  2817. }
  2818. //-------------------------------------------------------------------------------------------------
  2819. // this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
  2820. Bool W3DModelDraw::clientOnly_getRenderObjInfo(Coord3D* pos, Real* boundingSphereRadius, Matrix3D* transform) const
  2821. {
  2822. if (!m_renderObject)
  2823. return false;
  2824. Vector3 objPos = m_renderObject->Get_Position(); //get position of object
  2825. pos->x = objPos.X;
  2826. pos->y = objPos.Y;
  2827. pos->z = objPos.Z;
  2828. *transform = m_renderObject->Get_Transform();
  2829. *boundingSphereRadius = m_renderObject->Get_Bounding_Sphere().Radius;
  2830. return true;
  2831. }
  2832. //-------------------------------------------------------------------------------------------------
  2833. Bool W3DModelDraw::getProjectileLaunchOffset(
  2834. const ModelConditionFlags& condition,
  2835. WeaponSlotType wslot,
  2836. Int specificBarrelToUse,
  2837. Matrix3D* launchPos,
  2838. WhichTurretType tur,
  2839. Coord3D* turretRotPos,
  2840. Coord3D* turretPitchPos
  2841. ) const
  2842. {
  2843. /*
  2844. Note, this recalcs the state every time, rather than using m_curState,
  2845. because m_curState could be a transition state, and we really need
  2846. to get the pristine bone(s) for the state that logic believes to be current,
  2847. not the one the client might currently be using...
  2848. */
  2849. const ModelConditionInfo* stateToUse = findBestInfo(condition);
  2850. if (!stateToUse)
  2851. {
  2852. CRCDEBUG_LOG(("can't find best info\n"));
  2853. //BONEPOS_LOG(("can't find best info\n"));
  2854. return false;
  2855. }
  2856. #if defined(_DEBUG) || defined(_INTERNAL)
  2857. CRCDEBUG_LOG(("W3DModelDraw::getProjectileLaunchOffset() for %s\n",
  2858. stateToUse->getDescription().str()));
  2859. #endif
  2860. #ifdef INTENSE_DEBUG
  2861. AsciiString flags;
  2862. for (Int i=0; i<condition.size(); ++i)
  2863. {
  2864. if (condition.test(i))
  2865. {
  2866. if (flags.isNotEmpty())
  2867. flags.concat(',');
  2868. flags.concat(condition.getNameFromSingleBit(i));
  2869. }
  2870. }
  2871. #endif // INTENSE_DEBUG
  2872. const W3DModelDrawModuleData* d = getW3DModelDrawModuleData();
  2873. //CRCDEBUG_LOG(("validateStuffs() from within W3DModelDraw::getProjectileLaunchOffset()\n"));
  2874. //DUMPREAL(getDrawable()->getScale());
  2875. //BONEPOS_LOG(("validateStuffs() from within W3DModelDraw::getProjectileLaunchOffset()\n"));
  2876. //BONEPOS_DUMPREAL(getDrawable()->getScale());
  2877. stateToUse->validateStuff(NULL, getDrawable()->getScale(), d->m_extraPublicBones);
  2878. DEBUG_ASSERTCRASH(stateToUse->m_transitionSig == NO_TRANSITION,
  2879. ("It is never legal to getProjectileLaunchOffset from a Transition state (they vary on a per-client basis)... however, we can fix this (see srj)\n"));
  2880. DEBUG_ASSERTCRASH(specificBarrelToUse >= 0, ("specificBarrelToUse should now always be explicit"));
  2881. #ifdef CACHE_ATTACH_BONE
  2882. #else
  2883. Coord3D techOffset;
  2884. techOffset.zero();
  2885. // must use pristine bone here since the result is used by logic
  2886. Matrix3D pivot;
  2887. if (d->m_attachToDrawableBone.isNotEmpty() &&
  2888. getDrawable()->getPristineBonePositions( d->m_attachToDrawableBone.str(), 0, NULL, &pivot, 1 ) == 1)
  2889. {
  2890. pivot.Pre_Rotate_Z(getDrawable()->getOrientation());
  2891. techOffset.x = pivot.Get_X_Translation();
  2892. techOffset.y = pivot.Get_Y_Translation();
  2893. techOffset.z = pivot.Get_Z_Translation();
  2894. }
  2895. #endif
  2896. CRCDEBUG_LOG(("wslot = %d\n", wslot));
  2897. const ModelConditionInfo::WeaponBarrelInfoVec& wbvec = stateToUse->m_weaponBarrelInfoVec[wslot];
  2898. if( wbvec.empty() )
  2899. {
  2900. // Can't find the launch pos, but they might still want the other info they asked for
  2901. CRCDEBUG_LOG(("empty wbvec\n"));
  2902. //BONEPOS_LOG(("empty wbvec\n"));
  2903. launchPos = NULL;
  2904. }
  2905. else
  2906. {
  2907. if (specificBarrelToUse < 0 || specificBarrelToUse >= wbvec.size())
  2908. specificBarrelToUse = 0;
  2909. if (launchPos)
  2910. {
  2911. CRCDEBUG_LOG(("specificBarrelToUse = %d\n", specificBarrelToUse));
  2912. *launchPos = wbvec[specificBarrelToUse].m_projectileOffsetMtx;
  2913. if (tur != TURRET_INVALID)
  2914. {
  2915. launchPos->Pre_Rotate_Z(stateToUse->m_turrets[tur].m_turretArtAngle);
  2916. launchPos->Pre_Rotate_Y(-stateToUse->m_turrets[tur].m_turretArtPitch);
  2917. }
  2918. #ifdef CACHE_ATTACH_BONE
  2919. #else
  2920. launchPos->Adjust_Translation( Vector3( techOffset.x, techOffset.y, techOffset.z ) );
  2921. #endif
  2922. DUMPMATRIX3D(launchPos);
  2923. }
  2924. }
  2925. if (turretRotPos)
  2926. turretRotPos->zero();
  2927. if (turretPitchPos)
  2928. turretPitchPos->zero();
  2929. if (tur != TURRET_INVALID)
  2930. {
  2931. #ifdef CACHE_ATTACH_BONE
  2932. const Vector3* offset = d->getAttachToDrawableBoneOffset(getDrawable());
  2933. #endif
  2934. const ModelConditionInfo::TurretInfo& turInfo = stateToUse->m_turrets[tur];
  2935. if (turretRotPos)
  2936. {
  2937. if (turInfo.m_turretAngleNameKey != NAMEKEY_INVALID &&
  2938. !stateToUse->findPristineBonePos(turInfo.m_turretAngleNameKey, *turretRotPos))
  2939. {
  2940. DEBUG_CRASH(("*** ASSET ERROR: TurretBone %s not found!\n",KEYNAME(turInfo.m_turretAngleNameKey).str()));
  2941. }
  2942. #ifdef CACHE_ATTACH_BONE
  2943. if (offset)
  2944. {
  2945. turretRotPos->x += offset->X;
  2946. turretRotPos->y += offset->Y;
  2947. turretRotPos->z += offset->Z;
  2948. }
  2949. #endif
  2950. }
  2951. if (turretPitchPos)
  2952. {
  2953. if (turInfo.m_turretPitchNameKey != NAMEKEY_INVALID &&
  2954. !stateToUse->findPristineBonePos(turInfo.m_turretPitchNameKey, *turretPitchPos))
  2955. {
  2956. DEBUG_CRASH(("*** ASSET ERROR: TurretBone %s not found!\n",KEYNAME(turInfo.m_turretPitchNameKey).str()));
  2957. }
  2958. #ifdef CACHE_ATTACH_BONE
  2959. if (offset)
  2960. {
  2961. turretPitchPos->x += offset->X;
  2962. turretPitchPos->y += offset->Y;
  2963. turretPitchPos->z += offset->Z;
  2964. }
  2965. #endif
  2966. }
  2967. }
  2968. return launchPos != NULL;// return if LaunchPos is valid or not
  2969. }
  2970. //-------------------------------------------------------------------------------------------------
  2971. Int W3DModelDraw::getPristineBonePositionsForConditionState(
  2972. const ModelConditionFlags& condition,
  2973. const char* boneNamePrefix,
  2974. Int startIndex,
  2975. Coord3D* positions,
  2976. Matrix3D* transforms,
  2977. Int maxBones
  2978. ) const
  2979. {
  2980. /*
  2981. Note, this recalcs the state every time, rather than using m_curState,
  2982. because m_curState could be a transition state, and we really need
  2983. to get the pristine bone(s) for the state that logic believes to be current,
  2984. not the one the client might currently be using...
  2985. */
  2986. const ModelConditionInfo* stateToUse = findBestInfo(condition);
  2987. if (!stateToUse)
  2988. return 0;
  2989. // if (isValidTimeToCalcLogicStuff())
  2990. // {
  2991. // CRCDEBUG_LOG(("W3DModelDraw::getPristineBonePositionsForConditionState() - state = '%s'\n",
  2992. // stateToUse->getDescription().str()));
  2993. // //CRCDEBUG_LOG(("renderObject == NULL: %d\n", (stateToUse==m_curState)?(m_renderObject == NULL):1));
  2994. // }
  2995. //BONEPOS_LOG(("validateStuff() from within W3DModelDraw::getPristineBonePositionsForConditionState()\n"));
  2996. //BONEPOS_DUMPREAL(getDrawable()->getScale());
  2997. // we must call this every time we set m_nextState, to ensure cached bones are happy
  2998. stateToUse->validateStuff(
  2999. // if the state is the current state, pass in the current render object
  3000. // so that we don't have to re-create it!
  3001. stateToUse == m_curState ? m_renderObject : NULL,
  3002. getDrawable()->getScale(),
  3003. getW3DModelDrawModuleData()->m_extraPublicBones);
  3004. const int MAX_BONE_GET = 64;
  3005. Matrix3D tmpMtx[MAX_BONE_GET];
  3006. if (maxBones > MAX_BONE_GET)
  3007. maxBones = MAX_BONE_GET;
  3008. if (transforms == NULL)
  3009. transforms = tmpMtx;
  3010. Int posCount = 0;
  3011. Int endIndex = (startIndex == 0) ? 0 : 99;
  3012. char buffer[256];
  3013. for (Int i = startIndex; i <= endIndex; ++i)
  3014. {
  3015. if (i == 0)
  3016. strcpy(buffer, boneNamePrefix);
  3017. else
  3018. sprintf(buffer, "%s%02d", boneNamePrefix, i);
  3019. for (char *c = buffer; c && *c; ++c)
  3020. {
  3021. // convert to all-lowercase since that's how we filled in the map
  3022. *c = tolower(*c);
  3023. }
  3024. const Matrix3D* mtx = stateToUse->findPristineBone(NAMEKEY(buffer), NULL);
  3025. if (mtx)
  3026. {
  3027. transforms[posCount] = *mtx;
  3028. // if (isValidTimeToCalcLogicStuff())
  3029. // {
  3030. // DUMPMATRIX3D(mtx);
  3031. // }
  3032. }
  3033. else
  3034. {
  3035. //DEBUG_CRASH(("*** ASSET ERROR: Bone %s not found!\n",buffer));
  3036. const Object *obj = getDrawable()->getObject();
  3037. if (obj)
  3038. transforms[posCount] = *obj->getTransformMatrix();
  3039. else
  3040. transforms[posCount].Make_Identity();
  3041. break;
  3042. }
  3043. ++posCount;
  3044. if (posCount >= maxBones)
  3045. break;
  3046. }
  3047. if (positions && transforms)
  3048. {
  3049. for (i = 0; i < posCount; ++i)
  3050. {
  3051. Vector3 pos = transforms[i].Get_Translation();
  3052. positions[i].x = pos.X;
  3053. positions[i].y = pos.Y;
  3054. positions[i].z = pos.Z;
  3055. // if (isValidTimeToCalcLogicStuff())
  3056. // {
  3057. // DUMPCOORD3D(&(positions[i]));
  3058. // }
  3059. }
  3060. }
  3061. // if (isValidTimeToCalcLogicStuff())
  3062. // {
  3063. // CRCDEBUG_LOG(("end of W3DModelDraw::getPristineBonePositionsForConditionState()\n"));
  3064. // }
  3065. return posCount;
  3066. }
  3067. //-------------------------------------------------------------------------------------------------
  3068. // (gth) C&C3 Added this accessor for the bounding box of a render object in a W3DModelDraw module
  3069. // this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
  3070. Bool W3DModelDraw::clientOnly_getRenderObjBoundBox(OBBoxClass * boundbox) const
  3071. {
  3072. if (!m_renderObject)
  3073. return false;
  3074. AABoxClass aabox;
  3075. m_renderObject->Get_Obj_Space_Bounding_Box(aabox);
  3076. Matrix3D tm = m_renderObject->Get_Transform();
  3077. // build an OBB for this AAB,transform
  3078. OBBoxClass box0(aabox.Center,aabox.Extent);
  3079. OBBoxClass::Transform(tm,box0,boundbox);
  3080. return true;
  3081. }
  3082. //-------------------------------------------------------------------------------------------------
  3083. // (gth) C&C3 Added this accessor for a bone transform in the render object
  3084. // this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
  3085. Bool W3DModelDraw::clientOnly_getRenderObjBoneTransform(const AsciiString & boneName,Matrix3D * set_tm) const
  3086. {
  3087. if (!m_renderObject) {
  3088. return false;
  3089. }
  3090. int idx = m_renderObject->Get_Bone_Index(boneName.str());
  3091. if (idx == 0) {
  3092. set_tm->Make_Identity();
  3093. return false;
  3094. } else {
  3095. *set_tm = m_renderObject->Get_Bone_Transform(idx);
  3096. return true;
  3097. }
  3098. }
  3099. //-------------------------------------------------------------------------------------------------
  3100. Bool W3DModelDraw::getCurrentWorldspaceClientBonePositions(const char* boneName, Matrix3D& transform) const
  3101. {
  3102. if (!m_renderObject)
  3103. return false;
  3104. Int boneIndex = m_renderObject->Get_Bone_Index(boneName);
  3105. if (boneIndex == 0)
  3106. return false;
  3107. transform = m_renderObject->Get_Bone_Transform(boneIndex);
  3108. return true;
  3109. }
  3110. //-------------------------------------------------------------------------------------------------
  3111. Int W3DModelDraw::getCurrentBonePositions(
  3112. const char* boneNamePrefix,
  3113. Int startIndex,
  3114. Coord3D* positions,
  3115. Matrix3D* transforms,
  3116. Int maxBones
  3117. ) const
  3118. {
  3119. const int MAX_BONE_GET = 64;
  3120. Matrix3D tmpMtx[MAX_BONE_GET];
  3121. if (maxBones > MAX_BONE_GET)
  3122. maxBones = MAX_BONE_GET;
  3123. if (transforms == NULL)
  3124. transforms = tmpMtx;
  3125. if( !m_renderObject )
  3126. return 0;
  3127. // ugh... kill the mtx so we get it in modelspace, not world space
  3128. Matrix3D originalTransform = m_renderObject->Get_Transform(); // save the transform
  3129. // which is faster: slamming the xform (and invalidating the objects xforms)
  3130. // or inverting it ourself? gotta profile to see. (srj)
  3131. #define DO_INV
  3132. #ifdef DO_INV
  3133. Matrix3D inverse;
  3134. originalTransform.Get_Orthogonal_Inverse(inverse);
  3135. inverse.Scale(getDrawable()->getScale());
  3136. #else
  3137. Matrix3D tmp(true);
  3138. tmp.Scale(getDrawable()->getScale());
  3139. m_renderObject->Set_Transform(tmp); // set to identity transform
  3140. #endif
  3141. Int posCount = 0;
  3142. Int endIndex = (startIndex == 0) ? 0 : 99;
  3143. char buffer[256];
  3144. for (Int i = startIndex; i <= endIndex; ++i)
  3145. {
  3146. if (i == 0)
  3147. strcpy(buffer, boneNamePrefix);
  3148. else
  3149. sprintf(buffer, "%s%02d", boneNamePrefix, i);
  3150. Int boneIndex = m_renderObject->Get_Bone_Index(buffer);
  3151. if (boneIndex == 0)
  3152. break;
  3153. transforms[posCount] = m_renderObject->Get_Bone_Transform(boneIndex);
  3154. #ifdef DO_INV
  3155. transforms[posCount].preMul(inverse);
  3156. #endif
  3157. //DEBUG_ASSERTCRASH(!m_renderObject->Is_Bone_Captured(boneIndex), ("bone is captured!"));
  3158. ++posCount;
  3159. if (posCount >= maxBones)
  3160. break;
  3161. }
  3162. if (positions && transforms)
  3163. {
  3164. for (i = 0; i < posCount; ++i)
  3165. {
  3166. Vector3 pos = transforms[i].Get_Translation();
  3167. positions[i].x = pos.X;
  3168. positions[i].y = pos.Y;
  3169. positions[i].z = pos.Z;
  3170. }
  3171. }
  3172. #ifdef DO_INV
  3173. #else
  3174. m_renderObject->Set_Transform(originalTransform); // restore it
  3175. #endif
  3176. return posCount;
  3177. }
  3178. //-------------------------------------------------------------------------------------------------
  3179. void W3DModelDraw::reactToTransformChange( const Matrix3D* oldMtx,
  3180. const Coord3D* oldPos,
  3181. Real oldAngle )
  3182. {
  3183. // set the position of our render object
  3184. if( m_renderObject )
  3185. {
  3186. Matrix3D mtx = *getDrawable()->getTransformMatrix();
  3187. adjustTransformMtx(mtx);
  3188. m_renderObject->Set_Transform(mtx);
  3189. }
  3190. if (m_trackRenderObject)
  3191. {
  3192. Object *obj = getDrawable()->getObject();
  3193. const Coord3D* pos = getDrawable()->getPosition();
  3194. if ( m_fullyObscuredByShroud || obj->testStatus( OBJECT_STATUS_STEALTHED ) == TRUE )
  3195. {
  3196. m_trackRenderObject->addCapEdgeToTrack(pos->x, pos->y);
  3197. }
  3198. else
  3199. {
  3200. if (obj && obj->isSignificantlyAboveTerrain())
  3201. {
  3202. m_trackRenderObject->setAirborne();
  3203. }
  3204. m_trackRenderObject->addEdgeToTrack(pos->x, pos->y);
  3205. }
  3206. }
  3207. }
  3208. //-------------------------------------------------------------------------------------------------
  3209. const ModelConditionInfo* W3DModelDraw::findBestInfo(const ModelConditionFlags& c) const
  3210. {
  3211. return getW3DModelDrawModuleData()->findBestInfo(c);
  3212. }
  3213. //-------------------------------------------------------------------------------------------------
  3214. Int W3DModelDraw::getBarrelCount(WeaponSlotType wslot) const
  3215. {
  3216. return (m_curState && (m_curState->m_validStuff & ModelConditionInfo::BARRELS_VALID)) ?
  3217. m_curState->m_weaponBarrelInfoVec[wslot].size() : 0;
  3218. }
  3219. //-------------------------------------------------------------------------------------------------
  3220. Bool W3DModelDraw::handleWeaponFireFX(WeaponSlotType wslot, Int specificBarrelToUse, const FXList* fxl, Real weaponSpeed, const Coord3D* victimPos, Real damageRadius)
  3221. {
  3222. DEBUG_ASSERTCRASH(specificBarrelToUse >= 0, ("specificBarrelToUse should now always be explicit"));
  3223. if (!m_curState || !(m_curState->m_validStuff & ModelConditionInfo::BARRELS_VALID))
  3224. return false;
  3225. const ModelConditionInfo::WeaponBarrelInfoVec& wbvec = m_curState->m_weaponBarrelInfoVec[wslot];
  3226. if (wbvec.empty())
  3227. {
  3228. // don't do this... some other module of our drawable may have handled it.
  3229. // just return false and let the caller sort it out.
  3230. // FXList::doFXPos(fxl, getDrawable()->getPosition(), getDrawable()->getTransformMatrix(), weaponSpeed, victimPos);
  3231. return false;
  3232. }
  3233. Bool handled = false;
  3234. if (specificBarrelToUse < 0 || specificBarrelToUse > wbvec.size())
  3235. specificBarrelToUse = 0;
  3236. const ModelConditionInfo::WeaponBarrelInfo& info = wbvec[specificBarrelToUse];
  3237. if (fxl)
  3238. {
  3239. if (info.m_fxBone && m_renderObject)
  3240. {
  3241. const Object *logicObject = getDrawable()->getObject();// This is slow, so store it
  3242. if( ! m_renderObject->Is_Hidden() || (logicObject == NULL) )
  3243. {
  3244. // I can ask the drawable's bone position if I am not hidden (if I have no object I have no choice)
  3245. Matrix3D mtx = m_renderObject->Get_Bone_Transform(info.m_fxBone);
  3246. Coord3D pos;
  3247. pos.x = mtx.Get_X_Translation();
  3248. pos.y = mtx.Get_Y_Translation();
  3249. pos.z = mtx.Get_Z_Translation();
  3250. FXList::doFXPos(fxl, &pos, &mtx, weaponSpeed, victimPos, damageRadius);
  3251. }
  3252. else
  3253. {
  3254. // Else, I should just use my logic position for the effect placement.
  3255. // Things in transports regularly fire from inside (hidden), so this is not weird.
  3256. const Matrix3D *mtx;
  3257. Coord3D pos;
  3258. mtx = logicObject->getTransformMatrix();
  3259. pos = *(logicObject->getPosition());
  3260. /** @todo Once Firepoint bones are actually implemented, this matrix will become correct.
  3261. // Unless of course they decide to not have the tracers come out of the windows, but rather go towards the target.
  3262. // In that case, tracers will have to be rewritten to be "point towards secondary" instead of
  3263. // "point straight ahead" which assumes we are facing the target.
  3264. */
  3265. FXList::doFXPos(fxl, &pos, mtx, weaponSpeed, victimPos, damageRadius);
  3266. }
  3267. handled = true;
  3268. }
  3269. else
  3270. {
  3271. DEBUG_LOG(("*** no FXBone found for a non-null FXL\n"));
  3272. }
  3273. }
  3274. if (info.m_recoilBone || info.m_muzzleFlashBone)
  3275. {
  3276. //DEBUG_LOG(("START muzzleflash %08lx for Draw %08lx state %s at frame %d\n",info.m_muzzleFlashBone,this,m_curState->m_description.str(),TheGameLogic->getFrame()));
  3277. WeaponRecoilInfo& recoil = m_weaponRecoilInfoVec[wslot][specificBarrelToUse];
  3278. recoil.m_state = WeaponRecoilInfo::RECOIL_START;
  3279. recoil.m_recoilRate = getW3DModelDrawModuleData()->m_initialRecoil;
  3280. if (info.m_muzzleFlashBone != 0)
  3281. info.setMuzzleFlashHidden(m_renderObject, false);
  3282. }
  3283. return handled;
  3284. }
  3285. //-------------------------------------------------------------------------------------------------
  3286. void W3DModelDraw::setAnimationLoopDuration(UnsignedInt numFrames)
  3287. {
  3288. // this is never defined -- srj
  3289. #ifdef NO_DURATIONS_ON_TRANSITIONS
  3290. if (m_curState != NULL && m_curState->m_transition != NO_TRANSITION &&
  3291. m_nextState != NULL && m_nextState->m_transition == NO_TRANSITION)
  3292. {
  3293. DEBUG_LOG(("deferring pending duration of %d frames\n",numFrames));
  3294. m_nextStateAnimLoopDuration = numFrames;
  3295. return;
  3296. }
  3297. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  3298. DEBUG_ASSERTCRASH(m_curState != NULL && m_curState->m_transition == NO_TRANSITION, ("Hmm, setAnimationLoopDuration on a transition state is probably not right... see srj"));
  3299. #else
  3300. m_nextStateAnimLoopDuration = NO_NEXT_DURATION;
  3301. #endif
  3302. Real desiredDurationInMsec = ceilf(numFrames * MSEC_PER_LOGICFRAME_REAL);
  3303. setCurAnimDurationInMsec(desiredDurationInMsec);
  3304. }
  3305. //-------------------------------------------------------------------------------------------------
  3306. /**
  3307. similar to the above, but assumes that the current state is a "ONCE",
  3308. and is smart about transition states... if there is a transition state
  3309. "inbetween", it is included in the completion time.
  3310. */
  3311. void W3DModelDraw::setAnimationCompletionTime(UnsignedInt numFrames)
  3312. {
  3313. if (m_curState != NULL && m_curState->m_transitionSig != NO_TRANSITION && m_curState->m_animations.size() > 0 &&
  3314. m_nextState != NULL && m_nextState->m_transitionSig == NO_TRANSITION && m_nextState->m_animations.size() > 0)
  3315. {
  3316. // we have a transition; split up the time suitably.
  3317. // note that this is just a guess, and assumes that the states
  3318. // have only one anim each (or, if multiple, that they are
  3319. // all pretty close in natural duration)
  3320. const Real t1 = m_curState->m_animations.front().getNaturalDurationInMsec();
  3321. const Real t2 = m_nextState->m_animations.front().getNaturalDurationInMsec();
  3322. UnsignedInt transTime = REAL_TO_INT_FLOOR((numFrames * t1) / (t1 + t2));
  3323. setAnimationLoopDuration(transTime);
  3324. m_nextStateAnimLoopDuration = numFrames - transTime;
  3325. }
  3326. else
  3327. {
  3328. setAnimationLoopDuration(numFrames);
  3329. }
  3330. }
  3331. //-------------------------------------------------------------------------------------------------
  3332. void W3DModelDraw::setAnimationFrame( int frame )
  3333. {
  3334. if( m_renderObject && m_whichAnimInCurState >= 0 )
  3335. {
  3336. const W3DAnimationInfo& animInfo = m_curState->m_animations[ m_whichAnimInCurState ];
  3337. HAnimClass* animHandle = animInfo.getAnimHandle(); // note that this now returns an ADDREFED handle, which must be released by the caller!
  3338. m_renderObject->Set_Animation( animHandle, frame );
  3339. REF_PTR_RELEASE(animHandle);
  3340. }
  3341. }
  3342. //-------------------------------------------------------------------------------------------------
  3343. void W3DModelDraw::setPauseAnimation(Bool pauseAnim)
  3344. {
  3345. if (m_pauseAnimation == pauseAnim)
  3346. {
  3347. return;
  3348. }
  3349. m_pauseAnimation = pauseAnim;
  3350. if (m_renderObject && m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD)
  3351. {
  3352. float framenum, dummy;
  3353. int mode, numFrames;
  3354. HLodClass* hlod = (HLodClass*)m_renderObject;
  3355. HAnimClass* anim = hlod->Peek_Animation_And_Info(framenum, numFrames, mode, dummy);
  3356. if (anim)
  3357. {
  3358. if (m_pauseAnimation)
  3359. {
  3360. m_animationMode = mode;
  3361. hlod->Set_Animation(anim, framenum, RenderObjClass::ANIM_MODE_MANUAL);
  3362. }
  3363. else
  3364. {
  3365. hlod->Set_Animation(anim, framenum, m_animationMode);
  3366. }
  3367. }
  3368. }
  3369. }
  3370. //-------------------------------------------------------------------------------------------------
  3371. #ifdef ALLOW_ANIM_INQUIRIES
  3372. // srj sez: not sure if this is a good idea, for net sync reasons...
  3373. Real W3DModelDraw::getAnimationScrubScalar( void ) const
  3374. {
  3375. return getCurAnimDistanceCovered();
  3376. }
  3377. #endif
  3378. //-------------------------------------------------------------------------------------------------
  3379. void W3DModelDraw::rebuildWeaponRecoilInfo(const ModelConditionInfo* state)
  3380. {
  3381. Int wslot;
  3382. if (state == NULL)
  3383. {
  3384. for (wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  3385. {
  3386. m_weaponRecoilInfoVec[wslot].clear();
  3387. }
  3388. }
  3389. else
  3390. {
  3391. for (wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot)
  3392. {
  3393. Int ncount = state->m_weaponBarrelInfoVec[wslot].size();
  3394. if (m_weaponRecoilInfoVec[wslot].size() != ncount)
  3395. {
  3396. WeaponRecoilInfo tmp;
  3397. m_weaponRecoilInfoVec[wslot].resize(ncount, tmp);
  3398. }
  3399. for (WeaponRecoilInfoVec::iterator it = m_weaponRecoilInfoVec[wslot].begin(); it != m_weaponRecoilInfoVec[wslot].end(); ++it)
  3400. {
  3401. it->clear();
  3402. }
  3403. }
  3404. }
  3405. }
  3406. //-------------------------------------------------------------------------------------------------
  3407. /** Preload any assets for the time of day requested */
  3408. //-------------------------------------------------------------------------------------------------
  3409. void W3DModelDraw::preloadAssets( TimeOfDay timeOfDay )
  3410. {
  3411. const W3DModelDrawModuleData *modData = getW3DModelDrawModuleData();
  3412. if( modData )
  3413. modData->preloadAssets( timeOfDay, getDrawable()->getScale() );
  3414. }
  3415. //-------------------------------------------------------------------------------------------------
  3416. Bool W3DModelDraw::isVisible() const
  3417. {
  3418. return (m_renderObject && m_renderObject->Is_Really_Visible());
  3419. }
  3420. //-------------------------------------------------------------------------------------------------
  3421. void W3DModelDraw::updateProjectileClipStatus( UnsignedInt shotsRemaining, UnsignedInt maxShots, WeaponSlotType slot )
  3422. {
  3423. if ((getW3DModelDrawModuleData()->m_projectileBoneFeedbackEnabledSlots & (1 << slot)) == 0)
  3424. return; // Only if specified.
  3425. doHideShowProjectileObjects( shotsRemaining, maxShots, slot );// Means effectively, show m of n.
  3426. }
  3427. //-------------------------------------------------------------------------------------------------
  3428. void W3DModelDraw::updateDrawModuleSupplyStatus( Int , Int currentSupply )
  3429. {
  3430. // This level only cares if you have anything or not
  3431. if( currentSupply > 0 )
  3432. {
  3433. getDrawable()->setModelConditionState( MODELCONDITION_CARRYING );
  3434. }
  3435. else
  3436. {
  3437. getDrawable()->clearModelConditionState( MODELCONDITION_CARRYING );
  3438. }
  3439. }
  3440. //-------------------------------------------------------------------------------------------------
  3441. void W3DModelDraw::doHideShowProjectileObjects( UnsignedInt showCount, UnsignedInt maxCount, WeaponSlotType slot )
  3442. {
  3443. // Loop through the projectile bones, and start hiding off the front. That is, 8,9 means to hide the first only.
  3444. if (maxCount < showCount)
  3445. {
  3446. DEBUG_CRASH(("Someone is trying to show more projectiles than they have.") );
  3447. return;
  3448. }
  3449. Int hideCount = maxCount - showCount;
  3450. std::vector<ModelConditionInfo::HideShowSubObjInfo> showHideVector;
  3451. ModelConditionInfo::HideShowSubObjInfo oneEntry;
  3452. if (m_curState->m_weaponProjectileHideShowName[slot].isEmpty())
  3453. {
  3454. for( Int projectileIndex = 0; projectileIndex < maxCount; projectileIndex++ )
  3455. {
  3456. oneEntry.subObjName.format("%s%02d", m_curState->m_weaponProjectileLaunchBoneName[slot].str(), (projectileIndex + 1));
  3457. oneEntry.hide = ((projectileIndex + 1) <= hideCount);
  3458. showHideVector.push_back( oneEntry );
  3459. }
  3460. }
  3461. else
  3462. {
  3463. oneEntry.subObjName = m_curState->m_weaponProjectileHideShowName[slot];
  3464. oneEntry.hide = (0 < hideCount);
  3465. showHideVector.push_back( oneEntry );
  3466. }
  3467. // Rather than duplicate recursive utility stuff, I will build a vector like those used by the main ShowHide setting
  3468. doHideShowSubObjs(&showHideVector);
  3469. }
  3470. //-------------------------------------------------------------------------------------------------
  3471. void W3DModelDraw::updateSubObjects()
  3472. {
  3473. if (!m_renderObject)
  3474. return;
  3475. if (!m_subObjectVec.empty())
  3476. {
  3477. for (std::vector<ModelConditionInfo::HideShowSubObjInfo>::const_iterator it = m_subObjectVec.begin(); it != m_subObjectVec.end(); ++it)
  3478. {
  3479. Int objIndex;
  3480. RenderObjClass* subObj;
  3481. if ((subObj = m_renderObject->Get_Sub_Object_By_Name(it->subObjName.str(), &objIndex)) != NULL)
  3482. {
  3483. subObj->Set_Hidden(it->hide);
  3484. const HTreeClass *htree = m_renderObject->Get_HTree();
  3485. if (htree)
  3486. {
  3487. //get the bone of this subobject so we can hide all other child objects that use this bone
  3488. //as a parent.
  3489. Int boneIdx = m_renderObject->Get_Sub_Object_Bone_Index(0, objIndex);
  3490. doHideShowBoneSubObjs( it->hide, m_renderObject->Get_Num_Sub_Objects(), boneIdx, m_renderObject, htree);
  3491. }
  3492. subObj->Release_Ref();
  3493. }
  3494. else
  3495. {
  3496. DEBUG_CRASH(("*** ASSET ERROR: SubObject %s not found (%s)!\n",it->subObjName.str(),getDrawable()->getTemplate()->getName().str()));
  3497. }
  3498. }
  3499. }
  3500. }
  3501. // ------------------------------------------------------------------------------------------------
  3502. /** CRC */
  3503. // ------------------------------------------------------------------------------------------------
  3504. void W3DModelDraw::crc( Xfer *xfer )
  3505. {
  3506. // extend base class
  3507. DrawModule::crc( xfer );
  3508. } // end crc
  3509. // ------------------------------------------------------------------------------------------------
  3510. /** Xfer method
  3511. * Version Info:
  3512. * 1: Initial version
  3513. * 2: Added animation frame (CBD)
  3514. */
  3515. // ------------------------------------------------------------------------------------------------
  3516. void W3DModelDraw::xfer( Xfer *xfer )
  3517. {
  3518. // version
  3519. const XferVersion currentVersion = 2;
  3520. XferVersion version = currentVersion;
  3521. xfer->xferVersion( &version, currentVersion );
  3522. // extend base class
  3523. DrawModule::xfer( xfer );
  3524. // weapon recoil info vectors
  3525. UnsignedByte recoilInfoCount;
  3526. WeaponRecoilInfo weaponRecoilInfo;
  3527. for( Int i = 0; i < WEAPONSLOT_COUNT; ++i )
  3528. {
  3529. // count of data here
  3530. recoilInfoCount = m_weaponRecoilInfoVec[ i ].size();
  3531. xfer->xferUnsignedByte( &recoilInfoCount );
  3532. if( xfer->getXferMode() == XFER_SAVE )
  3533. {
  3534. WeaponRecoilInfoVec::const_iterator it;
  3535. for( it = m_weaponRecoilInfoVec[ i ].begin(); it != m_weaponRecoilInfoVec[ i ].end(); ++it )
  3536. {
  3537. // state
  3538. weaponRecoilInfo.m_state = (*it).m_state;
  3539. xfer->xferUser( &weaponRecoilInfo.m_state, sizeof( WeaponRecoilInfo::RecoilState ) );
  3540. // shift
  3541. weaponRecoilInfo.m_shift = (*it).m_shift;
  3542. xfer->xferReal( &weaponRecoilInfo.m_shift );
  3543. // recoil rate
  3544. weaponRecoilInfo.m_recoilRate = (*it).m_recoilRate;
  3545. xfer->xferReal( &weaponRecoilInfo.m_recoilRate );
  3546. } // end for, it
  3547. } // end if, save
  3548. else
  3549. {
  3550. // clear this list before loading
  3551. m_weaponRecoilInfoVec[ i ].clear();
  3552. // read each data item
  3553. for( Int j = 0; j < recoilInfoCount; ++j )
  3554. {
  3555. // read state
  3556. xfer->xferUser( &weaponRecoilInfo.m_state, sizeof( WeaponRecoilInfo::RecoilState ) );
  3557. // read shift
  3558. xfer->xferReal( &weaponRecoilInfo.m_shift );
  3559. // read recoil rate
  3560. xfer->xferReal( &weaponRecoilInfo.m_recoilRate );
  3561. // stuff it in the vector
  3562. m_weaponRecoilInfoVec[ i ].push_back( weaponRecoilInfo );
  3563. } // end for, j
  3564. } // end else, load
  3565. } // end for, i
  3566. // sub object vector
  3567. UnsignedByte subObjectCount = m_subObjectVec.size();
  3568. xfer->xferUnsignedByte( &subObjectCount );
  3569. ModelConditionInfo::HideShowSubObjInfo hideShowSubObjInfo;
  3570. if( xfer->getXferMode() == XFER_SAVE )
  3571. {
  3572. std::vector<ModelConditionInfo::HideShowSubObjInfo>::const_iterator it;
  3573. for( it = m_subObjectVec.begin(); it != m_subObjectVec.end(); ++it )
  3574. {
  3575. // sub object name
  3576. hideShowSubObjInfo.subObjName = (*it).subObjName;
  3577. xfer->xferAsciiString( &hideShowSubObjInfo.subObjName );
  3578. // hide
  3579. hideShowSubObjInfo.hide = (*it).hide;
  3580. xfer->xferBool( &hideShowSubObjInfo.hide );
  3581. } // end for, it
  3582. } // end if, save
  3583. else
  3584. {
  3585. // the vector must be emtpy
  3586. m_subObjectVec.clear();
  3587. // read each data item
  3588. for( UnsignedByte i = 0; i < subObjectCount; ++i )
  3589. {
  3590. // name
  3591. xfer->xferAsciiString( &hideShowSubObjInfo.subObjName );
  3592. // hide
  3593. xfer->xferBool( &hideShowSubObjInfo.hide );
  3594. // stuff in vector
  3595. m_subObjectVec.push_back( hideShowSubObjInfo );
  3596. } // end for, i
  3597. } // end else, load
  3598. // animation
  3599. if( version >= 2 )
  3600. {
  3601. if( xfer->getXferMode() == XFER_SAVE )
  3602. {
  3603. // srj sez: don't save info for transition states, since we can't really
  3604. // restore them effectively.
  3605. if ( m_renderObject
  3606. && m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD
  3607. && m_curState
  3608. && m_curState->m_transitionSig == NO_TRANSITION )
  3609. {
  3610. // cast to HLod
  3611. HLodClass *hlod = (HLodClass*)m_renderObject;
  3612. // get animation info
  3613. Int mode, numFrames;
  3614. Real frame, dummy;
  3615. HAnimClass *anim = hlod->Peek_Animation_And_Info( frame, numFrames, mode, dummy );
  3616. // animation data is present
  3617. Bool present = anim ? TRUE : FALSE;
  3618. xfer->xferBool( &present );
  3619. // if animation data is present
  3620. if( anim )
  3621. {
  3622. // xfer mode
  3623. xfer->xferInt( &mode );
  3624. //
  3625. // xfer frame as a fraction (this will allow the animations to change
  3626. // in future patches but will still be mostly correct)
  3627. //
  3628. Real percent = frame / INT_TO_REAL( anim->Get_Num_Frames()-1 );
  3629. xfer->xferReal( &percent );
  3630. } // end if, anim
  3631. } // end if
  3632. else
  3633. {
  3634. // animation data is *NOT* present
  3635. Bool present = FALSE;
  3636. xfer->xferBool( &present );
  3637. } // end else
  3638. } // end if, save
  3639. else
  3640. {
  3641. // read animation present flag
  3642. Bool present;
  3643. xfer->xferBool( &present );
  3644. if( present )
  3645. {
  3646. {
  3647. // read mode
  3648. Int mode;
  3649. xfer->xferInt( &mode ); // note, this will be ignored
  3650. }
  3651. // read percent
  3652. Real percent;
  3653. xfer->xferReal( &percent );
  3654. //
  3655. // cast render object to HLod, if this is no longer possible we have read the
  3656. // data already and can just ignore it
  3657. //
  3658. if( m_renderObject && m_renderObject->Class_ID() == RenderObjClass::CLASSID_HLOD )
  3659. {
  3660. HLodClass *hlod = (HLodClass *)m_renderObject;
  3661. // get anim
  3662. HAnimClass *anim = hlod->Peek_Animation();
  3663. // set animation data
  3664. if( anim )
  3665. {
  3666. // figure out frame number given percent written in file and total frames on anim
  3667. Real frame = percent * INT_TO_REAL( anim->Get_Num_Frames()-1 );
  3668. float dummy1, dummy2;
  3669. int curMode, dummy3;
  3670. hlod->Peek_Animation_And_Info(dummy1, dummy3, curMode, dummy2);
  3671. // srj sez: do not change the animation mode. it's too risky, since if you change (say) a nonlooping
  3672. // to a looping, something might break since it could rely on that anim terminating.
  3673. // set animation data
  3674. hlod->Set_Animation( anim, frame, curMode );
  3675. } // end if, anim
  3676. } // end if
  3677. } // end if
  3678. } // end else, load
  3679. } // end if, version with animation info
  3680. // when loading, update the sub objects if we have any
  3681. if( xfer->getXferMode() == XFER_LOAD && m_subObjectVec.empty() == FALSE )
  3682. updateSubObjects();
  3683. } // end xfer
  3684. // ------------------------------------------------------------------------------------------------
  3685. /** Load post process */
  3686. // ------------------------------------------------------------------------------------------------
  3687. void W3DModelDraw::loadPostProcess( void )
  3688. {
  3689. // extend base class
  3690. DrawModule::loadPostProcess();
  3691. } // end loadPostProcess
  3692. // ------------------------------------------------------------------------------------------------
  3693. // ------------------------------------------------------------------------------------------------
  3694. // ------------------------------------------------------------------------------------------------
  3695. void W3DModelDrawModuleData::crc( Xfer *x )
  3696. {
  3697. xfer(x);
  3698. }
  3699. // ------------------------------------------------------------------------------------------------
  3700. void W3DModelDrawModuleData::xfer( Xfer *x )
  3701. {
  3702. // version
  3703. const XferVersion currentVersion = 1;
  3704. XferVersion version = currentVersion;
  3705. x->xferVersion( &version, currentVersion );
  3706. for (ModelConditionVector::iterator it = m_conditionStates.begin(); it != m_conditionStates.end(); ++it)
  3707. {
  3708. ModelConditionInfo *info = &(*it);
  3709. x->xferByte(&(info->m_validStuff));
  3710. #if defined(_DEBUG) || defined(_INTERNAL)
  3711. x->xferAsciiString(&(info->m_description));
  3712. #endif
  3713. if (info->m_validStuff)
  3714. {
  3715. for (PristineBoneInfoMap::iterator bit = info->m_pristineBones.begin(); bit != info->m_pristineBones.end(); ++bit)
  3716. {
  3717. PristineBoneInfo *bone = &(bit->second);
  3718. x->xferInt(&(bone->boneIndex));
  3719. x->xferUser(&(bone->mtx), sizeof(Matrix3D));
  3720. }
  3721. for (Int i=0; i<MAX_TURRETS; ++i)
  3722. {
  3723. x->xferInt(&(info->m_turrets[i].m_turretAngleBone));
  3724. x->xferInt(&(info->m_turrets[i].m_turretPitchBone));
  3725. }
  3726. for (i=0; i<WEAPONSLOT_COUNT; ++i)
  3727. {
  3728. for (ModelConditionInfo::WeaponBarrelInfoVec::iterator wit = info->m_weaponBarrelInfoVec[i].begin(); wit != info->m_weaponBarrelInfoVec[i].end(); ++wit)
  3729. {
  3730. x->xferUser(&(wit->m_projectileOffsetMtx), sizeof(Matrix3D));
  3731. }
  3732. }
  3733. }
  3734. }
  3735. }
  3736. // ------------------------------------------------------------------------------------------------
  3737. void W3DModelDrawModuleData::loadPostProcess( void )
  3738. {
  3739. }