123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, Inc.
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- #include "platform/platform.h"
- #include "T3D/shapeBase.h"
- #include "core/dnet.h"
- #include "sfx/sfxSystem.h"
- #include "sfx/sfxSource.h"
- #include "sfx/sfxTrack.h"
- #include "sfx/sfxDescription.h"
- #include "T3D/sfx/sfx3DWorld.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "core/stream/bitStream.h"
- #include "ts/tsPartInstance.h"
- #include "ts/tsShapeInstance.h"
- #include "ts/tsMaterialList.h"
- #include "scene/sceneManager.h"
- #include "scene/sceneRenderState.h"
- #include "scene/sceneObjectLightingPlugin.h"
- #include "T3D/fx/explosion.h"
- #include "T3D/fx/cameraFXMgr.h"
- #include "environment/waterBlock.h"
- #include "T3D/debris.h"
- #include "T3D/physicalZone.h"
- #include "T3D/containerQuery.h"
- #include "math/mathUtils.h"
- #include "math/mMatrix.h"
- #include "math/mTransform.h"
- #include "math/mRandom.h"
- #include "platform/profiler.h"
- #include "gfx/gfxCubemap.h"
- #include "gfx/gfxDrawUtil.h"
- #include "gfx/gfxTransformSaver.h"
- #include "renderInstance/renderPassManager.h"
- #include "collision/earlyOutPolyList.h"
- #include "core/resourceManager.h"
- #include "scene/reflectionManager.h"
- #include "gfx/sim/cubemapData.h"
- #include "materials/materialManager.h"
- #include "materials/materialFeatureTypes.h"
- #include "renderInstance/renderOcclusionMgr.h"
- #include "core/stream/fileStream.h"
- #include "T3D/accumulationVolume.h"
- IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
- ConsoleDocClass( ShapeBaseData,
- "@brief Defines properties for a ShapeBase object.\n\n"
- "@see ShapeBase\n"
- "@ingroup gameObjects\n"
- );
- IMPLEMENT_CALLBACK( ShapeBaseData, onEnabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
- "@brief Called when the object damage state changes to Enabled.\n\n"
- "@param obj The ShapeBase object\n"
- "@param lastState The previous damage state\n" );
- IMPLEMENT_CALLBACK( ShapeBaseData, onDisabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
- "@brief Called when the object damage state changes to Disabled.\n\n"
- "@param obj The ShapeBase object\n"
- "@param lastState The previous damage state\n" );
- IMPLEMENT_CALLBACK( ShapeBaseData, onDestroyed, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
- "@brief Called when the object damage state changes to Destroyed.\n\n"
- "@param obj The ShapeBase object\n"
- "@param lastState The previous damage state\n" );
- IMPLEMENT_CALLBACK( ShapeBaseData, onImpact, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ),
- "@brief Called when we collide with another object beyond some impact speed.\n\n"
- "The Player class makes use of this callback when a collision speed is more than PlayerData::minImpactSpeed.\n"
- "@param obj The ShapeBase object\n"
- "@param collObj The object we collided with\n"
- "@param vec Collision impact vector\n"
- "@param len Length of the impact vector\n" );
- IMPLEMENT_CALLBACK( ShapeBaseData, onCollision, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ),
- "@brief Called when we collide with another object.\n\n"
- "@param obj The ShapeBase object\n"
- "@param collObj The object we collided with\n"
- "@param vec Collision impact vector\n"
- "@param len Length of the impact vector\n" );
- IMPLEMENT_CALLBACK( ShapeBaseData, onDamage, void, ( ShapeBase* obj, F32 delta ), ( obj, delta ),
- "@brief Called when the object is damaged.\n\n"
- "@param obj The ShapeBase object\n"
- "@param obj The ShapeBase object\n"
- "@param delta The amount of damage received." );
- IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index, bool state ), ( obj, index, state ),
- "@brief Called when a move trigger input changes state.\n\n"
- "@param obj The ShapeBase object\n"
- "@param index Index of the trigger that changed\n"
- "@param state New state of the trigger\n" );
- IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const char* name), (obj, slot, name),
- "@brief Called when a thread playing a non-cyclic sequence reaches the end of the "
- "sequence.\n\n"
- "@param obj The ShapeBase object\n"
- "@param slot Thread slot that finished playing\n"
- "@param name Thread name that finished playing\n");
- IMPLEMENT_CALLBACK( ShapeBaseData, onForceUncloak, void, ( ShapeBase* obj, const char* reason ), ( obj, reason ),
- "@brief Called when the object is forced to uncloak.\n\n"
- "@param obj The ShapeBase object\n"
- "@param reason String describing why the object was uncloaked\n" );
- //----------------------------------------------------------------------------
- // Timeout for non-looping sounds on a channel
- static SimTime sAudioTimeout = 500;
- F32 ShapeBase::sWhiteoutDec = 0.007f;
- F32 ShapeBase::sDamageFlashDec = 0.02f;
- F32 ShapeBase::sFullCorrectionDistance = 0.5f;
- F32 ShapeBase::sCloakSpeed = 0.5;
- U32 ShapeBase::sLastRenderFrame = 0;
- static const char *sDamageStateName[] =
- {
- // Index by enum ShapeBase::DamageState
- "Enabled",
- "Disabled",
- "Destroyed"
- };
- //----------------------------------------------------------------------------
- ShapeBaseData::ShapeBaseData()
- : shadowEnable( false ),
- shadowSize( 128 ),
- shadowMaxVisibleDistance( 80.0f ),
- shadowProjectionDistance( 10.0f ),
- shadowSphereAdjust( 1.0f ),
- shapeName( StringTable->EmptyString() ),
- shapeAsset(StringTable->EmptyString()),
- shapeAssetId(StringTable->EmptyString()),
- cloakTexName( StringTable->EmptyString() ),
- cubeDescId( 0 ),
- reflectorDesc( NULL ),
- debris( NULL ),
- debrisID( 0 ),
- debrisShapeName( StringTable->EmptyString() ),
- explosion( NULL ),
- explosionID( 0 ),
- underwaterExplosion( NULL ),
- underwaterExplosionID( 0 ),
- mass( 1.0f ),
- drag( 0.0f ),
- density( 1.0f ),
- maxEnergy( 0.0f ),
- maxDamage( 1.0f ),
- repairRate( 0.0033f ),
- disabledLevel( 1.0f ),
- destroyedLevel( 1.0f ),
- cameraMaxDist( 0.0f ),
- cameraMinDist( 0.2f ),
- cameraDefaultFov( 75.0f ),
- cameraMinFov( 5.0f ),
- cameraMaxFov( 120.f ),
- cameraCanBank( false ),
- mountedImagesBank( false ),
- mCRC( 0 ),
- computeCRC( false ),
- eyeNode( -1 ),
- earNode( -1 ),
- cameraNode( -1 ),
- debrisDetail( -1 ),
- damageSequence( -1 ),
- hulkSequence( -1 ),
- observeThroughObject( false ),
- firstPersonOnly( false ),
- useEyePoint( false ),
- isInvincible( false ),
- renderWhenDestroyed( true ),
- inheritEnergyFromMount( false )
- {
- dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
- remap_txr_tags = NULL;
- remap_buffer = NULL;
- silent_bbox_check = false;
- }
- ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : GameBaseData(other, temp_clone)
- {
- shadowEnable = other.shadowEnable;
- shadowSize = other.shadowSize;
- shadowMaxVisibleDistance = other.shadowMaxVisibleDistance;
- shadowProjectionDistance = other.shadowProjectionDistance;
- shadowSphereAdjust = other.shadowSphereAdjust;
- shapeName = other.shapeName;
- shapeAsset = other.shapeAsset;
- shapeAssetId = other.shapeAssetId;
- cloakTexName = other.cloakTexName;
- cubeDescName = other.cubeDescName;
- cubeDescId = other.cubeDescId;
- reflectorDesc = other.reflectorDesc;
- debris = other.debris;
- debrisID = other.debrisID; // -- for pack/unpack of debris ptr
- debrisShapeName = other.debrisShapeName;
- debrisShape = other.debrisShape; // -- TSShape loaded using debrisShapeName
- explosion = other.explosion;
- explosionID = other.explosionID; // -- for pack/unpack of explosion ptr
- underwaterExplosion = other.underwaterExplosion;
- underwaterExplosionID = other.underwaterExplosionID; // -- for pack/unpack of underwaterExplosion ptr
- mass = other.mass;
- drag = other.drag;
- density = other.density;
- maxEnergy = other.maxEnergy;
- maxDamage = other.maxDamage;
- repairRate = other.repairRate;
- disabledLevel = other.disabledLevel;
- destroyedLevel = other.destroyedLevel;
- cameraMaxDist = other.cameraMaxDist;
- cameraMinDist = other.cameraMinDist;
- cameraDefaultFov = other.cameraDefaultFov;
- cameraMinFov = other.cameraMinFov;
- cameraMaxFov = other.cameraMaxFov;
- cameraCanBank = other.cameraCanBank;
- mountedImagesBank = other.mountedImagesBank;
- mShape = other.mShape; // -- TSShape loaded using shapeName
- mCRC = other.mCRC; // -- from shape, used to verify client shape
- computeCRC = other.computeCRC;
- eyeNode = other.eyeNode; // -- from shape node "eye"
- earNode = other.earNode; // -- from shape node "ear"
- cameraNode = other.cameraNode; // -- from shape node "cam"
- dMemcpy(mountPointNode, other.mountPointNode, sizeof(mountPointNode)); // -- from shape nodes "mount#" 0-31
- debrisDetail = other.debrisDetail; // -- from shape detail "Debris-17"
- damageSequence = other.damageSequence; // -- from shape sequence "Damage"
- hulkSequence = other.hulkSequence; // -- from shape sequence "Visibility"
- observeThroughObject = other.observeThroughObject;
- collisionDetails = other.collisionDetails; // -- calc from shape (this is a Vector copy)
- collisionBounds = other.collisionBounds; // -- calc from shape (this is a Vector copy)
- LOSDetails = other.LOSDetails; // -- calc from shape (this is a Vector copy)
- firstPersonOnly = other.firstPersonOnly;
- useEyePoint = other.useEyePoint;
- isInvincible = other.isInvincible;
- renderWhenDestroyed = other.renderWhenDestroyed;
- inheritEnergyFromMount = other.inheritEnergyFromMount;
- remap_txr_tags = other.remap_txr_tags;
- remap_buffer = other.remap_buffer;
- txr_tag_remappings = other.txr_tag_remappings;
- silent_bbox_check = other.silent_bbox_check;
- }
- struct ShapeBaseDataProto
- {
- F32 mass;
- F32 drag;
- F32 density;
- F32 maxEnergy;
- F32 cameraMaxDist;
- F32 cameraMinDist;
- F32 cameraDefaultFov;
- F32 cameraMinFov;
- F32 cameraMaxFov;
- ShapeBaseDataProto()
- {
- mass = 1;
- drag = 0;
- density = 1;
- maxEnergy = 0;
- cameraMaxDist = 0;
- cameraMinDist = 0.2f;
- cameraDefaultFov = 75.f;
- cameraMinFov = 5.0f;
- cameraMaxFov = 120.f;
- }
- };
- static ShapeBaseDataProto gShapeBaseDataProto;
- ShapeBaseData::~ShapeBaseData()
- {
- if (remap_buffer && !isTempClone())
- dFree(remap_buffer);
- }
- bool ShapeBaseData::preload(bool server, String &errorStr)
- {
- if (!Parent::preload(server, errorStr))
- return false;
- bool shapeError = false;
- // Resolve objects transmitted from server
- if (!server) {
- if( !explosion && explosionID != 0 )
- {
- if( Sim::findObject( explosionID, explosion ) == false)
- {
- Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(explosion): 0x%x", explosionID );
- }
- AssertFatal(!(explosion && ((explosionID < DataBlockObjectIdFirst) || (explosionID > DataBlockObjectIdLast))),
- "ShapeBaseData::preload: invalid explosion data");
- }
- if( !underwaterExplosion && underwaterExplosionID != 0 )
- {
- if( Sim::findObject( underwaterExplosionID, underwaterExplosion ) == false)
- {
- Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(underwaterExplosion): 0x%x", underwaterExplosionID );
- }
- AssertFatal(!(underwaterExplosion && ((underwaterExplosionID < DataBlockObjectIdFirst) || (underwaterExplosionID > DataBlockObjectIdLast))),
- "ShapeBaseData::preload: invalid underwaterExplosion data");
- }
- if( !debris && debrisID != 0 )
- {
- Sim::findObject( debrisID, debris );
- AssertFatal(!(debris && ((debrisID < DataBlockObjectIdFirst) || (debrisID > DataBlockObjectIdLast))),
- "ShapeBaseData::preload: invalid debris data");
- }
- if( debrisShapeName && debrisShapeName[0] != '\0' && !bool(debrisShape) )
- {
- debrisShape = ResourceManager::get().load(debrisShapeName);
- if( bool(debrisShape) == false )
- {
- errorStr = String::ToString("ShapeBaseData::load: Couldn't load shape \"%s\"", debrisShapeName);
- return false;
- }
- else
- {
- if(!server && !debrisShape->preloadMaterialList(debrisShape.getPath()) && NetConnection::filesWereDownloaded())
- shapeError = true;
- TSShapeInstance* pDummy = new TSShapeInstance(debrisShape, !server);
- delete pDummy;
- }
- }
- }
- //Legacy catch
- if (shapeName != StringTable->EmptyString())
- {
- shapeAssetId = ShapeAsset::getAssetIdByFilename(shapeName);
- }
- U32 assetState = ShapeAsset::getAssetById(shapeAssetId, &shapeAsset);
- if (ShapeAsset::Failed != assetState)
- {
- //only clear the legacy direct file reference if everything checks out fully
- if (assetState == ShapeAsset::Ok)
- {
- shapeName = StringTable->EmptyString();
- }
- else Con::warnf("Warning: ShapeBaseData::preload-%s", ShapeAsset::getAssetErrstrn(assetState).c_str());
- S32 i;
- // Resolve shapename
- mShape = shapeAsset->getShapeResource();
- if (bool(mShape) == false)
- {
- errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",shapeName);
- return false;
- }
- if(!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
- shapeError = true;
- if(computeCRC)
- {
- Con::printf("Validation required for shape: %s", shapeName);
- Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShape.getPath());
- if (!fileRef)
- {
- errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",shapeName);
- return false;
- }
- if(server)
- mCRC = fileRef->getChecksum();
- else if(mCRC != fileRef->getChecksum())
- {
- errorStr = String::ToString("Shape \"%s\" does not match version on server.",shapeName);
- return false;
- }
- }
- // Resolve details and camera node indexes.
- static const String sCollisionStr( "collision-" );
- for (i = 0; i < mShape->details.size(); i++)
- {
- const String &name = mShape->names[mShape->details[i].nameIndex];
- if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
- {
- collisionDetails.push_back(i);
- collisionBounds.increment();
- mShape->computeBounds(collisionDetails.last(), collisionBounds.last());
- mShape->getAccelerator(collisionDetails.last());
- if (!mShape->mBounds.isContained(collisionBounds.last()))
- {
- if (!silent_bbox_check)
- Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", shapeName, collisionDetails.size() - 1, collisionDetails.last());
- collisionBounds.last() = mShape->mBounds;
- }
- else if (collisionBounds.last().isValidBox() == false)
- {
- if (!silent_bbox_check)
- Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", shapeName, collisionDetails.size() - 1, collisionDetails.last());
- collisionBounds.last() = mShape->mBounds;
- }
- // The way LOS works is that it will check to see if there is a LOS detail that matches
- // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
- // the future). If it can't find a matching LOS it will simply use the collision instead.
- // We check for any "unmatched" LOS's further down
- LOSDetails.increment();
- String buff = String::ToString("LOS-%d", i + 1 + MaxCollisionShapes);
- U32 los = mShape->findDetail(buff);
- if (los == -1)
- LOSDetails.last() = i;
- else
- LOSDetails.last() = los;
- }
- }
- // Snag any "unmatched" LOS details
- static const String sLOSStr( "LOS-" );
- for (i = 0; i < mShape->details.size(); i++)
- {
- const String &name = mShape->names[mShape->details[i].nameIndex];
- if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
- {
- // See if we already have this LOS
- bool found = false;
- for (U32 j = 0; j < LOSDetails.size(); j++)
- {
- if (LOSDetails[j] == i)
- {
- found = true;
- break;
- }
- }
- if (!found)
- LOSDetails.push_back(i);
- }
- }
- debrisDetail = mShape->findDetail("Debris-17");
- eyeNode = mShape->findNode("eye");
- earNode = mShape->findNode( "ear" );
- if( earNode == -1 )
- earNode = eyeNode;
- cameraNode = mShape->findNode("cam");
- if (cameraNode == -1)
- cameraNode = eyeNode;
- // Resolve mount point node indexes
- for (i = 0; i < SceneObject::NumMountPoints; i++) {
- char fullName[256];
- dSprintf(fullName,sizeof(fullName),"mount%d",i);
- mountPointNode[i] = mShape->findNode(fullName);
- }
- // find the AIRepairNode - hardcoded to be the last node in the array...
- mountPointNode[AIRepairNode] = mShape->findNode("AIRepairNode");
- //
- hulkSequence = mShape->findSequence("Visibility");
- damageSequence = mShape->findSequence("Damage");
- //
- F32 w = mShape->mBounds.len_y() / 2;
- if (cameraMaxDist < w)
- cameraMaxDist = w;
- // just parse up the string and collect the remappings in txr_tag_remappings.
- if (!server && remap_txr_tags != NULL && remap_txr_tags != StringTable->insert(""))
- {
- txr_tag_remappings.clear();
- if (remap_buffer)
- dFree(remap_buffer);
- remap_buffer = dStrdup(remap_txr_tags);
- char* remap_token = dStrtok(remap_buffer, " \t");
- while (remap_token != NULL)
- {
- char* colon = dStrchr(remap_token, ':');
- if (colon)
- {
- *colon = '\0';
- txr_tag_remappings.increment();
- txr_tag_remappings.last().old_tag = remap_token;
- txr_tag_remappings.last().new_tag = colon+1;
- }
- remap_token = dStrtok(NULL, " \t");
- }
- }
- }
- if(!server)
- {
- /*
- // grab all the hud images
- for(U32 i = 0; i < NumHudRenderImages; i++)
- {
- if(hudImageNameFriendly[i] && hudImageNameFriendly[i][0])
- hudImageFriendly[i] = TextureHandle(hudImageNameFriendly[i], BitmapTexture);
- if(hudImageNameEnemy[i] && hudImageNameEnemy[i][0])
- hudImageEnemy[i] = TextureHandle(hudImageNameEnemy[i], BitmapTexture);
- }
- */
- }
- // Resolve CubeReflectorDesc.
- if ( cubeDescName.isNotEmpty() )
- {
- Sim::findObject( cubeDescName, reflectorDesc );
- }
- else if( cubeDescId > 0 )
- {
- Sim::findObject( cubeDescId, reflectorDesc );
- }
- return !shapeError;
- }
- bool ShapeBaseData::_setMass( void* object, const char* index, const char* data )
- {
- ShapeBaseData* shape = reinterpret_cast< ShapeBaseData* >( object );
- F32 mass = dAtof(data);
- if (mass <= 0)
- mass = 0.01f;
- shape->mass = mass;
- return false;
- }
- void ShapeBaseData::initPersistFields()
- {
- addGroup( "Shadows" );
- addField( "shadowEnable", TypeBool, Offset(shadowEnable, ShapeBaseData),
- "Enable shadows for this shape (currently unused, shadows are always enabled)." );
- addField( "shadowSize", TypeS32, Offset(shadowSize, ShapeBaseData),
- "Size of the projected shadow texture (must be power of 2)." );
- addField( "shadowMaxVisibleDistance", TypeF32, Offset(shadowMaxVisibleDistance, ShapeBaseData),
- "Maximum distance at which shadow is visible (currently unused)." );
- addField( "shadowProjectionDistance", TypeF32, Offset(shadowProjectionDistance, ShapeBaseData),
- "Maximum height above ground to project shadow. If the object is higher "
- "than this no shadow will be rendered." );
- addField( "shadowSphereAdjust", TypeF32, Offset(shadowSphereAdjust, ShapeBaseData),
- "Scalar applied to the radius of spot shadows (initial radius is based "
- "on the shape bounds but can be adjusted with this field)." );
- endGroup( "Shadows" );
- addGroup( "Render" );
- addField("shapeAsset", TypeShapeAssetId, Offset(shapeAssetId, ShapeBaseData),
- "The source shape asset.");
- addField( "shapeFile", TypeShapeFilename, Offset(shapeName, ShapeBaseData),
- "The DTS or DAE model to use for this object." );
- endGroup( "Render" );
- addGroup( "Destruction", "Parameters related to the destruction effects of this object." );
- addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData),
- "%Explosion to generate when this shape is blown up." );
- addField( "underwaterExplosion", TYPEID< ExplosionData >(), Offset(underwaterExplosion, ShapeBaseData),
- "%Explosion to generate when this shape is blown up underwater." );
- addField( "debris", TYPEID< DebrisData >(), Offset(debris, ShapeBaseData),
- "%Debris to generate when this shape is blown up." );
- addField( "renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData),
- "Whether to render the shape when it is in the \"Destroyed\" damage state." );
- addField( "debrisShapeName", TypeShapeFilename, Offset(debrisShapeName, ShapeBaseData),
- "The DTS or DAE model to use for auto-generated breakups. @note may not be functional." );
- endGroup( "Destruction" );
- addGroup( "Physics" );
-
- addProtectedField("mass", TypeF32, Offset(mass, ShapeBaseData), &_setMass, &defaultProtectedGetFn, "Shape mass.\nUsed in simulation of moving objects.\n" );
- addField( "drag", TypeF32, Offset(drag, ShapeBaseData),
- "Drag factor.\nReduces velocity of moving objects." );
- addField( "density", TypeF32, Offset(density, ShapeBaseData),
- "Shape density.\nUsed when computing buoyancy when in water.\n" );
- endGroup( "Physics" );
- addGroup( "Damage/Energy" );
- addField( "maxEnergy", TypeF32, Offset(maxEnergy, ShapeBaseData),
- "Maximum energy level for this object." );
- addField( "maxDamage", TypeF32, Offset(maxDamage, ShapeBaseData),
- "Maximum damage level for this object." );
- addField( "disabledLevel", TypeF32, Offset(disabledLevel, ShapeBaseData),
- "Damage level above which the object is disabled.\n"
- "Currently unused." );
- addField( "destroyedLevel", TypeF32, Offset(destroyedLevel, ShapeBaseData),
- "Damage level above which the object is destroyed.\n"
- "When the damage level increases above this value, the object damage "
- "state is set to \"Destroyed\"." );
- addField( "repairRate", TypeF32, Offset(repairRate, ShapeBaseData),
- "Rate at which damage is repaired in damage units/tick.\n"
- "This value is subtracted from the damage level until it reaches 0." );
- addField( "inheritEnergyFromMount", TypeBool, Offset(inheritEnergyFromMount, ShapeBaseData),
- "Flag controlling whether to manage our own energy level, or to use "
- "the energy level of the object we are mounted to." );
- addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData),
- "Invincible flag; when invincible, the object cannot be damaged or "
- "repaired." );
- endGroup( "Damage/Energy" );
- addGroup( "Camera", "The settings used by the shape when it is the camera." );
- addField( "cameraMaxDist", TypeF32, Offset(cameraMaxDist, ShapeBaseData),
- "The maximum distance from the camera to the object.\n"
- "Used when computing a custom camera transform for this object.\n\n"
- "@see observeThroughObject" );
- addField( "cameraMinDist", TypeF32, Offset(cameraMinDist, ShapeBaseData),
- "The minimum distance from the camera to the object.\n"
- "Used when computing a custom camera transform for this object.\n\n"
- "@see observeThroughObject" );
- addField( "cameraDefaultFov", TypeF32, Offset(cameraDefaultFov, ShapeBaseData),
- "The default camera vertical FOV in degrees." );
- addField( "cameraMinFov", TypeF32, Offset(cameraMinFov, ShapeBaseData),
- "The minimum camera vertical FOV allowed in degrees." );
- addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData),
- "The maximum camera vertical FOV allowed in degrees." );
- addField( "cameraCanBank", TypeBool, Offset(cameraCanBank, ShapeBaseData),
- "If the derrived class supports it, allow the camera to bank." );
- addField( "mountedImagesBank", TypeBool, Offset(mountedImagesBank, ShapeBaseData),
- "Do mounted images bank along with the camera?" );
- addField( "firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData),
- "Flag controlling whether the view from this object is first person "
- "only." );
- addField( "useEyePoint", TypeBool, Offset(useEyePoint, ShapeBaseData),
- "Flag controlling whether the client uses this object's eye point to "
- "view from." );
- addField( "observeThroughObject", TypeBool, Offset(observeThroughObject, ShapeBaseData),
- "Observe this object through its camera transform and default fov.\n"
- "If true, when this object is the camera it can provide a custom camera "
- "transform and FOV (instead of the default eye transform)." );
- endGroup("Camera");
- addGroup( "Misc" );
- addField( "computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData),
- "If true, verify that the CRC of the client's shape model matches the "
- "server's CRC for the shape model when loaded by the client." );
- endGroup( "Misc" );
- addGroup( "Reflection" );
- addField( "cubeReflectorDesc", TypeRealString, Offset( cubeDescName, ShapeBaseData ),
- "References a ReflectorDesc datablock that defines performance and quality properties for dynamic reflections.\n");
- //addField( "reflectMaxRateMs", TypeS32, Offset( reflectMaxRateMs, ShapeBaseData ), "reflection will not be updated more frequently than this" );
- //addField( "reflectMaxDist", TypeF32, Offset( reflectMaxDist, ShapeBaseData ), "distance at which reflection is never updated" );
- //addField( "reflectMinDist", TypeF32, Offset( reflectMinDist, ShapeBaseData ), "distance at which reflection is always updated" );
- //addField( "reflectDetailAdjust", TypeF32, Offset( reflectDetailAdjust, ShapeBaseData ), "scale up or down the detail level for objects rendered in a reflection" );
- endGroup( "Reflection" );
- addField("remapTextureTags", TypeString, Offset(remap_txr_tags, ShapeBaseData));
- addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
- // disallow some field substitutions
- onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK
- onlyKeepClearSubstitutions("explosion");
- onlyKeepClearSubstitutions("underwaterExplosion");
- Parent::initPersistFields();
- }
- DefineEngineMethod( ShapeBaseData, checkDeployPos, bool, ( TransformF txfm ),,
- "@brief Check if there is the space at the given transform is free to spawn into.\n\n"
- "The shape's bounding box volume is used to check for collisions at the given world "
- "transform. Only interior and static objects are checked for collision.\n"
- "@param txfm Deploy transform to check\n"
- "@return True if the space is free, false if there is already something in "
- "the way.\n"
- "@note This is a server side only check, and is not actually limited to spawning.\n")
- {
- if (bool(object->mShape) == false)
- return false;
- MatrixF mat = txfm.getMatrix();
- Box3F objBox = object->mShape->mBounds;
- Point3F boxCenter = (objBox.minExtents + objBox.maxExtents) * 0.5f;
- objBox.minExtents = boxCenter + (objBox.minExtents - boxCenter) * 0.9f;
- objBox.maxExtents = boxCenter + (objBox.maxExtents - boxCenter) * 0.9f;
- Box3F wBox = objBox;
- mat.mul(wBox);
- EarlyOutPolyList polyList;
- polyList.mNormal.set(0,0,0);
- polyList.mPlaneList.clear();
- polyList.mPlaneList.setSize(6);
- polyList.mPlaneList[0].set(objBox.minExtents,VectorF(-1,0,0));
- polyList.mPlaneList[1].set(objBox.maxExtents,VectorF(0,1,0));
- polyList.mPlaneList[2].set(objBox.maxExtents,VectorF(1,0,0));
- polyList.mPlaneList[3].set(objBox.minExtents,VectorF(0,-1,0));
- polyList.mPlaneList[4].set(objBox.minExtents,VectorF(0,0,-1));
- polyList.mPlaneList[5].set(objBox.maxExtents,VectorF(0,0,1));
- for (U32 i = 0; i < 6; i++)
- {
- PlaneF temp;
- mTransformPlane(mat, Point3F(1, 1, 1), polyList.mPlaneList[i], &temp);
- polyList.mPlaneList[i] = temp;
- }
- if (gServerContainer.buildPolyList(PLC_Collision, wBox, StaticShapeObjectType, &polyList))
- return false;
- return true;
- }
- DefineEngineMethod(ShapeBaseData, getDeployTransform, TransformF, ( Point3F pos, Point3F normal ),,
- "@brief Helper method to get a transform from a position and vector (suitable for use with setTransform).\n\n"
- "@param pos Desired transform position\n"
- "@param normal Vector of desired direction\n"
- "@return The deploy transform\n" )
- {
- normal.normalize();
- VectorF xAxis;
- if( mFabs(normal.z) > mFabs(normal.x) && mFabs(normal.z) > mFabs(normal.y))
- mCross( VectorF( 0, 1, 0 ), normal, &xAxis );
- else
- mCross( VectorF( 0, 0, 1 ), normal, &xAxis );
- VectorF yAxis;
- mCross( normal, xAxis, &yAxis );
- MatrixF testMat(true);
- testMat.setColumn( 0, xAxis );
- testMat.setColumn( 1, yAxis );
- testMat.setColumn( 2, normal );
- testMat.setPosition( pos );
- return testMat;
- }
- void ShapeBaseData::packData(BitStream* stream)
- {
- Parent::packData(stream);
- if(stream->writeFlag(computeCRC))
- stream->write(mCRC);
- stream->writeFlag(shadowEnable);
- stream->write(shadowSize);
- stream->write(shadowMaxVisibleDistance);
- stream->write(shadowProjectionDistance);
- stream->write(shadowSphereAdjust);
- if (stream->writeFlag(shapeAsset.notNull()))
- {
- stream->writeString(shapeAsset.getAssetId());
- }
- else
- {
- stream->writeString(shapeName);
- }
- stream->writeString(cloakTexName);
- if(stream->writeFlag(mass != gShapeBaseDataProto.mass))
- stream->write(mass);
- if(stream->writeFlag(drag != gShapeBaseDataProto.drag))
- stream->write(drag);
- if(stream->writeFlag(density != gShapeBaseDataProto.density))
- stream->write(density);
- if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy))
- stream->write(maxEnergy);
- if(stream->writeFlag(cameraMaxDist != gShapeBaseDataProto.cameraMaxDist))
- stream->write(cameraMaxDist);
- if(stream->writeFlag(cameraMinDist != gShapeBaseDataProto.cameraMinDist))
- stream->write(cameraMinDist);
- cameraDefaultFov = mClampF(cameraDefaultFov, cameraMinFov, cameraMaxFov);
- if(stream->writeFlag(cameraDefaultFov != gShapeBaseDataProto.cameraDefaultFov))
- stream->write(cameraDefaultFov);
- if(stream->writeFlag(cameraMinFov != gShapeBaseDataProto.cameraMinFov))
- stream->write(cameraMinFov);
- if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov))
- stream->write(cameraMaxFov);
- stream->writeFlag(cameraCanBank);
- stream->writeFlag(mountedImagesBank);
- stream->writeString( debrisShapeName );
- stream->writeFlag(observeThroughObject);
- if( stream->writeFlag( debris != NULL ) )
- {
- stream->writeRangedU32(mPacked? SimObjectId((uintptr_t)debris):
- debris->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
- }
- stream->writeFlag(isInvincible);
- stream->writeFlag(renderWhenDestroyed);
- if( stream->writeFlag( explosion != NULL ) )
- {
- stream->writeRangedU32( explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- if( stream->writeFlag( underwaterExplosion != NULL ) )
- {
- stream->writeRangedU32( underwaterExplosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- stream->writeFlag(inheritEnergyFromMount);
- stream->writeFlag(firstPersonOnly);
- stream->writeFlag(useEyePoint);
-
- if( stream->writeFlag( reflectorDesc != NULL ) )
- {
- stream->writeRangedU32( reflectorDesc->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- //stream->write(reflectPriority);
- //stream->write(reflectMaxRateMs);
- //stream->write(reflectMinDist);
- //stream->write(reflectMaxDist);
- //stream->write(reflectDetailAdjust);
- stream->writeString(remap_txr_tags);
- stream->writeFlag(silent_bbox_check);
- }
- void ShapeBaseData::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- computeCRC = stream->readFlag();
- if(computeCRC)
- stream->read(&mCRC);
- shadowEnable = stream->readFlag();
- stream->read(&shadowSize);
- stream->read(&shadowMaxVisibleDistance);
- stream->read(&shadowProjectionDistance);
- stream->read(&shadowSphereAdjust);
- if (stream->readFlag())
- {
- shapeAssetId = stream->readSTString();
- ShapeAsset::getAssetById(shapeAssetId, &shapeAsset);
- shapeName = shapeAsset->getShapeFilename();
- }
- else
- {
- shapeName = stream->readSTString();
- }
- cloakTexName = stream->readSTString();
- if(stream->readFlag())
- stream->read(&mass);
- else
- mass = gShapeBaseDataProto.mass;
- if(stream->readFlag())
- stream->read(&drag);
- else
- drag = gShapeBaseDataProto.drag;
- if(stream->readFlag())
- stream->read(&density);
- else
- density = gShapeBaseDataProto.density;
- if(stream->readFlag())
- stream->read(&maxEnergy);
- else
- maxEnergy = gShapeBaseDataProto.maxEnergy;
- if(stream->readFlag())
- stream->read(&cameraMaxDist);
- else
- cameraMaxDist = gShapeBaseDataProto.cameraMaxDist;
- if(stream->readFlag())
- stream->read(&cameraMinDist);
- else
- cameraMinDist = gShapeBaseDataProto.cameraMinDist;
- if(stream->readFlag())
- stream->read(&cameraDefaultFov);
- else
- cameraDefaultFov = gShapeBaseDataProto.cameraDefaultFov;
- if(stream->readFlag())
- stream->read(&cameraMinFov);
- else
- cameraMinFov = gShapeBaseDataProto.cameraMinFov;
- if(stream->readFlag())
- stream->read(&cameraMaxFov);
- else
- cameraMaxFov = gShapeBaseDataProto.cameraMaxFov;
- cameraCanBank = stream->readFlag();
- mountedImagesBank = stream->readFlag();
- debrisShapeName = stream->readSTString();
- observeThroughObject = stream->readFlag();
- if( stream->readFlag() )
- {
- debrisID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- isInvincible = stream->readFlag();
- renderWhenDestroyed = stream->readFlag();
- if( stream->readFlag() )
- {
- explosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- if( stream->readFlag() )
- {
- underwaterExplosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- inheritEnergyFromMount = stream->readFlag();
- firstPersonOnly = stream->readFlag();
- useEyePoint = stream->readFlag();
- if( stream->readFlag() )
- {
- cubeDescId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
- }
- //stream->read(&reflectPriority);
- //stream->read(&reflectMaxRateMs);
- //stream->read(&reflectMinDist);
- //stream->read(&reflectMaxDist);
- //stream->read(&reflectDetailAdjust);
- remap_txr_tags = stream->readSTString();
- silent_bbox_check = stream->readFlag();
- }
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- Chunker<ShapeBase::CollisionTimeout> sTimeoutChunker;
- ShapeBase::CollisionTimeout* ShapeBase::sFreeTimeoutList = 0;
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_NETOBJECT_V1(ShapeBase);
- ConsoleDocClass( ShapeBase,
- "@ingroup gameObjects"
- );
- IMPLEMENT_CALLBACK( ShapeBase, validateCameraFov, F32, (F32 fov), (fov),
- "@brief Called on the server when the client has requested a FOV change.\n\n"
- "When the client requests that its field of view should be changed (because "
- "they want to use a sniper scope, for example) this new FOV needs to be validated "
- "by the server. This method is called if it exists (it is optional) to validate "
- "the requested FOV, and modify it if necessary. This could be as simple as checking "
- "that the FOV falls within a correct range, to making sure that the FOV matches the "
- "capabilities of the current weapon.\n\n"
- "Following this method, ShapeBase ensures that the given FOV still falls within "
- "the datablock's cameraMinFov and cameraMaxFov. If that is good enough for your "
- "purposes, then you do not need to define the validateCameraFov() callback for "
- "your ShapeBase.\n\n"
- "@param fov The FOV that has been requested by the client.\n"
- "@return The FOV as validated by the server.\n\n"
- "@see ShapeBaseData\n\n");
- ShapeBase::ShapeBase()
- : mDataBlock( NULL ),
- mIsAiControlled( false ),
- mControllingObject( NULL ),
- mAiPose( 0 ),
- mMoveMotion( false ),
- mShapeBaseMount( NULL ),
- mShapeInstance( NULL ),
- mConvexList( new Convex ),
- mEnergy( 0.0f ),
- mRechargeRate( 0.0f ),
- mMass( 1.0f ),
- mOneOverMass( 1.0f ),
- mDrag( 0.0f ),
- mBuoyancy( 0.0f ),
- mLiquidHeight( 0.0f ),
- mWaterCoverage( 0.0f ),
- mAppliedForce( Point3F::Zero ),
- mNetGravity( 1.0f ),
- mDamageFlash( 0.0f ),
- mWhiteOut( 0.0f ),
- mFlipFadeVal( false ),
- mTimeoutList( NULL ),
- mDamage( 0.0f ),
- mRepairRate( 0.0f ),
- mRepairReserve( 0.0f ),
- mDamageState( Enabled ),
- mDamageThread( NULL ),
- mHulkThread( NULL ),
- damageDir( 0.0f, 0.0f, 1.0f ),
- mCloaked( false ),
- mCloakLevel( 0.0f ),
- mFadeOut( true ),
- mFading( false ),
- mFadeVal( 1.0f ),
- mFadeElapsedTime( 0.0f ),
- mFadeTime( 1.0f ),
- mFadeDelay( 0.0f ),
- mCameraFov( 90.0f ),
- mIsControlled( false ),
- mLastRenderFrame( 0 ),
- mLastRenderDistance( 0.0f )
- {
- mTypeMask |= ShapeBaseObjectType | LightObjectType;
- S32 i;
- for (i = 0; i < MaxSoundThreads; i++) {
- mSoundThread[i].play = false;
- mSoundThread[i].profile = 0;
- mSoundThread[i].sound = 0;
- }
- for (i = 0; i < MaxScriptThreads; i++) {
- mScriptThread[i].sequence = -1;
- mScriptThread[i].thread = 0;
- mScriptThread[i].state = Thread::Stop;
- mScriptThread[i].atEnd = false;
- mScriptThread[i].timescale = 1.f;
- mScriptThread[i].position = -1.f;
- }
- for (i = 0; i < MaxTriggerKeys; i++)
- mTrigger[i] = false;
- anim_clip_flags = 0;
- last_anim_id = -1;
- last_anim_tag = 0;
- last_anim_lock_tag = 0;
- saved_seq_id = -1;
- saved_pos = 0.0f;
- saved_rate = 1.0f;
- }
- ShapeBase::~ShapeBase()
- {
- SAFE_DELETE( mConvexList );
- AssertFatal(mMount.link == 0,"ShapeBase::~ShapeBase: An object is still mounted");
- if( mShapeInstance && (mShapeInstance->getDebrisRefCount() == 0) )
- {
- delete mShapeInstance;
- mShapeInstance = NULL;
- }
- CollisionTimeout* ptr = mTimeoutList;
- while (ptr) {
- CollisionTimeout* cur = ptr;
- ptr = ptr->next;
- cur->next = sFreeTimeoutList;
- sFreeTimeoutList = cur;
- }
- }
- void ShapeBase::initPersistFields()
- {
- addProtectedField( "skin", TypeRealString, Offset(mAppliedSkinName, ShapeBase), &_setFieldSkin, &_getFieldSkin,
- "@brief The skin applied to the shape.\n\n"
- "'Skinning' the shape effectively renames the material targets, allowing "
- "different materials to be used on different instances of the same model. "
- "Using getSkinName() and setSkinName() is equivalent to reading and "
- "writing the skin field directly.\n\n"
- "Any material targets that start with the old skin name have that part "
- "of the name replaced with the new skin name. The initial old skin name is "
- "\"base\". For example, if a new skin of \"blue\" was applied to a model "
- "that had material targets <i>base_body</i> and <i>face</i>, the new targets "
- "would be <i>blue_body</i> and <i>face</i>. Note that <i>face</i> was not "
- "renamed since it did not start with the old skin name of \"base\".\n\n"
- "To support models that do not use the default \"base\" naming convention, "
- "you can also specify the part of the name to replace in the skin field "
- "itself. For example, if a model had a material target called <i>shapemat</i>, "
- "we could apply a new skin \"shape=blue\", and the material target would be "
- "renamed to <i>bluemat</i> (note \"shape\" has been replaced with \"blue\").\n\n"
- "Multiple skin updates can also be applied at the same time by separating "
- "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n"
- "Material targets are only renamed if an existing Material maps to that "
- "name, or if there is a diffuse texture in the model folder with the same "
- "name as the new target.\n\n" );
- addField( "isAiControlled", TypeBool, Offset(mIsAiControlled, ShapeBase),
- "@brief Is this object AI controlled.\n\n"
- "If True then this object is considered AI controlled and not player controlled.\n" );
- Parent::initPersistFields();
- }
- bool ShapeBase::_setFieldSkin( void *object, const char *index, const char *data )
- {
- ShapeBase* shape = static_cast<ShapeBase*>( object );
- if ( shape )
- shape->setSkinName( data );
- return false;
- }
- const char *ShapeBase::_getFieldSkin( void *object, const char *data )
- {
- ShapeBase* shape = static_cast<ShapeBase*>( object );
- return shape ? shape->getSkinName() : "";
- }
- //----------------------------------------------------------------------------
- bool ShapeBase::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
-
- if( !mDataBlock )
- {
- Con::errorf( "ShapeBase::onAdd - no datablock on shape %i:%s (%s)",
- getId(), getClassName(), getName() );
- return false;
- }
- // Resolve sounds that arrived in the initial update
- S32 i;
- for (i = 0; i < MaxSoundThreads; i++)
- updateAudioState(mSoundThread[i]);
- for (i = 0; i < MaxScriptThreads; i++)
- {
- Thread& st = mScriptThread[i];
- if(st.thread)
- updateThread(st);
- }
- /*
- if(mDataBlock->cloakTexName != StringTable->EmptyString())
- mCloakTexture = TextureHandle(mDataBlock->cloakTexName, MeshTexture, false);
- */
- // Accumulation and environment mapping
- if (isClientObject() && mShapeInstance)
- {
- AccumulationVolume::addObject(this);
- }
- return true;
- }
- void ShapeBase::onRemove()
- {
- mConvexList->nukeList();
- Parent::onRemove();
- // Stop any running sounds on the client
- if (isGhost())
- for (S32 i = 0; i < MaxSoundThreads; i++)
- stopAudio(i);
- // Accumulation and environment mapping
- if (isClientObject() && mShapeInstance)
- {
- if (mShapeInstance->hasAccumulation())
- AccumulationVolume::removeObject(this);
- }
- if ( isClientObject() )
- {
- mCubeReflector.unregisterReflector();
- }
- }
- void ShapeBase::onSceneRemove()
- {
- mConvexList->nukeList();
- Parent::onSceneRemove();
- }
- bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
- {
- // need to destroy blend-clips or we crash
- if (isGhost())
- {
- for (S32 i = 0; i < blend_clips.size(); i++)
- {
- if (blend_clips[i].thread)
- mShapeInstance->destroyThread(blend_clips[i].thread);
- blend_clips.erase_fast(i);
- }
- }
- ShapeBaseData *prevDB = dynamic_cast<ShapeBaseData*>( mDataBlock );
- bool isInitialDataBlock = (prevDB == 0);
- if ( Parent::onNewDataBlock( dptr, reload ) == false )
- return false;
- mDataBlock = dynamic_cast<ShapeBaseData*>(dptr);
- if (!mDataBlock)
- return false;
- setMaskBits(DamageMask);
- mDamageThread = 0;
- mHulkThread = 0;
- // Even if loadShape succeeds, there may not actually be
- // a shape assigned to this object.
- if (bool(mDataBlock->mShape)) {
- delete mShapeInstance;
- if (isClientObject() && mDataBlock->txr_tag_remappings.size() > 0)
- {
- // temporarily substitute material tags with alternates
- TSMaterialList* mat_list = mDataBlock->mShape->materialList;
- if (mat_list)
- {
- for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
- {
- ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
- Vector<String>& mat_names = (Vector<String>&) mat_list->getMaterialNameList();
- S32 old_tagLen = dStrlen(remap->old_tag);
- for (S32 j = 0; j < mat_names.size(); j++)
- {
- if (mat_names[j].compare(remap->old_tag, old_tagLen, String::NoCase) == 0)
- {
- mat_names[j] = String(remap->new_tag);
- mat_names[j].insert(0, '#');
- break;
- }
- }
- }
- }
- }
- mShapeInstance = new TSShapeInstance(mDataBlock->mShape, isClientObject());
- if (isClientObject())
- {
- mShapeInstance->cloneMaterialList();
- // restore the material tags to original form
- if (mDataBlock->txr_tag_remappings.size() > 0)
- {
- TSMaterialList* mat_list = mDataBlock->mShape->materialList;
- if (mat_list)
- {
- for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
- {
- ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
- Vector<String>& mat_names = (Vector<String>&) mat_list->getMaterialNameList();
- S32 new_tagLen = dStrlen(remap->new_tag);
- for (S32 j = 0; j < mat_names.size(); j++)
- {
- String::SizeType len = mat_names[j].length();
- if (len > 1)
- {
- String temp_name = mat_names[j].substr(1, len - 1);
- if (temp_name.compare(remap->new_tag, new_tagLen) == 0)
- {
- mat_names[j] = String(remap->old_tag);
- break;
- }
- }
- }
- }
- }
- }
- }
- mObjBox = mDataBlock->mShape->mBounds;
- resetWorldBox();
- // Set the initial mesh hidden state.
- mMeshHidden.setSize(mDataBlock->mShape->objects.size());
- mMeshHidden.clear();
- // Initialize the threads
- for (U32 i = 0; i < MaxScriptThreads; i++) {
- Thread& st = mScriptThread[i];
- if (st.sequence != -1) {
- // TG: Need to see about suppressing non-cyclic sounds
- // if the sequences were activated before the object was
- // ghosted.
- // TG: Cyclic animations need to have a random pos if
- // they were started before the object was ghosted.
- // If there was something running on the old shape, the thread
- // needs to be reset. Otherwise we assume that it's been
- // initialized either by the constructor or from the server.
- bool reset = st.thread != 0;
- st.thread = 0;
- // New datablock/shape may not actually HAVE this sequence.
- // In that case stop playing it.
- AssertFatal(prevDB != NULL, "ShapeBase::onNewDataBlock - how did you have a sequence playing without a prior datablock?");
- const TSShape* prevShape = prevDB->mShape;
- const TSShape::Sequence& prevSeq = prevShape->sequences[st.sequence];
- const String& prevSeqName = prevShape->names[prevSeq.nameIndex];
- st.sequence = mDataBlock->mShape->findSequence(prevSeqName);
- if (st.sequence != -1)
- {
- setThreadSequence(i, st.sequence, reset);
- }
- }
- }
- if (mDataBlock->damageSequence != -1) {
- mDamageThread = mShapeInstance->addThread();
- mShapeInstance->setSequence(mDamageThread,
- mDataBlock->damageSequence, 0);
- }
- if (mDataBlock->hulkSequence != -1) {
- mHulkThread = mShapeInstance->addThread();
- mShapeInstance->setSequence(mHulkThread,
- mDataBlock->hulkSequence, 0);
- }
- if (isGhost())
- {
- // Reapply the current skin
- mAppliedSkinName = "";
- reSkin();
- }
- }
- //
- mEnergy = 0;
- mDamage = 0;
- mDamageState = Enabled;
- mRepairReserve = 0;
- updateMass();
- updateDamageLevel();
- updateDamageState();
- mDrag = mDataBlock->drag;
- mCameraFov = mDataBlock->cameraDefaultFov;
- updateMass();
- if( !isInitialDataBlock && mLightPlugin )
- mLightPlugin->reset();
-
- if ( isClientObject() )
- {
- mCubeReflector.unregisterReflector();
- if ( mDataBlock->reflectorDesc )
- mCubeReflector.registerReflector( this, mDataBlock->reflectorDesc );
- }
- return true;
- }
- void ShapeBase::onDeleteNotify( SimObject *obj )
- {
- if ( obj == mCurrentWaterObject )
- mCurrentWaterObject = NULL;
- Parent::onDeleteNotify( obj );
- }
- void ShapeBase::onImpact(SceneObject* obj, const VectorF& vec)
- {
- if (!isGhost())
- mDataBlock->onImpact_callback( this, obj, vec, vec.len() );
- }
- void ShapeBase::onImpact(const VectorF& vec)
- {
- if (!isGhost())
- mDataBlock->onImpact_callback( this, NULL, vec, vec.len() );
- }
- //----------------------------------------------------------------------------
- void ShapeBase::processTick(const Move* move)
- {
- PROFILE_SCOPE( ShapeBase_ProcessTick );
- // Energy management
- if (mDamageState == Enabled && mDataBlock->inheritEnergyFromMount == false) {
- F32 store = mEnergy;
- mEnergy += mRechargeRate;
- if (mEnergy > mDataBlock->maxEnergy)
- mEnergy = mDataBlock->maxEnergy;
- else
- if (mEnergy < 0)
- mEnergy = 0;
- // Virtual setEnergyLevel is used here by some derived classes to
- // decide whether they really want to set the energy mask bit.
- if (mEnergy != store)
- setEnergyLevel(mEnergy);
- }
- // Repair management
- if (mDataBlock->isInvincible == false)
- {
- F32 store = mDamage;
- mDamage -= mRepairRate;
- mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage);
- if (mRepairReserve > mDamage)
- mRepairReserve = mDamage;
- if (mRepairReserve > 0.0)
- {
- F32 rate = getMin(mDataBlock->repairRate, mRepairReserve);
- mDamage -= rate;
- mRepairReserve -= rate;
- }
- if (store != mDamage)
- {
- updateDamageLevel();
- if (isServerObject()) {
- setMaskBits(DamageMask);
- mDataBlock->onDamage_callback( this, mDamage - store );
- }
- }
- }
- if (isServerObject()) {
- // Server only...
- advanceThreads(TickSec);
- updateServerAudio();
- // update wet state
- setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered
- // update motion state
- mMoveMotion = false;
- if (move && (move->x || move->y || move->z))
- {
- mMoveMotion = true;
- }
- for (S32 i = 0; i < MaxMountedImages; i++)
- {
- setImageMotionState(i, mMoveMotion);
- }
- if(mFading)
- {
- F32 dt = TickMs / 1000.0f;
- F32 newFadeET = mFadeElapsedTime + dt;
- if(mFadeElapsedTime < mFadeDelay && newFadeET >= mFadeDelay)
- setMaskBits(CloakMask);
- mFadeElapsedTime = newFadeET;
- if(mFadeElapsedTime > mFadeTime + mFadeDelay)
- {
- mFadeVal = F32(!mFadeOut);
- mFading = false;
- }
- }
- }
- // Advance images
- if (isServerObject())
- {
- for (S32 i = 0; i < MaxMountedImages; i++)
- {
- if (mMountedImageList[i].dataBlock)
- updateImageState(i, TickSec);
- }
- }
- // Call script on trigger state changes
- if (move && mDataBlock && isServerObject())
- {
- for (S32 i = 0; i < MaxTriggerKeys; i++)
- {
- if (move->trigger[i] != mTrigger[i])
- {
- mTrigger[i] = move->trigger[i];
- mDataBlock->onTrigger_callback( this, i, move->trigger[i] );
- }
- }
- }
- // Update the damage flash and the whiteout
- //
- if (mDamageFlash > 0.0)
- {
- mDamageFlash -= sDamageFlashDec;
- if (mDamageFlash <= 0.0)
- mDamageFlash = 0.0;
- }
- if (mWhiteOut > 0.0)
- {
- mWhiteOut -= sWhiteoutDec;
- if (mWhiteOut <= 0.0)
- mWhiteOut = 0.0;
- }
- if (isMounted()) {
- MatrixF mat;
- mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
- Parent::setTransform(mat);
- }
- }
- void ShapeBase::advanceTime(F32 dt)
- {
- // On the client, the shape threads and images are
- // advanced at framerate.
- advanceThreads(dt);
- updateAudioPos();
- for (S32 i = 0; i < MaxMountedImages; i++)
- if (mMountedImageList[i].dataBlock)
- {
- updateImageState(i, dt);
- updateImageAnimation(i, dt);
- }
- // Cloaking
- if (mCloaked && mCloakLevel != 1.0) {
- if (sCloakSpeed <= 0.0f)
- {
- // Instantaneous
- mCloakLevel = 1.0;
- }
- else
- {
- // Over time determined by sCloakSpeed
- mCloakLevel += dt / sCloakSpeed;
- if (mCloakLevel >= 1.0)
- mCloakLevel = 1.0;
- }
- } else if (!mCloaked && mCloakLevel != 0.0) {
- if (sCloakSpeed <= 0.0f)
- {
- // Instantaneous
- mCloakLevel = 0.0;
- }
- else
- {
- // Over time determined by sCloakSpeed
- mCloakLevel -= dt / sCloakSpeed;
- if (mCloakLevel <= 0.0)
- mCloakLevel = 0.0;
- }
- }
- if(mFading)
- {
- mFadeElapsedTime += dt;
- if(mFadeElapsedTime > mFadeTime)
- {
- mFadeVal = F32(!mFadeOut);
- mFading = false;
- }
- else
- {
- mFadeVal = mFadeElapsedTime / mFadeTime;
- if(mFadeOut)
- mFadeVal = 1 - mFadeVal;
- }
- }
- if (isMounted()) {
- MatrixF mat;
- mMount.object->getRenderMountTransform( 0.0f, mMount.node, mMount.xfm, &mat );
- Parent::setRenderTransform(mat);
- }
- }
- void ShapeBase::setControllingClient( GameConnection* client )
- {
- if( isGhost() && gSFX3DWorld )
- {
- if( gSFX3DWorld->getListener() == this && !client && getControllingClient() && getControllingClient()->isConnectionToServer() )
- {
- // We are the current listener and are no longer a controller object on the
- // connection, so clear our listener status.
-
- gSFX3DWorld->setListener( NULL );
- }
- else if( client && client->isConnectionToServer() && !getControllingObject() )
- {
- // We're on the local client and not controlled by another object, so make
- // us the current SFX listener.
-
- gSFX3DWorld->setListener( this );
- }
- }
- Parent::setControllingClient( client );
- // Update all of the mounted images' shapes so they may
- // optimize their animation threads.
- for (U32 i=0; i<MaxMountedImages; ++i)
- {
- MountedImage& image = mMountedImageList[i];
- image.updateDoAnimateAllShapes( this );
- }
- }
- void ShapeBase::setControllingObject(ShapeBase* obj)
- {
- if (obj) {
- setProcessTick(false);
- // Even though we don't processTick, we still need to
- // process after the controller in case anyone is mounted
- // on this object.
- processAfter(obj);
- }
- else {
- setProcessTick(true);
- clearProcessAfter();
- // Catch the case of the controlling object actually
- // mounted on this object.
- if (mControllingObject->mMount.object == this)
- mControllingObject->processAfter(this);
- }
- mControllingObject = obj;
- }
- ShapeBase* ShapeBase::getControlObject()
- {
- return 0;
- }
- void ShapeBase::setControlObject(ShapeBase*)
- {
- }
- bool ShapeBase::isFirstPerson() const
- {
- // Always first person as far as the server is concerned.
- if (!isGhost())
- return true;
- if (const GameConnection* con = getControllingClient())
- return con->getControlObject() == this && con->isFirstPerson();
- return false;
- }
- bool ShapeBase::isValidCameraFov(F32 fov)
- {
- return((fov >= mDataBlock->cameraMinFov) && (fov <= mDataBlock->cameraMaxFov));
- }
- void ShapeBase::setCameraFov(F32 fov)
- {
- // On server allow for script side checking
- if ( !isGhost() && isMethod( "validateCameraFov" ) )
- {
- fov = validateCameraFov_callback( fov );
- }
- mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov);
- }
- void ShapeBase::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
- {
- // update the camera query
- query->camera = this;
- if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
- {
- // get the fov from the connection (in deg)
- F32 fov;
- if (con->getControlCameraFov(&fov))
- {
- query->fov = mDegToRad(fov/2);
- query->sinFov = mSin(query->fov);
- query->cosFov = mCos(query->fov);
- }
- }
- // use eye rather than camera transform (good enough and faster)
- MatrixF eyeTransform;
- getEyeTransform(&eyeTransform);
- eyeTransform.getColumn(3, &query->pos);
- eyeTransform.getColumn(1, &query->orientation);
- // Get the visible distance.
- if (getSceneManager() != NULL)
- query->visibleDistance = getSceneManager()->getVisibleDistance();
- Parent::onCameraScopeQuery( cr, query );
- }
- //----------------------------------------------------------------------------
- F32 ShapeBase::getEnergyLevel()
- {
- if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount )
- return mShapeBaseMount->getEnergyLevel();
- return mEnergy;
- }
- F32 ShapeBase::getEnergyValue()
- {
- if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount )
- {
- F32 maxEnergy = mShapeBaseMount->mDataBlock->maxEnergy;
- if ( maxEnergy > 0.f )
- return (mShapeBaseMount->getEnergyLevel() / maxEnergy);
- }
- else
- {
- F32 maxEnergy = mDataBlock->maxEnergy;
- if ( maxEnergy > 0.f )
- return (mEnergy / mDataBlock->maxEnergy);
- }
- return 0.0f;
- }
- void ShapeBase::setEnergyLevel(F32 energy)
- {
- if (mDataBlock->inheritEnergyFromMount == false || !mShapeBaseMount) {
- if (mDamageState == Enabled) {
- mEnergy = (energy > mDataBlock->maxEnergy)?
- mDataBlock->maxEnergy: (energy < 0)? 0: energy;
- }
- } else {
- // Pass the set onto whatever we're mounted to...
- if ( mShapeBaseMount )
- {
- mShapeBaseMount->setEnergyLevel(energy);
- }
- }
- }
- void ShapeBase::setDamageLevel(F32 damage)
- {
- if (!mDataBlock->isInvincible) {
- F32 store = mDamage;
- mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage);
- if (store != mDamage) {
- updateDamageLevel();
- if (isServerObject()) {
- setMaskBits(DamageMask);
- mDataBlock->onDamage_callback( this, mDamage - store );
- }
- }
- }
- }
- void ShapeBase::updateContainer()
- {
- PROFILE_SCOPE( ShapeBase_updateContainer );
- // Update container drag and buoyancy properties
- // Set default values.
- mDrag = mDataBlock->drag;
- mBuoyancy = 0.0f;
- mNetGravity = gGravity;
- mAppliedForce.set(0,0,0);
-
- ContainerQueryInfo info;
- info.box = getWorldBox();
- info.mass = getMass();
- mContainer->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
-
- mWaterCoverage = info.waterCoverage;
- mLiquidType = info.liquidType;
- mLiquidHeight = info.waterHeight;
- setCurrentWaterObject( info.waterObject );
-
- // This value might be useful as a datablock value,
- // This is what allows the player to stand in shallow water (below this coverage)
- // without jiggling from buoyancy
- if (mWaterCoverage >= 0.25f)
- {
- // water viscosity is used as drag for in water.
- // ShapeBaseData drag is used for drag outside of water.
- // Combine these two components to calculate this ShapeBase object's
- // current drag.
- mDrag = ( info.waterCoverage * info.waterViscosity ) +
- ( 1.0f - info.waterCoverage ) * mDataBlock->drag;
- mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
- }
- mAppliedForce = info.appliedForce;
- mNetGravity = (1.0-mBuoyancy)*info.gravityScale* gGravity;
- //Con::printf( "WaterCoverage: %f", mWaterCoverage );
- //Con::printf( "Drag: %f", mDrag );
- }
- //----------------------------------------------------------------------------
- void ShapeBase::applyRepair(F32 amount)
- {
- // Repair increases the repair reserve
- if (amount > 0 && ((mRepairReserve += amount) > mDamage))
- mRepairReserve = mDamage;
- }
- void ShapeBase::applyDamage(F32 amount)
- {
- if (amount > 0)
- setDamageLevel(mDamage + amount);
- }
- F32 ShapeBase::getDamageValue()
- {
- // Return a 0-1 damage value.
- return mDamage / mDataBlock->maxDamage;
- }
- F32 ShapeBase::getMaxDamage()
- {
- return mDataBlock->maxDamage;
- }
- void ShapeBase::updateDamageLevel()
- {
- if (mDamageThread) {
- // mDamage is already 0-1 on the client
- if (mDamage >= mDataBlock->destroyedLevel) {
- if (getDamageState() == Destroyed)
- mShapeInstance->setPos(mDamageThread, 0);
- else
- mShapeInstance->setPos(mDamageThread, 1);
- } else {
- mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel);
- }
- }
- }
- //----------------------------------------------------------------------------
- void ShapeBase::setDamageState(DamageState state)
- {
- if (mDamageState == state)
- return;
- bool invokeCallback = false;
- const char* lastState = 0;
- if (!isGhost()) {
- if (state != getDamageState())
- setMaskBits(DamageMask);
- lastState = getDamageStateName();
- switch (state) {
- case Destroyed: {
- if (mDamageState == Enabled)
- {
- setDamageState(Disabled);
- // It is possible that the setDamageState() call above has already
- // upgraded our state to Destroyed. If that is the case, no need
- // to continue.
- if (mDamageState == state)
- return;
- }
- invokeCallback = true;
- break;
- }
- case Disabled:
- if (mDamageState == Enabled)
- invokeCallback = true;
- break;
- case Enabled:
- invokeCallback = true;
- break;
- default:
- AssertFatal(false, "Invalid damage state");
- break;
- }
- }
- mDamageState = state;
- if (mDamageState != Enabled) {
- mRepairReserve = 0;
- mEnergy = 0;
- }
- if (invokeCallback) {
- // Like to call the scripts after the state has been intialize.
- // This should only end up being called on the server.
- switch (state) {
- case Destroyed:
- mDataBlock->onDestroyed_callback( this, lastState );
- break;
- case Disabled:
- mDataBlock->onDisabled_callback( this, lastState );
- break;
- case Enabled:
- mDataBlock->onEnabled_callback( this, lastState );
- break;
- default:
- break;
- }
- }
- updateDamageState();
- updateDamageLevel();
- }
- bool ShapeBase::setDamageState(const char* state)
- {
- for (S32 i = 0; i < NumDamageStates; i++)
- if (!dStricmp(state,sDamageStateName[i])) {
- setDamageState(DamageState(i));
- return true;
- }
- return false;
- }
- const char* ShapeBase::getDamageStateName()
- {
- return sDamageStateName[mDamageState];
- }
- void ShapeBase::updateDamageState()
- {
- if (mHulkThread) {
- F32 pos = (mDamageState == Destroyed) ? 1.0f : 0.0f;
- if (mShapeInstance->getPos(mHulkThread) != pos) {
- mShapeInstance->setPos(mHulkThread,pos);
- if (isClientObject())
- mShapeInstance->animate();
- }
- }
- }
- //----------------------------------------------------------------------------
- void ShapeBase::blowUp()
- {
- Point3F center;
- mObjBox.getCenter(¢er);
- center += getPosition();
- MatrixF trans = getTransform();
- trans.setPosition( center );
- // explode
- Explosion* pExplosion = NULL;
- if( pointInWater( (Point3F &)center ) && mDataBlock->underwaterExplosion )
- {
- pExplosion = new Explosion;
- pExplosion->onNewDataBlock(mDataBlock->underwaterExplosion, false);
- }
- else
- {
- if (mDataBlock->explosion)
- {
- pExplosion = new Explosion;
- pExplosion->onNewDataBlock(mDataBlock->explosion, false);
- }
- }
- if( pExplosion )
- {
- pExplosion->setTransform(trans);
- pExplosion->setInitialState(center, damageDir);
- if (pExplosion->registerObject() == false)
- {
- Con::errorf(ConsoleLogEntry::General, "ShapeBase(%s)::explode: couldn't register explosion",
- mDataBlock->getName() );
- delete pExplosion;
- pExplosion = NULL;
- }
- }
- TSShapeInstance *debShape = NULL;
- if( mDataBlock->debrisShape == NULL )
- {
- return;
- }
- else
- {
- debShape = new TSShapeInstance( mDataBlock->debrisShape, true);
- }
- Vector< TSPartInstance * > partList;
- TSPartInstance::breakShape( debShape, 0, partList, NULL, NULL, 0 );
- if( !mDataBlock->debris )
- {
- mDataBlock->debris = new DebrisData;
- }
- // cycle through partlist and create debris pieces
- for( U32 i=0; i<partList.size(); i++ )
- {
- //Point3F axis( 0.0, 0.0, 1.0 );
- Point3F randomDir = MathUtils::randomDir( damageDir, 0, 50 );
- Debris *debris = new Debris;
- debris->setPartInstance( partList[i] );
- debris->init( center, randomDir );
- debris->onNewDataBlock( mDataBlock->debris, false );
- debris->setTransform( trans );
- if( !debris->registerObject() )
- {
- Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
- delete debris;
- debris = NULL;
- }
- else
- {
- debShape->incDebrisRefCount();
- }
- }
- damageDir.set(0, 0, 1);
- }
- //----------------------------------------------------------------------------
- void ShapeBase::onMount( SceneObject *obj, S32 node )
- {
- mConvexList->nukeList();
- // Are we mounting to a ShapeBase object?
- mShapeBaseMount = dynamic_cast<ShapeBase*>( obj );
- Parent::onMount( obj, node );
- }
- void ShapeBase::onUnmount( SceneObject *obj, S32 node )
- {
- Parent::onUnmount( obj, node );
- mShapeBaseMount = NULL;
- }
- Point3F ShapeBase::getAIRepairPoint()
- {
- if (mDataBlock->mountPointNode[ShapeBaseData::AIRepairNode] < 0)
- return Point3F(0, 0, 0);
- MatrixF xf(true);
- getMountTransform( ShapeBaseData::AIRepairNode, MatrixF::Identity, &xf );
- Point3F pos(0, 0, 0);
- xf.getColumn(3,&pos);
- return pos;
- }
- //----------------------------------------------------------------------------
- void ShapeBase::getEyeTransform(MatrixF* mat)
- {
- getEyeBaseTransform(mat, true);
- }
- void ShapeBase::getEyeBaseTransform(MatrixF* mat, bool includeBank)
- {
- // Returns eye to world space transform
- S32 eyeNode = mDataBlock->eyeNode;
- if (eyeNode != -1)
- mat->mul(getTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
- else
- *mat = getTransform();
- }
- void ShapeBase::getRenderEyeTransform(MatrixF* mat)
- {
- getRenderEyeBaseTransform(mat, true);
- }
- void ShapeBase::getRenderEyeBaseTransform(MatrixF* mat, bool includeBank)
- {
- // Returns eye to world space transform
- S32 eyeNode = mDataBlock->eyeNode;
- if (eyeNode != -1)
- mat->mul(getRenderTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
- else
- *mat = getRenderTransform();
- }
- void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
- {
- // Returns camera to world space transform
- // Handles first person / third person camera position
- if (isServerObject() && mShapeInstance)
- mShapeInstance->animateNodeSubtrees(true);
- if (*pos != 0)
- {
- F32 min,max;
- Point3F offset;
- MatrixF eye,rot;
- getCameraParameters(&min,&max,&offset,&rot);
- getRenderEyeTransform(&eye);
- mat->mul(eye,rot);
- // Use the eye transform to orient the camera
- VectorF vp,vec;
- vp.x = vp.z = 0;
- vp.y = -(max - min) * *pos;
- eye.mulV(vp,&vec);
-
- VectorF minVec;
- vp.y = -min;
- eye.mulV( vp, &minVec );
- // Use the camera node's pos.
- Point3F osp,sp;
- if (mDataBlock->cameraNode != -1) {
- mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
- // Scale the camera position before applying the transform
- const Point3F& scale = getScale();
- osp.convolve( scale );
- getRenderTransform().mulP(osp,&sp);
- }
- else
- getRenderTransform().getColumn(3,&sp);
- // Make sure we don't extend the camera into anything solid
- Point3F ep = sp + minVec + vec + offset;
- disableCollision();
- if (isMounted())
- getObjectMount()->disableCollision();
- RayInfo collision;
- if( mContainer && mContainer->castRay(sp, ep,
- (0xFFFFFFFF & ~(WaterObjectType |
- GameBaseObjectType |
- TriggerObjectType |
- DefaultObjectType)),
- &collision) == true) {
- F32 vecLenSq = vec.lenSquared();
- F32 adj = (-mDot(vec, collision.normal) / vecLenSq) * 0.1;
- F32 newPos = getMax(0.0f, collision.t - adj);
- if (newPos == 0.0f)
- eye.getColumn(3,&ep);
- else
- ep = sp + offset + (vec * newPos);
- }
- mat->setColumn(3,ep);
- if (isMounted())
- getObjectMount()->enableCollision();
- enableCollision();
- }
- else
- {
- getRenderEyeTransform(mat);
- }
- // Apply Camera FX.
- mat->mul( gCamFXMgr.getTrans() );
- }
- void ShapeBase::getEyeCameraTransform(IDisplayDevice *displayDevice, U32 eyeId, MatrixF *outMat)
- {
- MatrixF temp(1);
- Point3F eyePos;
- Point3F rotEyePos;
- DisplayPose newPose;
- displayDevice->getFrameEyePose(&newPose, eyeId);
- // Ok, basically we just need to add on newPose to the camera transform
- // NOTE: currently we dont support third-person camera in this mode
- MatrixF cameraTransform(1);
- F32 fakePos = 0;
- //cameraTransform = getRenderTransform(); // use this for controllers TODO
- getCameraTransform(&fakePos, &cameraTransform);
- temp = MatrixF(1);
- newPose.orientation.setMatrix(&temp);
- temp.setPosition(newPose.position);
- *outMat = cameraTransform * temp;
- }
- void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
- {
- *min = mDataBlock->cameraMinDist;
- *max = mDataBlock->cameraMaxDist;
- off->set(0,0,0);
- rot->identity();
- }
- //----------------------------------------------------------------------------
- F32 ShapeBase::getDamageFlash() const
- {
- return mDamageFlash;
- }
- void ShapeBase::setDamageFlash(const F32 flash)
- {
- mDamageFlash = flash;
- if (mDamageFlash < 0.0)
- mDamageFlash = 0;
- else if (mDamageFlash > 1.0)
- mDamageFlash = 1.0;
- }
- //----------------------------------------------------------------------------
- F32 ShapeBase::getWhiteOut() const
- {
- return mWhiteOut;
- }
- void ShapeBase::setWhiteOut(const F32 flash)
- {
- mWhiteOut = flash;
- if (mWhiteOut < 0.0)
- mWhiteOut = 0;
- else if (mWhiteOut > 1.5)
- mWhiteOut = 1.5;
- }
- //----------------------------------------------------------------------------
- bool ShapeBase::onlyFirstPerson() const
- {
- return mDataBlock->firstPersonOnly;
- }
- bool ShapeBase::useObjsEyePoint() const
- {
- return mDataBlock->useEyePoint;
- }
- //----------------------------------------------------------------------------
- void ShapeBase::setVelocity(const VectorF&)
- {
- }
- void ShapeBase::applyImpulse(const Point3F&,const VectorF&)
- {
- }
- //----------------------------------------------------------------------------
- void ShapeBase::playAudio(U32 slot,SFXTrack* profile)
- {
- AssertFatal( slot < MaxSoundThreads, "ShapeBase::playAudio() bad slot index" );
- Sound& st = mSoundThread[slot];
- if( profile && ( !st.play || st.profile != profile ) )
- {
- setMaskBits(SoundMaskN << slot);
- st.play = true;
- st.profile = profile;
- updateAudioState(st);
- }
- }
- void ShapeBase::stopAudio(U32 slot)
- {
- AssertFatal( slot < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index" );
- Sound& st = mSoundThread[slot];
- if ( st.play )
- {
- st.play = false;
- setMaskBits(SoundMaskN << slot);
- updateAudioState(st);
- }
- }
- void ShapeBase::updateServerAudio()
- {
- // Timeout non-looping sounds
- for (S32 i = 0; i < MaxSoundThreads; i++) {
- Sound& st = mSoundThread[i];
- if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) {
- clearMaskBits(SoundMaskN << i);
- st.play = false;
- }
- }
- }
- void ShapeBase::updateAudioState(Sound& st)
- {
- SFX_DELETE( st.sound );
- if ( st.play && st.profile )
- {
- if ( isGhost() )
- {
- if ( Sim::findObject( SimObjectId((uintptr_t)st.profile), st.profile ) )
- {
- st.sound = SFX->createSource( st.profile, &getTransform() );
- if ( st.sound )
- st.sound->play();
- }
- else
- st.play = false;
- }
- else
- {
- // Non-looping sounds timeout on the server
- st.timeout = 0;
- if ( !st.profile->getDescription()->mIsLooping )
- st.timeout = Sim::getCurrentTime() + sAudioTimeout;
- }
- }
- else
- st.play = false;
- }
- void ShapeBase::updateAudioPos()
- {
- for (S32 i = 0; i < MaxSoundThreads; i++)
- {
- SFXSource* source = mSoundThread[i].sound;
- if ( source )
- source->setTransform( getTransform() );
- }
- }
- //----------------------------------------------------------------------------
- const char *ShapeBase::getThreadSequenceName( U32 slot )
- {
- Thread& st = mScriptThread[slot];
- if ( st.sequence == -1 )
- {
- // Invalid Animation.
- return "";
- }
- // Name Index
- const U32 nameIndex = getShape()->sequences[st.sequence].nameIndex;
- // Return Name.
- return getShape()->getName( nameIndex );
- }
- bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset)
- {
- Thread& st = mScriptThread[slot];
- if (st.thread && st.sequence == seq && st.state == Thread::Play)
- return true;
- // Handle a -1 sequence, as this may be set when a thread has been destroyed.
- if(seq == -1)
- return true;
- if (seq < MaxSequenceIndex) {
- setMaskBits(ThreadMaskN << slot);
- st.sequence = seq;
- if (reset) {
- st.state = Thread::Play;
- st.atEnd = false;
- st.timescale = 1.f;
- st.position = 0.f;
- }
- if (mShapeInstance) {
- if (!st.thread)
- st.thread = mShapeInstance->addThread();
- mShapeInstance->setSequence(st.thread,seq,st.position);
- updateThread(st);
- }
- return true;
- }
- return false;
- }
- void ShapeBase::updateThread(Thread& st)
- {
- switch (st.state)
- {
- case Thread::Stop:
- {
- mShapeInstance->setTimeScale( st.thread, 1.f );
- mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
- } // Drop through to pause state
- case Thread::Pause:
- {
- mShapeInstance->setTimeScale( st.thread, 0.f );
- } break;
- case Thread::Play:
- {
- if (st.atEnd)
- {
- mShapeInstance->setTimeScale(st.thread,1);
- mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
- mShapeInstance->setTimeScale(st.thread,0);
- st.state = Thread::Stop;
- }
- else
- {
- if ( st.position != -1.f )
- {
- mShapeInstance->setTimeScale( st.thread, 1.f );
- mShapeInstance->setPos( st.thread, st.position );
- }
- mShapeInstance->setTimeScale(st.thread, st.timescale );
- }
- } break;
- case Thread::Destroy:
- {
- st.atEnd = true;
- st.sequence = -1;
- if(st.thread)
- {
- mShapeInstance->destroyThread(st.thread);
- st.thread = 0;
- }
- } break;
- }
- }
- bool ShapeBase::stopThread(U32 slot)
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1 && st.state != Thread::Stop) {
- setMaskBits(ThreadMaskN << slot);
- st.state = Thread::Stop;
- updateThread(st);
- return true;
- }
- return false;
- }
- bool ShapeBase::destroyThread(U32 slot)
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1 && st.state != Thread::Destroy) {
- setMaskBits(ThreadMaskN << slot);
- st.state = Thread::Destroy;
- updateThread(st);
- return true;
- }
- return false;
- }
- bool ShapeBase::pauseThread(U32 slot)
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1 && st.state != Thread::Pause) {
- setMaskBits(ThreadMaskN << slot);
- st.state = Thread::Pause;
- updateThread(st);
- return true;
- }
- return false;
- }
- bool ShapeBase::playThread(U32 slot)
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1 && st.state != Thread::Play) {
- setMaskBits(ThreadMaskN << slot);
- st.state = Thread::Play;
- updateThread(st);
- return true;
- }
- return false;
- }
- bool ShapeBase::setThreadPosition( U32 slot, F32 pos )
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1)
- {
- setMaskBits(ThreadMaskN << slot);
- st.position = pos;
- st.atEnd = false;
- updateThread(st);
- return true;
- }
- return false;
- }
- bool ShapeBase::setThreadDir(U32 slot,bool forward)
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1)
- {
- if ( ( st.timescale >= 0.f ) != forward )
- {
- setMaskBits(ThreadMaskN << slot);
- st.timescale *= -1.f ;
- st.atEnd = false;
- updateThread(st);
- }
- return true;
- }
- return false;
- }
- bool ShapeBase::setThreadTimeScale( U32 slot, F32 timeScale )
- {
- Thread& st = mScriptThread[slot];
- if (st.sequence != -1)
- {
- if (st.timescale != timeScale)
- {
- setMaskBits(ThreadMaskN << slot);
- st.timescale = timeScale;
- updateThread(st);
- }
- return true;
- }
- return false;
- }
- void ShapeBase::advanceThreads(F32 dt)
- {
- for (U32 i = 0; i < MaxScriptThreads; i++) {
- Thread& st = mScriptThread[i];
- if (st.thread) {
- if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd &&
- ( ( st.timescale > 0.f )? mShapeInstance->getPos(st.thread) >= 1.0:
- mShapeInstance->getPos(st.thread) <= 0)) {
- st.atEnd = true;
- updateThread(st);
- if (!isGhost()) {
- mDataBlock->onEndSequence_callback(this, i, this->getThreadSequenceName(i));
- }
- }
- // Make sure the thread is still valid after the call to onEndSequence_callback().
- // Someone could have called destroyThread() while in there.
- if(st.thread)
- {
- mShapeInstance->advanceTime(dt,st.thread);
- st.position = mShapeInstance->getPos(st.thread);
- }
- }
- }
- }
- //----------------------------------------------------------------------------
- /// Emit particles on the given emitter, if we're within triggerHeight above some static surface with a
- /// material that has 'showDust' set to true. The particles will have a lifetime of 'numMilliseconds'
- /// and be emitted at the given offset from the contact point having a direction of 'axis'.
- void ShapeBase::emitDust( ParticleEmitter* emitter, F32 triggerHeight, const Point3F& offset, U32 numMilliseconds, const Point3F& axis )
- {
- if( !emitter )
- return;
- Point3F startPos = getPosition();
- Point3F endPos = startPos + Point3F( 0.0, 0.0, - triggerHeight );
- RayInfo rayInfo;
- if( getContainer()->castRay( startPos, endPos, STATIC_COLLISION_TYPEMASK, &rayInfo ) )
- {
- Material* material = ( rayInfo.material ? dynamic_cast< Material* >( rayInfo.material->getMaterial() ) : 0 );
- if( material && material->mShowDust )
- {
- LinearColorF colorList[ ParticleData::PDC_NUM_KEYS ];
- for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x )
- colorList[ x ] = material->mEffectColor[ x ];
- for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x )
- colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 );
- emitter->setColors( colorList );
- Point3F contactPoint = rayInfo.point + offset;
- emitter->emitParticles( contactPoint, true, ( axis == Point3F::Zero ? rayInfo.normal : axis ),
- getVelocity(), numMilliseconds );
- }
- }
- }
- //----------------------------------------------------------------------------
- TSShape const* ShapeBase::getShape()
- {
- return mShapeInstance? mShapeInstance->getShape(): 0;
- }
- void ShapeBase::prepRenderImage( SceneRenderState *state )
- {
- _prepRenderImage( state, true, true );
- }
- void ShapeBase::_prepRenderImage( SceneRenderState *state,
- bool renderSelf,
- bool renderMountedImages )
- {
- PROFILE_SCOPE( ShapeBase_PrepRenderImage );
- //if ( mIsCubemapUpdate )
- // return false;
- if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) )
- return;
- // We don't need to render if all the meshes are forced hidden.
- if ( mMeshHidden.getSize() > 0 && mMeshHidden.testAll() )
- return;
-
- // If we're rendering shadows don't render the mounted
- // images unless the shape is also rendered.
- if ( state->isShadowPass() && !renderSelf )
- return;
- // If we're currently rendering our own reflection we
- // don't want to render ourselves into it.
- if ( mCubeReflector.isRendering() )
- return;
- // We force all the shapes to use the highest detail
- // if we're the control object or mounted.
- bool forceHighestDetail = false;
- {
- GameConnection *con = GameConnection::getConnectionToServer();
- ShapeBase *co = NULL;
- if(con && ( (co = dynamic_cast<ShapeBase*>(con->getControlObject())) != NULL) )
- {
- if(co == this || co->getObjectMount() == this)
- forceHighestDetail = true;
- }
- }
- mLastRenderFrame = sLastRenderFrame;
- // get shape detail...we might not even need to be drawn
- Point3F cameraOffset = getWorldBox().getClosestPoint( state->getDiffuseCameraPosition() ) - state->getDiffuseCameraPosition();
- F32 dist = cameraOffset.len();
- if (dist < 0.01f)
- dist = 0.01f;
- F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
- if (mShapeInstance)
- {
- if ( forceHighestDetail )
- mShapeInstance->setCurrentDetail( 0 );
- else
- mShapeInstance->setDetailFromDistance( state, dist * invScale );
-
- mShapeInstance->animate();
- }
-
- if ( ( mShapeInstance && mShapeInstance->getCurrentDetail() < 0 ) ||
- ( !mShapeInstance && !gShowBoundingBox ) )
- {
- // no, don't draw anything
- return;
- }
- if( renderMountedImages )
- {
- for (U32 i = 0; i < MaxMountedImages; i++)
- {
- MountedImage& image = mMountedImageList[i];
- U32 imageShapeIndex = getImageShapeIndex(image);
- if (image.dataBlock && image.shapeInstance[imageShapeIndex])
- {
- // Select detail levels on mounted items but... always
- // draw the control object's mounted images in high detail.
- if ( forceHighestDetail )
- image.shapeInstance[imageShapeIndex]->setCurrentDetail( 0 );
- else
- image.shapeInstance[imageShapeIndex]->setDetailFromDistance( state, dist * invScale );
- if (!mIsZero( (1.0f - mCloakLevel) * mFadeVal))
- {
- prepBatchRender( state, i );
- // Debug rendering of the mounted shape bounds.
- if ( gShowBoundingBox )
- {
- ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
- ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox );
- ri->objectIndex = i;
- ri->type = RenderPassManager::RIT_Editor;
- state->getRenderPass()->addInst( ri );
- }
- }
- }
- }
- }
- // Debug rendering of the shape bounding box.
- if ( gShowBoundingBox )
- {
- ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
- ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox );
- ri->objectIndex = -1;
- ri->type = RenderPassManager::RIT_Editor;
- state->getRenderPass()->addInst( ri );
- }
- if ( mShapeInstance && renderSelf )
- prepBatchRender( state, -1 );
- calcClassRenderData();
- }
- //----------------------------------------------------------------------------
- // prepBatchRender
- //----------------------------------------------------------------------------
- void ShapeBase::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex )
- {
- // CHANGES IN HERE SHOULD BE DUPLICATED IN TSSTATIC!
-
- GFXTransformSaver saver;
-
- // Set up our TS render state.
- TSRenderState rdata;
- rdata.setSceneState( state );
- if ( mCubeReflector.isEnabled() )
- rdata.setCubemap( mCubeReflector.getCubemap() );
- rdata.setFadeOverride( (1.0f - mCloakLevel) * mFadeVal );
- // We might have some forward lit materials
- // so pass down a query to gather lights.
- LightQuery query;
- query.init( getWorldSphere() );
- rdata.setLightQuery( &query );
- if( mountedImageIndex != -1 )
- {
- MountedImage& image = mMountedImageList[mountedImageIndex];
- if( image.dataBlock && image.shapeInstance )
- {
- renderMountedImage( mountedImageIndex, rdata, state );
- }
- }
- else
- {
- MatrixF mat = getRenderTransform();
- mat.scale( mObjScale );
- GFX->setWorldMatrix( mat );
- if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() )
- {
- RenderPassManager *pass = state->getRenderPass();
- OccluderRenderInst *ri = pass->allocInst<OccluderRenderInst>();
- ri->type = RenderPassManager::RIT_Occluder;
- ri->query = mCubeReflector.getOcclusionQuery();
- mObjToWorld.mulP( mObjBox.getCenter(), &ri->position );
- ri->scale.set( mObjBox.getExtents() );
- ri->orientation = pass->allocUniqueXform( mObjToWorld );
- ri->isSphere = false;
- state->getRenderPass()->addInst( ri );
- }
- mShapeInstance->animate();
- mShapeInstance->render( rdata );
- }
- }
- void ShapeBase::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state )
- {
- GFX->pushWorldMatrix();
- MatrixF mat;
- getRenderImageTransform(imageSlot, &mat, rstate.getSceneState()->isShadowPass());
- GFX->setWorldMatrix( mat );
- MountedImage& image = mMountedImageList[imageSlot];
- U32 imageShapeIndex = getImageShapeIndex(image);
- image.shapeInstance[imageShapeIndex]->animate();
- image.shapeInstance[imageShapeIndex]->render( rstate );
- GFX->popWorldMatrix();
- }
- void ShapeBase::_renderBoundingBox( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
- {
- // If we got an override mat then this some
- // special rendering pass... skip out of it.
- if ( overrideMat )
- return;
- GFXStateBlockDesc desc;
- desc.setZReadWrite( true, false );
- desc.setBlend( true );
- desc.fillMode = GFXFillWireframe;
- GFXDrawUtil *drawer = GFX->getDrawUtil();
- if ( ri->objectIndex != -1 )
- {
- MountedImage &image = mMountedImageList[ ri->objectIndex ];
- if ( image.shapeInstance )
- {
- MatrixF mat;
- getRenderImageTransform( ri->objectIndex, &mat );
- const Box3F &objBox = image.shapeInstance[getImageShapeIndex(image)]->getShape()->mBounds;
- drawer->drawCube( desc, objBox, ColorI( 255, 255, 255 ), &mat );
- }
- }
- else
- drawer->drawCube( desc, mObjBox, ColorI( 255, 255, 255 ), &mRenderObjToWorld );
- }
- bool ShapeBase::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
- {
- if (mShapeInstance)
- {
- RayInfo shortest;
- shortest.t = 1e8;
- info->object = NULL;
- for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++)
- {
- mShapeInstance->animate(mDataBlock->LOSDetails[i]);
- if (mShapeInstance->castRay(start, end, info, mDataBlock->LOSDetails[i]))
- {
- info->object = this;
- if (info->t < shortest.t)
- shortest = *info;
- }
- }
- if (info->object == this)
- {
- // Copy out the shortest time...
- *info = shortest;
- return true;
- }
- }
- return false;
- }
- bool ShapeBase::castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info)
- {
- if (mShapeInstance)
- {
- RayInfo localInfo;
- mShapeInstance->animate();
- bool res = mShapeInstance->castRayRendered(start, end, &localInfo, mShapeInstance->getCurrentDetail());
- if (res)
- {
- *info = localInfo;
- info->object = this;
- return true;
- }
- }
- return false;
- }
- //----------------------------------------------------------------------------
- bool ShapeBase::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &, const SphereF &)
- {
- if ( !mShapeInstance )
- return false;
- polyList->setTransform(&mObjToWorld, mObjScale);
- polyList->setObject(this);
- if ( context == PLC_Selection )
- {
- mShapeInstance->animate();
- mShapeInstance->buildPolyList(polyList,mShapeInstance->getCurrentDetail());
- return true;
- }
- else if ( context == PLC_Export )
- {
- // Try to call on the client so we can export materials
- ShapeBase* exportObj = this;
- if ( isServerObject() && getClientObject() )
- exportObj = dynamic_cast<ShapeBase*>(getClientObject());
- S32 dl = 0;
- exportObj->mShapeInstance->animate();
- exportObj->mShapeInstance->buildPolyList(polyList, dl);
- return true;
- }
- else
- {
- bool ret = false;
- for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
- {
- mShapeInstance->buildPolyList(polyList,mDataBlock->collisionDetails[i]);
- ret = true;
- }
- return ret;
- }
- }
- void ShapeBase::buildConvex(const Box3F& box, Convex* convex)
- {
- if (mShapeInstance == NULL)
- return;
- // These should really come out of a pool
- mConvexList->collectGarbage();
- Box3F realBox = box;
- mWorldToObj.mul(realBox);
- realBox.minExtents.convolveInverse(mObjScale);
- realBox.maxExtents.convolveInverse(mObjScale);
- if (realBox.isOverlapped(getObjBox()) == false)
- return;
- for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
- {
- Box3F newbox = mDataBlock->collisionBounds[i];
- newbox.minExtents.convolve(mObjScale);
- newbox.maxExtents.convolve(mObjScale);
- mObjToWorld.mul(newbox);
- if (box.isOverlapped(newbox) == false)
- continue;
- // See if this hull exists in the working set already...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
- if (itr->mConvex->getType() == ShapeBaseConvexType &&
- (static_cast<ShapeBaseConvex*>(itr->mConvex)->pShapeBase == this &&
- static_cast<ShapeBaseConvex*>(itr->mConvex)->hullId == i)) {
- cc = itr->mConvex;
- break;
- }
- }
- if (cc)
- continue;
- // Create a new convex.
- ShapeBaseConvex* cp = new ShapeBaseConvex;
- mConvexList->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->mObject = this;
- cp->pShapeBase = this;
- cp->hullId = i;
- cp->box = mDataBlock->collisionBounds[i];
- cp->transform = 0;
- cp->findNodeTransform();
- }
- }
- //----------------------------------------------------------------------------
- void ShapeBase::queueCollision( SceneObject *obj, const VectorF &vec)
- {
- // Add object to list of collisions.
- SimTime time = Sim::getCurrentTime();
- S32 num = obj->getId();
- CollisionTimeout** adr = &mTimeoutList;
- CollisionTimeout* ptr = mTimeoutList;
- while (ptr) {
- if (ptr->objectNumber == num) {
- if (ptr->expireTime < time) {
- ptr->expireTime = time + CollisionTimeoutValue;
- ptr->object = obj;
- ptr->vector = vec;
- }
- return;
- }
- // Recover expired entries
- if (ptr->expireTime < time) {
- CollisionTimeout* cur = ptr;
- *adr = ptr->next;
- ptr = ptr->next;
- cur->next = sFreeTimeoutList;
- sFreeTimeoutList = cur;
- }
- else {
- adr = &ptr->next;
- ptr = ptr->next;
- }
- }
- // New entry for the object
- if (sFreeTimeoutList != NULL)
- {
- ptr = sFreeTimeoutList;
- sFreeTimeoutList = ptr->next;
- ptr->next = NULL;
- }
- else
- {
- ptr = sTimeoutChunker.alloc();
- }
- ptr->object = obj;
- ptr->objectNumber = obj->getId();
- ptr->vector = vec;
- ptr->expireTime = time + CollisionTimeoutValue;
- ptr->next = mTimeoutList;
- mTimeoutList = ptr;
- }
- void ShapeBase::notifyCollision()
- {
- // Notify all the objects that were just stamped during the queueing
- // process.
- SimTime expireTime = Sim::getCurrentTime() + CollisionTimeoutValue;
- for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next)
- {
- if (ptr->expireTime == expireTime && ptr->object)
- {
- SimObjectPtr<SceneObject> safePtr(ptr->object);
- SimObjectPtr<ShapeBase> safeThis(this);
- onCollision(ptr->object,ptr->vector);
- ptr->object = 0;
- if(!bool(safeThis))
- return;
- if(bool(safePtr))
- safePtr->onCollision(this,ptr->vector);
- if(!bool(safeThis))
- return;
- }
- }
- }
- void ShapeBase::onCollision( SceneObject *object, const VectorF &vec )
- {
- if (!isGhost())
- mDataBlock->onCollision_callback( this, object, vec, vec.len() );
- }
- //--------------------------------------------------------------------------
- bool ShapeBase::pointInWater( Point3F &point )
- {
- if ( mCurrentWaterObject == NULL )
- return false;
- return mCurrentWaterObject->isUnderwater( point );
- }
- //----------------------------------------------------------------------------
- void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream)
- {
- Parent::writePacketData(connection, stream);
- stream->write(getEnergyLevel());
- stream->write(mRechargeRate);
- }
- void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream)
- {
- Parent::readPacketData(connection, stream);
- F32 energy;
- stream->read(&energy);
- setEnergyLevel(energy);
- stream->read(&mRechargeRate);
- }
- F32 ShapeBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
- {
- // If it's the scope object, must be high priority
- if (camInfo->camera == this) {
- // Most priorities are between 0 and 1, so this
- // should be something larger.
- return 10.0f;
- }
- if( camInfo->camera )
- {
- ShapeBase* camera = dynamic_cast< ShapeBase* >( camInfo->camera );
- // see if the camera is mounted to this...
- // if it is, this should have a high priority
- if( camera && camera->getObjectMount() == this)
- return 10.0f;
- }
- return Parent::getUpdatePriority(camInfo, updateMask, updateSkips);
- }
- U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
- {
- U32 retMask = Parent::packUpdate(con, mask, stream);
- if (mask & InitialUpdateMask) {
- // mask off sounds that aren't playing
- S32 i;
- for (i = 0; i < MaxSoundThreads; i++)
- if (!mSoundThread[i].play)
- mask &= ~(SoundMaskN << i);
- // mask off threads that aren't running
- for (i = 0; i < MaxScriptThreads; i++)
- if (mScriptThread[i].sequence == -1)
- mask &= ~(ThreadMaskN << i);
- // mask off images that aren't updated
- for(i = 0; i < MaxMountedImages; i++)
- if(!mMountedImageList[i].dataBlock)
- mask &= ~(ImageMaskN << i);
- }
- if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask | MeshHiddenMask |
- ThreadMask | ImageMask | CloakMask | SkinMask)))
- return retMask;
- if (stream->writeFlag(mask & DamageMask)) {
- stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits);
- stream->writeInt(mDamageState,NumDamageStateBits);
- stream->writeNormalVector( damageDir, 8 );
- }
- if (stream->writeFlag(mask & ThreadMask)) {
- for (S32 i = 0; i < MaxScriptThreads; i++) {
- Thread& st = mScriptThread[i];
- if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) ) {
- stream->writeInt(st.sequence,ThreadSequenceBits);
- stream->writeInt(st.state,2);
- stream->write(st.timescale);
- stream->write(st.position);
- stream->writeFlag(st.atEnd);
- }
- }
- }
- if (stream->writeFlag(mask & SoundMask)) {
- for (S32 i = 0; i < MaxSoundThreads; i++) {
- Sound& st = mSoundThread[i];
- if (stream->writeFlag(mask & (SoundMaskN << i)))
- if (stream->writeFlag(st.play))
- stream->writeRangedU32(st.profile->getId(),DataBlockObjectIdFirst,
- DataBlockObjectIdLast);
- }
- }
- if (stream->writeFlag(mask & ImageMask)) {
- for (S32 i = 0; i < MaxMountedImages; i++)
- if (stream->writeFlag(mask & (ImageMaskN << i))) {
- MountedImage& image = mMountedImageList[i];
- if (stream->writeFlag(image.dataBlock))
- stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst,
- DataBlockObjectIdBitSize);
- con->packNetStringHandleU(stream, image.skinNameHandle);
- con->packNetStringHandleU(stream, image.scriptAnimPrefix);
- // Used to force the 1st person rendering on the client. This is required
- // as this object could be ghosted to the client prior to its controlling client
- // being set. Therefore there is a network tick when the object is in limbo...
- stream->writeFlag(image.dataBlock && image.dataBlock->animateAllShapes && getControllingClient() == con);
- stream->writeFlag(image.wet);
- stream->writeFlag(image.motion);
- stream->writeFlag(image.ammo);
- stream->writeFlag(image.loaded);
- stream->writeFlag(image.target);
- stream->writeFlag(image.triggerDown);
- stream->writeFlag(image.altTriggerDown);
- for (U32 j=0; j<ShapeBaseImageData::MaxGenericTriggers; ++j)
- {
- stream->writeFlag(image.genericTrigger[j]);
- }
- stream->writeInt(image.fireCount,3);
- stream->writeInt(image.altFireCount,3);
- stream->writeInt(image.reloadCount,3);
- stream->writeFlag(isImageFiring(i));
- stream->writeFlag(isImageAltFiring(i));
- stream->writeFlag(isImageReloading(i));
- }
- }
- // Group some of the uncommon stuff together.
- if (stream->writeFlag(mask & (NameMask | CloakMask | SkinMask | MeshHiddenMask ))) {
-
- if (stream->writeFlag(mask & CloakMask))
- {
- // cloaking
- stream->writeFlag( mCloaked );
- // piggyback control update
- stream->writeFlag(bool(getControllingClient()));
- // fading
- if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) {
- stream->writeFlag(mFadeOut);
- stream->write(mFadeTime);
- }
- else
- stream->writeFlag(mFadeVal == 1.0f);
- }
- if (stream->writeFlag(mask & NameMask)) {
- con->packNetStringHandleU(stream, mShapeNameHandle);
- }
- if ( stream->writeFlag( mask & MeshHiddenMask ) )
- stream->writeBits( mMeshHidden );
- if (stream->writeFlag(mask & SkinMask))
- con->packNetStringHandleU(stream, mSkinNameHandle);
- }
- return retMask;
- }
- void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
- {
- Parent::unpackUpdate(con, stream);
- mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event...
- if(!stream->readFlag())
- return;
- if (stream->readFlag()) {
- mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage);
- DamageState prevState = mDamageState;
- mDamageState = DamageState(stream->readInt(NumDamageStateBits));
- stream->readNormalVector( &damageDir, 8 );
- if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded())
- blowUp();
- updateDamageLevel();
- updateDamageState();
- }
- if (stream->readFlag()) {
- for (S32 i = 0; i < MaxScriptThreads; i++) {
- if (stream->readFlag()) {
- Thread& st = mScriptThread[i];
- U32 seq = stream->readInt(ThreadSequenceBits);
- st.state = Thread::State(stream->readInt(2));
- stream->read( &st.timescale );
- stream->read( &st.position );
- st.atEnd = stream->readFlag();
- if (st.sequence != seq && st.state != Thread::Destroy)
- setThreadSequence(i,seq,false);
- else
- updateThread(st);
- }
- }
- }
- if ( stream->readFlag() )
- {
- for ( S32 i = 0; i < MaxSoundThreads; i++ )
- {
- if ( stream->readFlag() )
- {
- Sound& st = mSoundThread[i];
- st.play = stream->readFlag();
- if ( st.play )
- {
- st.profile = (SFXTrack*)(uintptr_t)stream->readRangedU32( DataBlockObjectIdFirst,
- DataBlockObjectIdLast );
- }
- if ( isProperlyAdded() )
- updateAudioState( st );
- }
- }
- }
- // Mounted Images
- if (stream->readFlag()) {
- for (S32 i = 0; i < MaxMountedImages; i++) {
- if (stream->readFlag()) {
- MountedImage& image = mMountedImageList[i];
- ShapeBaseImageData* imageData = 0;
- if (stream->readFlag()) {
- SimObjectId id = stream->readInt(DataBlockObjectIdBitSize) +
- DataBlockObjectIdFirst;
- if (!Sim::findObject(id,imageData)) {
- con->setLastError("Invalid packet (mounted images).");
- return;
- }
- }
- NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);
- NetStringHandle scriptDesiredAnimPrefix = con->unpackNetStringHandleU(stream);
- image.forceAnimateAllShapes = stream->readFlag();
- image.wet = stream->readFlag();
- image.motion = stream->readFlag();
- image.ammo = stream->readFlag();
- image.loaded = stream->readFlag();
- image.target = stream->readFlag();
- image.triggerDown = stream->readFlag();
- image.altTriggerDown = stream->readFlag();
- for (U32 j=0; j<ShapeBaseImageData::MaxGenericTriggers; ++j)
- {
- image.genericTrigger[j] = stream->readFlag();
- }
- S32 count = stream->readInt(3);
- S32 altCount = stream->readInt(3);
- S32 reloadCount = stream->readInt(3);
- bool datablockChange = image.dataBlock != imageData;
- if (datablockChange || (image.skinNameHandle != skinDesiredNameHandle))
- {
- MountedImage& neoImage = mMountedImageList[i];
- neoImage.scriptAnimPrefix = scriptDesiredAnimPrefix;
- setImage( i, imageData,
- skinDesiredNameHandle, neoImage.loaded,
- neoImage.ammo, neoImage.triggerDown, neoImage.altTriggerDown,
- neoImage.motion, neoImage.genericTrigger[0], neoImage.genericTrigger[1], neoImage.genericTrigger[2], neoImage.genericTrigger[3],
- neoImage.target);
- }
-
- if (!datablockChange && image.scriptAnimPrefix != scriptDesiredAnimPrefix)
- {
- // We don't have a new image, but we do have a new script anim prefix to work with.
- // Notify the image of this change.
- MountedImage& animImage = mMountedImageList[i];
- animImage.scriptAnimPrefix = scriptDesiredAnimPrefix;
- updateAnimThread(i, getImageShapeIndex(animImage));
- }
- bool isFiring = stream->readFlag();
- bool isAltFiring = stream->readFlag();
- bool isReloading = stream->readFlag();
- if (isProperlyAdded()) {
- // Normal processing
- bool processFiring = false;
- if (count != image.fireCount)
- {
- image.fireCount = count;
- setImageState(i,getImageFireState(i),true);
- processFiring = true;
- }
- else if (altCount != image.altFireCount)
- {
- image.altFireCount = altCount;
- setImageState(i,getImageAltFireState(i),true);
- processFiring = true;
- }
- else if (reloadCount != image.reloadCount)
- {
- image.reloadCount = reloadCount;
- setImageState(i,getImageReloadState(i),true);
- }
- if (processFiring && imageData)
- {
- if ( imageData->lightType == ShapeBaseImageData::WeaponFireLight )
- image.lightStart = Sim::getCurrentTime();
- }
-
- updateImageState(i,0);
- }
- else
- {
- if(imageData)
- {
- // Initial state
- image.fireCount = count;
- image.altFireCount = altCount;
- image.reloadCount = reloadCount;
- if (isFiring)
- setImageState(i,getImageFireState(i),true);
- else if (isAltFiring)
- setImageState(i,getImageAltFireState(i),true);
- else if (isReloading)
- setImageState(i,getImageReloadState(i),true);
- }
- }
- }
- }
- }
- if (stream->readFlag())
- {
- if(stream->readFlag()) // CloakMask and control
- {
- // Read cloaking state.
-
- setCloakedState(stream->readFlag());
- mIsControlled = stream->readFlag();
- if (( mFading = stream->readFlag()) == true) {
- mFadeOut = stream->readFlag();
- if(mFadeOut)
- mFadeVal = 1.0f;
- else
- mFadeVal = 0;
- stream->read(&mFadeTime);
- mFadeDelay = 0;
- mFadeElapsedTime = 0;
- }
- else
- mFadeVal = F32(stream->readFlag());
- }
- if (stream->readFlag()) { // NameMask
- mShapeNameHandle = con->unpackNetStringHandleU(stream);
- }
-
- if ( stream->readFlag() ) // MeshHiddenMask
- {
- stream->readBits( &mMeshHidden );
- _updateHiddenMeshes();
- }
- if (stream->readFlag()) // SkinMask
- {
- NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);;
- if (mSkinNameHandle != skinDesiredNameHandle)
- {
- mSkinNameHandle = skinDesiredNameHandle;
- reSkin();
- }
- }
- }
- }
- //--------------------------------------------------------------------------
- void ShapeBase::forceUncloak(const char * reason)
- {
- AssertFatal(isServerObject(), "ShapeBase::forceUncloak: server only call");
- if(!mCloaked)
- return;
- mDataBlock->onForceUncloak_callback( this, reason ? reason : "" );
- }
- void ShapeBase::setCloakedState(bool cloaked)
- {
- if (cloaked == mCloaked)
- return;
- if (isServerObject())
- setMaskBits(CloakMask);
- // Have to do this for the client, if we are ghosted over in the initial
- // packet as cloaked, we set the state immediately to the extreme
- if (isProperlyAdded() == false) {
- mCloaked = cloaked;
- if (mCloaked)
- mCloakLevel = 1.0;
- else
- mCloakLevel = 0.0;
- } else {
- mCloaked = cloaked;
- }
- }
- //--------------------------------------------------------------------------
- void ShapeBase::setHidden( bool hidden )
- {
- if( hidden != isHidden() )
- {
- Parent::setHidden( hidden );
- if( hidden )
- setProcessTick( false );
- else
- setProcessTick( true );
- }
- }
- //--------------------------------------------------------------------------
- void ShapeBaseConvex::findNodeTransform()
- {
- S32 dl = pShapeBase->mDataBlock->collisionDetails[hullId];
- TSShapeInstance* si = pShapeBase->getShapeInstance();
- TSShape* shape = si->getShape();
- const TSShape::Detail* detail = &shape->details[dl];
- const S32 subs = detail->subShapeNum;
- const S32 start = shape->subShapeFirstObject[subs];
- const S32 end = start + shape->subShapeNumObjects[subs];
- // Find the first object that contains a mesh for this
- // detail level. There should only be one mesh per
- // collision detail level.
- for (S32 i = start; i < end; i++)
- {
- const TSShape::Object* obj = &shape->objects[i];
- if (obj->numMeshes && detail->objectDetailNum < obj->numMeshes)
- {
- nodeTransform = &si->mNodeTransforms[obj->nodeIndex];
- return;
- }
- }
- return;
- }
- const MatrixF& ShapeBaseConvex::getTransform() const
- {
- // If the transform isn't specified, it's assumed to be the
- // origin of the shape.
- const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
- // Multiply on the mesh shape offset
- // tg: Returning this static here is not really a good idea, but
- // all this Convex code needs to be re-organized.
- if (nodeTransform) {
- static MatrixF mat;
- mat.mul(omat,*nodeTransform);
- return mat;
- }
- return omat;
- }
- Box3F ShapeBaseConvex::getBoundingBox() const
- {
- const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
- return getBoundingBox(omat, mObject->getScale());
- }
- Box3F ShapeBaseConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
- {
- Box3F newBox = box;
- newBox.minExtents.convolve(scale);
- newBox.maxExtents.convolve(scale);
- mat.mul(newBox);
- return newBox;
- }
- Point3F ShapeBaseConvex::support(const VectorF& v) const
- {
- TSShape::ConvexHullAccelerator* pAccel =
- pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
- AssertFatal(pAccel != NULL, "Error, no accel!");
- F32 currMaxDP = mDot(pAccel->vertexList[0], v);
- U32 index = 0;
- for (U32 i = 1; i < pAccel->numVerts; i++) {
- F32 dp = mDot(pAccel->vertexList[i], v);
- if (dp > currMaxDP) {
- currMaxDP = dp;
- index = i;
- }
- }
- return pAccel->vertexList[index];
- }
- void ShapeBaseConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
- {
- cf->material = 0;
- cf->mObject = mObject;
- TSShape::ConvexHullAccelerator* pAccel =
- pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
- AssertFatal(pAccel != NULL, "Error, no accel!");
- F32 currMaxDP = mDot(pAccel->vertexList[0], n);
- U32 index = 0;
- U32 i;
- for (i = 1; i < pAccel->numVerts; i++) {
- F32 dp = mDot(pAccel->vertexList[i], n);
- if (dp > currMaxDP) {
- currMaxDP = dp;
- index = i;
- }
- }
- const U8* emitString = pAccel->emitStrings[index];
- U32 currPos = 0;
- U32 numVerts = emitString[currPos++];
- for (i = 0; i < numVerts; i++) {
- cf->mVertexList.increment();
- U32 vListIDx = emitString[currPos++];
- mat.mulP(pAccel->vertexList[vListIDx], &cf->mVertexList.last());
- }
- U32 numEdges = emitString[currPos++];
- for (i = 0; i < numEdges; i++) {
- U32 ev0 = emitString[currPos++];
- U32 ev1 = emitString[currPos++];
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = ev0;
- cf->mEdgeList.last().vertex[1] = ev1;
- }
- U32 numFaces = emitString[currPos++];
- for (i = 0; i < numFaces; i++) {
- cf->mFaceList.increment();
- U32 plane = emitString[currPos++];
- mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal);
- for (U32 j = 0; j < 3; j++)
- cf->mFaceList.last().vertex[j] = emitString[currPos++];
- }
- }
- void ShapeBaseConvex::getPolyList(AbstractPolyList* list)
- {
- list->setTransform(&pShapeBase->getTransform(), pShapeBase->getScale());
- list->setObject(pShapeBase);
- pShapeBase->mShapeInstance->animate(pShapeBase->mDataBlock->collisionDetails[hullId]);
- pShapeBase->mShapeInstance->buildPolyList(list,pShapeBase->mDataBlock->collisionDetails[hullId]);
- }
- //--------------------------------------------------------------------------
- bool ShapeBase::isInvincible()
- {
- if( mDataBlock )
- {
- return mDataBlock->isInvincible;
- }
- return false;
- }
- void ShapeBase::startFade( F32 fadeTime, F32 fadeDelay, bool fadeOut )
- {
- setMaskBits(CloakMask);
- mFadeElapsedTime = 0;
- mFading = true;
- if(fadeDelay < 0)
- fadeDelay = 0;
- if(fadeTime < 0)
- fadeTime = 0;
- mFadeTime = fadeTime;
- mFadeDelay = fadeDelay;
- mFadeOut = fadeOut;
- mFadeVal = F32(mFadeOut);
- }
- //--------------------------------------------------------------------------
- void ShapeBase::setShapeName(const char* name)
- {
- if (!isGhost()) {
- if (name[0] != '\0') {
- // Use tags for better network performance
- // Should be a tag, but we'll convert to one if it isn't.
- if (name[0] == StringTagPrefixByte)
- mShapeNameHandle = NetStringHandle(U32(dAtoi(name + 1)));
- else
- mShapeNameHandle = NetStringHandle(name);
- }
- else {
- mShapeNameHandle = NetStringHandle();
- }
- setMaskBits(NameMask);
- }
- }
- void ShapeBase::setSkinName(const char* name)
- {
- if (!isGhost()) {
- if (name[0] != '\0') {
- // Use tags for better network performance
- // Should be a tag, but we'll convert to one if it isn't.
- if (name[0] == StringTagPrefixByte)
- mSkinNameHandle = NetStringHandle(U32(dAtoi(name + 1)));
- else
- mSkinNameHandle = NetStringHandle(name);
- }
- else
- mSkinNameHandle = NetStringHandle();
- setMaskBits(SkinMask);
- }
- }
- //----------------------------------------------------------------------------
- void ShapeBase::reSkin()
- {
- if (isGhost() && mShapeInstance)
- {
- if (mSkinNameHandle.isValidString())
- {
- mShapeInstance->resetMaterialList();
- Vector<String> skins;
- String(mSkinNameHandle.getString()).split(";", skins);
- for (S32 i = 0; i < skins.size(); i++)
- {
- String oldSkin(mAppliedSkinName.c_str());
- String newSkin(skins[i]);
- // Check if the skin handle contains an explicit "old" base string. This
- // allows all models to support skinning, even if they don't follow the
- // "base_xxx" material naming convention.
- S32 split = newSkin.find('='); // "old=new" format skin?
- if (split != String::NPos)
- {
- oldSkin = newSkin.substr(0, split);
- newSkin = newSkin.erase(0, split + 1);
- }
- else
- {
- oldSkin = "";
- }
- mShapeInstance->reSkin(newSkin, oldSkin);
- mAppliedSkinName = newSkin;
- }
- }
- else
- {
- mShapeInstance->reSkin("", mAppliedSkinName);
- mAppliedSkinName = "";
- }
- }
- }
- void ShapeBase::setCurrentWaterObject( WaterObject *obj )
- {
- if ( obj )
- deleteNotify( obj );
- if ( mCurrentWaterObject )
- clearNotify( mCurrentWaterObject );
- mCurrentWaterObject = obj;
- }
- void ShapeBase::setTransform(const MatrixF & mat)
- {
- Parent::setTransform(mat);
- // Accumulation and environment mapping
- if (isClientObject() && mShapeInstance)
- {
- if (mShapeInstance->hasAccumulation())
- AccumulationVolume::updateObject(this);
- }
- }
- void ShapeBase::notifyCollisionCallbacks(SceneObject* obj, const VectorF& vel)
- {
- for (S32 i = 0; i < collision_callbacks.size(); i++)
- if (collision_callbacks[i])
- collision_callbacks[i]->collisionNotify(this, obj, vel);
- }
- void ShapeBase::registerCollisionCallback(CollisionEventCallback* ce_cb)
- {
- for (S32 i = 0; i < collision_callbacks.size(); i++)
- if (collision_callbacks[i] == ce_cb)
- return;
- collision_callbacks.push_back(ce_cb);
- }
- void ShapeBase::unregisterCollisionCallback(CollisionEventCallback* ce_cb)
- {
- for (S32 i = 0; i < collision_callbacks.size(); i++)
- if (collision_callbacks[i] == ce_cb)
- {
- collision_callbacks.erase(i);
- return;
- }
- }
- //--------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- DefineEngineMethod( ShapeBase, setHidden, void, ( bool show ),,
- "@brief Add or remove this object from the scene.\n\n"
- "When removed from the scene, the object will not be processed or rendered.\n"
- "@param show False to hide the object, true to re-show it\n\n" )
- {
- object->setHidden( show );
- }
- DefineEngineMethod( ShapeBase, isHidden, bool, (),,
- "Check if the object is hidden.\n"
- "@return true if the object is hidden, false if visible.\n\n" )
- {
- return object->isHidden();
- }
- //----------------------------------------------------------------------------
- DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, SFXTrack* track ),,
- "@brief Attach a sound to this shape and start playing it.\n\n"
- "@param slot Audio slot index for the sound (valid range is 0 - 3)\n" // 3 = ShapeBase::MaxSoundThreads-1
- "@param track SFXTrack to play\n"
- "@return true if the sound was attached successfully, false if failed\n\n"
-
- "@see stopAudio()\n")
- {
- if (track && slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
- object->playAudio(slot,track);
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, stopAudio, bool, ( S32 slot ),,
- "@brief Stop a sound started with playAudio.\n\n"
- "@param slot audio slot index (started with playAudio)\n"
- "@return true if the sound was stopped successfully, false if failed\n\n"
-
- "@see playAudio()\n")
- {
- if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
- object->stopAudio(slot);
- return true;
- }
- return false;
- }
- //----------------------------------------------------------------------------
- DefineEngineMethod( ShapeBase, playThread, bool, ( S32 slot, const char* name ), ( "" ),
- "@brief Start a new animation thread, or restart one that has been paused or "
- "stopped.\n\n"
- "@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = ShapeBase::MaxScriptThreads-1
- "@param name name of the animation sequence to play in this slot. If not "
- "specified, the paused or stopped thread in this slot will be resumed.\n"
- "@return true if successful, false if failed\n\n"
- "@tsexample\n"
- "%obj.playThread( 0, \"ambient\" ); // Play the ambient sequence in slot 0\n"
- "%obj.setThreadTimeScale( 0, 0.5 ); // Play at half-speed\n"
- "%obj.pauseThread( 0 ); // Pause the sequence\n"
- "%obj.playThread( 0 ); // Resume playback\n"
- "%obj.playThread( 0, \"spin\" ); // Replace the sequence in slot 0\n"
- "@endtsexample\n"
-
- "@see pauseThread()\n"
- "@see stopThread()\n"
- "@see setThreadDir()\n"
- "@see setThreadTimeScale()\n"
- "@see destroyThread()\n")
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (!dStrEqual(name, "")) {
- if (object->getShape()) {
- S32 seq = object->getShape()->findSequence(name);
- if (seq != -1 && object->setThreadSequence(slot,seq))
- return true;
- }
- }
- else
- if (object->playThread(slot))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, setThreadDir, bool, ( S32 slot, bool fwd ),,
- "@brief Set the playback direction of an animation thread.\n\n"
- "@param slot thread slot to modify\n"
- "@param fwd true to play the animation forwards, false to play backwards\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread()\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->setThreadDir(slot,fwd))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, setThreadTimeScale, bool, ( S32 slot, F32 scale ),,
- "@brief Set the playback time scale of an animation thread.\n\n"
- "@param slot thread slot to modify\n"
- "@param scale new thread time scale (1=normal speed, 0.5=half speed etc)\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->setThreadTimeScale(slot,scale))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, setThreadPosition, bool, ( S32 slot, F32 pos ),,
- "@brief Set the position within an animation thread.\n\n"
- "@param slot thread slot to modify\n"
- "@param pos position within thread\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->setThreadPosition(slot,pos))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, stopThread, bool, ( S32 slot ),,
- "@brief Stop an animation thread.\n\n"
- "If restarted using playThread, the animation "
- "will start from the beginning again.\n"
- "@param slot thread slot to stop\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->stopThread(slot))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, destroyThread, bool, ( S32 slot ),,
- "@brief Destroy an animation thread, which prevents it from playing.\n\n"
- "@param slot thread slot to destroy\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->destroyThread(slot))
- return true;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, pauseThread, bool, ( S32 slot ),,
- "@brief Pause an animation thread.\n\n"
-
- "If restarted using playThread, the animation "
- "will resume from the paused position.\n"
- "@param slot thread slot to stop\n"
- "@return true if successful, false if failed\n\n"
-
- "@see playThread\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
- if (object->pauseThread(slot))
- return true;
- }
- return false;
- }
- //----------------------------------------------------------------------------
- DefineEngineMethod( ShapeBase, mountImage, bool,
- ( ShapeBaseImageData* image, S32 slot, bool loaded, const char* skinTag ), ( true, "" ),
- "@brief Mount a new Image.\n\n"
- "@param image the Image to mount\n"
- "@param slot Image slot to mount into (valid range is 0 - 3)\n"
- "@param loaded initial loaded state for the Image\n"
- "@param skinTag tagged string to reskin the mounted Image\n"
- "@return true if successful, false if failed\n\n"
- "@tsexample\n"
- "%player.mountImage( PistolImage, 1 );\n"
- "%player.mountImage( CrossbowImage, 0, false );\n"
- "%player.mountImage( RocketLauncherImage, 0, true, 'blue' );\n"
- "@endtsexample\n"
-
- "@see unmountImage()\n"
- "@see getMountedImage()\n"
- "@see getPendingImage()\n"
- "@see isImageMounted()\n")
- {
- if (image && slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- NetStringHandle team;
- if (skinTag[0] == StringTagPrefixByte)
- team = NetStringHandle(U32(dAtoi(skinTag+1)));
- return object->mountImage( image, slot, loaded, team );
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, unmountImage, bool, ( S32 slot ),,
- "@brief Unmount the mounted Image in the specified slot.\n\n"
- "@param slot Image slot to unmount\n"
- "@return true if successful, false if failed\n\n"
-
- "@see mountImage()\n")
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->unmountImage(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, getMountedImage, S32, ( S32 slot ),,
- "@brief Get the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return ID of the ShapeBaseImageData datablock mounted in the slot, or 0 "
- "if no Image is mounted there.\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- if (ShapeBaseImageData* data = object->getMountedImage(slot))
- return data->getId();
- return 0;
- }
- DefineEngineMethod( ShapeBase, getPendingImage, S32, ( S32 slot ),,
- "@brief Get the Image that will be mounted next in the specified slot.\n\n"
- "Calling mountImage when an Image is already mounted does one of two things: "
- "<ol><li>Mount the new Image immediately, the old Image is discarded and "
- "whatever state it was in is ignored.</li>"
- "<li>If the current Image state does not allow Image changes, the new "
- "Image is marked as pending, and will not be mounted until the current "
- "state completes. eg. if the user changes weapons, you may wish to ensure "
- "that the current weapon firing state plays to completion first.</li></ol>\n"
- "This command retrieves the ID of the pending Image (2nd case above).\n"
-
- "@param slot Image slot to query\n"
- "@return ID of the pending ShapeBaseImageData datablock, or 0 if none.\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- if (ShapeBaseImageData* data = object->getPendingImage(slot))
- return data->getId();
- return 0;
- }
- DefineEngineMethod( ShapeBase, isImageFiring, bool, ( S32 slot ),,
- "@brief Check if the current Image state is firing.\n\n"
- "@param slot Image slot to query\n"
- "@return true if the current Image state in this slot has the 'stateFire' flag set.\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->isImageFiring(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, isImageMounted, bool, ( ShapeBaseImageData* image ),,
- "@brief Check if the given datablock is mounted to any slot on this object.\n\n"
- "@param image ShapeBaseImageData datablock to query\n"
- "@return true if the Image is mounted to any slot, false otherwise.\n\n" )
- {
- return (image && object->isImageMounted(image));
- }
- DefineEngineMethod( ShapeBase, getMountSlot, S32, ( ShapeBaseImageData* image ),,
- "@brief Get the first slot the given datablock is mounted to on this object.\n\n"
- "@param image ShapeBaseImageData datablock to query\n"
- "@return index of the first slot the Image is mounted in, or -1 if the Image "
- "is not mounted in any slot on this object.\n\n" )
- {
- return image ? object->getMountSlot(image) : -1;
- }
- DefineEngineMethod( ShapeBase, getImageSkinTag, S32, ( S32 slot ),,
- "@brief Get the skin tag ID for the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the skinTag value passed to mountImage when the image was "
- "mounted\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageSkinTag(slot).getIndex();
- return -1;
- }
- DefineEngineMethod( ShapeBase, getImageState, const char*, ( S32 slot ),,
- "@brief Get the name of the current state of the Image in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return name of the current Image state, or \"Error\" if slot is invalid\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageState(slot);
- return "Error";
- }
- DefineEngineMethod( ShapeBase, hasImageState, bool, ( S32 slot, const char* state ),,
- "@brief Check if the given state exists on the mounted Image.\n\n"
- "@param slot Image slot to query\n"
- "@param state Image state to check for\n"
- "@return true if the Image has the requested state defined.\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->hasImageState(slot, state);
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageTrigger, bool, ( S32 slot ),,
- "@brief Get the trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current trigger state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageTriggerState(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageTrigger, bool, ( S32 slot, bool state ),,
- "@brief Set the trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param state new trigger state for the Image\n"
- "@return the Image's new trigger state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- object->setImageTriggerState(slot,state);
- return object->getImageTriggerState(slot);
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageGenericTrigger, bool, ( S32 slot, S32 trigger ),,
- "@brief Get the generic trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@param trigger Generic trigger number\n"
- "@return the Image's current generic trigger state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers)
- return object->getImageGenericTriggerState(slot, trigger);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageGenericTrigger, S32, ( S32 slot, S32 trigger, bool state ),,
- "@brief Set the generic trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param trigger Generic trigger number\n"
- "@param state new generic trigger state for the Image\n"
- "@return the Image's new generic trigger state or -1 if there was a problem.\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers) {
- object->setImageGenericTriggerState(slot,trigger,state);
- return object->getImageGenericTriggerState(slot,trigger);
- }
- return -1;
- }
- DefineEngineMethod( ShapeBase, getImageAltTrigger, bool, ( S32 slot ),,
- "@brief Get the alt trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current alt trigger state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageAltTriggerState(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageAltTrigger, bool, ( S32 slot, bool state ),,
- "@brief Set the alt trigger state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param state new alt trigger state for the Image\n"
- "@return the Image's new alt trigger state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- object->setImageAltTriggerState(slot,state);
- return object->getImageAltTriggerState(slot);
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageAmmo, bool, ( S32 slot ),,
- "@brief Get the ammo state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current ammo state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageAmmoState(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageAmmo, bool, ( S32 slot, bool state ),,
- "@brief Set the ammo state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param state new ammo state for the Image\n"
- "@return the Image's new ammo state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- object->setImageAmmoState(slot,state);
- return state;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageLoaded, bool, ( S32 slot ),,
- "@brief Get the loaded state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current loaded state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageLoadedState(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageLoaded, bool, ( S32 slot, bool state ),,
- "@brief Set the loaded state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param state new loaded state for the Image\n"
- "@return the Image's new loaded state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- object->setImageLoadedState(slot, state);
- return state;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageTarget, bool, ( S32 slot ),,
- "@brief Get the target state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current target state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageTargetState(slot);
- return false;
- }
- DefineEngineMethod( ShapeBase, setImageTarget, bool, ( S32 slot, bool state ),,
- "@brief Set the target state of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to modify\n"
- "@param state new target state for the Image\n"
- "@return the Image's new target state\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- object->setImageTargetState(slot,state);
- return state;
- }
- return false;
- }
- DefineEngineMethod( ShapeBase, getImageScriptAnimPrefix, const char*, ( S32 slot ),,
- "@brief Get the script animation prefix of the Image mounted in the specified slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the Image's current script animation prefix\n\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- return object->getImageScriptAnimPrefix(slot).getString();
- return "";
- }
- DefineEngineMethod( ShapeBase, setImageScriptAnimPrefix, void, ( S32 slot, const char* prefix ),,
- "@brief Set the script animation prefix for the Image mounted in the specified slot.\n\n"
- "This is used to further modify the prefix used when deciding which animation sequence to "
- "play while this image is mounted.\n"
- "@param slot Image slot to modify\n"
- "@param prefix The prefix applied to the image\n" )
- {
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
- NetStringHandle prefixHandle;
- if (prefix[0] == StringTagPrefixByte)
- prefixHandle = NetStringHandle(U32(dAtoi(prefix+1)));
- object->setImageScriptAnimPrefix(slot, prefixHandle);
- }
- }
- DefineEngineMethod( ShapeBase, getMuzzleVector, VectorF, ( S32 slot ),,
- "@brief Get the muzzle vector of the Image mounted in the specified slot.\n\n"
- "If the Image shape contains a node called 'muzzlePoint', then the muzzle "
- "vector is the forward direction vector of that node's transform in world "
- "space. If no such node is specified, the slot's mount node is used "
- "instead.\n\n"
- "If the correctMuzzleVector flag (correctMuzzleVectorTP in 3rd person) "
- "is set in the Image, the muzzle vector is computed to point at whatever "
- "object is right in front of the object's 'eye' node.\n"
- "@param slot Image slot to query\n"
- "@return the muzzle vector, or \"0 1 0\" if the slot is invalid\n\n" )
- {
- VectorF vec(0, 1, 0);
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- object->getMuzzleVector(slot, &vec);
- return vec;
- }
- DefineEngineMethod( ShapeBase, getMuzzlePoint, Point3F, ( S32 slot ),,
- "@brief Get the muzzle position of the Image mounted in the specified slot.\n\n"
- "If the Image shape contains a node called 'muzzlePoint', then the muzzle "
- "position is the position of that node in world space. If no such node "
- "is specified, the slot's mount node is used instead.\n"
- "@param slot Image slot to query\n"
- "@return the muzzle position, or \"0 0 0\" if the slot is invalid\n\n" )
- {
- Point3F pos(0, 0, 0);
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- object->getMuzzlePoint(slot, &pos);
- return pos;
- }
- DefineEngineMethod( ShapeBase, getSlotTransform, TransformF, ( S32 slot ),,
- "@brief Get the world transform of the specified mount slot.\n\n"
- "@param slot Image slot to query\n"
- "@return the mount transform\n\n" )
- {
- MatrixF xf(true);
- if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
- object->getMountTransform( slot, MatrixF::Identity, &xf );
- return xf;
- }
- //----------------------------------------------------------------------------
- DefineEngineMethod( ShapeBase, getAIRepairPoint, Point3F, (),,
- "@brief Get the position at which the AI should stand to repair things.\n\n"
- "If the shape defines a node called \"AIRepairNode\", this method will "
- "return the current world position of that node, otherwise \"0 0 0\".\n"
- "@return the AI repair position\n\n" )
- {
- return object->getAIRepairPoint();
- }
- DefineEngineMethod( ShapeBase, getVelocity, VectorF, (),,
- "@brief Get the object's current velocity.\n\n"
- "@return the current velocity\n\n" )
- {
- return object->getVelocity();
- }
- DefineEngineMethod( ShapeBase, setVelocity, bool, ( Point3F vel ),,
- "@brief Set the object's velocity.\n\n"
- "@param vel new velocity for the object\n"
- "@return true\n\n" )
- {
- object->setVelocity( vel );
- return true;
- }
- DefineEngineMethod( ShapeBase, applyImpulse, bool, ( Point3F pos, Point3F vec ),,
- "@brief Apply an impulse to the object.\n\n"
- "@param pos world position of the impulse\n"
- "@param vec impulse momentum (velocity * mass)\n"
- "@return true\n\n" )
- {
- object->applyImpulse( pos, vec );
- return true;
- }
- DefineEngineMethod( ShapeBase, getEyeVector, VectorF, (),,
- "@brief Get the forward direction of the 'eye' for this object.\n\n"
- "If the object model has a node called 'eye', this method will return that "
- "node's current forward direction vector, otherwise it will return the "
- "object's current forward direction vector.\n"
- "@return the eye vector for this object\n"
- "@see getEyePoint\n"
- "@see getEyeTransform\n" )
- {
- MatrixF mat;
- object->getEyeTransform(&mat);
- return mat.getForwardVector();
- }
- DefineEngineMethod( ShapeBase, getEyePoint, Point3F, (),,
- "@brief Get the position of the 'eye' for this object.\n\n"
- "If the object model has a node called 'eye', this method will return that "
- "node's current world position, otherwise it will return the object's current "
- "world position.\n"
- "@return the eye position for this object\n"
- "@see getEyeVector\n"
- "@see getEyeTransform\n" )
- {
- MatrixF mat;
- object->getEyeTransform(&mat);
- return mat.getPosition();
- }
- DefineEngineMethod( ShapeBase, getEyeTransform, TransformF, (),,
- "@brief Get the 'eye' transform for this object.\n\n"
- "If the object model has a node called 'eye', this method will return that "
- "node's current transform, otherwise it will return the object's current "
- "transform.\n"
- "@return the eye transform for this object\n"
- "@see getEyeVector\n"
- "@see getEyePoint\n" )
- {
- MatrixF mat;
- object->getEyeTransform(&mat);
- return mat;
- }
- DefineEngineMethod( ShapeBase, getLookAtPoint, const char*, ( F32 distance, U32 typeMask ), ( 2000, 0xFFFFFFFF ),
- "@brief Get the world position this object is looking at.\n\n"
- "Casts a ray from the eye and returns information about what the ray hits.\n"
- "@param distance maximum distance of the raycast\n"
- "@param typeMask typeMask of objects to include for raycast collision testing\n"
- "@return look-at information as \"Object HitX HitY HitZ [Material]\" or empty string for no hit\n\n"
- "@tsexample\n"
- "%lookat = %obj.getLookAtPoint();\n"
- "echo( \"Looking at: \" @ getWords( %lookat, 1, 3 ) );\n"
- "@endtsexample\n" )
- {
- MatrixF mat;
- object->getEyeTransform( &mat );
-
- // Get eye vector.
-
- VectorF eyeVector;
- mat.getColumn( 1, &eyeVector );
-
- // Get eye position.
-
- VectorF eyePos;
- mat.getColumn( 3, &eyePos );
-
- // Make sure the eye vector covers the distance.
-
- eyeVector *= distance;
-
- // Do a container search.
-
- VectorF start = eyePos;
- VectorF end = eyePos + eyeVector;
-
- RayInfo ri;
- if( !gServerContainer.castRay( start, end, typeMask, &ri ) || !ri.object )
- return ""; // No hit.
-
- // Gather hit info.
-
- enum { BUFFER_SIZE = 256 };
- char* buffer = Con::getReturnBuffer( BUFFER_SIZE );
- if( ri.material )
- dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f %u",
- ri.object->getId(),
- ri.point.x,
- ri.point.y,
- ri.point.z,
- ri.material->getMaterial()->getId() );
- else
- dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f",
- ri.object->getId(),
- ri.point.x,
- ri.point.y,
- ri.point.z );
- return buffer;
- }
- DefineEngineMethod( ShapeBase, setEnergyLevel, void, ( F32 level ),,
- "@brief Set this object's current energy level.\n\n"
- "@param level new energy level\n"
-
- "@see getEnergyLevel()\n"
- "@see getEnergyPercent()\n")
- {
- object->setEnergyLevel( level );
- }
- DefineEngineMethod( ShapeBase, getEnergyLevel, F32, (),,
- "@brief Get the object's current energy level.\n\n"
- "@return energy level\n"
-
- "@see setEnergyLevel()\n")
- {
- return object->getEnergyLevel();
- }
- DefineEngineMethod( ShapeBase, getEnergyPercent, F32, (),,
- "@brief Get the object's current energy level as a percentage of maxEnergy.\n\n"
- "@return energyLevel / datablock.maxEnergy\n"
- "@see setEnergyLevel()\n")
- {
- return object->getEnergyValue();
- }
- DefineEngineMethod( ShapeBase, setDamageLevel, void, ( F32 level ),,
- "@brief Set the object's current damage level.\n\n"
- "@param level new damage level\n"
-
- "@see getDamageLevel()\n"
- "@see getDamagePercent()\n")
- {
- object->setDamageLevel( level );
- }
- DefineEngineMethod( ShapeBase, getDamageLevel, F32, (),,
- "@brief Get the object's current damage level.\n\n"
- "@return damage level\n"
-
- "@see setDamageLevel()\n")
- {
- return object->getDamageLevel();
- }
- DefineEngineMethod( ShapeBase, getDamagePercent, F32, (),,
- "@brief Get the object's current damage level as a percentage of maxDamage.\n\n"
- "@return damageLevel / datablock.maxDamage\n"
-
- "@see setDamageLevel()\n")
- {
- return object->getDamageValue();
- }
-
- DefineEngineMethod(ShapeBase, getMaxDamage, F32, (),,
- "Get the object's maxDamage level.\n"
- "@return datablock.maxDamage\n")
- {
- return object->getMaxDamage();
- }
- DefineEngineMethod( ShapeBase, setDamageState, bool, ( const char* state ),,
- "@brief Set the object's damage state.\n\n"
- "@param state should be one of \"Enabled\", \"Disabled\", \"Destroyed\"\n"
- "@return true if successful, false if failed\n"
-
- "@see getDamageState()\n")
- {
- return object->setDamageState( state );
- }
- DefineEngineMethod( ShapeBase, getDamageState, const char*, (),,
- "@brief Get the object's damage state.\n\n"
- "@return the damage state; one of \"Enabled\", \"Disabled\", \"Destroyed\"\n"
-
- "@see setDamageState()\n")
- {
- return object->getDamageStateName();
- }
- DefineEngineMethod( ShapeBase, isDestroyed, bool, (),,
- "@brief Check if the object is in the Destroyed damage state.\n\n"
- "@return true if damage state is \"Destroyed\", false if not\n"
-
- "@see isDisabled()\n"
- "@see isEnabled()\n")
- {
- return object->isDestroyed();
- }
- DefineEngineMethod( ShapeBase, isDisabled, bool, (),,
- "@brief Check if the object is in the Disabled or Destroyed damage state.\n\n"
- "@return true if damage state is not \"Enabled\", false if it is\n"
-
- "@see isDestroyed()\n"
- "@see isEnabled()\n")
- {
- return object->getDamageState() != ShapeBase::Enabled;
- }
- DefineEngineMethod( ShapeBase, isEnabled, bool, (),,
- "@brief Check if the object is in the Enabled damage state.\n\n"
- "@return true if damage state is \"Enabled\", false if not\n"
-
- "@see isDestroyed()\n"
- "@see isDisabled()\n")
- {
- return object->getDamageState() == ShapeBase::Enabled;
- }
- DefineEngineMethod(ShapeBase, blowUp, void, (),, "@brief Explodes an object into pieces.")
- {
- object->blowUp();
- }
- DefineEngineMethod( ShapeBase, applyDamage, void, ( F32 amount ),,
- "@brief Increment the current damage level by the specified amount.\n\n"
- "@param amount value to add to current damage level\n" )
- {
- object->applyDamage( amount );
- }
- DefineEngineMethod( ShapeBase, applyRepair, void, ( F32 amount ),,
- "@brief Repair damage by the specified amount.\n\n"
- "Note that the damage level is only reduced by repairRate per tick, so it may "
- "take several ticks for the total repair to complete.\n"
- "@param amount total repair value (subtracted from damage level over time)\n" )
- {
- object->applyRepair( amount );
- }
- DefineEngineMethod( ShapeBase, setRepairRate, void, ( F32 rate ),,
- "@brief Set amount to repair damage by each tick.\n\n"
- "Note that this value is separate to the repairRate field in ShapeBaseData. "
- "This value will be subtracted from the damage level each tick, whereas the "
- "ShapeBaseData field limits how much of the applyRepair value is subtracted "
- "each tick. Both repair types can be active at the same time.\n"
-
- "@param rate value to subtract from damage level each tick (must be > 0)\n"
-
- "@see getRepairRate()\n")
- {
- if(rate < 0)
- rate = 0;
- object->setRepairRate( rate );
- }
- DefineEngineMethod( ShapeBase, getRepairRate, F32, (),,
- "@brief Get the per-tick repair amount.\n\n"
- "@return the current value to be subtracted from damage level each tick\n"
- "@see setRepairRate\n" )
- {
- return object->getRepairRate();
- }
- DefineEngineMethod( ShapeBase, setRechargeRate, void, ( F32 rate ),,
- "@brief Set the recharge rate.\n\n"
- "The recharge rate is added to the object's current energy level each tick, "
- "up to the maxEnergy level set in the ShapeBaseData datablock.\n"
-
- "@param rate the recharge rate (per tick)\n"
-
- "@see getRechargeRate()\n")
- {
- object->setRechargeRate( rate );
- }
- DefineEngineMethod( ShapeBase, getRechargeRate, F32, (),,
- "@brief Get the current recharge rate.\n\n"
- "@return the recharge rate (per tick)\n"
-
- "@see setRechargeRate()\n")
- {
- return object->getRechargeRate();
- }
- DefineEngineMethod( ShapeBase, getControllingClient, S32, (),,
- "@brief Get the client (if any) that controls this object.\n\n"
- "The controlling client is the one that will send moves to us to act on.\n"
- "@return the ID of the controlling GameConnection, or 0 if this object is not "
- "controlled by any client.\n"
-
- "@see GameConnection\n")
- {
- if (GameConnection* con = object->getControllingClient())
- return con->getId();
- return 0;
- }
- DefineEngineMethod( ShapeBase, getControllingObject, S32, (),,
- "@brief Get the object (if any) that controls this object.\n\n"
- "@return the ID of the controlling ShapeBase object, or 0 if this object is "
- "not controlled by another object.\n" )
- {
- if (ShapeBase* con = object->getControllingObject())
- return con->getId();
- return 0;
- }
- DefineEngineMethod( ShapeBase, canCloak, bool, (),,
- "@brief Check if this object can cloak.\n\n"
- "@return true\n"
-
- "@note Not implemented as it always returns true.")
- {
- return true;
- }
- DefineEngineMethod( ShapeBase, setCloaked, void, ( bool cloak ),,
- "@brief Set the cloaked state of this object.\n\n"
- "When an object is cloaked it is not rendered.\n"
- "@param cloak true to cloak the object, false to uncloak\n"
-
- "@see isCloaked()\n")
- {
- if (object->isServerObject())
- object->setCloakedState( cloak );
- }
- DefineEngineMethod( ShapeBase, isCloaked, bool, (),,
- "@brief Check if this object is cloaked.\n\n"
- "@return true if cloaked, false if not\n"
-
- "@see setCloaked()\n")
- {
- return object->getCloakedState();
- }
- DefineEngineMethod( ShapeBase, setDamageFlash, void, ( F32 level ),,
- "@brief Set the damage flash level.\n\n"
- "Damage flash may be used as a postfx effect to flash the screen when the "
- "client is damaged.\n"
- "@note Relies on the flash postFx.\n"
- "@param level flash level (0-1)\n"
-
- "@see getDamageFlash()\n")
- {
- if (object->isServerObject())
- object->setDamageFlash( level );
- }
- DefineEngineMethod( ShapeBase, getDamageFlash, F32, (),,
- "@brief Get the damage flash level.\n\n"
- "@return flash level\n"
- "@see setDamageFlash\n" )
- {
- return object->getDamageFlash();
- }
- DefineEngineMethod( ShapeBase, setWhiteOut, void, ( F32 level ),,
- "@brief Set the white-out level.\n\n"
- "White-out may be used as a postfx effect to brighten the screen in response "
- "to a game event.\n"
- "@note Relies on the flash postFx.\n"
- "@param level flash level (0-1)\n"
-
- "@see getWhiteOut()\n")
- {
- if (object->isServerObject())
- object->setWhiteOut( level );
- }
- DefineEngineMethod( ShapeBase, getWhiteOut, F32, (),,
- "@brief Get the white-out level.\n\n"
- "@return white-out level\n"
- "@see setWhiteOut\n" )
- {
- return object->getWhiteOut();
- }
- DefineEngineMethod( ShapeBase, getDefaultCameraFov, F32, (),,
- "@brief Returns the default vertical field of view in degrees for this object if used as a camera.\n\n"
- "@return Default FOV\n" )
- {
- if (object->isServerObject())
- return object->getDefaultCameraFov();
- return 0.0;
- }
- DefineEngineMethod( ShapeBase, getCameraFov, F32, (),,
- "@brief Returns the vertical field of view in degrees for this object if used as a camera.\n\n"
- "@return current FOV as defined in ShapeBaseData::cameraDefaultFov\n" )
- {
- if (object->isServerObject())
- return object->getCameraFov();
- return 0.0;
- }
- DefineEngineMethod( ShapeBase, setCameraFov, void, ( F32 fov ),,
- "@brief Set the vertical field of view in degrees for this object if used as a camera.\n\n"
- "@param fov new FOV value\n" )
- {
- if (object->isServerObject())
- object->setCameraFov( fov );
- }
- DefineEngineMethod( ShapeBase, startFade, void, ( S32 time, S32 delay, bool fadeOut ),,
- "@brief Fade the object in or out without removing it from the scene.\n\n"
- "A faded out object is still in the scene and can still be collided with, "
- "so if you want to disable collisions for this shape after it fades out "
- "use setHidden to temporarily remove this shape from the scene.\n"
-
- "@note Items have the ability to light their surroundings. When an Item with "
- "an active light is fading out, the light it emits is correspondingly "
- "reduced until it goes out. Likewise, when the item fades in, the light is "
- "turned-up till it reaches it's normal brightntess.\n"
- "@param time duration of the fade effect in ms\n"
- "@param delay delay in ms before the fade effect begins\n"
- "@param fadeOut true to fade-out to invisible, false to fade-in to full visibility\n" )
- {
- object->startFade( (F32)time / (F32)1000.0, delay / 1000.0, fadeOut );
- }
- DefineEngineMethod( ShapeBase, setDamageVector, void, ( Point3F vec ),,
- "@brief Set the damage direction vector.\n\n"
- "Currently this is only used to initialise the explosion if this object "
- "is blown up.\n"
- "@param vec damage direction vector\n\n"
- "@tsexample\n"
- "%obj.setDamageVector( \"0 0 1\" );\n"
- "@endtsexample\n" )
- {
- vec.normalize();
- object->setDamageDir( vec );
- }
- DefineEngineMethod( ShapeBase, setShapeName, void, ( const char* name ),,
- "@brief Set the name of this shape.\n\n"
- "@note This is the name of the shape object that is sent to the client, "
- "not the DTS or DAE model filename.\n"
- "@param name new name for the shape\n\n"
-
- "@see getShapeName()\n")
- {
- object->setShapeName( name );
- }
- DefineEngineMethod( ShapeBase, getShapeName, const char*, (),,
- "@brief Get the name of the shape.\n\n"
- "@note This is the name of the shape object that is sent to the client, "
- "not the DTS or DAE model filename.\n"
- "@return the name of the shape\n\n"
-
- "@see setShapeName()\n")
- {
- return object->getShapeName();
- }
- DefineEngineMethod( ShapeBase, setSkinName, void, ( const char* name ),,
- "@brief Apply a new skin to this shape.\n\n"
- "'Skinning' the shape effectively renames the material targets, allowing "
- "different materials to be used on different instances of the same model.\n\n"
- "@param name name of the skin to apply\n\n"
- "@see skin\n"
- "@see getSkinName()\n")
- {
- object->setSkinName( name );
- }
- DefineEngineMethod( ShapeBase, getSkinName, const char*, (),,
- "@brief Get the name of the skin applied to this shape.\n\n"
- "@return the name of the skin\n\n"
- "@see skin\n"
- "@see setSkinName()\n")
- {
- return object->getSkinName();
- }
- //----------------------------------------------------------------------------
- void ShapeBase::consoleInit()
- {
- Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec, "Speed to reduce the damage flash effect per tick.\n\n"
- "@see ShapeBase::setDamageFlash()\n"
- "@see ShapeBase::getDamageFlash()\n"
- "@note Relies on the flash postFx.\n"
- "@ingroup gameObjects\n");
- Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec, "Speed to reduce the whiteout effect per tick.\n\n"
- "@see ShapeBase::setWhiteOut()\n"
- "@see ShapeBase::getWhiteOut"
- "@note Relies on the flash postFx.\n"
- "@ingroup gameObjects\n");
- Con::addVariable("SB::FullCorrectionDistance", TypeF32, &sFullCorrectionDistance,
- "@brief Distance at which a weapon's muzzle vector is fully corrected to match where the player is looking.\n\n"
- "When a weapon image has correctMuzzleVector set and the Player is in 1st person, the muzzle vector from the "
- "weapon is modified to match where the player is looking. Beyond the FullCorrectionDistance the muzzle vector "
- "is always corrected. Between FullCorrectionDistance and the player, the weapon's muzzle vector is adjusted so that "
- "the closer the aim point is to the player, the closer the muzzle vector is to the true (non-corrected) one.\n"
- "@ingroup gameObjects\n");
- Con::addVariable("SB::CloakSpeed", TypeF32, &sCloakSpeed,
- "@brief Time to cloak, in seconds.\n\n"
- "@ingroup gameObjects\n");
- }
- void ShapeBase::_updateHiddenMeshes()
- {
- if ( !mShapeInstance )
- return;
- // This may happen at some point in the future... lets
- // detect it so that it can be fixed at that time.
- AssertFatal( mMeshHidden.getSize() == mShapeInstance->mMeshObjects.size(),
- "ShapeBase::_updateMeshVisibility() - Mesh visibility size mismatch!" );
- for ( U32 i = 0; i < mMeshHidden.getSize(); i++ )
- setMeshHidden( i, mMeshHidden.test( i ) );
- }
- void ShapeBase::setMeshHidden( const char *meshName, bool forceHidden )
- {
- setMeshHidden( mDataBlock->mShape->findObject( meshName ), forceHidden );
- }
- void ShapeBase::setMeshHidden( S32 meshIndex, bool forceHidden )
- {
- if ( meshIndex == -1 || meshIndex >= mMeshHidden.getSize() )
- return;
- if ( forceHidden )
- mMeshHidden.set( meshIndex );
- else
- mMeshHidden.clear( meshIndex );
- if ( mShapeInstance )
- mShapeInstance->setMeshForceHidden( meshIndex, forceHidden );
- setMaskBits( MeshHiddenMask );
- }
- void ShapeBase::setAllMeshesHidden( bool forceHidden )
- {
- if ( forceHidden )
- mMeshHidden.set();
- else
- mMeshHidden.clear();
- if ( mShapeInstance )
- {
- for ( U32 i = 0; i < mMeshHidden.getSize(); i++ )
- mShapeInstance->setMeshForceHidden( i, forceHidden );
- }
- setMaskBits( MeshHiddenMask );
- }
- DefineEngineMethod( ShapeBase, setAllMeshesHidden, void, ( bool hide ),,
- "@brief Set the hidden state on all the shape meshes.\n\n"
- "This allows you to hide all meshes in the shape, for example, and then only "
- "enable a few.\n"
- "@param hide new hidden state for all meshes\n\n" )
- {
- object->setAllMeshesHidden( hide );
- }
- DefineEngineMethod( ShapeBase, setMeshHidden, void, ( const char* name, bool hide ),,
- "@brief Set the hidden state on the named shape mesh.\n\n"
- "@param name name of the mesh to hide/show\n"
- "@param hide new hidden state for the mesh\n\n" )
- {
- object->setMeshHidden( name, hide );
- }
- // Some development-handy functions
- #ifndef TORQUE_SHIPPING
- void ShapeBase::dumpMeshVisibility()
- {
- if ( !mShapeInstance )
- return;
- const Vector<TSShapeInstance::MeshObjectInstance> &meshes = mShapeInstance->mMeshObjects;
- for ( U32 i = 0; i < meshes.size(); i++)
- {
- const TSShapeInstance::MeshObjectInstance &mesh = meshes[i];
- const String &meshName = mDataBlock->mShape->getMeshName( i );
- Con::printf( "%d - %s - forceHidden = %s, visibility = %f",
- i,
- meshName.c_str(),
- mesh.forceHidden ? "true" : "false",
- mesh.visible );
- }
- }
- DefineEngineMethod( ShapeBase, dumpMeshVisibility, void, (),,
- "@brief Print a list of visible and hidden meshes in the shape to the console "
- "for debugging purposes.\n\n"
- "@note Only in a SHIPPING build.\n")
- {
- object->dumpMeshVisibility();
- }
- #endif // #ifndef TORQUE_SHIPPING
- //------------------------------------------------------------------------
- //These functions are duplicated in tsStatic and shapeBase.
- //They each function a little differently; but achieve the same purpose of gathering
- //target names/counts without polluting simObject.
- DefineEngineMethod( ShapeBase, getTargetName, const char*, ( S32 index ),,
- "@brief Get the name of the indexed shape material.\n\n"
- "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n"
- "@return the name of the indexed material.\n\n"
-
- "@see getTargetCount()\n")
- {
- ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
- if(obj)
- {
- // Try to use the client object (so we get the reskinned targets in the Material Editor)
- if ((ShapeBase*)obj->getClientObject())
- obj = (ShapeBase*)obj->getClientObject();
- return obj->getShapeInstance()->getTargetName(index);
- }
- return "";
- }
- DefineEngineMethod( ShapeBase, getTargetCount, S32, (),,
- "@brief Get the number of materials in the shape.\n\n"
- "@return the number of materials in the shape.\n\n"
-
- "@see getTargetName()\n")
- {
- ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
- if(obj)
- {
- // Try to use the client object (so we get the reskinned targets in the Material Editor)
- if ((ShapeBase*)obj->getClientObject())
- obj = (ShapeBase*)obj->getClientObject();
- if (obj->getShapeInstance() != NULL)
- return obj->getShapeInstance()->getTargetCount();
- }
-
- return -1;
- }
- DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),,
- "@brief Change one of the materials on the shape.\n\n"
- "This method changes materials per mapTo with others. The material that "
- "is being replaced is mapped to unmapped_mat as a part of this transition.\n"
- "@note Warning, right now this only sort of works. It doesn't do a live "
- "update like it should.\n"
- "@param mapTo the name of the material target to remap (from getTargetName)\n"
- "@param oldMat the old Material that was mapped \n"
- "@param newMat the new Material to map\n\n"
- "@tsexample\n"
- "// remap the first material in the shape\n"
- "%mapTo = %obj.getTargetName( 0 );\n"
- "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n"
- "@endtsexample\n" )
- {
- // if no valid new material, theres no reason for doing this
- if( !newMat )
- {
- Con::errorf("ShapeBase::changeMaterial failed: New material does not exist!");
- return;
- }
- // initilize server/client versions
- ShapeBase *serverObj = object;
- ShapeBase *clientObj = dynamic_cast< ShapeBase* > ( object->getClientObject() );
- // Check the mapTo name exists for this shape
- S32 matIndex = serverObj->getShape()->materialList->getMaterialNameList().find_next(String(mapTo));
- if (matIndex < 0)
- {
- Con::errorf("ShapeBase::changeMaterial failed: Invalid mapTo name '%s'", mapTo);
- return;
- }
- // Lets remap the old material off, so as to let room for our current material room to claim its spot
- if( oldMat )
- oldMat->mMapTo = String("unmapped_mat");
- newMat->mMapTo = mapTo;
- // Map the material by name in the matmgr
- MATMGR->mapMaterial( mapTo, newMat->getName() );
- // Replace instances with the new material being traded in. For ShapeBase
- // class we have to update the server/client objects separately so both
- // represent our changes
- delete serverObj->getShape()->materialList->mMatInstList[matIndex];
- serverObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance();
- if (clientObj)
- {
- delete clientObj->getShape()->materialList->mMatInstList[matIndex];
- clientObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance();
- }
- // Finish up preparing the material instances for rendering
- const GFXVertexFormat *flags = getGFXVertexFormat<GFXVertexPNTTB>();
- FeatureSet features = MATMGR->getDefaultFeatures();
- serverObj->getShape()->materialList->getMaterialInst(matIndex)->init( features, flags );
- if (clientObj)
- clientObj->getShapeInstance()->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
- }
- DefineEngineMethod( ShapeBase, getModelFile, const char *, (),,
- "@brief Get the model filename used by this shape.\n\n"
- "@return the shape filename\n\n" )
- {
- GameBaseData * datablock = object->getDataBlock();
- if( !datablock )
- return String::EmptyString;
- const char *fieldName = StringTable->insert( String("shapeFile") );
- return datablock->getDataField( fieldName, NULL );
- }
- U32 ShapeBase::unique_anim_tag_counter = 1;
- U32 ShapeBase::playBlendAnimation(S32 seq_id, F32 pos, F32 rate)
- {
- BlendThread blend_clip;
- blend_clip.tag = ((unique_anim_tag_counter++) | BLENDED_CLIP);
- blend_clip.thread = 0;
- if (isClientObject())
- {
- blend_clip.thread = mShapeInstance->addThread();
- mShapeInstance->setSequence(blend_clip.thread, seq_id, pos);
- mShapeInstance->setTimeScale(blend_clip.thread, rate);
- }
- blend_clips.push_back(blend_clip);
- return blend_clip.tag;
- }
- void ShapeBase::restoreBlendAnimation(U32 tag)
- {
- for (S32 i = 0; i < blend_clips.size(); i++)
- {
- if (blend_clips[i].tag == tag)
- {
- if (blend_clips[i].thread)
- {
- mShapeInstance->destroyThread(blend_clips[i].thread);
- }
- blend_clips.erase_fast(i);
- break;
- }
- }
- }
- //
- void ShapeBase::restoreAnimation(U32 tag)
- {
- if (!isClientObject())
- return;
- // check if this is a blended clip
- if ((tag & BLENDED_CLIP) != 0)
- {
- restoreBlendAnimation(tag);
- return;
- }
- if (tag != 0 && tag == last_anim_tag)
- {
- anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
- stopThread(0);
- if (saved_seq_id != -1)
- {
- setThreadSequence(0, saved_seq_id);
- setThreadPosition(0, saved_pos);
- setThreadTimeScale(0, saved_rate);
- setThreadDir(0, (saved_rate >= 0));
- playThread(0);
- saved_seq_id = -1;
- saved_pos = 0.0f;
- saved_rate = 1.0f;
- }
- last_anim_tag = 0;
- last_anim_id = -1;
- }
- }
- U32 ShapeBase::getAnimationID(const char* name)
- {
- const TSShape* ts_shape = getShape();
- S32 seq_id = (ts_shape) ? ts_shape->findSequence(name) : -1;
- return (seq_id >= 0) ? (U32) seq_id : BAD_ANIM_ID;
- }
- U32 ShapeBase::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
- {
- if (!isClientObject())
- return 0;
- if (anim_id == BAD_ANIM_ID)
- return 0;
- const TSShape* ts_shape = getShape();
- if (!ts_shape)
- return 0;
- S32 seq_id = (S32) anim_id;
- if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
- return playBlendAnimation(seq_id, pos, rate);
- if (last_anim_tag == 0)
- {
- // try to save state of playing animation
- Thread& st = mScriptThread[0];
- if (st.sequence != -1)
- {
- saved_seq_id = st.sequence;
- saved_pos = st.position;
- saved_rate = st.timescale;
- }
- }
- // START OR TRANSITION TO SEQUENCE HERE
- setThreadSequence(0, seq_id);
- setThreadPosition(0, pos);
- setThreadTimeScale(0, rate);
- setThreadDir(0, (rate >= 0));
- playThread(0);
- if (is_death_anim)
- anim_clip_flags |= IS_DEATH_ANIM;
- else
- anim_clip_flags &= ~IS_DEATH_ANIM;
- anim_clip_flags |= ANIM_OVERRIDDEN;
- last_anim_tag = unique_anim_tag_counter++;
- last_anim_id = anim_id;
- return last_anim_tag;
- }
- F32 ShapeBase::getAnimationDurationByID(U32 anim_id)
- {
- if (anim_id == BAD_ANIM_ID)
- return 0.0f;
- S32 seq_id = (S32) anim_id;
- if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
- return mDataBlock->mShape->sequences[seq_id].duration;
- return 0.0f;
- }
- bool ShapeBase::isBlendAnimation(const char* name)
- {
- U32 anim_id = getAnimationID(name);
- if (anim_id == BAD_ANIM_ID)
- return false;
- S32 seq_id = (S32) anim_id;
- if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
- return mDataBlock->mShape->sequences[seq_id].isBlend();
- return false;
- }
- const char* ShapeBase::getLastClipName(U32 clip_tag)
- {
- if (clip_tag != last_anim_tag)
- return "";
- S32 seq_id = (S32) last_anim_id;
- S32 idx = mDataBlock->mShape->sequences[seq_id].nameIndex;
- if (idx < 0 || idx >= mDataBlock->mShape->names.size())
- return 0;
- return mDataBlock->mShape->names[idx];
- }
- //
- U32 ShapeBase::playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
- {
- return playAnimationByID(getAnimationID(name), pos, rate, trans, hold, wait, is_death_anim);
- }
- F32 ShapeBase::getAnimationDuration(const char* name)
- {
- return getAnimationDurationByID(getAnimationID(name));
- }
- void ShapeBase::setSelectionFlags(U8 flags)
- {
- Parent::setSelectionFlags(flags);
- if (!mShapeInstance || !isClientObject())
- return;
-
- if (!mShapeInstance->ownMaterialList())
- return;
-
- TSMaterialList* pMatList = mShapeInstance->getMaterialList();
- for (S32 j = 0; j < pMatList->size(); j++)
- {
- BaseMatInstance * bmi = pMatList->getMaterialInst(j);
- bmi->setSelectionHighlighting(needsSelectionHighlighting());
- }
- }
|