W3DModelDraw.cpp 132 KB

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