2
0

shapeBase.cpp 165 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "T3D/shapeBase.h"
  28. #include "core/dnet.h"
  29. #include "sfx/sfxSystem.h"
  30. #include "sfx/sfxSource.h"
  31. #include "sfx/sfxTrack.h"
  32. #include "sfx/sfxDescription.h"
  33. #include "T3D/sfx/sfx3DWorld.h"
  34. #include "T3D/gameBase/gameConnection.h"
  35. #include "console/consoleTypes.h"
  36. #include "console/engineAPI.h"
  37. #include "core/stream/bitStream.h"
  38. #include "ts/tsPartInstance.h"
  39. #include "ts/tsShapeInstance.h"
  40. #include "ts/tsMaterialList.h"
  41. #include "scene/sceneManager.h"
  42. #include "scene/sceneRenderState.h"
  43. #include "scene/sceneObjectLightingPlugin.h"
  44. #include "T3D/fx/explosion.h"
  45. #include "T3D/fx/cameraFXMgr.h"
  46. #include "environment/waterBlock.h"
  47. #include "T3D/debris.h"
  48. #include "T3D/physicalZone.h"
  49. #include "T3D/containerQuery.h"
  50. #include "math/mathUtils.h"
  51. #include "math/mMatrix.h"
  52. #include "math/mTransform.h"
  53. #include "math/mRandom.h"
  54. #include "platform/profiler.h"
  55. #include "gfx/gfxCubemap.h"
  56. #include "gfx/gfxDrawUtil.h"
  57. #include "gfx/gfxTransformSaver.h"
  58. #include "renderInstance/renderPassManager.h"
  59. #include "collision/earlyOutPolyList.h"
  60. #include "core/resourceManager.h"
  61. #include "scene/reflectionManager.h"
  62. #include "gfx/sim/cubemapData.h"
  63. #include "materials/materialManager.h"
  64. #include "materials/materialFeatureTypes.h"
  65. #include "renderInstance/renderOcclusionMgr.h"
  66. #include "core/stream/fileStream.h"
  67. #include "T3D/accumulationVolume.h"
  68. #include "console/persistenceManager.h"
  69. IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
  70. ConsoleDocClass( ShapeBaseData,
  71. "@brief Defines properties for a ShapeBase object.\n\n"
  72. "@see ShapeBase\n"
  73. "@ingroup gameObjects\n"
  74. );
  75. IMPLEMENT_CALLBACK( ShapeBaseData, onEnabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
  76. "@brief Called when the object damage state changes to Enabled.\n\n"
  77. "@param obj The ShapeBase object\n"
  78. "@param lastState The previous damage state\n" );
  79. IMPLEMENT_CALLBACK( ShapeBaseData, onDisabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
  80. "@brief Called when the object damage state changes to Disabled.\n\n"
  81. "@param obj The ShapeBase object\n"
  82. "@param lastState The previous damage state\n" );
  83. IMPLEMENT_CALLBACK( ShapeBaseData, onDestroyed, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ),
  84. "@brief Called when the object damage state changes to Destroyed.\n\n"
  85. "@param obj The ShapeBase object\n"
  86. "@param lastState The previous damage state\n" );
  87. IMPLEMENT_CALLBACK( ShapeBaseData, onImpact, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ),
  88. "@brief Called when we collide with another object beyond some impact speed.\n\n"
  89. "The Player class makes use of this callback when a collision speed is more than PlayerData::minImpactSpeed.\n"
  90. "@param obj The ShapeBase object\n"
  91. "@param collObj The object we collided with\n"
  92. "@param vec Collision impact vector\n"
  93. "@param len Length of the impact vector\n" );
  94. IMPLEMENT_CALLBACK( ShapeBaseData, onCollision, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ),
  95. "@brief Called when we collide with another object.\n\n"
  96. "@param obj The ShapeBase object\n"
  97. "@param collObj The object we collided with\n"
  98. "@param vec Collision impact vector\n"
  99. "@param len Length of the impact vector\n" );
  100. IMPLEMENT_CALLBACK( ShapeBaseData, onDamage, void, ( ShapeBase* obj, F32 delta ), ( obj, delta ),
  101. "@brief Called when the object is damaged.\n\n"
  102. "@param obj The ShapeBase object\n"
  103. "@param obj The ShapeBase object\n"
  104. "@param delta The amount of damage received." );
  105. IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index, bool state ), ( obj, index, state ),
  106. "@brief Called when a move trigger input changes state.\n\n"
  107. "@param obj The ShapeBase object\n"
  108. "@param index Index of the trigger that changed\n"
  109. "@param state New state of the trigger\n" );
  110. IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const char* name), (obj, slot, name),
  111. "@brief Called when a thread playing a non-cyclic sequence reaches the end of the "
  112. "sequence.\n\n"
  113. "@param obj The ShapeBase object\n"
  114. "@param slot Thread slot that finished playing\n"
  115. "@param name Thread name that finished playing\n");
  116. IMPLEMENT_CALLBACK( ShapeBaseData, onForceUncloak, void, ( ShapeBase* obj, const char* reason ), ( obj, reason ),
  117. "@brief Called when the object is forced to uncloak.\n\n"
  118. "@param obj The ShapeBase object\n"
  119. "@param reason String describing why the object was uncloaked\n" );
  120. //----------------------------------------------------------------------------
  121. // Timeout for non-looping sounds on a channel
  122. static SimTime sAudioTimeout = 500;
  123. F32 ShapeBase::sWhiteoutDec = 0.007f;
  124. F32 ShapeBase::sDamageFlashDec = 0.02f;
  125. F32 ShapeBase::sFullCorrectionDistance = 0.5f;
  126. F32 ShapeBase::sCloakSpeed = 0.5;
  127. U32 ShapeBase::sLastRenderFrame = 0;
  128. static const char *sDamageStateName[] =
  129. {
  130. // Index by enum ShapeBase::DamageState
  131. "Enabled",
  132. "Disabled",
  133. "Destroyed"
  134. };
  135. //----------------------------------------------------------------------------
  136. ShapeBaseData::ShapeBaseData()
  137. :
  138. shadowSize( 128 ),
  139. shadowMaxVisibleDistance( 80.0f ),
  140. shadowProjectionDistance( 10.0f ),
  141. shadowSphereAdjust( 1.0f ),
  142. cloakTexName( StringTable->EmptyString() ),
  143. cubeDescId( 0 ),
  144. reflectorDesc( NULL ),
  145. debris( NULL ),
  146. debrisID( 0 ),
  147. explosion( NULL ),
  148. explosionID( 0 ),
  149. underwaterExplosion( NULL ),
  150. underwaterExplosionID( 0 ),
  151. mass( 1.0f ),
  152. drag( 0.0f ),
  153. density( 1.0f ),
  154. maxEnergy( 0.0f ),
  155. maxDamage( 1.0f ),
  156. repairRate( 0.0033f ),
  157. disabledLevel( 1.0f ),
  158. destroyedLevel( 1.0f ),
  159. cameraMaxDist( 0.0f ),
  160. cameraMinDist( 0.2f ),
  161. cameraDefaultFov( 75.0f ),
  162. cameraMinFov( 5.0f ),
  163. cameraMaxFov( 120.f ),
  164. cameraCanBank( false ),
  165. mountedImagesBank( false ),
  166. mCRC( 0 ),
  167. computeCRC( false ),
  168. eyeNode( -1 ),
  169. earNode( -1 ),
  170. cameraNode( -1 ),
  171. debrisDetail( -1 ),
  172. damageSequence( -1 ),
  173. hulkSequence( -1 ),
  174. observeThroughObject( false ),
  175. firstPersonOnly( false ),
  176. useEyePoint( false ),
  177. isInvincible( false ),
  178. renderWhenDestroyed( true ),
  179. inheritEnergyFromMount( false )
  180. {
  181. INIT_ASSET(Shape);
  182. INIT_ASSET(DebrisShape);
  183. dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
  184. remap_txr_tags = NULL;
  185. remap_buffer = NULL;
  186. silent_bbox_check = false;
  187. }
  188. ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : GameBaseData(other, temp_clone)
  189. {
  190. shadowSize = other.shadowSize;
  191. shadowMaxVisibleDistance = other.shadowMaxVisibleDistance;
  192. shadowProjectionDistance = other.shadowProjectionDistance;
  193. shadowSphereAdjust = other.shadowSphereAdjust;
  194. cloakTexName = other.cloakTexName;
  195. CLONE_ASSET(Shape);
  196. cubeDescName = other.cubeDescName;
  197. cubeDescId = other.cubeDescId;
  198. reflectorDesc = other.reflectorDesc;
  199. debris = other.debris;
  200. debrisID = other.debrisID; // -- for pack/unpack of debris ptr
  201. CLONE_ASSET(DebrisShape);
  202. explosion = other.explosion;
  203. explosionID = other.explosionID; // -- for pack/unpack of explosion ptr
  204. underwaterExplosion = other.underwaterExplosion;
  205. underwaterExplosionID = other.underwaterExplosionID; // -- for pack/unpack of underwaterExplosion ptr
  206. mass = other.mass;
  207. drag = other.drag;
  208. density = other.density;
  209. maxEnergy = other.maxEnergy;
  210. maxDamage = other.maxDamage;
  211. repairRate = other.repairRate;
  212. disabledLevel = other.disabledLevel;
  213. destroyedLevel = other.destroyedLevel;
  214. cameraMaxDist = other.cameraMaxDist;
  215. cameraMinDist = other.cameraMinDist;
  216. cameraDefaultFov = other.cameraDefaultFov;
  217. cameraMinFov = other.cameraMinFov;
  218. cameraMaxFov = other.cameraMaxFov;
  219. cameraCanBank = other.cameraCanBank;
  220. mountedImagesBank = other.mountedImagesBank;
  221. mShape = other.mShape; // -- TSShape loaded using shapeName
  222. mCRC = other.mCRC; // -- from shape, used to verify client shape
  223. computeCRC = other.computeCRC;
  224. eyeNode = other.eyeNode; // -- from shape node "eye"
  225. earNode = other.earNode; // -- from shape node "ear"
  226. cameraNode = other.cameraNode; // -- from shape node "cam"
  227. dMemcpy(mountPointNode, other.mountPointNode, sizeof(mountPointNode)); // -- from shape nodes "mount#" 0-31
  228. debrisDetail = other.debrisDetail; // -- from shape detail "Debris-17"
  229. damageSequence = other.damageSequence; // -- from shape sequence "Damage"
  230. hulkSequence = other.hulkSequence; // -- from shape sequence "Visibility"
  231. observeThroughObject = other.observeThroughObject;
  232. collisionDetails = other.collisionDetails; // -- calc from shape (this is a Vector copy)
  233. collisionBounds = other.collisionBounds; // -- calc from shape (this is a Vector copy)
  234. LOSDetails = other.LOSDetails; // -- calc from shape (this is a Vector copy)
  235. firstPersonOnly = other.firstPersonOnly;
  236. useEyePoint = other.useEyePoint;
  237. isInvincible = other.isInvincible;
  238. renderWhenDestroyed = other.renderWhenDestroyed;
  239. inheritEnergyFromMount = other.inheritEnergyFromMount;
  240. remap_txr_tags = other.remap_txr_tags;
  241. remap_buffer = other.remap_buffer;
  242. txr_tag_remappings = other.txr_tag_remappings;
  243. silent_bbox_check = other.silent_bbox_check;
  244. }
  245. struct ShapeBaseDataProto
  246. {
  247. F32 mass;
  248. F32 drag;
  249. F32 density;
  250. F32 maxEnergy;
  251. F32 cameraMaxDist;
  252. F32 cameraMinDist;
  253. F32 cameraDefaultFov;
  254. F32 cameraMinFov;
  255. F32 cameraMaxFov;
  256. ShapeBaseDataProto()
  257. {
  258. mass = 1;
  259. drag = 0;
  260. density = 1;
  261. maxEnergy = 0;
  262. cameraMaxDist = 0;
  263. cameraMinDist = 0.2f;
  264. cameraDefaultFov = 75.f;
  265. cameraMinFov = 5.0f;
  266. cameraMaxFov = 120.f;
  267. }
  268. };
  269. static ShapeBaseDataProto gShapeBaseDataProto;
  270. ShapeBaseData::~ShapeBaseData()
  271. {
  272. if (remap_buffer && !isTempClone())
  273. dFree(remap_buffer);
  274. }
  275. bool ShapeBaseData::preload(bool server, String &errorStr)
  276. {
  277. if (!Parent::preload(server, errorStr))
  278. return false;
  279. bool shapeError = false;
  280. // Resolve objects transmitted from server
  281. if (!server) {
  282. if( !explosion && explosionID != 0 )
  283. {
  284. if( Sim::findObject( explosionID, explosion ) == false)
  285. {
  286. Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(explosion): 0x%x", explosionID );
  287. }
  288. AssertFatal(!(explosion && ((explosionID < DataBlockObjectIdFirst) || (explosionID > DataBlockObjectIdLast))),
  289. "ShapeBaseData::preload: invalid explosion data");
  290. }
  291. if( !underwaterExplosion && underwaterExplosionID != 0 )
  292. {
  293. if( Sim::findObject( underwaterExplosionID, underwaterExplosion ) == false)
  294. {
  295. Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(underwaterExplosion): 0x%x", underwaterExplosionID );
  296. }
  297. AssertFatal(!(underwaterExplosion && ((underwaterExplosionID < DataBlockObjectIdFirst) || (underwaterExplosionID > DataBlockObjectIdLast))),
  298. "ShapeBaseData::preload: invalid underwaterExplosion data");
  299. }
  300. if( !debris && debrisID != 0 )
  301. {
  302. Sim::findObject( debrisID, debris );
  303. AssertFatal(!(debris && ((debrisID < DataBlockObjectIdFirst) || (debrisID > DataBlockObjectIdLast))),
  304. "ShapeBaseData::preload: invalid debris data");
  305. }
  306. if( bool(mDebrisShape))
  307. {
  308. TSShapeInstance* pDummy = new TSShapeInstance(mDebrisShape, !server);
  309. delete pDummy;
  310. }
  311. }
  312. S32 i;
  313. if (ShapeAsset::getAssetErrCode(mShapeAsset) != ShapeAsset::Failed && ShapeAsset::getAssetErrCode(mShapeAsset) != ShapeAsset::BadFileReference)
  314. {
  315. if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
  316. shapeError = true;
  317. if(computeCRC)
  318. {
  319. Con::printf("Validation required for shape asset: %s", mShapeAsset.getAssetId());
  320. Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShapeAsset->getShapePath());
  321. if (!fileRef)
  322. {
  323. errorStr = String::ToString("ShapeBaseData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
  324. return false;
  325. }
  326. if(server)
  327. mCRC = fileRef->getChecksum();
  328. else if(mCRC != fileRef->getChecksum())
  329. {
  330. errorStr = String::ToString("Shape asset \"%s\" does not match version on server.", mShapeAsset.getAssetId());
  331. return false;
  332. }
  333. }
  334. // Resolve details and camera node indexes.
  335. static const String sCollisionStr( "collision-" );
  336. for (i = 0; i < mShape->details.size(); i++)
  337. {
  338. const String &name = mShape->names[mShape->details[i].nameIndex];
  339. if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
  340. {
  341. collisionDetails.push_back(i);
  342. collisionBounds.increment();
  343. mShape->computeBounds(collisionDetails.last(), collisionBounds.last());
  344. mShape->getAccelerator(collisionDetails.last());
  345. if (!mShape->mBounds.isContained(collisionBounds.last()))
  346. {
  347. if (!silent_bbox_check)
  348. Con::warnf("Warning: shape asset %s collision detail %d (Collision-%d) bounds exceed that of shape.", mShapeAsset.getAssetId(), collisionDetails.size() - 1, collisionDetails.last());
  349. collisionBounds.last() = mShape->mBounds;
  350. }
  351. else if (collisionBounds.last().isValidBox() == false)
  352. {
  353. if (!silent_bbox_check)
  354. Con::errorf("Error: shape asset %s-collision detail %d (Collision-%d) bounds box invalid!", mShapeAsset.getAssetId(), collisionDetails.size() - 1, collisionDetails.last());
  355. collisionBounds.last() = mShape->mBounds;
  356. }
  357. // The way LOS works is that it will check to see if there is a LOS detail that matches
  358. // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
  359. // the future). If it can't find a matching LOS it will simply use the collision instead.
  360. // We check for any "unmatched" LOS's further down
  361. LOSDetails.increment();
  362. String buff = String::ToString("LOS-%d", i + 1 + MaxCollisionShapes);
  363. U32 los = mShape->findDetail(buff);
  364. if (los == -1)
  365. LOSDetails.last() = i;
  366. else
  367. LOSDetails.last() = los;
  368. }
  369. }
  370. // Snag any "unmatched" LOS details
  371. static const String sLOSStr( "LOS-" );
  372. for (i = 0; i < mShape->details.size(); i++)
  373. {
  374. const String &name = mShape->names[mShape->details[i].nameIndex];
  375. if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
  376. {
  377. // See if we already have this LOS
  378. bool found = false;
  379. for (U32 j = 0; j < LOSDetails.size(); j++)
  380. {
  381. if (LOSDetails[j] == i)
  382. {
  383. found = true;
  384. break;
  385. }
  386. }
  387. if (!found)
  388. LOSDetails.push_back(i);
  389. }
  390. }
  391. debrisDetail = mShape->findDetail("Debris-17");
  392. eyeNode = mShape->findNode("eye");
  393. earNode = mShape->findNode( "ear" );
  394. if( earNode == -1 )
  395. earNode = eyeNode;
  396. cameraNode = mShape->findNode("cam");
  397. if (cameraNode == -1)
  398. cameraNode = eyeNode;
  399. // Resolve mount point node indexes
  400. for (i = 0; i < SceneObject::NumMountPoints; i++) {
  401. char fullName[256];
  402. dSprintf(fullName,sizeof(fullName),"mount%d",i);
  403. mountPointNode[i] = mShape->findNode(fullName);
  404. }
  405. // find the AIRepairNode - hardcoded to be the last node in the array...
  406. mountPointNode[AIRepairNode] = mShape->findNode("AIRepairNode");
  407. //
  408. hulkSequence = mShape->findSequence("Visibility");
  409. damageSequence = mShape->findSequence("Damage");
  410. //
  411. F32 w = mShape->mBounds.len_y() / 2;
  412. if (cameraMaxDist < w)
  413. cameraMaxDist = w;
  414. // just parse up the string and collect the remappings in txr_tag_remappings.
  415. if (!server && remap_txr_tags != NULL && remap_txr_tags != StringTable->insert(""))
  416. {
  417. txr_tag_remappings.clear();
  418. if (remap_buffer)
  419. dFree(remap_buffer);
  420. remap_buffer = dStrdup(remap_txr_tags);
  421. char* remap_token = dStrtok(remap_buffer, " \t");
  422. while (remap_token != NULL)
  423. {
  424. char* colon = dStrchr(remap_token, ':');
  425. if (colon)
  426. {
  427. *colon = '\0';
  428. txr_tag_remappings.increment();
  429. txr_tag_remappings.last().old_tag = remap_token;
  430. txr_tag_remappings.last().new_tag = colon+1;
  431. }
  432. remap_token = dStrtok(NULL, " \t");
  433. }
  434. }
  435. }
  436. if(!server)
  437. {
  438. /*
  439. // grab all the hud images
  440. for(U32 i = 0; i < NumHudRenderImages; i++)
  441. {
  442. if(hudImageNameFriendly[i] && hudImageNameFriendly[i][0])
  443. hudImageFriendly[i] = TextureHandle(hudImageNameFriendly[i], BitmapTexture);
  444. if(hudImageNameEnemy[i] && hudImageNameEnemy[i][0])
  445. hudImageEnemy[i] = TextureHandle(hudImageNameEnemy[i], BitmapTexture);
  446. }
  447. */
  448. }
  449. // Resolve CubeReflectorDesc.
  450. if ( cubeDescName.isNotEmpty() )
  451. {
  452. Sim::findObject( cubeDescName, reflectorDesc );
  453. }
  454. else if( cubeDescId > 0 )
  455. {
  456. Sim::findObject( cubeDescId, reflectorDesc );
  457. }
  458. return !shapeError;
  459. }
  460. bool ShapeBaseData::_setMass( void* object, const char* index, const char* data )
  461. {
  462. ShapeBaseData* shape = reinterpret_cast< ShapeBaseData* >( object );
  463. F32 mass = dAtof(data);
  464. if (mass <= 0)
  465. mass = 0.01f;
  466. shape->mass = mass;
  467. return false;
  468. }
  469. void ShapeBaseData::initPersistFields()
  470. {
  471. docsURL;
  472. addGroup( "Shapes" );
  473. INITPERSISTFIELD_SHAPEASSET(Shape, ShapeBaseData, "The source shape asset.");
  474. addField("computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData),
  475. "If true, verify that the CRC of the client's shape model matches the "
  476. "server's CRC for the shape model when loaded by the client.");
  477. addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
  478. INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional.");
  479. endGroup( "Shapes" );
  480. addGroup("Particle Effects");
  481. addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData),
  482. "%Explosion to generate when this shape is blown up." );
  483. addField( "underwaterExplosion", TYPEID< ExplosionData >(), Offset(underwaterExplosion, ShapeBaseData),
  484. "%Explosion to generate when this shape is blown up underwater." );
  485. addField( "debris", TYPEID< DebrisData >(), Offset(debris, ShapeBaseData),
  486. "%Debris to generate when this shape is blown up." );
  487. addField( "renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData),
  488. "Whether to render the shape when it is in the \"Destroyed\" damage state." );
  489. endGroup("Particle Effects");
  490. addGroup( "Physics" );
  491. addProtectedField("mass", TypeF32, Offset(mass, ShapeBaseData), &_setMass, &defaultProtectedGetFn, "Shape mass.\nUsed in simulation of moving objects.\n" );
  492. addField( "drag", TypeF32, Offset(drag, ShapeBaseData),
  493. "Drag factor.\nReduces velocity of moving objects." );
  494. addField( "density", TypeF32, Offset(density, ShapeBaseData),
  495. "Shape density.\nUsed when computing buoyancy when in water.\n" );
  496. endGroup( "Physics" );
  497. addGroup( "Damage/Energy" );
  498. addField( "maxEnergy", TypeF32, Offset(maxEnergy, ShapeBaseData),
  499. "Maximum energy level for this object." );
  500. addField( "maxDamage", TypeF32, Offset(maxDamage, ShapeBaseData),
  501. "Maximum damage level for this object." );
  502. addField( "disabledLevel", TypeF32, Offset(disabledLevel, ShapeBaseData),
  503. "Damage level above which the object is disabled.\n"
  504. "Currently unused." );
  505. addField( "destroyedLevel", TypeF32, Offset(destroyedLevel, ShapeBaseData),
  506. "Damage level above which the object is destroyed.\n"
  507. "When the damage level increases above this value, the object damage "
  508. "state is set to \"Destroyed\"." );
  509. addField( "repairRate", TypeF32, Offset(repairRate, ShapeBaseData),
  510. "Rate at which damage is repaired in damage units/tick.\n"
  511. "This value is subtracted from the damage level until it reaches 0." );
  512. addField( "inheritEnergyFromMount", TypeBool, Offset(inheritEnergyFromMount, ShapeBaseData),
  513. "Flag controlling whether to manage our own energy level, or to use "
  514. "the energy level of the object we are mounted to." );
  515. addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData),
  516. "Invincible flag; when invincible, the object cannot be damaged or "
  517. "repaired." );
  518. endGroup( "Damage/Energy" );
  519. addGroup( "Camera", "The settings used by the shape when it is the camera." );
  520. addField( "cameraMaxDist", TypeF32, Offset(cameraMaxDist, ShapeBaseData),
  521. "The maximum distance from the camera to the object.\n"
  522. "Used when computing a custom camera transform for this object.\n\n"
  523. "@see observeThroughObject" );
  524. addField( "cameraMinDist", TypeF32, Offset(cameraMinDist, ShapeBaseData),
  525. "The minimum distance from the camera to the object.\n"
  526. "Used when computing a custom camera transform for this object.\n\n"
  527. "@see observeThroughObject" );
  528. addField( "cameraDefaultFov", TypeF32, Offset(cameraDefaultFov, ShapeBaseData),
  529. "The default camera vertical FOV in degrees." );
  530. addField( "cameraMinFov", TypeF32, Offset(cameraMinFov, ShapeBaseData),
  531. "The minimum camera vertical FOV allowed in degrees." );
  532. addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData),
  533. "The maximum camera vertical FOV allowed in degrees." );
  534. addField( "cameraCanBank", TypeBool, Offset(cameraCanBank, ShapeBaseData),
  535. "If the derrived class supports it, allow the camera to bank." );
  536. addField( "mountedImagesBank", TypeBool, Offset(mountedImagesBank, ShapeBaseData),
  537. "Do mounted images bank along with the camera?" );
  538. addField( "firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData),
  539. "Flag controlling whether the view from this object is first person "
  540. "only." );
  541. addField( "useEyePoint", TypeBool, Offset(useEyePoint, ShapeBaseData),
  542. "Flag controlling whether the client uses this object's eye point to "
  543. "view from." );
  544. addField( "observeThroughObject", TypeBool, Offset(observeThroughObject, ShapeBaseData),
  545. "Observe this object through its camera transform and default fov.\n"
  546. "If true, when this object is the camera it can provide a custom camera "
  547. "transform and FOV (instead of the default eye transform)." );
  548. endGroup("Camera");
  549. addGroup( "Reflection" );
  550. addField( "cubeReflectorDesc", TypeRealString, Offset( cubeDescName, ShapeBaseData ),
  551. "References a ReflectorDesc datablock that defines performance and quality properties for dynamic reflections.\n");
  552. //addField( "reflectMaxRateMs", TypeS32, Offset( reflectMaxRateMs, ShapeBaseData ), "reflection will not be updated more frequently than this" );
  553. //addField( "reflectMaxDist", TypeF32, Offset( reflectMaxDist, ShapeBaseData ), "distance at which reflection is never updated" );
  554. //addField( "reflectMinDist", TypeF32, Offset( reflectMinDist, ShapeBaseData ), "distance at which reflection is always updated" );
  555. //addField( "reflectDetailAdjust", TypeF32, Offset( reflectDetailAdjust, ShapeBaseData ), "scale up or down the detail level for objects rendered in a reflection" );
  556. endGroup( "Reflection" );
  557. addField("remapTextureTags", TypeString, Offset(remap_txr_tags, ShapeBaseData));
  558. // disallow some field substitutions
  559. onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK
  560. onlyKeepClearSubstitutions("explosion");
  561. onlyKeepClearSubstitutions("underwaterExplosion");
  562. Parent::initPersistFields();
  563. addGroup("BL Projected Shadows");
  564. addField("shadowSize", TypeS32, Offset(shadowSize, ShapeBaseData),
  565. "Size of the projected shadow texture (must be power of 2).");
  566. addField("shadowMaxVisibleDistance", TypeF32, Offset(shadowMaxVisibleDistance, ShapeBaseData),
  567. "Maximum distance at which shadow is visible (currently unused).");
  568. addField("shadowProjectionDistance", TypeF32, Offset(shadowProjectionDistance, ShapeBaseData),
  569. "Maximum height above ground to project shadow. If the object is higher "
  570. "than this no shadow will be rendered.");
  571. addField("shadowSphereAdjust", TypeF32, Offset(shadowSphereAdjust, ShapeBaseData),
  572. "Scalar applied to the radius of spot shadows (initial radius is based "
  573. "on the shape bounds but can be adjusted with this field).");
  574. endGroup("BL Projected Shadows");
  575. }
  576. DefineEngineMethod( ShapeBaseData, checkDeployPos, bool, ( TransformF txfm ),,
  577. "@brief Check if there is the space at the given transform is free to spawn into.\n\n"
  578. "The shape's bounding box volume is used to check for collisions at the given world "
  579. "transform. Only interior and static objects are checked for collision.\n"
  580. "@param txfm Deploy transform to check\n"
  581. "@return True if the space is free, false if there is already something in "
  582. "the way.\n"
  583. "@note This is a server side only check, and is not actually limited to spawning.\n")
  584. {
  585. if (bool(object->mShape) == false)
  586. return false;
  587. MatrixF mat = txfm.getMatrix();
  588. Box3F objBox = object->mShape->mBounds;
  589. Point3F boxCenter = (objBox.minExtents + objBox.maxExtents) * 0.5f;
  590. objBox.minExtents = boxCenter + (objBox.minExtents - boxCenter) * 0.9f;
  591. objBox.maxExtents = boxCenter + (objBox.maxExtents - boxCenter) * 0.9f;
  592. Box3F wBox = objBox;
  593. mat.mul(wBox);
  594. EarlyOutPolyList polyList;
  595. polyList.mNormal.set(0,0,0);
  596. polyList.mPlaneList.clear();
  597. polyList.mPlaneList.setSize(6);
  598. polyList.mPlaneList[0].set(objBox.minExtents,VectorF(-1,0,0));
  599. polyList.mPlaneList[1].set(objBox.maxExtents,VectorF(0,1,0));
  600. polyList.mPlaneList[2].set(objBox.maxExtents,VectorF(1,0,0));
  601. polyList.mPlaneList[3].set(objBox.minExtents,VectorF(0,-1,0));
  602. polyList.mPlaneList[4].set(objBox.minExtents,VectorF(0,0,-1));
  603. polyList.mPlaneList[5].set(objBox.maxExtents,VectorF(0,0,1));
  604. for (U32 i = 0; i < 6; i++)
  605. {
  606. PlaneF temp;
  607. mTransformPlane(mat, Point3F(1, 1, 1), polyList.mPlaneList[i], &temp);
  608. polyList.mPlaneList[i] = temp;
  609. }
  610. if (gServerContainer.buildPolyList(PLC_Collision, wBox, StaticShapeObjectType, &polyList))
  611. return false;
  612. return true;
  613. }
  614. DefineEngineMethod(ShapeBaseData, getDeployTransform, TransformF, ( Point3F pos, Point3F normal ),,
  615. "@brief Helper method to get a transform from a position and vector (suitable for use with setTransform).\n\n"
  616. "@param pos Desired transform position\n"
  617. "@param normal Vector of desired direction\n"
  618. "@return The deploy transform\n" )
  619. {
  620. normal.normalize();
  621. VectorF xAxis;
  622. if( mFabs(normal.z) > mFabs(normal.x) && mFabs(normal.z) > mFabs(normal.y))
  623. mCross( VectorF( 0, 1, 0 ), normal, &xAxis );
  624. else
  625. mCross( VectorF( 0, 0, 1 ), normal, &xAxis );
  626. VectorF yAxis;
  627. mCross( normal, xAxis, &yAxis );
  628. MatrixF testMat(true);
  629. testMat.setColumn( 0, xAxis );
  630. testMat.setColumn( 1, yAxis );
  631. testMat.setColumn( 2, normal );
  632. testMat.setPosition( pos );
  633. return testMat;
  634. }
  635. void ShapeBaseData::packData(BitStream* stream)
  636. {
  637. Parent::packData(stream);
  638. if(stream->writeFlag(computeCRC))
  639. stream->write(mCRC);
  640. stream->write(shadowSize);
  641. stream->write(shadowMaxVisibleDistance);
  642. stream->write(shadowProjectionDistance);
  643. stream->write(shadowSphereAdjust);
  644. PACKDATA_ASSET(Shape);
  645. PACKDATA_ASSET(DebrisShape);
  646. stream->writeString(cloakTexName);
  647. if(stream->writeFlag(mass != gShapeBaseDataProto.mass))
  648. stream->write(mass);
  649. if(stream->writeFlag(drag != gShapeBaseDataProto.drag))
  650. stream->write(drag);
  651. if(stream->writeFlag(density != gShapeBaseDataProto.density))
  652. stream->write(density);
  653. if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy))
  654. stream->write(maxEnergy);
  655. if(stream->writeFlag(cameraMaxDist != gShapeBaseDataProto.cameraMaxDist))
  656. stream->write(cameraMaxDist);
  657. if(stream->writeFlag(cameraMinDist != gShapeBaseDataProto.cameraMinDist))
  658. stream->write(cameraMinDist);
  659. cameraDefaultFov = mClampF(cameraDefaultFov, cameraMinFov, cameraMaxFov);
  660. if(stream->writeFlag(cameraDefaultFov != gShapeBaseDataProto.cameraDefaultFov))
  661. stream->write(cameraDefaultFov);
  662. if(stream->writeFlag(cameraMinFov != gShapeBaseDataProto.cameraMinFov))
  663. stream->write(cameraMinFov);
  664. if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov))
  665. stream->write(cameraMaxFov);
  666. stream->writeFlag(cameraCanBank);
  667. stream->writeFlag(mountedImagesBank);
  668. stream->writeFlag(observeThroughObject);
  669. if( stream->writeFlag( debris != NULL ) )
  670. {
  671. stream->writeRangedU32(mPacked? SimObjectId((uintptr_t)debris):
  672. debris->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
  673. }
  674. stream->writeFlag(isInvincible);
  675. stream->writeFlag(renderWhenDestroyed);
  676. if( stream->writeFlag( explosion != NULL ) )
  677. {
  678. stream->writeRangedU32( explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  679. }
  680. if( stream->writeFlag( underwaterExplosion != NULL ) )
  681. {
  682. stream->writeRangedU32( underwaterExplosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  683. }
  684. stream->writeFlag(inheritEnergyFromMount);
  685. stream->writeFlag(firstPersonOnly);
  686. stream->writeFlag(useEyePoint);
  687. if( stream->writeFlag( reflectorDesc != NULL ) )
  688. {
  689. stream->writeRangedU32( reflectorDesc->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  690. }
  691. //stream->write(reflectPriority);
  692. //stream->write(reflectMaxRateMs);
  693. //stream->write(reflectMinDist);
  694. //stream->write(reflectMaxDist);
  695. //stream->write(reflectDetailAdjust);
  696. stream->writeString(remap_txr_tags);
  697. stream->writeFlag(silent_bbox_check);
  698. }
  699. void ShapeBaseData::unpackData(BitStream* stream)
  700. {
  701. Parent::unpackData(stream);
  702. computeCRC = stream->readFlag();
  703. if(computeCRC)
  704. stream->read(&mCRC);
  705. stream->read(&shadowSize);
  706. stream->read(&shadowMaxVisibleDistance);
  707. stream->read(&shadowProjectionDistance);
  708. stream->read(&shadowSphereAdjust);
  709. UNPACKDATA_ASSET(Shape);
  710. UNPACKDATA_ASSET(DebrisShape);
  711. cloakTexName = stream->readSTString();
  712. if(stream->readFlag())
  713. stream->read(&mass);
  714. else
  715. mass = gShapeBaseDataProto.mass;
  716. if(stream->readFlag())
  717. stream->read(&drag);
  718. else
  719. drag = gShapeBaseDataProto.drag;
  720. if(stream->readFlag())
  721. stream->read(&density);
  722. else
  723. density = gShapeBaseDataProto.density;
  724. if(stream->readFlag())
  725. stream->read(&maxEnergy);
  726. else
  727. maxEnergy = gShapeBaseDataProto.maxEnergy;
  728. if(stream->readFlag())
  729. stream->read(&cameraMaxDist);
  730. else
  731. cameraMaxDist = gShapeBaseDataProto.cameraMaxDist;
  732. if(stream->readFlag())
  733. stream->read(&cameraMinDist);
  734. else
  735. cameraMinDist = gShapeBaseDataProto.cameraMinDist;
  736. if(stream->readFlag())
  737. stream->read(&cameraDefaultFov);
  738. else
  739. cameraDefaultFov = gShapeBaseDataProto.cameraDefaultFov;
  740. if(stream->readFlag())
  741. stream->read(&cameraMinFov);
  742. else
  743. cameraMinFov = gShapeBaseDataProto.cameraMinFov;
  744. if(stream->readFlag())
  745. stream->read(&cameraMaxFov);
  746. else
  747. cameraMaxFov = gShapeBaseDataProto.cameraMaxFov;
  748. cameraCanBank = stream->readFlag();
  749. mountedImagesBank = stream->readFlag();
  750. observeThroughObject = stream->readFlag();
  751. if( stream->readFlag() )
  752. {
  753. debrisID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  754. }
  755. isInvincible = stream->readFlag();
  756. renderWhenDestroyed = stream->readFlag();
  757. if( stream->readFlag() )
  758. {
  759. explosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  760. }
  761. if( stream->readFlag() )
  762. {
  763. underwaterExplosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  764. }
  765. inheritEnergyFromMount = stream->readFlag();
  766. firstPersonOnly = stream->readFlag();
  767. useEyePoint = stream->readFlag();
  768. if( stream->readFlag() )
  769. {
  770. cubeDescId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  771. }
  772. //stream->read(&reflectPriority);
  773. //stream->read(&reflectMaxRateMs);
  774. //stream->read(&reflectMinDist);
  775. //stream->read(&reflectMaxDist);
  776. //stream->read(&reflectDetailAdjust);
  777. remap_txr_tags = stream->readSTString();
  778. silent_bbox_check = stream->readFlag();
  779. }
  780. //----------------------------------------------------------------------------
  781. //----------------------------------------------------------------------------
  782. Chunker<ShapeBase::CollisionTimeout> sTimeoutChunker;
  783. ShapeBase::CollisionTimeout* ShapeBase::sFreeTimeoutList = 0;
  784. //----------------------------------------------------------------------------
  785. IMPLEMENT_CO_NETOBJECT_V1(ShapeBase);
  786. ConsoleDocClass( ShapeBase,
  787. "@ingroup gameObjects"
  788. );
  789. IMPLEMENT_CALLBACK( ShapeBase, validateCameraFov, F32, (F32 fov), (fov),
  790. "@brief Called on the server when the client has requested a FOV change.\n\n"
  791. "When the client requests that its field of view should be changed (because "
  792. "they want to use a sniper scope, for example) this new FOV needs to be validated "
  793. "by the server. This method is called if it exists (it is optional) to validate "
  794. "the requested FOV, and modify it if necessary. This could be as simple as checking "
  795. "that the FOV falls within a correct range, to making sure that the FOV matches the "
  796. "capabilities of the current weapon.\n\n"
  797. "Following this method, ShapeBase ensures that the given FOV still falls within "
  798. "the datablock's cameraMinFov and cameraMaxFov. If that is good enough for your "
  799. "purposes, then you do not need to define the validateCameraFov() callback for "
  800. "your ShapeBase.\n\n"
  801. "@param fov The FOV that has been requested by the client.\n"
  802. "@return The FOV as validated by the server.\n\n"
  803. "@see ShapeBaseData\n\n");
  804. ShapeBase::ShapeBase()
  805. : mDataBlock( NULL ),
  806. mIsAiControlled( false ),
  807. mControllingObject( NULL ),
  808. mAiPose( 0 ),
  809. mMoveMotion( false ),
  810. mShapeBaseMount( NULL ),
  811. mShapeInstance( NULL ),
  812. mConvexList( new Convex ),
  813. mEnergy( 0.0f ),
  814. mRechargeRate( 0.0f ),
  815. mMass( 1.0f ),
  816. mOneOverMass( 1.0f ),
  817. mDrag( 0.0f ),
  818. mBuoyancy( 0.0f ),
  819. mLiquidHeight( 0.0f ),
  820. mWaterCoverage( 0.0f ),
  821. mAppliedForce( Point3F::Zero ),
  822. mNetGravity( 1.0f ),
  823. mDamageFlash( 0.0f ),
  824. mWhiteOut( 0.0f ),
  825. mFlipFadeVal( false ),
  826. mTimeoutList( NULL ),
  827. mDamage( 0.0f ),
  828. mRepairRate( 0.0f ),
  829. mRepairReserve( 0.0f ),
  830. mDamageState( Enabled ),
  831. mDamageThread( NULL ),
  832. mHulkThread( NULL ),
  833. damageDir( 0.0f, 0.0f, 1.0f ),
  834. mCloaked( false ),
  835. mCloakLevel( 0.0f ),
  836. mFadeOut( true ),
  837. mFading( false ),
  838. mFadeVal( 1.0f ),
  839. mFadeElapsedTime( 0.0f ),
  840. mFadeTime( 1.0f ),
  841. mFadeDelay( 0.0f ),
  842. mCameraFov( 90.0f ),
  843. mIsControlled( false ),
  844. mLastRenderFrame( 0 ),
  845. mLastRenderDistance( 0.0f )
  846. {
  847. mTypeMask |= ShapeBaseObjectType | LightObjectType;
  848. S32 i;
  849. for (i = 0; i < MaxSoundThreads; i++) {
  850. mSoundThread[i].play = false;
  851. mSoundThread[i].asset = 0;
  852. mSoundThread[i].sound = 0;
  853. }
  854. for (i = 0; i < MaxScriptThreads; i++) {
  855. mScriptThread[i].sequence = -1;
  856. mScriptThread[i].thread = 0;
  857. mScriptThread[i].state = Thread::Stop;
  858. mScriptThread[i].atEnd = false;
  859. mScriptThread[i].timescale = 1.f;
  860. mScriptThread[i].position = -1.f;
  861. }
  862. for (i = 0; i < MaxTriggerKeys; i++)
  863. mTrigger[i] = false;
  864. anim_clip_flags = 0;
  865. last_anim_id = -1;
  866. last_anim_tag = 0;
  867. last_anim_lock_tag = 0;
  868. saved_seq_id = -1;
  869. saved_pos = 0.0f;
  870. saved_rate = 1.0f;
  871. }
  872. ShapeBase::~ShapeBase()
  873. {
  874. SAFE_DELETE( mConvexList );
  875. AssertFatal(mMount.link == 0,"ShapeBase::~ShapeBase: An object is still mounted");
  876. if( mShapeInstance && (mShapeInstance->getDebrisRefCount() == 0) )
  877. {
  878. delete mShapeInstance;
  879. mShapeInstance = NULL;
  880. }
  881. CollisionTimeout* ptr = mTimeoutList;
  882. while (ptr) {
  883. CollisionTimeout* cur = ptr;
  884. ptr = ptr->next;
  885. cur->next = sFreeTimeoutList;
  886. sFreeTimeoutList = cur;
  887. }
  888. }
  889. void ShapeBase::initPersistFields()
  890. {
  891. docsURL;
  892. addProtectedField( "skin", TypeRealString, Offset(mAppliedSkinName, ShapeBase), &_setFieldSkin, &_getFieldSkin,
  893. "@brief The skin applied to the shape.\n\n"
  894. "'Skinning' the shape effectively renames the material targets, allowing "
  895. "different materials to be used on different instances of the same model. "
  896. "Using getSkinName() and setSkinName() is equivalent to reading and "
  897. "writing the skin field directly.\n\n"
  898. "Any material targets that start with the old skin name have that part "
  899. "of the name replaced with the new skin name. The initial old skin name is "
  900. "\"base\". For example, if a new skin of \"blue\" was applied to a model "
  901. "that had material targets <i>base_body</i> and <i>face</i>, the new targets "
  902. "would be <i>blue_body</i> and <i>face</i>. Note that <i>face</i> was not "
  903. "renamed since it did not start with the old skin name of \"base\".\n\n"
  904. "To support models that do not use the default \"base\" naming convention, "
  905. "you can also specify the part of the name to replace in the skin field "
  906. "itself. For example, if a model had a material target called <i>shapemat</i>, "
  907. "we could apply a new skin \"shape=blue\", and the material target would be "
  908. "renamed to <i>bluemat</i> (note \"shape\" has been replaced with \"blue\").\n\n"
  909. "Multiple skin updates can also be applied at the same time by separating "
  910. "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n"
  911. "Material targets are only renamed if an existing Material maps to that "
  912. "name, or if there is a diffuse texture in the model folder with the same "
  913. "name as the new target.\n\n" );
  914. addField( "isAiControlled", TypeBool, Offset(mIsAiControlled, ShapeBase),
  915. "@brief Is this object AI controlled.\n\n"
  916. "If True then this object is considered AI controlled and not player controlled.\n" );
  917. Parent::initPersistFields();
  918. }
  919. bool ShapeBase::_setFieldSkin( void *object, const char *index, const char *data )
  920. {
  921. ShapeBase* shape = static_cast<ShapeBase*>( object );
  922. if ( shape )
  923. shape->setSkinName( data );
  924. return false;
  925. }
  926. const char *ShapeBase::_getFieldSkin( void *object, const char *data )
  927. {
  928. ShapeBase* shape = static_cast<ShapeBase*>( object );
  929. return shape ? shape->getSkinName() : "";
  930. }
  931. //----------------------------------------------------------------------------
  932. bool ShapeBase::onAdd()
  933. {
  934. if( !Parent::onAdd() )
  935. return false;
  936. if( !mDataBlock )
  937. {
  938. Con::errorf( "ShapeBase::onAdd - no datablock on shape %i:%s (%s)",
  939. getId(), getClassName(), getName() );
  940. return false;
  941. }
  942. // Resolve sounds that arrived in the initial update
  943. S32 i;
  944. for (i = 0; i < MaxSoundThreads; i++)
  945. updateAudioState(mSoundThread[i]);
  946. for (i = 0; i < MaxScriptThreads; i++)
  947. {
  948. Thread& st = mScriptThread[i];
  949. if(st.thread)
  950. updateThread(st);
  951. }
  952. /*
  953. if(mDataBlock->cloakTexName != StringTable->EmptyString())
  954. mCloakTexture = TextureHandle(mDataBlock->cloakTexName, MeshTexture, false);
  955. */
  956. // Accumulation and environment mapping
  957. if (isClientObject() && mShapeInstance)
  958. {
  959. AccumulationVolume::addObject(this);
  960. }
  961. return true;
  962. }
  963. void ShapeBase::onRemove()
  964. {
  965. mConvexList->nukeList();
  966. Parent::onRemove();
  967. // Stop any running sounds on the client
  968. if (isGhost())
  969. for (S32 i = 0; i < MaxSoundThreads; i++)
  970. stopAudio(i);
  971. // Accumulation and environment mapping
  972. if (isClientObject() && mShapeInstance)
  973. {
  974. if (mShapeInstance->hasAccumulation())
  975. AccumulationVolume::removeObject(this);
  976. }
  977. if ( isClientObject() )
  978. {
  979. mCubeReflector.unregisterReflector();
  980. }
  981. }
  982. void ShapeBase::onSceneRemove()
  983. {
  984. mConvexList->nukeList();
  985. Parent::onSceneRemove();
  986. }
  987. bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
  988. {
  989. // need to destroy blend-clips or we crash
  990. if (isGhost())
  991. {
  992. for (S32 i = 0; i < blend_clips.size(); i++)
  993. {
  994. if (blend_clips[i].thread)
  995. mShapeInstance->destroyThread(blend_clips[i].thread);
  996. blend_clips.erase_fast(i);
  997. }
  998. }
  999. ShapeBaseData *prevDB = dynamic_cast<ShapeBaseData*>( mDataBlock );
  1000. bool isInitialDataBlock = (prevDB == 0);
  1001. if ( Parent::onNewDataBlock( dptr, reload ) == false )
  1002. return false;
  1003. mDataBlock = dynamic_cast<ShapeBaseData*>(dptr);
  1004. if (!mDataBlock)
  1005. return false;
  1006. setMaskBits(DamageMask);
  1007. mDamageThread = 0;
  1008. mHulkThread = 0;
  1009. // Even if loadShape succeeds, there may not actually be
  1010. // a shape assigned to this object.
  1011. if (bool(mDataBlock->mShape)) {
  1012. delete mShapeInstance;
  1013. if (isClientObject() && mDataBlock->txr_tag_remappings.size() > 0)
  1014. {
  1015. // temporarily substitute material tags with alternates
  1016. TSMaterialList* mat_list = mDataBlock->mShape->materialList;
  1017. if (mat_list)
  1018. {
  1019. for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
  1020. {
  1021. ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
  1022. Vector<String>& mat_names = (Vector<String>&) mat_list->getMaterialNameList();
  1023. S32 old_tagLen = dStrlen(remap->old_tag);
  1024. for (S32 j = 0; j < mat_names.size(); j++)
  1025. {
  1026. if (mat_names[j].compare(remap->old_tag, old_tagLen, String::NoCase) == 0)
  1027. {
  1028. mat_names[j] = String(remap->new_tag);
  1029. mat_names[j].insert(0, '#');
  1030. break;
  1031. }
  1032. }
  1033. }
  1034. }
  1035. }
  1036. mShapeInstance = new TSShapeInstance(mDataBlock->mShape, isClientObject());
  1037. if (isClientObject())
  1038. {
  1039. mShapeInstance->cloneMaterialList();
  1040. // restore the material tags to original form
  1041. if (mDataBlock->txr_tag_remappings.size() > 0)
  1042. {
  1043. TSMaterialList* mat_list = mDataBlock->mShape->materialList;
  1044. if (mat_list)
  1045. {
  1046. for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
  1047. {
  1048. ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
  1049. Vector<String>& mat_names = (Vector<String>&) mat_list->getMaterialNameList();
  1050. S32 new_tagLen = dStrlen(remap->new_tag);
  1051. for (S32 j = 0; j < mat_names.size(); j++)
  1052. {
  1053. String::SizeType len = mat_names[j].length();
  1054. if (len > 1)
  1055. {
  1056. String temp_name = mat_names[j].substr(1, len - 1);
  1057. if (temp_name.compare(remap->new_tag, new_tagLen) == 0)
  1058. {
  1059. mat_names[j] = String(remap->old_tag);
  1060. break;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. }
  1066. }
  1067. }
  1068. mObjBox = mDataBlock->mShape->mBounds;
  1069. resetWorldBox();
  1070. // Set the initial mesh hidden state.
  1071. mMeshHidden.setSize(mDataBlock->mShape->objects.size());
  1072. mMeshHidden.clear();
  1073. // Initialize the threads
  1074. for (U32 i = 0; i < MaxScriptThreads; i++) {
  1075. Thread& st = mScriptThread[i];
  1076. if (st.sequence != -1) {
  1077. // TG: Need to see about suppressing non-cyclic sounds
  1078. // if the sequences were activated before the object was
  1079. // ghosted.
  1080. // TG: Cyclic animations need to have a random pos if
  1081. // they were started before the object was ghosted.
  1082. // If there was something running on the old shape, the thread
  1083. // needs to be reset. Otherwise we assume that it's been
  1084. // initialized either by the constructor or from the server.
  1085. bool reset = st.thread != 0;
  1086. st.thread = 0;
  1087. // New datablock/shape may not actually HAVE this sequence.
  1088. // In that case stop playing it.
  1089. AssertFatal(prevDB != NULL, "ShapeBase::onNewDataBlock - how did you have a sequence playing without a prior datablock?");
  1090. const TSShape* prevShape = prevDB->mShape;
  1091. const TSShape::Sequence& prevSeq = prevShape->sequences[st.sequence];
  1092. const String& prevSeqName = prevShape->names[prevSeq.nameIndex];
  1093. st.sequence = mDataBlock->mShape->findSequence(prevSeqName);
  1094. if (st.sequence != -1)
  1095. {
  1096. setThreadSequence(i, st.sequence, reset);
  1097. }
  1098. }
  1099. }
  1100. if (mDataBlock->damageSequence != -1) {
  1101. mDamageThread = mShapeInstance->addThread();
  1102. mShapeInstance->setSequence(mDamageThread,
  1103. mDataBlock->damageSequence, 0);
  1104. }
  1105. if (mDataBlock->hulkSequence != -1) {
  1106. mHulkThread = mShapeInstance->addThread();
  1107. mShapeInstance->setSequence(mHulkThread,
  1108. mDataBlock->hulkSequence, 0);
  1109. }
  1110. if (isGhost())
  1111. {
  1112. // Reapply the current skin
  1113. mAppliedSkinName = "";
  1114. reSkin();
  1115. }
  1116. }
  1117. //
  1118. mEnergy = 0;
  1119. mDamage = 0;
  1120. mDamageState = Enabled;
  1121. mRepairReserve = 0;
  1122. updateMass();
  1123. updateDamageLevel();
  1124. updateDamageState();
  1125. mDrag = mDataBlock->drag;
  1126. mCameraFov = mDataBlock->cameraDefaultFov;
  1127. updateMass();
  1128. if( !isInitialDataBlock && mLightPlugin )
  1129. mLightPlugin->reset();
  1130. if ( isClientObject() )
  1131. {
  1132. mCubeReflector.unregisterReflector();
  1133. if ( mDataBlock->reflectorDesc )
  1134. mCubeReflector.registerReflector( this, mDataBlock->reflectorDesc );
  1135. }
  1136. return true;
  1137. }
  1138. void ShapeBase::onDeleteNotify( SimObject *obj )
  1139. {
  1140. if ( obj == mCurrentWaterObject )
  1141. mCurrentWaterObject = NULL;
  1142. Parent::onDeleteNotify( obj );
  1143. }
  1144. void ShapeBase::onImpact(SceneObject* obj, const VectorF& vec)
  1145. {
  1146. if (!isGhost())
  1147. mDataBlock->onImpact_callback( this, obj, vec, vec.len() );
  1148. }
  1149. void ShapeBase::onImpact(const VectorF& vec)
  1150. {
  1151. if (!isGhost())
  1152. mDataBlock->onImpact_callback( this, NULL, vec, vec.len() );
  1153. }
  1154. //----------------------------------------------------------------------------
  1155. void ShapeBase::processTick(const Move* move)
  1156. {
  1157. PROFILE_SCOPE( ShapeBase_ProcessTick );
  1158. // Energy management
  1159. if (mDamageState == Enabled && mDataBlock->inheritEnergyFromMount == false) {
  1160. F32 store = mEnergy;
  1161. mEnergy += mRechargeRate;
  1162. if (mEnergy > mDataBlock->maxEnergy)
  1163. mEnergy = mDataBlock->maxEnergy;
  1164. else
  1165. if (mEnergy < 0)
  1166. mEnergy = 0;
  1167. // Virtual setEnergyLevel is used here by some derived classes to
  1168. // decide whether they really want to set the energy mask bit.
  1169. if (mEnergy != store)
  1170. setEnergyLevel(mEnergy);
  1171. }
  1172. // Repair management
  1173. if (mDataBlock->isInvincible == false)
  1174. {
  1175. F32 store = mDamage;
  1176. mDamage -= mRepairRate;
  1177. mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage);
  1178. if (mRepairReserve > mDamage)
  1179. mRepairReserve = mDamage;
  1180. if (mRepairReserve > 0.0)
  1181. {
  1182. F32 rate = getMin(mDataBlock->repairRate, mRepairReserve);
  1183. mDamage -= rate;
  1184. mRepairReserve -= rate;
  1185. }
  1186. if (store != mDamage)
  1187. {
  1188. updateDamageLevel();
  1189. if (isServerObject()) {
  1190. setMaskBits(DamageMask);
  1191. mDataBlock->onDamage_callback( this, mDamage - store );
  1192. }
  1193. }
  1194. }
  1195. if (isServerObject()) {
  1196. // Server only...
  1197. advanceThreads(TickSec);
  1198. updateServerAudio();
  1199. // update wet state
  1200. setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered
  1201. // update motion state
  1202. mMoveMotion = false;
  1203. if (move && (move->x || move->y || move->z))
  1204. {
  1205. mMoveMotion = true;
  1206. }
  1207. for (S32 i = 0; i < MaxMountedImages; i++)
  1208. {
  1209. setImageMotionState(i, mMoveMotion);
  1210. }
  1211. if(mFading)
  1212. {
  1213. F32 dt = TickMs / 1000.0f;
  1214. F32 newFadeET = mFadeElapsedTime + dt;
  1215. if(mFadeElapsedTime < mFadeDelay && newFadeET >= mFadeDelay)
  1216. setMaskBits(CloakMask);
  1217. mFadeElapsedTime = newFadeET;
  1218. if(mFadeElapsedTime > mFadeTime + mFadeDelay)
  1219. {
  1220. mFadeVal = F32(!mFadeOut);
  1221. mFading = false;
  1222. }
  1223. }
  1224. }
  1225. // Advance images
  1226. if (isServerObject())
  1227. {
  1228. for (S32 i = 0; i < MaxMountedImages; i++)
  1229. {
  1230. if (mMountedImageList[i].dataBlock)
  1231. updateImageState(i, TickSec);
  1232. }
  1233. }
  1234. // Call script on trigger state changes
  1235. if (move && mDataBlock && isServerObject())
  1236. {
  1237. for (S32 i = 0; i < MaxTriggerKeys; i++)
  1238. {
  1239. if (move->trigger[i] != mTrigger[i])
  1240. {
  1241. mTrigger[i] = move->trigger[i];
  1242. mDataBlock->onTrigger_callback( this, i, move->trigger[i] );
  1243. }
  1244. }
  1245. }
  1246. // Update the damage flash and the whiteout
  1247. //
  1248. if (mDamageFlash > 0.0)
  1249. {
  1250. mDamageFlash -= sDamageFlashDec;
  1251. if (mDamageFlash <= 0.0)
  1252. mDamageFlash = 0.0;
  1253. }
  1254. if (mWhiteOut > 0.0)
  1255. {
  1256. mWhiteOut -= sWhiteoutDec;
  1257. if (mWhiteOut <= 0.0)
  1258. mWhiteOut = 0.0;
  1259. }
  1260. if (isMounted()) {
  1261. MatrixF mat;
  1262. mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
  1263. Parent::setTransform(mat);
  1264. }
  1265. }
  1266. void ShapeBase::advanceTime(F32 dt)
  1267. {
  1268. // On the client, the shape threads and images are
  1269. // advanced at framerate.
  1270. advanceThreads(dt);
  1271. updateAudioPos();
  1272. for (S32 i = 0; i < MaxMountedImages; i++)
  1273. if (mMountedImageList[i].dataBlock)
  1274. {
  1275. updateImageState(i, dt);
  1276. updateImageAnimation(i, dt);
  1277. }
  1278. // Cloaking
  1279. if (mCloaked && mCloakLevel != 1.0) {
  1280. if (sCloakSpeed <= 0.0f)
  1281. {
  1282. // Instantaneous
  1283. mCloakLevel = 1.0;
  1284. }
  1285. else
  1286. {
  1287. // Over time determined by sCloakSpeed
  1288. mCloakLevel += dt / sCloakSpeed;
  1289. if (mCloakLevel >= 1.0)
  1290. mCloakLevel = 1.0;
  1291. }
  1292. } else if (!mCloaked && mCloakLevel != 0.0) {
  1293. if (sCloakSpeed <= 0.0f)
  1294. {
  1295. // Instantaneous
  1296. mCloakLevel = 0.0;
  1297. }
  1298. else
  1299. {
  1300. // Over time determined by sCloakSpeed
  1301. mCloakLevel -= dt / sCloakSpeed;
  1302. if (mCloakLevel <= 0.0)
  1303. mCloakLevel = 0.0;
  1304. }
  1305. }
  1306. if(mFading)
  1307. {
  1308. mFadeElapsedTime += dt;
  1309. if(mFadeElapsedTime > mFadeTime)
  1310. {
  1311. mFadeVal = F32(!mFadeOut);
  1312. mFading = false;
  1313. }
  1314. else
  1315. {
  1316. mFadeVal = mFadeElapsedTime / mFadeTime;
  1317. if(mFadeOut)
  1318. mFadeVal = 1 - mFadeVal;
  1319. }
  1320. }
  1321. if (isMounted()) {
  1322. MatrixF mat;
  1323. mMount.object->getRenderMountTransform( 0.0f, mMount.node, mMount.xfm, &mat );
  1324. Parent::setRenderTransform(mat);
  1325. }
  1326. }
  1327. void ShapeBase::setControllingClient( GameConnection* client )
  1328. {
  1329. if( isGhost() && gSFX3DWorld )
  1330. {
  1331. if( gSFX3DWorld->getListener() == this && !client && getControllingClient() && getControllingClient()->isConnectionToServer() )
  1332. {
  1333. // We are the current listener and are no longer a controller object on the
  1334. // connection, so clear our listener status.
  1335. gSFX3DWorld->setListener( NULL );
  1336. }
  1337. else if( client && client->isConnectionToServer() && (getControllingObject() != this) )
  1338. {
  1339. // We're on the local client and not controlled by another object, so make
  1340. // us the current SFX listener.
  1341. gSFX3DWorld->setListener( this );
  1342. }
  1343. }
  1344. Parent::setControllingClient( client );
  1345. // Update all of the mounted images' shapes so they may
  1346. // optimize their animation threads.
  1347. for (U32 i=0; i<MaxMountedImages; ++i)
  1348. {
  1349. MountedImage& image = mMountedImageList[i];
  1350. image.updateDoAnimateAllShapes( this );
  1351. }
  1352. }
  1353. void ShapeBase::setControllingObject(ShapeBase* obj)
  1354. {
  1355. if (obj) {
  1356. setProcessTick(false);
  1357. // Even though we don't processTick, we still need to
  1358. // process after the controller in case anyone is mounted
  1359. // on this object.
  1360. processAfter(obj);
  1361. }
  1362. else {
  1363. setProcessTick(true);
  1364. clearProcessAfter();
  1365. // Catch the case of the controlling object actually
  1366. // mounted on this object.
  1367. if (mControllingObject->mMount.object == this)
  1368. mControllingObject->processAfter(this);
  1369. }
  1370. mControllingObject = obj;
  1371. }
  1372. ShapeBase* ShapeBase::getControlObject()
  1373. {
  1374. return 0;
  1375. }
  1376. void ShapeBase::setControlObject(ShapeBase*)
  1377. {
  1378. }
  1379. bool ShapeBase::isFirstPerson() const
  1380. {
  1381. // Always first person as far as the server is concerned.
  1382. if (!isGhost())
  1383. return true;
  1384. if (const GameConnection* con = getControllingClient())
  1385. return con->getControlObject() == this && con->isFirstPerson();
  1386. return false;
  1387. }
  1388. bool ShapeBase::isValidCameraFov(F32 fov)
  1389. {
  1390. return((fov >= mDataBlock->cameraMinFov) && (fov <= mDataBlock->cameraMaxFov));
  1391. }
  1392. void ShapeBase::setCameraFov(F32 fov)
  1393. {
  1394. // On server allow for script side checking
  1395. if ( !isGhost() && isMethod( "validateCameraFov" ) )
  1396. {
  1397. fov = validateCameraFov_callback( fov );
  1398. }
  1399. mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov);
  1400. }
  1401. void ShapeBase::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
  1402. {
  1403. // update the camera query
  1404. query->camera = this;
  1405. if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
  1406. {
  1407. // get the fov from the connection (in deg)
  1408. F32 fov;
  1409. if (con->getControlCameraFov(&fov))
  1410. {
  1411. query->fov = mDegToRad(fov/2);
  1412. query->sinFov = mSin(query->fov);
  1413. query->cosFov = mCos(query->fov);
  1414. }
  1415. }
  1416. // use eye rather than camera transform (good enough and faster)
  1417. MatrixF eyeTransform;
  1418. getEyeTransform(&eyeTransform);
  1419. eyeTransform.getColumn(3, &query->pos);
  1420. eyeTransform.getColumn(1, &query->orientation);
  1421. // Get the visible distance.
  1422. if (getSceneManager() != NULL)
  1423. query->visibleDistance = getSceneManager()->getVisibleDistance();
  1424. Parent::onCameraScopeQuery( cr, query );
  1425. }
  1426. //----------------------------------------------------------------------------
  1427. F32 ShapeBase::getEnergyLevel()
  1428. {
  1429. if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount )
  1430. return mShapeBaseMount->getEnergyLevel();
  1431. return mEnergy;
  1432. }
  1433. F32 ShapeBase::getEnergyValue()
  1434. {
  1435. if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount )
  1436. {
  1437. F32 maxEnergy = mShapeBaseMount->mDataBlock->maxEnergy;
  1438. if ( maxEnergy > 0.f )
  1439. return (mShapeBaseMount->getEnergyLevel() / maxEnergy);
  1440. }
  1441. else
  1442. {
  1443. F32 maxEnergy = mDataBlock->maxEnergy;
  1444. if ( maxEnergy > 0.f )
  1445. return (mEnergy / mDataBlock->maxEnergy);
  1446. }
  1447. return 0.0f;
  1448. }
  1449. void ShapeBase::setEnergyLevel(F32 energy)
  1450. {
  1451. if (mDataBlock->inheritEnergyFromMount == false || !mShapeBaseMount) {
  1452. if (mDamageState == Enabled) {
  1453. mEnergy = (energy > mDataBlock->maxEnergy)?
  1454. mDataBlock->maxEnergy: (energy < 0)? 0: energy;
  1455. }
  1456. } else {
  1457. // Pass the set onto whatever we're mounted to...
  1458. if ( mShapeBaseMount )
  1459. {
  1460. mShapeBaseMount->setEnergyLevel(energy);
  1461. }
  1462. }
  1463. }
  1464. void ShapeBase::setDamageLevel(F32 damage)
  1465. {
  1466. if (!mDataBlock->isInvincible) {
  1467. F32 store = mDamage;
  1468. mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage);
  1469. if (store != mDamage) {
  1470. updateDamageLevel();
  1471. if (isServerObject()) {
  1472. setMaskBits(DamageMask);
  1473. mDataBlock->onDamage_callback( this, mDamage - store );
  1474. }
  1475. }
  1476. }
  1477. }
  1478. void ShapeBase::updateContainer()
  1479. {
  1480. PROFILE_SCOPE( ShapeBase_updateContainer );
  1481. // Update container drag and buoyancy properties
  1482. // Set default values.
  1483. mDrag = mDataBlock->drag;
  1484. mBuoyancy = 0.0f;
  1485. mNetGravity = gGravity;
  1486. mAppliedForce.set(0,0,0);
  1487. ContainerQueryInfo info;
  1488. info.box = getWorldBox();
  1489. info.mass = getMass();
  1490. mContainer->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
  1491. mWaterCoverage = info.waterCoverage;
  1492. mLiquidType = info.liquidType;
  1493. mLiquidHeight = info.waterHeight;
  1494. setCurrentWaterObject( info.waterObject );
  1495. // This value might be useful as a datablock value,
  1496. // This is what allows the player to stand in shallow water (below this coverage)
  1497. // without jiggling from buoyancy
  1498. if (mWaterCoverage >= 0.25f)
  1499. {
  1500. // water viscosity is used as drag for in water.
  1501. // ShapeBaseData drag is used for drag outside of water.
  1502. // Combine these two components to calculate this ShapeBase object's
  1503. // current drag.
  1504. mDrag = ( info.waterCoverage * info.waterViscosity ) +
  1505. ( 1.0f - info.waterCoverage ) * mDataBlock->drag;
  1506. mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
  1507. }
  1508. mAppliedForce = info.appliedForce;
  1509. mNetGravity = (1.0-mBuoyancy)*info.gravityScale* gGravity;
  1510. //Con::printf( "WaterCoverage: %f", mWaterCoverage );
  1511. //Con::printf( "Drag: %f", mDrag );
  1512. }
  1513. //----------------------------------------------------------------------------
  1514. void ShapeBase::applyRepair(F32 amount)
  1515. {
  1516. // Repair increases the repair reserve
  1517. if (amount > 0 && ((mRepairReserve += amount) > mDamage))
  1518. mRepairReserve = mDamage;
  1519. }
  1520. void ShapeBase::applyDamage(F32 amount)
  1521. {
  1522. if (amount > 0)
  1523. setDamageLevel(mDamage + amount);
  1524. }
  1525. F32 ShapeBase::getDamageValue()
  1526. {
  1527. // Return a 0-1 damage value.
  1528. return mDamage / mDataBlock->maxDamage;
  1529. }
  1530. F32 ShapeBase::getMaxDamage()
  1531. {
  1532. return mDataBlock->maxDamage;
  1533. }
  1534. void ShapeBase::updateDamageLevel()
  1535. {
  1536. if (mDamageThread) {
  1537. // mDamage is already 0-1 on the client
  1538. if (mDamage >= mDataBlock->destroyedLevel) {
  1539. if (getDamageState() == Destroyed)
  1540. mShapeInstance->setPos(mDamageThread, 0);
  1541. else
  1542. mShapeInstance->setPos(mDamageThread, 1);
  1543. } else {
  1544. mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel);
  1545. }
  1546. }
  1547. }
  1548. //----------------------------------------------------------------------------
  1549. void ShapeBase::setDamageState(DamageState state)
  1550. {
  1551. if (mDamageState == state)
  1552. return;
  1553. bool invokeCallback = false;
  1554. const char* lastState = 0;
  1555. if (!isGhost()) {
  1556. if (state != getDamageState())
  1557. setMaskBits(DamageMask);
  1558. lastState = getDamageStateName();
  1559. switch (state) {
  1560. case Destroyed: {
  1561. if (mDamageState == Enabled)
  1562. {
  1563. setDamageState(Disabled);
  1564. // It is possible that the setDamageState() call above has already
  1565. // upgraded our state to Destroyed. If that is the case, no need
  1566. // to continue.
  1567. if (mDamageState == state)
  1568. return;
  1569. }
  1570. invokeCallback = true;
  1571. break;
  1572. }
  1573. case Disabled:
  1574. if (mDamageState == Enabled)
  1575. invokeCallback = true;
  1576. break;
  1577. case Enabled:
  1578. invokeCallback = true;
  1579. break;
  1580. default:
  1581. AssertFatal(false, "Invalid damage state");
  1582. break;
  1583. }
  1584. }
  1585. mDamageState = state;
  1586. if (mDamageState != Enabled) {
  1587. mRepairReserve = 0;
  1588. mEnergy = 0;
  1589. }
  1590. if (invokeCallback) {
  1591. // Like to call the scripts after the state has been intialize.
  1592. // This should only end up being called on the server.
  1593. switch (state) {
  1594. case Destroyed:
  1595. mDataBlock->onDestroyed_callback( this, lastState );
  1596. break;
  1597. case Disabled:
  1598. mDataBlock->onDisabled_callback( this, lastState );
  1599. break;
  1600. case Enabled:
  1601. mDataBlock->onEnabled_callback( this, lastState );
  1602. break;
  1603. default:
  1604. break;
  1605. }
  1606. }
  1607. updateDamageState();
  1608. updateDamageLevel();
  1609. }
  1610. bool ShapeBase::setDamageState(const char* state)
  1611. {
  1612. for (S32 i = 0; i < NumDamageStates; i++)
  1613. if (!dStricmp(state,sDamageStateName[i])) {
  1614. setDamageState(DamageState(i));
  1615. return true;
  1616. }
  1617. return false;
  1618. }
  1619. const char* ShapeBase::getDamageStateName()
  1620. {
  1621. return sDamageStateName[mDamageState];
  1622. }
  1623. void ShapeBase::updateDamageState()
  1624. {
  1625. if (mHulkThread) {
  1626. F32 pos = (mDamageState == Destroyed) ? 1.0f : 0.0f;
  1627. if (mShapeInstance->getPos(mHulkThread) != pos) {
  1628. mShapeInstance->setPos(mHulkThread,pos);
  1629. if (isClientObject())
  1630. mShapeInstance->animate();
  1631. }
  1632. }
  1633. }
  1634. //----------------------------------------------------------------------------
  1635. void ShapeBase::blowUp()
  1636. {
  1637. Point3F center;
  1638. mObjBox.getCenter(&center);
  1639. center += getPosition();
  1640. MatrixF trans = getTransform();
  1641. trans.setPosition( center );
  1642. // explode
  1643. Explosion* pExplosion = NULL;
  1644. if( pointInWater( (Point3F &)center ) && mDataBlock->underwaterExplosion )
  1645. {
  1646. pExplosion = new Explosion;
  1647. pExplosion->onNewDataBlock(mDataBlock->underwaterExplosion, false);
  1648. }
  1649. else
  1650. {
  1651. if (mDataBlock->explosion)
  1652. {
  1653. pExplosion = new Explosion;
  1654. pExplosion->onNewDataBlock(mDataBlock->explosion, false);
  1655. }
  1656. }
  1657. if( pExplosion )
  1658. {
  1659. pExplosion->setTransform(trans);
  1660. pExplosion->setInitialState(center, damageDir);
  1661. if (pExplosion->registerObject() == false)
  1662. {
  1663. Con::errorf(ConsoleLogEntry::General, "ShapeBase(%s)::explode: couldn't register explosion",
  1664. mDataBlock->getName() );
  1665. delete pExplosion;
  1666. pExplosion = NULL;
  1667. }
  1668. }
  1669. TSShapeInstance *debShape = NULL;
  1670. if( mDataBlock->mDebrisShape == NULL )
  1671. {
  1672. return;
  1673. }
  1674. else
  1675. {
  1676. debShape = new TSShapeInstance( mDataBlock->mDebrisShape, true);
  1677. }
  1678. Vector< TSPartInstance * > partList;
  1679. TSPartInstance::breakShape( debShape, 0, partList, NULL, NULL, 0 );
  1680. if( !mDataBlock->debris )
  1681. {
  1682. mDataBlock->debris = new DebrisData;
  1683. }
  1684. // cycle through partlist and create debris pieces
  1685. for( U32 i=0; i<partList.size(); i++ )
  1686. {
  1687. //Point3F axis( 0.0, 0.0, 1.0 );
  1688. Point3F randomDir = MathUtils::randomDir( damageDir, 0, 50 );
  1689. Debris *debris = new Debris;
  1690. debris->setPartInstance( partList[i] );
  1691. debris->init( center, randomDir );
  1692. debris->onNewDataBlock( mDataBlock->debris, false );
  1693. debris->setTransform( trans );
  1694. if( !debris->registerObject() )
  1695. {
  1696. Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
  1697. delete debris;
  1698. debris = NULL;
  1699. }
  1700. else
  1701. {
  1702. debShape->incDebrisRefCount();
  1703. }
  1704. }
  1705. damageDir.set(0, 0, 1);
  1706. }
  1707. //----------------------------------------------------------------------------
  1708. void ShapeBase::onMount( SceneObject *obj, S32 node )
  1709. {
  1710. mConvexList->nukeList();
  1711. // Are we mounting to a ShapeBase object?
  1712. mShapeBaseMount = dynamic_cast<ShapeBase*>( obj );
  1713. Parent::onMount( obj, node );
  1714. }
  1715. void ShapeBase::onUnmount( SceneObject *obj, S32 node )
  1716. {
  1717. Parent::onUnmount( obj, node );
  1718. mShapeBaseMount = NULL;
  1719. }
  1720. Point3F ShapeBase::getAIRepairPoint()
  1721. {
  1722. if (mDataBlock->mountPointNode[ShapeBaseData::AIRepairNode] < 0)
  1723. return Point3F(0, 0, 0);
  1724. MatrixF xf(true);
  1725. getMountTransform( ShapeBaseData::AIRepairNode, MatrixF::Identity, &xf );
  1726. Point3F pos(0, 0, 0);
  1727. xf.getColumn(3,&pos);
  1728. return pos;
  1729. }
  1730. //----------------------------------------------------------------------------
  1731. void ShapeBase::getEyeTransform(MatrixF* mat)
  1732. {
  1733. getEyeBaseTransform(mat, true);
  1734. }
  1735. void ShapeBase::getEyeBaseTransform(MatrixF* mat, bool includeBank)
  1736. {
  1737. // Returns eye to world space transform
  1738. S32 eyeNode = mDataBlock->eyeNode;
  1739. if (eyeNode != -1)
  1740. mat->mul(getTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
  1741. else
  1742. *mat = getTransform();
  1743. }
  1744. void ShapeBase::getRenderEyeTransform(MatrixF* mat)
  1745. {
  1746. getRenderEyeBaseTransform(mat, true);
  1747. }
  1748. void ShapeBase::getRenderEyeBaseTransform(MatrixF* mat, bool includeBank)
  1749. {
  1750. // Returns eye to world space transform
  1751. S32 eyeNode = mDataBlock->eyeNode;
  1752. if (eyeNode != -1)
  1753. mat->mul(getRenderTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
  1754. else
  1755. *mat = getRenderTransform();
  1756. }
  1757. void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
  1758. {
  1759. // Returns camera to world space transform
  1760. // Handles first person / third person camera position
  1761. if (isServerObject() && mShapeInstance)
  1762. mShapeInstance->animateNodeSubtrees(true);
  1763. if (*pos != 0)
  1764. {
  1765. F32 min,max;
  1766. Point3F offset;
  1767. MatrixF eye,rot;
  1768. getCameraParameters(&min,&max,&offset,&rot);
  1769. getRenderEyeTransform(&eye);
  1770. mat->mul(eye,rot);
  1771. // Use the eye transform to orient the camera
  1772. VectorF vp,vec;
  1773. vp.x = vp.z = 0;
  1774. vp.y = -(max - min) * *pos;
  1775. eye.mulV(vp,&vec);
  1776. VectorF minVec;
  1777. vp.y = -min;
  1778. eye.mulV( vp, &minVec );
  1779. // Use the camera node's pos.
  1780. Point3F osp,sp;
  1781. if (mDataBlock->cameraNode != -1) {
  1782. mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
  1783. // Scale the camera position before applying the transform
  1784. const Point3F& scale = getScale();
  1785. osp.convolve( scale );
  1786. getRenderTransform().mulP(osp,&sp);
  1787. }
  1788. else
  1789. getRenderTransform().getColumn(3,&sp);
  1790. // Make sure we don't extend the camera into anything solid
  1791. Point3F ep = sp + minVec + vec + offset;
  1792. disableCollision();
  1793. if (isMounted())
  1794. getObjectMount()->disableCollision();
  1795. RayInfo collision;
  1796. if( mContainer && mContainer->castRay(sp, ep,
  1797. (0xFFFFFFFF & ~(WaterObjectType |
  1798. GameBaseObjectType |
  1799. TriggerObjectType |
  1800. DefaultObjectType)),
  1801. &collision) == true) {
  1802. F32 vecLenSq = vec.lenSquared();
  1803. F32 adj = (-mDot(vec, collision.normal) / vecLenSq) * 0.1;
  1804. F32 newPos = getMax(0.0f, collision.t - adj);
  1805. if (newPos == 0.0f)
  1806. eye.getColumn(3,&ep);
  1807. else
  1808. ep = sp + offset + (vec * newPos);
  1809. }
  1810. mat->setColumn(3,ep);
  1811. if (isMounted())
  1812. getObjectMount()->enableCollision();
  1813. enableCollision();
  1814. }
  1815. else
  1816. {
  1817. getRenderEyeTransform(mat);
  1818. }
  1819. // Apply Camera FX.
  1820. mat->mul( gCamFXMgr.getTrans() );
  1821. }
  1822. void ShapeBase::getEyeCameraTransform(IDisplayDevice *displayDevice, U32 eyeId, MatrixF *outMat)
  1823. {
  1824. MatrixF temp(1);
  1825. Point3F eyePos;
  1826. Point3F rotEyePos;
  1827. DisplayPose newPose;
  1828. displayDevice->getFrameEyePose(&newPose, eyeId);
  1829. // Ok, basically we just need to add on newPose to the camera transform
  1830. // NOTE: currently we dont support third-person camera in this mode
  1831. MatrixF cameraTransform(1);
  1832. F32 fakePos = 0;
  1833. //cameraTransform = getRenderTransform(); // use this for controllers TODO
  1834. getCameraTransform(&fakePos, &cameraTransform);
  1835. temp = MatrixF(1);
  1836. newPose.orientation.setMatrix(&temp);
  1837. temp.setPosition(newPose.position);
  1838. *outMat = cameraTransform * temp;
  1839. }
  1840. void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
  1841. {
  1842. *min = mDataBlock->cameraMinDist;
  1843. *max = mDataBlock->cameraMaxDist;
  1844. off->set(0,0,0);
  1845. rot->identity();
  1846. }
  1847. //----------------------------------------------------------------------------
  1848. F32 ShapeBase::getDamageFlash() const
  1849. {
  1850. return mDamageFlash;
  1851. }
  1852. void ShapeBase::setDamageFlash(const F32 flash)
  1853. {
  1854. mDamageFlash = flash;
  1855. if (mDamageFlash < 0.0)
  1856. mDamageFlash = 0;
  1857. else if (mDamageFlash > 1.0)
  1858. mDamageFlash = 1.0;
  1859. }
  1860. //----------------------------------------------------------------------------
  1861. F32 ShapeBase::getWhiteOut() const
  1862. {
  1863. return mWhiteOut;
  1864. }
  1865. void ShapeBase::setWhiteOut(const F32 flash)
  1866. {
  1867. mWhiteOut = flash;
  1868. if (mWhiteOut < 0.0)
  1869. mWhiteOut = 0;
  1870. else if (mWhiteOut > 1.5)
  1871. mWhiteOut = 1.5;
  1872. }
  1873. //----------------------------------------------------------------------------
  1874. bool ShapeBase::onlyFirstPerson() const
  1875. {
  1876. return mDataBlock->firstPersonOnly;
  1877. }
  1878. bool ShapeBase::useObjsEyePoint() const
  1879. {
  1880. return mDataBlock->useEyePoint;
  1881. }
  1882. //----------------------------------------------------------------------------
  1883. void ShapeBase::setVelocity(const VectorF&)
  1884. {
  1885. }
  1886. void ShapeBase::applyImpulse(const Point3F&,const VectorF&)
  1887. {
  1888. }
  1889. //----------------------------------------------------------------------------
  1890. void ShapeBase::playAudio(U32 slot, StringTableEntry assetId)
  1891. {
  1892. AssertFatal( slot < MaxSoundThreads, "ShapeBase::playAudio() bad slot index" );
  1893. if (AssetDatabase.isDeclaredAsset(assetId))
  1894. {
  1895. AssetPtr<SoundAsset> tempSoundAsset;
  1896. tempSoundAsset = assetId;
  1897. SoundThread& st = mSoundThread[slot];
  1898. if (tempSoundAsset && (!st.play || st.asset != tempSoundAsset))
  1899. {
  1900. setMaskBits(SoundMaskN << slot);
  1901. st.play = true;
  1902. st.asset = tempSoundAsset;
  1903. updateAudioState(st);
  1904. }
  1905. }
  1906. }
  1907. void ShapeBase::stopAudio(U32 slot)
  1908. {
  1909. AssertFatal( slot < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index" );
  1910. SoundThread& st = mSoundThread[slot];
  1911. if ( st.play )
  1912. {
  1913. st.play = false;
  1914. setMaskBits(SoundMaskN << slot);
  1915. updateAudioState(st);
  1916. }
  1917. }
  1918. void ShapeBase::updateServerAudio()
  1919. {
  1920. // Timeout non-looping sounds
  1921. for (S32 i = 0; i < MaxSoundThreads; i++) {
  1922. SoundThread& st = mSoundThread[i];
  1923. if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) {
  1924. clearMaskBits(SoundMaskN << i);
  1925. st.play = false;
  1926. }
  1927. }
  1928. }
  1929. void ShapeBase::updateAudioState(SoundThread& st)
  1930. {
  1931. SFX_DELETE( st.sound );
  1932. if ( st.play && st.asset )
  1933. {
  1934. if ( isGhost() )
  1935. {
  1936. // if asset is valid, play
  1937. if (st.asset->isAssetValid() )
  1938. {
  1939. st.sound = SFX->createSource( st.asset->getSfxProfile() , &getTransform() );
  1940. if ( st.sound )
  1941. st.sound->play();
  1942. }
  1943. else
  1944. st.play = false;
  1945. }
  1946. else
  1947. {
  1948. // Non-looping sounds timeout on the server
  1949. st.timeout = 0;
  1950. if ( !st.asset->getSfxDescription()->mIsLooping )
  1951. st.timeout = Sim::getCurrentTime() + sAudioTimeout;
  1952. }
  1953. }
  1954. else
  1955. {
  1956. // st.sound was not stopped before. If this causes issues remove.
  1957. st.play = false;
  1958. if (st.sound)
  1959. st.sound->stop();
  1960. }
  1961. }
  1962. void ShapeBase::updateAudioPos()
  1963. {
  1964. for (S32 i = 0; i < MaxSoundThreads; i++)
  1965. {
  1966. SFXSource* source = mSoundThread[i].sound;
  1967. if ( source )
  1968. source->setTransform( getTransform() );
  1969. }
  1970. }
  1971. //----------------------------------------------------------------------------
  1972. const char *ShapeBase::getThreadSequenceName( U32 slot )
  1973. {
  1974. Thread& st = mScriptThread[slot];
  1975. if ( st.sequence == -1 )
  1976. {
  1977. // Invalid Animation.
  1978. return "";
  1979. }
  1980. // Name Index
  1981. const U32 nameIndex = getShape()->sequences[st.sequence].nameIndex;
  1982. // Return Name.
  1983. return getShape()->getName( nameIndex );
  1984. }
  1985. bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset)
  1986. {
  1987. Thread& st = mScriptThread[slot];
  1988. if (st.thread && st.sequence == seq && st.state == Thread::Play)
  1989. return true;
  1990. // Handle a -1 sequence, as this may be set when a thread has been destroyed.
  1991. if(seq == -1)
  1992. return true;
  1993. if (seq < MaxSequenceIndex) {
  1994. setMaskBits(ThreadMaskN << slot);
  1995. st.sequence = seq;
  1996. if (reset) {
  1997. st.state = Thread::Play;
  1998. st.atEnd = false;
  1999. st.timescale = 1.f;
  2000. st.position = 0.f;
  2001. }
  2002. if (mShapeInstance) {
  2003. if (!st.thread)
  2004. st.thread = mShapeInstance->addThread();
  2005. mShapeInstance->setSequence(st.thread,seq,st.position);
  2006. updateThread(st);
  2007. }
  2008. return true;
  2009. }
  2010. return false;
  2011. }
  2012. void ShapeBase::updateThread(Thread& st)
  2013. {
  2014. switch (st.state)
  2015. {
  2016. case Thread::Stop:
  2017. {
  2018. mShapeInstance->setTimeScale( st.thread, 1.f );
  2019. mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
  2020. } // Drop through to pause state
  2021. case Thread::Pause:
  2022. {
  2023. mShapeInstance->setTimeScale( st.thread, 0.f );
  2024. } break;
  2025. case Thread::Play:
  2026. {
  2027. if (st.atEnd)
  2028. {
  2029. mShapeInstance->setTimeScale(st.thread,1);
  2030. mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
  2031. mShapeInstance->setTimeScale(st.thread,0);
  2032. st.state = Thread::Stop;
  2033. }
  2034. else
  2035. {
  2036. if ( st.position != -1.f )
  2037. {
  2038. mShapeInstance->setTimeScale( st.thread, 1.f );
  2039. mShapeInstance->setPos( st.thread, st.position );
  2040. }
  2041. mShapeInstance->setTimeScale(st.thread, st.timescale );
  2042. }
  2043. } break;
  2044. case Thread::Destroy:
  2045. {
  2046. st.atEnd = true;
  2047. st.sequence = -1;
  2048. if(st.thread)
  2049. {
  2050. mShapeInstance->destroyThread(st.thread);
  2051. st.thread = 0;
  2052. }
  2053. } break;
  2054. }
  2055. }
  2056. bool ShapeBase::stopThread(U32 slot)
  2057. {
  2058. Thread& st = mScriptThread[slot];
  2059. if (st.sequence != -1 && st.state != Thread::Stop) {
  2060. setMaskBits(ThreadMaskN << slot);
  2061. st.state = Thread::Stop;
  2062. updateThread(st);
  2063. return true;
  2064. }
  2065. return false;
  2066. }
  2067. bool ShapeBase::destroyThread(U32 slot)
  2068. {
  2069. Thread& st = mScriptThread[slot];
  2070. if (st.sequence != -1 && st.state != Thread::Destroy) {
  2071. setMaskBits(ThreadMaskN << slot);
  2072. st.state = Thread::Destroy;
  2073. updateThread(st);
  2074. return true;
  2075. }
  2076. return false;
  2077. }
  2078. bool ShapeBase::pauseThread(U32 slot)
  2079. {
  2080. Thread& st = mScriptThread[slot];
  2081. if (st.sequence != -1 && st.state != Thread::Pause) {
  2082. setMaskBits(ThreadMaskN << slot);
  2083. st.state = Thread::Pause;
  2084. updateThread(st);
  2085. return true;
  2086. }
  2087. return false;
  2088. }
  2089. bool ShapeBase::playThread(U32 slot)
  2090. {
  2091. Thread& st = mScriptThread[slot];
  2092. if (st.sequence != -1 && st.state != Thread::Play) {
  2093. setMaskBits(ThreadMaskN << slot);
  2094. st.state = Thread::Play;
  2095. updateThread(st);
  2096. return true;
  2097. }
  2098. return false;
  2099. }
  2100. bool ShapeBase::setThreadPosition( U32 slot, F32 pos )
  2101. {
  2102. Thread& st = mScriptThread[slot];
  2103. if (st.sequence != -1)
  2104. {
  2105. setMaskBits(ThreadMaskN << slot);
  2106. st.position = pos;
  2107. st.atEnd = false;
  2108. updateThread(st);
  2109. return true;
  2110. }
  2111. return false;
  2112. }
  2113. bool ShapeBase::setThreadDir(U32 slot,bool forward)
  2114. {
  2115. Thread& st = mScriptThread[slot];
  2116. if (st.sequence != -1)
  2117. {
  2118. if ( ( st.timescale >= 0.f ) != forward )
  2119. {
  2120. setMaskBits(ThreadMaskN << slot);
  2121. st.timescale *= -1.f ;
  2122. st.atEnd = false;
  2123. updateThread(st);
  2124. }
  2125. return true;
  2126. }
  2127. return false;
  2128. }
  2129. bool ShapeBase::setThreadTimeScale( U32 slot, F32 timeScale )
  2130. {
  2131. Thread& st = mScriptThread[slot];
  2132. if (st.sequence != -1)
  2133. {
  2134. if (st.timescale != timeScale)
  2135. {
  2136. setMaskBits(ThreadMaskN << slot);
  2137. st.timescale = timeScale;
  2138. updateThread(st);
  2139. }
  2140. return true;
  2141. }
  2142. return false;
  2143. }
  2144. void ShapeBase::advanceThreads(F32 dt)
  2145. {
  2146. for (U32 i = 0; i < MaxScriptThreads; i++) {
  2147. Thread& st = mScriptThread[i];
  2148. if (st.thread) {
  2149. if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd &&
  2150. ( ( st.timescale > 0.f )? mShapeInstance->getPos(st.thread) >= 1.0:
  2151. mShapeInstance->getPos(st.thread) <= 0)) {
  2152. st.atEnd = true;
  2153. updateThread(st);
  2154. if (!isGhost()) {
  2155. mDataBlock->onEndSequence_callback(this, i, this->getThreadSequenceName(i));
  2156. }
  2157. }
  2158. // Make sure the thread is still valid after the call to onEndSequence_callback().
  2159. // Someone could have called destroyThread() while in there.
  2160. if(st.thread)
  2161. {
  2162. mShapeInstance->advanceTime(dt,st.thread);
  2163. st.position = mShapeInstance->getPos(st.thread);
  2164. }
  2165. }
  2166. }
  2167. }
  2168. //----------------------------------------------------------------------------
  2169. /// Emit particles on the given emitter, if we're within triggerHeight above some static surface with a
  2170. /// material that has 'showDust' set to true. The particles will have a lifetime of 'numMilliseconds'
  2171. /// and be emitted at the given offset from the contact point having a direction of 'axis'.
  2172. void ShapeBase::emitDust( ParticleEmitter* emitter, F32 triggerHeight, const Point3F& offset, U32 numMilliseconds, const Point3F& axis )
  2173. {
  2174. if( !emitter )
  2175. return;
  2176. Point3F startPos = getPosition();
  2177. Point3F endPos = startPos + Point3F( 0.0, 0.0, - triggerHeight );
  2178. RayInfo rayInfo;
  2179. if( getContainer()->castRay( startPos, endPos, STATIC_COLLISION_TYPEMASK, &rayInfo ) )
  2180. {
  2181. Material* material = ( rayInfo.material ? dynamic_cast< Material* >( rayInfo.material->getMaterial() ) : 0 );
  2182. if( material && material->mShowDust )
  2183. {
  2184. LinearColorF colorList[ ParticleData::PDC_NUM_KEYS ];
  2185. for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x )
  2186. colorList[ x ] = material->mEffectColor[ x ];
  2187. for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x )
  2188. colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 );
  2189. emitter->setColors( colorList );
  2190. Point3F contactPoint = rayInfo.point + offset;
  2191. emitter->emitParticles( contactPoint, true, ( axis == Point3F::Zero ? rayInfo.normal : axis ),
  2192. getVelocity(), numMilliseconds );
  2193. }
  2194. }
  2195. }
  2196. //----------------------------------------------------------------------------
  2197. TSShape const* ShapeBase::getShape()
  2198. {
  2199. return mShapeInstance? mShapeInstance->getShape(): 0;
  2200. }
  2201. void ShapeBase::prepRenderImage( SceneRenderState *state )
  2202. {
  2203. _prepRenderImage( state, true, true );
  2204. }
  2205. void ShapeBase::_prepRenderImage( SceneRenderState *state,
  2206. bool renderSelf,
  2207. bool renderMountedImages )
  2208. {
  2209. PROFILE_SCOPE( ShapeBase_PrepRenderImage );
  2210. //if ( mIsCubemapUpdate )
  2211. // return false;
  2212. if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) )
  2213. return;
  2214. // We don't need to render if all the meshes are forced hidden.
  2215. if ( mMeshHidden.getSize() > 0 && mMeshHidden.testAll() )
  2216. return;
  2217. // If we're rendering shadows don't render the mounted
  2218. // images unless the shape is also rendered.
  2219. if ( state->isShadowPass() && !renderSelf )
  2220. return;
  2221. // If we're currently rendering our own reflection we
  2222. // don't want to render ourselves into it.
  2223. if ( mCubeReflector.isRendering() )
  2224. return;
  2225. // We force all the shapes to use the highest detail
  2226. // if we're the control object or mounted.
  2227. bool forceHighestDetail = false;
  2228. {
  2229. GameConnection *con = GameConnection::getConnectionToServer();
  2230. ShapeBase *co = NULL;
  2231. if(con && ( (co = dynamic_cast<ShapeBase*>(con->getControlObject())) != NULL) )
  2232. {
  2233. if(co == this || co->getObjectMount() == this)
  2234. forceHighestDetail = true;
  2235. }
  2236. }
  2237. mLastRenderFrame = sLastRenderFrame;
  2238. // get shape detail...we might not even need to be drawn
  2239. Point3F cameraOffset = getWorldBox().getClosestPoint( state->getDiffuseCameraPosition() ) - state->getDiffuseCameraPosition();
  2240. F32 dist = cameraOffset.len();
  2241. if (dist < 0.01f)
  2242. dist = 0.01f;
  2243. F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
  2244. if (mShapeInstance)
  2245. {
  2246. if ( forceHighestDetail )
  2247. mShapeInstance->setCurrentDetail( 0 );
  2248. else
  2249. mShapeInstance->setDetailFromDistance( state, dist * invScale );
  2250. mShapeInstance->animate();
  2251. }
  2252. if ( ( mShapeInstance && mShapeInstance->getCurrentDetail() < 0 ) ||
  2253. ( !mShapeInstance && !gShowBoundingBox ) )
  2254. {
  2255. // no, don't draw anything
  2256. return;
  2257. }
  2258. if( renderMountedImages )
  2259. {
  2260. for (U32 i = 0; i < MaxMountedImages; i++)
  2261. {
  2262. MountedImage& image = mMountedImageList[i];
  2263. U32 imageShapeIndex = getImageShapeIndex(image);
  2264. if (image.dataBlock && image.shapeInstance[imageShapeIndex])
  2265. {
  2266. // Select detail levels on mounted items but... always
  2267. // draw the control object's mounted images in high detail.
  2268. if ( forceHighestDetail )
  2269. image.shapeInstance[imageShapeIndex]->setCurrentDetail( 0 );
  2270. else
  2271. image.shapeInstance[imageShapeIndex]->setDetailFromDistance( state, dist * invScale );
  2272. if (!mIsZero( (1.0f - mCloakLevel) * mFadeVal))
  2273. {
  2274. prepBatchRender( state, i );
  2275. // Debug rendering of the mounted shape bounds.
  2276. if ( gShowBoundingBox )
  2277. {
  2278. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  2279. ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox );
  2280. ri->objectIndex = i;
  2281. ri->type = RenderPassManager::RIT_Editor;
  2282. state->getRenderPass()->addInst( ri );
  2283. }
  2284. }
  2285. }
  2286. }
  2287. }
  2288. // Debug rendering of the shape bounding box.
  2289. if ( gShowBoundingBox )
  2290. {
  2291. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  2292. ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox );
  2293. ri->objectIndex = -1;
  2294. ri->type = RenderPassManager::RIT_Editor;
  2295. state->getRenderPass()->addInst( ri );
  2296. }
  2297. if ( mShapeInstance && renderSelf )
  2298. prepBatchRender( state, -1 );
  2299. calcClassRenderData();
  2300. }
  2301. //----------------------------------------------------------------------------
  2302. // prepBatchRender
  2303. //----------------------------------------------------------------------------
  2304. void ShapeBase::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex )
  2305. {
  2306. // CHANGES IN HERE SHOULD BE DUPLICATED IN TSSTATIC!
  2307. GFXTransformSaver saver;
  2308. // Set up our TS render state.
  2309. TSRenderState rdata;
  2310. rdata.setSceneState( state );
  2311. if ( mCubeReflector.isEnabled() )
  2312. rdata.setCubemap( mCubeReflector.getCubemap() );
  2313. rdata.setFadeOverride( (1.0f - mCloakLevel) * mFadeVal );
  2314. // We might have some forward lit materials
  2315. // so pass down a query to gather lights.
  2316. LightQuery query;
  2317. query.init( getWorldSphere() );
  2318. rdata.setLightQuery( &query );
  2319. if( mountedImageIndex != -1 )
  2320. {
  2321. MountedImage& image = mMountedImageList[mountedImageIndex];
  2322. if( image.dataBlock && image.shapeInstance )
  2323. {
  2324. renderMountedImage( mountedImageIndex, rdata, state );
  2325. }
  2326. }
  2327. else
  2328. {
  2329. MatrixF mat = getRenderTransform();
  2330. mat.scale( mObjScale );
  2331. GFX->setWorldMatrix( mat );
  2332. if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() )
  2333. {
  2334. RenderPassManager *pass = state->getRenderPass();
  2335. OccluderRenderInst *ri = pass->allocInst<OccluderRenderInst>();
  2336. ri->type = RenderPassManager::RIT_Occluder;
  2337. ri->query = mCubeReflector.getOcclusionQuery();
  2338. mObjToWorld.mulP( mObjBox.getCenter(), &ri->position );
  2339. ri->scale.set( mObjBox.getExtents() );
  2340. ri->orientation = pass->allocUniqueXform( mObjToWorld );
  2341. ri->isSphere = false;
  2342. state->getRenderPass()->addInst( ri );
  2343. }
  2344. mShapeInstance->animate();
  2345. mShapeInstance->render( rdata );
  2346. }
  2347. }
  2348. void ShapeBase::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state )
  2349. {
  2350. GFX->pushWorldMatrix();
  2351. MatrixF mat;
  2352. getRenderImageTransform(imageSlot, &mat, rstate.getSceneState()->isShadowPass());
  2353. GFX->setWorldMatrix( mat );
  2354. MountedImage& image = mMountedImageList[imageSlot];
  2355. U32 imageShapeIndex = getImageShapeIndex(image);
  2356. image.shapeInstance[imageShapeIndex]->animate();
  2357. image.shapeInstance[imageShapeIndex]->render( rstate );
  2358. GFX->popWorldMatrix();
  2359. }
  2360. void ShapeBase::_renderBoundingBox( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
  2361. {
  2362. // If we got an override mat then this some
  2363. // special rendering pass... skip out of it.
  2364. if ( overrideMat )
  2365. return;
  2366. GFXStateBlockDesc desc;
  2367. desc.setZReadWrite( true, false );
  2368. desc.setBlend( true );
  2369. desc.fillMode = GFXFillWireframe;
  2370. GFXDrawUtil *drawer = GFX->getDrawUtil();
  2371. if ( ri->objectIndex != -1 )
  2372. {
  2373. MountedImage &image = mMountedImageList[ ri->objectIndex ];
  2374. if ( image.shapeInstance )
  2375. {
  2376. MatrixF mat;
  2377. getRenderImageTransform( ri->objectIndex, &mat );
  2378. const Box3F &objBox = image.shapeInstance[getImageShapeIndex(image)]->getShape()->mBounds;
  2379. drawer->drawCube( desc, objBox, ColorI( 255, 255, 255 ), &mat );
  2380. }
  2381. }
  2382. else
  2383. drawer->drawCube( desc, mObjBox, ColorI( 255, 255, 255 ), &mRenderObjToWorld );
  2384. }
  2385. bool ShapeBase::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
  2386. {
  2387. if (mShapeInstance)
  2388. {
  2389. RayInfo shortest;
  2390. shortest.t = 1e8;
  2391. info->object = NULL;
  2392. for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++)
  2393. {
  2394. mShapeInstance->animate(mDataBlock->LOSDetails[i]);
  2395. if (mShapeInstance->castRay(start, end, info, mDataBlock->LOSDetails[i]))
  2396. {
  2397. info->object = this;
  2398. if (info->t < shortest.t)
  2399. shortest = *info;
  2400. }
  2401. }
  2402. if (info->object == this)
  2403. {
  2404. // Copy out the shortest time...
  2405. *info = shortest;
  2406. return true;
  2407. }
  2408. }
  2409. return false;
  2410. }
  2411. bool ShapeBase::castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info)
  2412. {
  2413. if (mShapeInstance)
  2414. {
  2415. RayInfo localInfo;
  2416. mShapeInstance->animate();
  2417. bool res = mShapeInstance->castRayRendered(start, end, &localInfo, mShapeInstance->getCurrentDetail());
  2418. if (res)
  2419. {
  2420. *info = localInfo;
  2421. info->object = this;
  2422. return true;
  2423. }
  2424. }
  2425. return false;
  2426. }
  2427. //----------------------------------------------------------------------------
  2428. bool ShapeBase::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &, const SphereF &)
  2429. {
  2430. if ( !mShapeInstance )
  2431. return false;
  2432. polyList->setTransform(&mObjToWorld, mObjScale);
  2433. polyList->setObject(this);
  2434. if ( context == PLC_Selection )
  2435. {
  2436. mShapeInstance->animate();
  2437. mShapeInstance->buildPolyList(polyList,mShapeInstance->getCurrentDetail());
  2438. return true;
  2439. }
  2440. else if ( context == PLC_Export )
  2441. {
  2442. // Try to call on the client so we can export materials
  2443. ShapeBase* exportObj = this;
  2444. if ( isServerObject() && getClientObject() )
  2445. exportObj = dynamic_cast<ShapeBase*>(getClientObject());
  2446. S32 dl = 0;
  2447. exportObj->mShapeInstance->animate();
  2448. exportObj->mShapeInstance->buildPolyList(polyList, dl);
  2449. return true;
  2450. }
  2451. else
  2452. {
  2453. bool ret = false;
  2454. for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
  2455. {
  2456. mShapeInstance->buildPolyList(polyList,mDataBlock->collisionDetails[i]);
  2457. ret = true;
  2458. }
  2459. return ret;
  2460. }
  2461. }
  2462. void ShapeBase::buildConvex(const Box3F& box, Convex* convex)
  2463. {
  2464. if (mShapeInstance == NULL)
  2465. return;
  2466. // These should really come out of a pool
  2467. mConvexList->collectGarbage();
  2468. Box3F realBox = box;
  2469. mWorldToObj.mul(realBox);
  2470. realBox.minExtents.convolveInverse(mObjScale);
  2471. realBox.maxExtents.convolveInverse(mObjScale);
  2472. if (realBox.isOverlapped(getObjBox()) == false)
  2473. return;
  2474. for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
  2475. {
  2476. Box3F newbox = mDataBlock->collisionBounds[i];
  2477. newbox.minExtents.convolve(mObjScale);
  2478. newbox.maxExtents.convolve(mObjScale);
  2479. mObjToWorld.mul(newbox);
  2480. if (box.isOverlapped(newbox) == false)
  2481. continue;
  2482. // See if this hull exists in the working set already...
  2483. Convex* cc = 0;
  2484. CollisionWorkingList& wl = convex->getWorkingList();
  2485. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  2486. if (itr->mConvex->getType() == ShapeBaseConvexType &&
  2487. (static_cast<ShapeBaseConvex*>(itr->mConvex)->pShapeBase == this &&
  2488. static_cast<ShapeBaseConvex*>(itr->mConvex)->hullId == i)) {
  2489. cc = itr->mConvex;
  2490. break;
  2491. }
  2492. }
  2493. if (cc)
  2494. continue;
  2495. // Create a new convex.
  2496. ShapeBaseConvex* cp = new ShapeBaseConvex;
  2497. mConvexList->registerObject(cp);
  2498. convex->addToWorkingList(cp);
  2499. cp->mObject = this;
  2500. cp->pShapeBase = this;
  2501. cp->hullId = i;
  2502. cp->box = mDataBlock->collisionBounds[i];
  2503. cp->transform = 0;
  2504. cp->findNodeTransform();
  2505. }
  2506. }
  2507. //----------------------------------------------------------------------------
  2508. void ShapeBase::queueCollision( SceneObject *obj, const VectorF &vec)
  2509. {
  2510. // Add object to list of collisions.
  2511. SimTime time = Sim::getCurrentTime();
  2512. S32 num = obj->getId();
  2513. CollisionTimeout** adr = &mTimeoutList;
  2514. CollisionTimeout* ptr = mTimeoutList;
  2515. while (ptr) {
  2516. if (ptr->objectNumber == num) {
  2517. if (ptr->expireTime < time) {
  2518. ptr->expireTime = time + CollisionTimeoutValue;
  2519. ptr->object = obj;
  2520. ptr->vector = vec;
  2521. }
  2522. return;
  2523. }
  2524. // Recover expired entries
  2525. if (ptr->expireTime < time) {
  2526. CollisionTimeout* cur = ptr;
  2527. *adr = ptr->next;
  2528. ptr = ptr->next;
  2529. cur->next = sFreeTimeoutList;
  2530. sFreeTimeoutList = cur;
  2531. }
  2532. else {
  2533. adr = &ptr->next;
  2534. ptr = ptr->next;
  2535. }
  2536. }
  2537. // New entry for the object
  2538. if (sFreeTimeoutList != NULL)
  2539. {
  2540. ptr = sFreeTimeoutList;
  2541. sFreeTimeoutList = ptr->next;
  2542. ptr->next = NULL;
  2543. }
  2544. else
  2545. {
  2546. ptr = sTimeoutChunker.alloc();
  2547. }
  2548. ptr->object = obj;
  2549. ptr->objectNumber = obj->getId();
  2550. ptr->vector = vec;
  2551. ptr->expireTime = time + CollisionTimeoutValue;
  2552. ptr->next = mTimeoutList;
  2553. mTimeoutList = ptr;
  2554. }
  2555. void ShapeBase::notifyCollision()
  2556. {
  2557. // Notify all the objects that were just stamped during the queueing
  2558. // process.
  2559. SimTime expireTime = Sim::getCurrentTime() + CollisionTimeoutValue;
  2560. for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next)
  2561. {
  2562. if (ptr->expireTime == expireTime && ptr->object)
  2563. {
  2564. SimObjectPtr<SceneObject> safePtr(ptr->object);
  2565. SimObjectPtr<ShapeBase> safeThis(this);
  2566. onCollision(ptr->object,ptr->vector);
  2567. ptr->object = 0;
  2568. if(!bool(safeThis))
  2569. return;
  2570. if(bool(safePtr))
  2571. safePtr->onCollision(this,ptr->vector);
  2572. if(!bool(safeThis))
  2573. return;
  2574. }
  2575. }
  2576. }
  2577. void ShapeBase::onCollision( SceneObject *object, const VectorF &vec )
  2578. {
  2579. if (!isGhost())
  2580. mDataBlock->onCollision_callback( this, object, vec, vec.len() );
  2581. }
  2582. //--------------------------------------------------------------------------
  2583. bool ShapeBase::pointInWater( Point3F &point )
  2584. {
  2585. if ( mCurrentWaterObject == NULL )
  2586. return false;
  2587. return mCurrentWaterObject->isUnderwater( point );
  2588. }
  2589. //----------------------------------------------------------------------------
  2590. void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream)
  2591. {
  2592. Parent::writePacketData(connection, stream);
  2593. stream->write(getEnergyLevel());
  2594. stream->write(mRechargeRate);
  2595. }
  2596. void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream)
  2597. {
  2598. Parent::readPacketData(connection, stream);
  2599. F32 energy;
  2600. stream->read(&energy);
  2601. setEnergyLevel(energy);
  2602. stream->read(&mRechargeRate);
  2603. }
  2604. F32 ShapeBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
  2605. {
  2606. // If it's the scope object, must be high priority
  2607. if (camInfo->camera == this) {
  2608. // Most priorities are between 0 and 1, so this
  2609. // should be something larger.
  2610. return 10.0f;
  2611. }
  2612. if( camInfo->camera )
  2613. {
  2614. ShapeBase* camera = dynamic_cast< ShapeBase* >( camInfo->camera );
  2615. // see if the camera is mounted to this...
  2616. // if it is, this should have a high priority
  2617. if( camera && camera->getObjectMount() == this)
  2618. return 10.0f;
  2619. }
  2620. return Parent::getUpdatePriority(camInfo, updateMask, updateSkips);
  2621. }
  2622. U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
  2623. {
  2624. U32 retMask = Parent::packUpdate(con, mask, stream);
  2625. if (mask & InitialUpdateMask) {
  2626. // mask off sounds that aren't playing
  2627. S32 i;
  2628. for (i = 0; i < MaxSoundThreads; i++)
  2629. if (!mSoundThread[i].play)
  2630. mask &= ~(SoundMaskN << i);
  2631. // mask off threads that aren't running
  2632. for (i = 0; i < MaxScriptThreads; i++)
  2633. if (mScriptThread[i].sequence == -1)
  2634. mask &= ~(ThreadMaskN << i);
  2635. // mask off images that aren't updated
  2636. for(i = 0; i < MaxMountedImages; i++)
  2637. if(!mMountedImageList[i].dataBlock)
  2638. mask &= ~(ImageMaskN << i);
  2639. }
  2640. if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask | MeshHiddenMask |
  2641. ThreadMask | ImageMask | CloakMask | SkinMask)))
  2642. return retMask;
  2643. if (stream->writeFlag(mask & DamageMask)) {
  2644. stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits);
  2645. stream->writeInt(mDamageState,NumDamageStateBits);
  2646. stream->writeNormalVector( damageDir, 8 );
  2647. }
  2648. if (stream->writeFlag(mask & ThreadMask)) {
  2649. for (S32 i = 0; i < MaxScriptThreads; i++) {
  2650. Thread& st = mScriptThread[i];
  2651. if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) ) {
  2652. stream->writeInt(st.sequence,ThreadSequenceBits);
  2653. stream->writeInt(st.state,2);
  2654. stream->write(st.timescale);
  2655. stream->write(st.position);
  2656. stream->writeFlag(st.atEnd);
  2657. }
  2658. }
  2659. }
  2660. if (stream->writeFlag(mask & SoundMask)) {
  2661. for (S32 i = 0; i < MaxSoundThreads; i++) {
  2662. SoundThread& st = mSoundThread[i];
  2663. if (stream->writeFlag(mask & (SoundMaskN << i)))
  2664. if (stream->writeFlag(st.play))
  2665. {
  2666. NetStringHandle assetIdStr = st.asset->getAssetId();
  2667. con->packNetStringHandleU(stream, assetIdStr);
  2668. }
  2669. }
  2670. }
  2671. if (stream->writeFlag(mask & ImageMask)) {
  2672. for (S32 i = 0; i < MaxMountedImages; i++)
  2673. if (stream->writeFlag(mask & (ImageMaskN << i))) {
  2674. MountedImage& image = mMountedImageList[i];
  2675. if (stream->writeFlag(image.dataBlock))
  2676. stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst,
  2677. DataBlockObjectIdBitSize);
  2678. con->packNetStringHandleU(stream, image.skinNameHandle);
  2679. con->packNetStringHandleU(stream, image.scriptAnimPrefix);
  2680. // Used to force the 1st person rendering on the client. This is required
  2681. // as this object could be ghosted to the client prior to its controlling client
  2682. // being set. Therefore there is a network tick when the object is in limbo...
  2683. stream->writeFlag(image.dataBlock && image.dataBlock->animateAllShapes && getControllingClient() == con);
  2684. stream->writeFlag(image.wet);
  2685. stream->writeFlag(image.motion);
  2686. stream->writeFlag(image.ammo);
  2687. stream->writeFlag(image.loaded);
  2688. stream->writeFlag(image.target);
  2689. stream->writeFlag(image.triggerDown);
  2690. stream->writeFlag(image.altTriggerDown);
  2691. for (U32 j=0; j<ShapeBaseImageData::MaxGenericTriggers; ++j)
  2692. {
  2693. stream->writeFlag(image.genericTrigger[j]);
  2694. }
  2695. stream->writeInt(image.fireCount,3);
  2696. stream->writeInt(image.altFireCount,3);
  2697. stream->writeInt(image.reloadCount,3);
  2698. stream->writeFlag(isImageFiring(i));
  2699. stream->writeFlag(isImageAltFiring(i));
  2700. stream->writeFlag(isImageReloading(i));
  2701. }
  2702. }
  2703. // Group some of the uncommon stuff together.
  2704. if (stream->writeFlag(mask & (NameMask | CloakMask | SkinMask | MeshHiddenMask ))) {
  2705. if (stream->writeFlag(mask & CloakMask))
  2706. {
  2707. // cloaking
  2708. stream->writeFlag( mCloaked );
  2709. // piggyback control update
  2710. stream->writeFlag(bool(getControllingClient()));
  2711. // fading
  2712. if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) {
  2713. stream->writeFlag(mFadeOut);
  2714. stream->write(mFadeTime);
  2715. }
  2716. else
  2717. stream->writeFlag(mFadeVal == 1.0f);
  2718. }
  2719. if (stream->writeFlag(mask & NameMask)) {
  2720. con->packNetStringHandleU(stream, mShapeNameHandle);
  2721. }
  2722. if ( stream->writeFlag( mask & MeshHiddenMask ) )
  2723. stream->writeBits( mMeshHidden );
  2724. if (stream->writeFlag(mask & SkinMask))
  2725. con->packNetStringHandleU(stream, mSkinNameHandle);
  2726. }
  2727. return retMask;
  2728. }
  2729. void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
  2730. {
  2731. Parent::unpackUpdate(con, stream);
  2732. mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event...
  2733. if(!stream->readFlag())
  2734. return;
  2735. if (stream->readFlag()) {
  2736. mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage);
  2737. DamageState prevState = mDamageState;
  2738. mDamageState = DamageState(stream->readInt(NumDamageStateBits));
  2739. stream->readNormalVector( &damageDir, 8 );
  2740. if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded())
  2741. blowUp();
  2742. updateDamageLevel();
  2743. updateDamageState();
  2744. }
  2745. if (stream->readFlag()) {
  2746. for (S32 i = 0; i < MaxScriptThreads; i++) {
  2747. if (stream->readFlag()) {
  2748. Thread& st = mScriptThread[i];
  2749. U32 seq = stream->readInt(ThreadSequenceBits);
  2750. st.state = Thread::State(stream->readInt(2));
  2751. stream->read( &st.timescale );
  2752. stream->read( &st.position );
  2753. st.atEnd = stream->readFlag();
  2754. if (st.sequence != seq && st.state != Thread::Destroy)
  2755. setThreadSequence(i,seq,false);
  2756. else
  2757. updateThread(st);
  2758. }
  2759. }
  2760. }
  2761. if ( stream->readFlag() )
  2762. {
  2763. for ( S32 i = 0; i < MaxSoundThreads; i++ )
  2764. {
  2765. if ( stream->readFlag() )
  2766. {
  2767. SoundThread& st = mSoundThread[i];
  2768. st.play = stream->readFlag();
  2769. if ( st.play )
  2770. {
  2771. StringTableEntry temp = StringTable->insert(con->unpackNetStringHandleU(stream).getString());
  2772. if (AssetDatabase.isDeclaredAsset(temp))
  2773. {
  2774. AssetPtr<SoundAsset> tempSoundAsset;
  2775. tempSoundAsset = temp;
  2776. st.asset = temp;
  2777. }
  2778. }
  2779. if ( isProperlyAdded() )
  2780. updateAudioState( st );
  2781. }
  2782. }
  2783. }
  2784. // Mounted Images
  2785. if (stream->readFlag()) {
  2786. for (S32 i = 0; i < MaxMountedImages; i++) {
  2787. if (stream->readFlag()) {
  2788. MountedImage& image = mMountedImageList[i];
  2789. ShapeBaseImageData* imageData = 0;
  2790. if (stream->readFlag()) {
  2791. SimObjectId id = stream->readInt(DataBlockObjectIdBitSize) +
  2792. DataBlockObjectIdFirst;
  2793. if (!Sim::findObject(id,imageData)) {
  2794. con->setLastError("Invalid packet (mounted images).");
  2795. return;
  2796. }
  2797. }
  2798. NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);
  2799. NetStringHandle scriptDesiredAnimPrefix = con->unpackNetStringHandleU(stream);
  2800. image.forceAnimateAllShapes = stream->readFlag();
  2801. image.wet = stream->readFlag();
  2802. image.motion = stream->readFlag();
  2803. image.ammo = stream->readFlag();
  2804. image.loaded = stream->readFlag();
  2805. image.target = stream->readFlag();
  2806. image.triggerDown = stream->readFlag();
  2807. image.altTriggerDown = stream->readFlag();
  2808. for (U32 j=0; j<ShapeBaseImageData::MaxGenericTriggers; ++j)
  2809. {
  2810. image.genericTrigger[j] = stream->readFlag();
  2811. }
  2812. S32 count = stream->readInt(3);
  2813. S32 altCount = stream->readInt(3);
  2814. S32 reloadCount = stream->readInt(3);
  2815. bool datablockChange = image.dataBlock != imageData;
  2816. if (datablockChange || (image.skinNameHandle != skinDesiredNameHandle))
  2817. {
  2818. MountedImage& neoImage = mMountedImageList[i];
  2819. neoImage.scriptAnimPrefix = scriptDesiredAnimPrefix;
  2820. setImage( i, imageData,
  2821. skinDesiredNameHandle, neoImage.loaded,
  2822. neoImage.ammo, neoImage.triggerDown, neoImage.altTriggerDown,
  2823. neoImage.motion, neoImage.genericTrigger[0], neoImage.genericTrigger[1], neoImage.genericTrigger[2], neoImage.genericTrigger[3],
  2824. neoImage.target);
  2825. }
  2826. if (!datablockChange && image.scriptAnimPrefix != scriptDesiredAnimPrefix)
  2827. {
  2828. // We don't have a new image, but we do have a new script anim prefix to work with.
  2829. // Notify the image of this change.
  2830. MountedImage& animImage = mMountedImageList[i];
  2831. animImage.scriptAnimPrefix = scriptDesiredAnimPrefix;
  2832. updateAnimThread(i, getImageShapeIndex(animImage));
  2833. }
  2834. bool isFiring = stream->readFlag();
  2835. bool isAltFiring = stream->readFlag();
  2836. bool isReloading = stream->readFlag();
  2837. if (isProperlyAdded()) {
  2838. // Normal processing
  2839. bool processFiring = false;
  2840. if (count != image.fireCount)
  2841. {
  2842. image.fireCount = count;
  2843. setImageState(i,getImageFireState(i),true);
  2844. processFiring = true;
  2845. }
  2846. else if (altCount != image.altFireCount)
  2847. {
  2848. image.altFireCount = altCount;
  2849. setImageState(i,getImageAltFireState(i),true);
  2850. processFiring = true;
  2851. }
  2852. else if (reloadCount != image.reloadCount)
  2853. {
  2854. image.reloadCount = reloadCount;
  2855. setImageState(i,getImageReloadState(i),true);
  2856. }
  2857. if (processFiring && imageData)
  2858. {
  2859. if ( imageData->lightType == ShapeBaseImageData::WeaponFireLight )
  2860. image.lightStart = Sim::getCurrentTime();
  2861. }
  2862. updateImageState(i,0);
  2863. }
  2864. else
  2865. {
  2866. if(imageData)
  2867. {
  2868. // Initial state
  2869. image.fireCount = count;
  2870. image.altFireCount = altCount;
  2871. image.reloadCount = reloadCount;
  2872. if (isFiring)
  2873. setImageState(i,getImageFireState(i),true);
  2874. else if (isAltFiring)
  2875. setImageState(i,getImageAltFireState(i),true);
  2876. else if (isReloading)
  2877. setImageState(i,getImageReloadState(i),true);
  2878. }
  2879. }
  2880. }
  2881. }
  2882. }
  2883. if (stream->readFlag())
  2884. {
  2885. if(stream->readFlag()) // CloakMask and control
  2886. {
  2887. // Read cloaking state.
  2888. setCloakedState(stream->readFlag());
  2889. mIsControlled = stream->readFlag();
  2890. if (( mFading = stream->readFlag()) == true) {
  2891. mFadeOut = stream->readFlag();
  2892. if(mFadeOut)
  2893. mFadeVal = 1.0f;
  2894. else
  2895. mFadeVal = 0;
  2896. stream->read(&mFadeTime);
  2897. mFadeDelay = 0;
  2898. mFadeElapsedTime = 0;
  2899. }
  2900. else
  2901. mFadeVal = F32(stream->readFlag());
  2902. }
  2903. if (stream->readFlag()) { // NameMask
  2904. mShapeNameHandle = con->unpackNetStringHandleU(stream);
  2905. }
  2906. if ( stream->readFlag() ) // MeshHiddenMask
  2907. {
  2908. stream->readBits( &mMeshHidden );
  2909. _updateHiddenMeshes();
  2910. }
  2911. if (stream->readFlag()) // SkinMask
  2912. {
  2913. NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);;
  2914. if (mSkinNameHandle != skinDesiredNameHandle)
  2915. {
  2916. mSkinNameHandle = skinDesiredNameHandle;
  2917. reSkin();
  2918. }
  2919. }
  2920. }
  2921. }
  2922. //--------------------------------------------------------------------------
  2923. void ShapeBase::forceUncloak(const char * reason)
  2924. {
  2925. AssertFatal(isServerObject(), "ShapeBase::forceUncloak: server only call");
  2926. if(!mCloaked)
  2927. return;
  2928. mDataBlock->onForceUncloak_callback( this, reason ? reason : "" );
  2929. }
  2930. void ShapeBase::setCloakedState(bool cloaked)
  2931. {
  2932. if (cloaked == mCloaked)
  2933. return;
  2934. if (isServerObject())
  2935. setMaskBits(CloakMask);
  2936. // Have to do this for the client, if we are ghosted over in the initial
  2937. // packet as cloaked, we set the state immediately to the extreme
  2938. if (isProperlyAdded() == false) {
  2939. mCloaked = cloaked;
  2940. if (mCloaked)
  2941. mCloakLevel = 1.0;
  2942. else
  2943. mCloakLevel = 0.0;
  2944. } else {
  2945. mCloaked = cloaked;
  2946. }
  2947. }
  2948. //--------------------------------------------------------------------------
  2949. void ShapeBase::setHidden( bool hidden )
  2950. {
  2951. if( hidden != isHidden() )
  2952. {
  2953. Parent::setHidden( hidden );
  2954. if( hidden )
  2955. setProcessTick( false );
  2956. else
  2957. setProcessTick( true );
  2958. }
  2959. }
  2960. //--------------------------------------------------------------------------
  2961. void ShapeBaseConvex::findNodeTransform()
  2962. {
  2963. S32 dl = pShapeBase->mDataBlock->collisionDetails[hullId];
  2964. TSShapeInstance* si = pShapeBase->getShapeInstance();
  2965. TSShape* shape = si->getShape();
  2966. const TSShape::Detail* detail = &shape->details[dl];
  2967. const S32 subs = detail->subShapeNum;
  2968. const S32 start = shape->subShapeFirstObject[subs];
  2969. const S32 end = start + shape->subShapeNumObjects[subs];
  2970. // Find the first object that contains a mesh for this
  2971. // detail level. There should only be one mesh per
  2972. // collision detail level.
  2973. for (S32 i = start; i < end; i++)
  2974. {
  2975. const TSShape::Object* obj = &shape->objects[i];
  2976. if (obj->numMeshes && detail->objectDetailNum < obj->numMeshes)
  2977. {
  2978. nodeTransform = &si->mNodeTransforms[obj->nodeIndex];
  2979. return;
  2980. }
  2981. }
  2982. return;
  2983. }
  2984. const MatrixF& ShapeBaseConvex::getTransform() const
  2985. {
  2986. // If the transform isn't specified, it's assumed to be the
  2987. // origin of the shape.
  2988. const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
  2989. // Multiply on the mesh shape offset
  2990. // tg: Returning this static here is not really a good idea, but
  2991. // all this Convex code needs to be re-organized.
  2992. if (nodeTransform) {
  2993. static MatrixF mat;
  2994. mat.mul(omat,*nodeTransform);
  2995. return mat;
  2996. }
  2997. return omat;
  2998. }
  2999. Box3F ShapeBaseConvex::getBoundingBox() const
  3000. {
  3001. const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
  3002. return getBoundingBox(omat, mObject->getScale());
  3003. }
  3004. Box3F ShapeBaseConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
  3005. {
  3006. Box3F newBox = box;
  3007. newBox.minExtents.convolve(scale);
  3008. newBox.maxExtents.convolve(scale);
  3009. mat.mul(newBox);
  3010. return newBox;
  3011. }
  3012. Point3F ShapeBaseConvex::support(const VectorF& v) const
  3013. {
  3014. TSShape::ConvexHullAccelerator* pAccel =
  3015. pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
  3016. AssertFatal(pAccel != NULL, "Error, no accel!");
  3017. F32 currMaxDP = mDot(pAccel->vertexList[0], v);
  3018. U32 index = 0;
  3019. for (U32 i = 1; i < pAccel->numVerts; i++) {
  3020. F32 dp = mDot(pAccel->vertexList[i], v);
  3021. if (dp > currMaxDP) {
  3022. currMaxDP = dp;
  3023. index = i;
  3024. }
  3025. }
  3026. return pAccel->vertexList[index];
  3027. }
  3028. void ShapeBaseConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
  3029. {
  3030. cf->material = 0;
  3031. cf->mObject = mObject;
  3032. TSShape::ConvexHullAccelerator* pAccel =
  3033. pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
  3034. AssertFatal(pAccel != NULL, "Error, no accel!");
  3035. F32 currMaxDP = mDot(pAccel->vertexList[0], n);
  3036. U32 index = 0;
  3037. U32 i;
  3038. for (i = 1; i < pAccel->numVerts; i++) {
  3039. F32 dp = mDot(pAccel->vertexList[i], n);
  3040. if (dp > currMaxDP) {
  3041. currMaxDP = dp;
  3042. index = i;
  3043. }
  3044. }
  3045. const U8* emitString = pAccel->emitStrings[index];
  3046. U32 currPos = 0;
  3047. U32 numVerts = emitString[currPos++];
  3048. for (i = 0; i < numVerts; i++) {
  3049. cf->mVertexList.increment();
  3050. U32 vListIDx = emitString[currPos++];
  3051. mat.mulP(pAccel->vertexList[vListIDx], &cf->mVertexList.last());
  3052. }
  3053. U32 numEdges = emitString[currPos++];
  3054. for (i = 0; i < numEdges; i++) {
  3055. U32 ev0 = emitString[currPos++];
  3056. U32 ev1 = emitString[currPos++];
  3057. cf->mEdgeList.increment();
  3058. cf->mEdgeList.last().vertex[0] = ev0;
  3059. cf->mEdgeList.last().vertex[1] = ev1;
  3060. }
  3061. U32 numFaces = emitString[currPos++];
  3062. for (i = 0; i < numFaces; i++) {
  3063. cf->mFaceList.increment();
  3064. U32 plane = emitString[currPos++];
  3065. mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal);
  3066. for (U32 j = 0; j < 3; j++)
  3067. cf->mFaceList.last().vertex[j] = emitString[currPos++];
  3068. }
  3069. }
  3070. void ShapeBaseConvex::getPolyList(AbstractPolyList* list)
  3071. {
  3072. list->setTransform(&pShapeBase->getTransform(), pShapeBase->getScale());
  3073. list->setObject(pShapeBase);
  3074. pShapeBase->mShapeInstance->animate(pShapeBase->mDataBlock->collisionDetails[hullId]);
  3075. pShapeBase->mShapeInstance->buildPolyList(list,pShapeBase->mDataBlock->collisionDetails[hullId]);
  3076. }
  3077. //--------------------------------------------------------------------------
  3078. bool ShapeBase::isInvincible()
  3079. {
  3080. if( mDataBlock )
  3081. {
  3082. return mDataBlock->isInvincible;
  3083. }
  3084. return false;
  3085. }
  3086. void ShapeBase::startFade( F32 fadeTime, F32 fadeDelay, bool fadeOut )
  3087. {
  3088. setMaskBits(CloakMask);
  3089. mFadeElapsedTime = 0;
  3090. mFading = true;
  3091. if(fadeDelay < 0)
  3092. fadeDelay = 0;
  3093. if(fadeTime < 0)
  3094. fadeTime = 0;
  3095. mFadeTime = fadeTime;
  3096. mFadeDelay = fadeDelay;
  3097. mFadeOut = fadeOut;
  3098. mFadeVal = F32(mFadeOut);
  3099. }
  3100. //--------------------------------------------------------------------------
  3101. void ShapeBase::setShapeName(const char* name)
  3102. {
  3103. if (!isGhost()) {
  3104. if (name[0] != '\0') {
  3105. // Use tags for better network performance
  3106. // Should be a tag, but we'll convert to one if it isn't.
  3107. if (name[0] == StringTagPrefixByte)
  3108. mShapeNameHandle = NetStringHandle(U32(dAtoi(name + 1)));
  3109. else
  3110. mShapeNameHandle = NetStringHandle(name);
  3111. }
  3112. else {
  3113. mShapeNameHandle = NetStringHandle();
  3114. }
  3115. setMaskBits(NameMask);
  3116. }
  3117. }
  3118. void ShapeBase::setSkinName(const char* name)
  3119. {
  3120. if (!isGhost()) {
  3121. if (name[0] != '\0') {
  3122. // Use tags for better network performance
  3123. // Should be a tag, but we'll convert to one if it isn't.
  3124. if (name[0] == StringTagPrefixByte)
  3125. mSkinNameHandle = NetStringHandle(U32(dAtoi(name + 1)));
  3126. else
  3127. mSkinNameHandle = NetStringHandle(name);
  3128. }
  3129. else
  3130. mSkinNameHandle = NetStringHandle();
  3131. setMaskBits(SkinMask);
  3132. }
  3133. }
  3134. //----------------------------------------------------------------------------
  3135. void ShapeBase::reSkin()
  3136. {
  3137. if (isGhost() && mShapeInstance)
  3138. {
  3139. if (mSkinNameHandle.isValidString())
  3140. {
  3141. mShapeInstance->resetMaterialList();
  3142. Vector<String> skins;
  3143. String(mSkinNameHandle.getString()).split(";", skins);
  3144. for (S32 i = 0; i < skins.size(); i++)
  3145. {
  3146. String oldSkin(mAppliedSkinName.c_str());
  3147. String newSkin(skins[i]);
  3148. // Check if the skin handle contains an explicit "old" base string. This
  3149. // allows all models to support skinning, even if they don't follow the
  3150. // "base_xxx" material naming convention.
  3151. S32 split = newSkin.find('='); // "old=new" format skin?
  3152. if (split != String::NPos)
  3153. {
  3154. oldSkin = newSkin.substr(0, split);
  3155. newSkin = newSkin.erase(0, split + 1);
  3156. }
  3157. else
  3158. {
  3159. oldSkin = "";
  3160. }
  3161. mShapeInstance->reSkin(newSkin, oldSkin);
  3162. mAppliedSkinName = newSkin;
  3163. }
  3164. }
  3165. else
  3166. {
  3167. mShapeInstance->reSkin("", mAppliedSkinName);
  3168. mAppliedSkinName = "";
  3169. }
  3170. }
  3171. }
  3172. void ShapeBase::setCurrentWaterObject( WaterObject *obj )
  3173. {
  3174. if ( obj )
  3175. deleteNotify( obj );
  3176. if ( mCurrentWaterObject )
  3177. clearNotify( mCurrentWaterObject );
  3178. mCurrentWaterObject = obj;
  3179. }
  3180. void ShapeBase::setTransform(const MatrixF & mat)
  3181. {
  3182. Parent::setTransform(mat);
  3183. // Accumulation and environment mapping
  3184. if (isClientObject() && mShapeInstance)
  3185. {
  3186. if (mShapeInstance->hasAccumulation())
  3187. AccumulationVolume::updateObject(this);
  3188. }
  3189. }
  3190. void ShapeBase::notifyCollisionCallbacks(SceneObject* obj, const VectorF& vel)
  3191. {
  3192. for (S32 i = 0; i < collision_callbacks.size(); i++)
  3193. if (collision_callbacks[i])
  3194. collision_callbacks[i]->collisionNotify(this, obj, vel);
  3195. }
  3196. void ShapeBase::registerCollisionCallback(CollisionEventCallback* ce_cb)
  3197. {
  3198. for (S32 i = 0; i < collision_callbacks.size(); i++)
  3199. if (collision_callbacks[i] == ce_cb)
  3200. return;
  3201. collision_callbacks.push_back(ce_cb);
  3202. }
  3203. void ShapeBase::unregisterCollisionCallback(CollisionEventCallback* ce_cb)
  3204. {
  3205. for (S32 i = 0; i < collision_callbacks.size(); i++)
  3206. if (collision_callbacks[i] == ce_cb)
  3207. {
  3208. collision_callbacks.erase(i);
  3209. return;
  3210. }
  3211. }
  3212. //--------------------------------------------------------------------------
  3213. //----------------------------------------------------------------------------
  3214. DefineEngineMethod( ShapeBase, setHidden, void, ( bool show ),,
  3215. "@brief Add or remove this object from the scene.\n\n"
  3216. "When removed from the scene, the object will not be processed or rendered.\n"
  3217. "@param show False to hide the object, true to re-show it\n\n" )
  3218. {
  3219. object->setHidden( show );
  3220. }
  3221. DefineEngineMethod( ShapeBase, isHidden, bool, (),,
  3222. "Check if the object is hidden.\n"
  3223. "@return true if the object is hidden, false if visible.\n\n" )
  3224. {
  3225. return object->isHidden();
  3226. }
  3227. //----------------------------------------------------------------------------
  3228. DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, StringTableEntry assetId),,
  3229. "@brief Attach a sound to this shape and start playing it.\n\n"
  3230. "@param slot Audio slot index for the sound (valid range is 0 - 3)\n" // 3 = ShapeBase::MaxSoundThreads-1
  3231. "@param track SFXTrack to play\n"
  3232. "@return true if the sound was attached successfully, false if failed\n\n"
  3233. "@see stopAudio()\n")
  3234. {
  3235. if (assetId && slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
  3236. object->playAudio(slot, assetId);
  3237. return true;
  3238. }
  3239. return false;
  3240. }
  3241. DefineEngineMethod( ShapeBase, stopAudio, bool, ( S32 slot ),,
  3242. "@brief Stop a sound started with playAudio.\n\n"
  3243. "@param slot audio slot index (started with playAudio)\n"
  3244. "@return true if the sound was stopped successfully, false if failed\n\n"
  3245. "@see playAudio()\n")
  3246. {
  3247. if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
  3248. object->stopAudio(slot);
  3249. return true;
  3250. }
  3251. return false;
  3252. }
  3253. //----------------------------------------------------------------------------
  3254. DefineEngineMethod( ShapeBase, playThread, bool, ( S32 slot, const char* name ), ( "" ),
  3255. "@brief Start a new animation thread, or restart one that has been paused or "
  3256. "stopped.\n\n"
  3257. "@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = ShapeBase::MaxScriptThreads-1
  3258. "@param name name of the animation sequence to play in this slot. If not "
  3259. "specified, the paused or stopped thread in this slot will be resumed.\n"
  3260. "@return true if successful, false if failed\n\n"
  3261. "@tsexample\n"
  3262. "%obj.playThread( 0, \"ambient\" ); // Play the ambient sequence in slot 0\n"
  3263. "%obj.setThreadTimeScale( 0, 0.5 ); // Play at half-speed\n"
  3264. "%obj.pauseThread( 0 ); // Pause the sequence\n"
  3265. "%obj.playThread( 0 ); // Resume playback\n"
  3266. "%obj.playThread( 0, \"spin\" ); // Replace the sequence in slot 0\n"
  3267. "@endtsexample\n"
  3268. "@see pauseThread()\n"
  3269. "@see stopThread()\n"
  3270. "@see setThreadDir()\n"
  3271. "@see setThreadTimeScale()\n"
  3272. "@see destroyThread()\n")
  3273. {
  3274. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3275. if (!dStrEqual(name, "")) {
  3276. if (object->getShape()) {
  3277. S32 seq = object->getShape()->findSequence(name);
  3278. if (seq != -1 && object->setThreadSequence(slot,seq))
  3279. return true;
  3280. }
  3281. }
  3282. else
  3283. if (object->playThread(slot))
  3284. return true;
  3285. }
  3286. return false;
  3287. }
  3288. DefineEngineMethod( ShapeBase, setThreadDir, bool, ( S32 slot, bool fwd ),,
  3289. "@brief Set the playback direction of an animation thread.\n\n"
  3290. "@param slot thread slot to modify\n"
  3291. "@param fwd true to play the animation forwards, false to play backwards\n"
  3292. "@return true if successful, false if failed\n\n"
  3293. "@see playThread()\n" )
  3294. {
  3295. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3296. if (object->setThreadDir(slot,fwd))
  3297. return true;
  3298. }
  3299. return false;
  3300. }
  3301. DefineEngineMethod( ShapeBase, setThreadTimeScale, bool, ( S32 slot, F32 scale ),,
  3302. "@brief Set the playback time scale of an animation thread.\n\n"
  3303. "@param slot thread slot to modify\n"
  3304. "@param scale new thread time scale (1=normal speed, 0.5=half speed etc)\n"
  3305. "@return true if successful, false if failed\n\n"
  3306. "@see playThread\n" )
  3307. {
  3308. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3309. if (object->setThreadTimeScale(slot,scale))
  3310. return true;
  3311. }
  3312. return false;
  3313. }
  3314. DefineEngineMethod( ShapeBase, setThreadPosition, bool, ( S32 slot, F32 pos ),,
  3315. "@brief Set the position within an animation thread.\n\n"
  3316. "@param slot thread slot to modify\n"
  3317. "@param pos position within thread\n"
  3318. "@return true if successful, false if failed\n\n"
  3319. "@see playThread\n" )
  3320. {
  3321. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3322. if (object->setThreadPosition(slot,pos))
  3323. return true;
  3324. }
  3325. return false;
  3326. }
  3327. DefineEngineMethod( ShapeBase, stopThread, bool, ( S32 slot ),,
  3328. "@brief Stop an animation thread.\n\n"
  3329. "If restarted using playThread, the animation "
  3330. "will start from the beginning again.\n"
  3331. "@param slot thread slot to stop\n"
  3332. "@return true if successful, false if failed\n\n"
  3333. "@see playThread\n" )
  3334. {
  3335. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3336. if (object->stopThread(slot))
  3337. return true;
  3338. }
  3339. return false;
  3340. }
  3341. DefineEngineMethod( ShapeBase, destroyThread, bool, ( S32 slot ),,
  3342. "@brief Destroy an animation thread, which prevents it from playing.\n\n"
  3343. "@param slot thread slot to destroy\n"
  3344. "@return true if successful, false if failed\n\n"
  3345. "@see playThread\n" )
  3346. {
  3347. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3348. if (object->destroyThread(slot))
  3349. return true;
  3350. }
  3351. return false;
  3352. }
  3353. DefineEngineMethod( ShapeBase, pauseThread, bool, ( S32 slot ),,
  3354. "@brief Pause an animation thread.\n\n"
  3355. "If restarted using playThread, the animation "
  3356. "will resume from the paused position.\n"
  3357. "@param slot thread slot to stop\n"
  3358. "@return true if successful, false if failed\n\n"
  3359. "@see playThread\n" )
  3360. {
  3361. if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
  3362. if (object->pauseThread(slot))
  3363. return true;
  3364. }
  3365. return false;
  3366. }
  3367. //----------------------------------------------------------------------------
  3368. DefineEngineMethod( ShapeBase, mountImage, bool,
  3369. ( ShapeBaseImageData* image, S32 slot, bool loaded, const char* skinTag ), ( true, "" ),
  3370. "@brief Mount a new Image.\n\n"
  3371. "@param image the Image to mount\n"
  3372. "@param slot Image slot to mount into (valid range is 0 - 3)\n"
  3373. "@param loaded initial loaded state for the Image\n"
  3374. "@param skinTag tagged string to reskin the mounted Image\n"
  3375. "@return true if successful, false if failed\n\n"
  3376. "@tsexample\n"
  3377. "%player.mountImage( PistolImage, 1 );\n"
  3378. "%player.mountImage( CrossbowImage, 0, false );\n"
  3379. "%player.mountImage( RocketLauncherImage, 0, true, 'blue' );\n"
  3380. "@endtsexample\n"
  3381. "@see unmountImage()\n"
  3382. "@see getMountedImage()\n"
  3383. "@see getPendingImage()\n"
  3384. "@see isImageMounted()\n")
  3385. {
  3386. if (image && slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3387. NetStringHandle team;
  3388. if (skinTag[0] == StringTagPrefixByte)
  3389. team = NetStringHandle(U32(dAtoi(skinTag+1)));
  3390. return object->mountImage( image, slot, loaded, team );
  3391. }
  3392. return false;
  3393. }
  3394. DefineEngineMethod( ShapeBase, unmountImage, bool, ( S32 slot ),,
  3395. "@brief Unmount the mounted Image in the specified slot.\n\n"
  3396. "@param slot Image slot to unmount\n"
  3397. "@return true if successful, false if failed\n\n"
  3398. "@see mountImage()\n")
  3399. {
  3400. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3401. return object->unmountImage(slot);
  3402. return false;
  3403. }
  3404. DefineEngineMethod( ShapeBase, getMountedImage, S32, ( S32 slot ),,
  3405. "@brief Get the Image mounted in the specified slot.\n\n"
  3406. "@param slot Image slot to query\n"
  3407. "@return ID of the ShapeBaseImageData datablock mounted in the slot, or 0 "
  3408. "if no Image is mounted there.\n\n" )
  3409. {
  3410. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3411. if (ShapeBaseImageData* data = object->getMountedImage(slot))
  3412. return data->getId();
  3413. return 0;
  3414. }
  3415. DefineEngineMethod( ShapeBase, getPendingImage, S32, ( S32 slot ),,
  3416. "@brief Get the Image that will be mounted next in the specified slot.\n\n"
  3417. "Calling mountImage when an Image is already mounted does one of two things: "
  3418. "<ol><li>Mount the new Image immediately, the old Image is discarded and "
  3419. "whatever state it was in is ignored.</li>"
  3420. "<li>If the current Image state does not allow Image changes, the new "
  3421. "Image is marked as pending, and will not be mounted until the current "
  3422. "state completes. eg. if the user changes weapons, you may wish to ensure "
  3423. "that the current weapon firing state plays to completion first.</li></ol>\n"
  3424. "This command retrieves the ID of the pending Image (2nd case above).\n"
  3425. "@param slot Image slot to query\n"
  3426. "@return ID of the pending ShapeBaseImageData datablock, or 0 if none.\n\n" )
  3427. {
  3428. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3429. if (ShapeBaseImageData* data = object->getPendingImage(slot))
  3430. return data->getId();
  3431. return 0;
  3432. }
  3433. DefineEngineMethod( ShapeBase, isImageFiring, bool, ( S32 slot ),,
  3434. "@brief Check if the current Image state is firing.\n\n"
  3435. "@param slot Image slot to query\n"
  3436. "@return true if the current Image state in this slot has the 'stateFire' flag set.\n" )
  3437. {
  3438. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3439. return object->isImageFiring(slot);
  3440. return false;
  3441. }
  3442. DefineEngineMethod( ShapeBase, isImageMounted, bool, ( ShapeBaseImageData* image ),,
  3443. "@brief Check if the given datablock is mounted to any slot on this object.\n\n"
  3444. "@param image ShapeBaseImageData datablock to query\n"
  3445. "@return true if the Image is mounted to any slot, false otherwise.\n\n" )
  3446. {
  3447. return (image && object->isImageMounted(image));
  3448. }
  3449. DefineEngineMethod( ShapeBase, getMountSlot, S32, ( ShapeBaseImageData* image ),,
  3450. "@brief Get the first slot the given datablock is mounted to on this object.\n\n"
  3451. "@param image ShapeBaseImageData datablock to query\n"
  3452. "@return index of the first slot the Image is mounted in, or -1 if the Image "
  3453. "is not mounted in any slot on this object.\n\n" )
  3454. {
  3455. return image ? object->getMountSlot(image) : -1;
  3456. }
  3457. DefineEngineMethod( ShapeBase, getImageSkinTag, S32, ( S32 slot ),,
  3458. "@brief Get the skin tag ID for the Image mounted in the specified slot.\n\n"
  3459. "@param slot Image slot to query\n"
  3460. "@return the skinTag value passed to mountImage when the image was "
  3461. "mounted\n\n" )
  3462. {
  3463. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3464. return object->getImageSkinTag(slot).getIndex();
  3465. return -1;
  3466. }
  3467. DefineEngineMethod( ShapeBase, getImageState, const char*, ( S32 slot ),,
  3468. "@brief Get the name of the current state of the Image in the specified slot.\n\n"
  3469. "@param slot Image slot to query\n"
  3470. "@return name of the current Image state, or \"Error\" if slot is invalid\n\n" )
  3471. {
  3472. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3473. return object->getImageState(slot);
  3474. return "Error";
  3475. }
  3476. DefineEngineMethod( ShapeBase, hasImageState, bool, ( S32 slot, const char* state ),,
  3477. "@brief Check if the given state exists on the mounted Image.\n\n"
  3478. "@param slot Image slot to query\n"
  3479. "@param state Image state to check for\n"
  3480. "@return true if the Image has the requested state defined.\n\n" )
  3481. {
  3482. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3483. return object->hasImageState(slot, state);
  3484. return false;
  3485. }
  3486. DefineEngineMethod( ShapeBase, getImageTrigger, bool, ( S32 slot ),,
  3487. "@brief Get the trigger state of the Image mounted in the specified slot.\n\n"
  3488. "@param slot Image slot to query\n"
  3489. "@return the Image's current trigger state\n\n" )
  3490. {
  3491. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3492. return object->getImageTriggerState(slot);
  3493. return false;
  3494. }
  3495. DefineEngineMethod( ShapeBase, setImageTrigger, bool, ( S32 slot, bool state ),,
  3496. "@brief Set the trigger state of the Image mounted in the specified slot.\n\n"
  3497. "@param slot Image slot to modify\n"
  3498. "@param state new trigger state for the Image\n"
  3499. "@return the Image's new trigger state\n\n" )
  3500. {
  3501. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3502. object->setImageTriggerState(slot,state);
  3503. return object->getImageTriggerState(slot);
  3504. }
  3505. return false;
  3506. }
  3507. DefineEngineMethod( ShapeBase, getImageGenericTrigger, bool, ( S32 slot, S32 trigger ),,
  3508. "@brief Get the generic trigger state of the Image mounted in the specified slot.\n\n"
  3509. "@param slot Image slot to query\n"
  3510. "@param trigger Generic trigger number\n"
  3511. "@return the Image's current generic trigger state\n\n" )
  3512. {
  3513. if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers)
  3514. return object->getImageGenericTriggerState(slot, trigger);
  3515. return false;
  3516. }
  3517. DefineEngineMethod( ShapeBase, setImageGenericTrigger, S32, ( S32 slot, S32 trigger, bool state ),,
  3518. "@brief Set the generic trigger state of the Image mounted in the specified slot.\n\n"
  3519. "@param slot Image slot to modify\n"
  3520. "@param trigger Generic trigger number\n"
  3521. "@param state new generic trigger state for the Image\n"
  3522. "@return the Image's new generic trigger state or -1 if there was a problem.\n\n" )
  3523. {
  3524. if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers) {
  3525. object->setImageGenericTriggerState(slot,trigger,state);
  3526. return object->getImageGenericTriggerState(slot,trigger);
  3527. }
  3528. return -1;
  3529. }
  3530. DefineEngineMethod( ShapeBase, getImageAltTrigger, bool, ( S32 slot ),,
  3531. "@brief Get the alt trigger state of the Image mounted in the specified slot.\n\n"
  3532. "@param slot Image slot to query\n"
  3533. "@return the Image's current alt trigger state\n\n" )
  3534. {
  3535. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3536. return object->getImageAltTriggerState(slot);
  3537. return false;
  3538. }
  3539. DefineEngineMethod( ShapeBase, setImageAltTrigger, bool, ( S32 slot, bool state ),,
  3540. "@brief Set the alt trigger state of the Image mounted in the specified slot.\n\n"
  3541. "@param slot Image slot to modify\n"
  3542. "@param state new alt trigger state for the Image\n"
  3543. "@return the Image's new alt trigger state\n\n" )
  3544. {
  3545. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3546. object->setImageAltTriggerState(slot,state);
  3547. return object->getImageAltTriggerState(slot);
  3548. }
  3549. return false;
  3550. }
  3551. DefineEngineMethod( ShapeBase, getImageAmmo, bool, ( S32 slot ),,
  3552. "@brief Get the ammo state of the Image mounted in the specified slot.\n\n"
  3553. "@param slot Image slot to query\n"
  3554. "@return the Image's current ammo state\n\n" )
  3555. {
  3556. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3557. return object->getImageAmmoState(slot);
  3558. return false;
  3559. }
  3560. DefineEngineMethod( ShapeBase, setImageAmmo, bool, ( S32 slot, bool state ),,
  3561. "@brief Set the ammo state of the Image mounted in the specified slot.\n\n"
  3562. "@param slot Image slot to modify\n"
  3563. "@param state new ammo state for the Image\n"
  3564. "@return the Image's new ammo state\n\n" )
  3565. {
  3566. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3567. object->setImageAmmoState(slot,state);
  3568. return state;
  3569. }
  3570. return false;
  3571. }
  3572. DefineEngineMethod( ShapeBase, getImageLoaded, bool, ( S32 slot ),,
  3573. "@brief Get the loaded state of the Image mounted in the specified slot.\n\n"
  3574. "@param slot Image slot to query\n"
  3575. "@return the Image's current loaded state\n\n" )
  3576. {
  3577. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3578. return object->getImageLoadedState(slot);
  3579. return false;
  3580. }
  3581. DefineEngineMethod( ShapeBase, setImageLoaded, bool, ( S32 slot, bool state ),,
  3582. "@brief Set the loaded state of the Image mounted in the specified slot.\n\n"
  3583. "@param slot Image slot to modify\n"
  3584. "@param state new loaded state for the Image\n"
  3585. "@return the Image's new loaded state\n\n" )
  3586. {
  3587. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3588. object->setImageLoadedState(slot, state);
  3589. return state;
  3590. }
  3591. return false;
  3592. }
  3593. DefineEngineMethod( ShapeBase, getImageTarget, bool, ( S32 slot ),,
  3594. "@brief Get the target state of the Image mounted in the specified slot.\n\n"
  3595. "@param slot Image slot to query\n"
  3596. "@return the Image's current target state\n\n" )
  3597. {
  3598. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3599. return object->getImageTargetState(slot);
  3600. return false;
  3601. }
  3602. DefineEngineMethod( ShapeBase, setImageTarget, bool, ( S32 slot, bool state ),,
  3603. "@brief Set the target state of the Image mounted in the specified slot.\n\n"
  3604. "@param slot Image slot to modify\n"
  3605. "@param state new target state for the Image\n"
  3606. "@return the Image's new target state\n\n" )
  3607. {
  3608. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3609. object->setImageTargetState(slot,state);
  3610. return state;
  3611. }
  3612. return false;
  3613. }
  3614. DefineEngineMethod( ShapeBase, getImageScriptAnimPrefix, const char*, ( S32 slot ),,
  3615. "@brief Get the script animation prefix of the Image mounted in the specified slot.\n\n"
  3616. "@param slot Image slot to query\n"
  3617. "@return the Image's current script animation prefix\n\n" )
  3618. {
  3619. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3620. return object->getImageScriptAnimPrefix(slot).getString();
  3621. return "";
  3622. }
  3623. DefineEngineMethod( ShapeBase, setImageScriptAnimPrefix, void, ( S32 slot, const char* prefix ),,
  3624. "@brief Set the script animation prefix for the Image mounted in the specified slot.\n\n"
  3625. "This is used to further modify the prefix used when deciding which animation sequence to "
  3626. "play while this image is mounted.\n"
  3627. "@param slot Image slot to modify\n"
  3628. "@param prefix The prefix applied to the image\n" )
  3629. {
  3630. if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
  3631. NetStringHandle prefixHandle;
  3632. if (prefix[0] == StringTagPrefixByte)
  3633. prefixHandle = NetStringHandle(U32(dAtoi(prefix+1)));
  3634. object->setImageScriptAnimPrefix(slot, prefixHandle);
  3635. }
  3636. }
  3637. DefineEngineMethod( ShapeBase, getMuzzleVector, VectorF, ( S32 slot ),,
  3638. "@brief Get the muzzle vector of the Image mounted in the specified slot.\n\n"
  3639. "If the Image shape contains a node called 'muzzlePoint', then the muzzle "
  3640. "vector is the forward direction vector of that node's transform in world "
  3641. "space. If no such node is specified, the slot's mount node is used "
  3642. "instead.\n\n"
  3643. "If the correctMuzzleVector flag (correctMuzzleVectorTP in 3rd person) "
  3644. "is set in the Image, the muzzle vector is computed to point at whatever "
  3645. "object is right in front of the object's 'eye' node.\n"
  3646. "@param slot Image slot to query\n"
  3647. "@return the muzzle vector, or \"0 1 0\" if the slot is invalid\n\n" )
  3648. {
  3649. VectorF vec(0, 1, 0);
  3650. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3651. object->getMuzzleVector(slot, &vec);
  3652. return vec;
  3653. }
  3654. DefineEngineMethod( ShapeBase, getMuzzlePoint, Point3F, ( S32 slot ),,
  3655. "@brief Get the muzzle position of the Image mounted in the specified slot.\n\n"
  3656. "If the Image shape contains a node called 'muzzlePoint', then the muzzle "
  3657. "position is the position of that node in world space. If no such node "
  3658. "is specified, the slot's mount node is used instead.\n"
  3659. "@param slot Image slot to query\n"
  3660. "@return the muzzle position, or \"0 0 0\" if the slot is invalid\n\n" )
  3661. {
  3662. Point3F pos(0, 0, 0);
  3663. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3664. object->getMuzzlePoint(slot, &pos);
  3665. return pos;
  3666. }
  3667. DefineEngineMethod( ShapeBase, getSlotTransform, TransformF, ( S32 slot ),,
  3668. "@brief Get the world transform of the specified mount slot.\n\n"
  3669. "@param slot Image slot to query\n"
  3670. "@return the mount transform\n\n" )
  3671. {
  3672. MatrixF xf(true);
  3673. if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
  3674. object->getMountTransform( slot, MatrixF::Identity, &xf );
  3675. return xf;
  3676. }
  3677. //----------------------------------------------------------------------------
  3678. DefineEngineMethod( ShapeBase, getAIRepairPoint, Point3F, (),,
  3679. "@brief Get the position at which the AI should stand to repair things.\n\n"
  3680. "If the shape defines a node called \"AIRepairNode\", this method will "
  3681. "return the current world position of that node, otherwise \"0 0 0\".\n"
  3682. "@return the AI repair position\n\n" )
  3683. {
  3684. return object->getAIRepairPoint();
  3685. }
  3686. DefineEngineMethod( ShapeBase, getVelocity, VectorF, (),,
  3687. "@brief Get the object's current velocity.\n\n"
  3688. "@return the current velocity\n\n" )
  3689. {
  3690. return object->getVelocity();
  3691. }
  3692. DefineEngineMethod( ShapeBase, setVelocity, bool, ( Point3F vel ),,
  3693. "@brief Set the object's velocity.\n\n"
  3694. "@param vel new velocity for the object\n"
  3695. "@return true\n\n" )
  3696. {
  3697. object->setVelocity( vel );
  3698. return true;
  3699. }
  3700. DefineEngineMethod( ShapeBase, applyImpulse, bool, ( Point3F pos, Point3F vec ),,
  3701. "@brief Apply an impulse to the object.\n\n"
  3702. "@param pos world position of the impulse\n"
  3703. "@param vec impulse momentum (velocity * mass)\n"
  3704. "@return true\n\n" )
  3705. {
  3706. object->applyImpulse( pos, vec );
  3707. return true;
  3708. }
  3709. DefineEngineMethod( ShapeBase, getEyeVector, VectorF, (),,
  3710. "@brief Get the forward direction of the 'eye' for this object.\n\n"
  3711. "If the object model has a node called 'eye', this method will return that "
  3712. "node's current forward direction vector, otherwise it will return the "
  3713. "object's current forward direction vector.\n"
  3714. "@return the eye vector for this object\n"
  3715. "@see getEyePoint\n"
  3716. "@see getEyeTransform\n" )
  3717. {
  3718. MatrixF mat;
  3719. object->getEyeTransform(&mat);
  3720. return mat.getForwardVector();
  3721. }
  3722. DefineEngineMethod( ShapeBase, getEyePoint, Point3F, (),,
  3723. "@brief Get the position of the 'eye' for this object.\n\n"
  3724. "If the object model has a node called 'eye', this method will return that "
  3725. "node's current world position, otherwise it will return the object's current "
  3726. "world position.\n"
  3727. "@return the eye position for this object\n"
  3728. "@see getEyeVector\n"
  3729. "@see getEyeTransform\n" )
  3730. {
  3731. MatrixF mat;
  3732. object->getEyeTransform(&mat);
  3733. return mat.getPosition();
  3734. }
  3735. DefineEngineMethod( ShapeBase, getEyeTransform, TransformF, (),,
  3736. "@brief Get the 'eye' transform for this object.\n\n"
  3737. "If the object model has a node called 'eye', this method will return that "
  3738. "node's current transform, otherwise it will return the object's current "
  3739. "transform.\n"
  3740. "@return the eye transform for this object\n"
  3741. "@see getEyeVector\n"
  3742. "@see getEyePoint\n" )
  3743. {
  3744. MatrixF mat;
  3745. object->getEyeTransform(&mat);
  3746. return mat;
  3747. }
  3748. DefineEngineMethod( ShapeBase, getLookAtPoint, const char*, ( F32 distance, U32 typeMask ), ( 2000, 0xFFFFFFFF ),
  3749. "@brief Get the world position this object is looking at.\n\n"
  3750. "Casts a ray from the eye and returns information about what the ray hits.\n"
  3751. "@param distance maximum distance of the raycast\n"
  3752. "@param typeMask typeMask of objects to include for raycast collision testing\n"
  3753. "@return look-at information as \"Object HitX HitY HitZ [Material]\" or empty string for no hit\n\n"
  3754. "@tsexample\n"
  3755. "%lookat = %obj.getLookAtPoint();\n"
  3756. "echo( \"Looking at: \" @ getWords( %lookat, 1, 3 ) );\n"
  3757. "@endtsexample\n" )
  3758. {
  3759. MatrixF mat;
  3760. object->getEyeTransform( &mat );
  3761. // Get eye vector.
  3762. VectorF eyeVector;
  3763. mat.getColumn( 1, &eyeVector );
  3764. // Get eye position.
  3765. VectorF eyePos;
  3766. mat.getColumn( 3, &eyePos );
  3767. // Make sure the eye vector covers the distance.
  3768. eyeVector *= distance;
  3769. // Do a container search.
  3770. VectorF start = eyePos;
  3771. VectorF end = eyePos + eyeVector;
  3772. RayInfo ri;
  3773. if( !gServerContainer.castRay( start, end, typeMask, &ri ) || !ri.object )
  3774. return ""; // No hit.
  3775. // Gather hit info.
  3776. enum { BUFFER_SIZE = 256 };
  3777. char* buffer = Con::getReturnBuffer( BUFFER_SIZE );
  3778. if( ri.material )
  3779. dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f %u",
  3780. ri.object->getId(),
  3781. ri.point.x,
  3782. ri.point.y,
  3783. ri.point.z,
  3784. ri.material->getMaterial()->getId() );
  3785. else
  3786. dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f",
  3787. ri.object->getId(),
  3788. ri.point.x,
  3789. ri.point.y,
  3790. ri.point.z );
  3791. return buffer;
  3792. }
  3793. DefineEngineMethod( ShapeBase, setEnergyLevel, void, ( F32 level ),,
  3794. "@brief Set this object's current energy level.\n\n"
  3795. "@param level new energy level\n"
  3796. "@see getEnergyLevel()\n"
  3797. "@see getEnergyPercent()\n")
  3798. {
  3799. object->setEnergyLevel( level );
  3800. }
  3801. DefineEngineMethod( ShapeBase, getEnergyLevel, F32, (),,
  3802. "@brief Get the object's current energy level.\n\n"
  3803. "@return energy level\n"
  3804. "@see setEnergyLevel()\n")
  3805. {
  3806. return object->getEnergyLevel();
  3807. }
  3808. DefineEngineMethod( ShapeBase, getEnergyPercent, F32, (),,
  3809. "@brief Get the object's current energy level as a percentage of maxEnergy.\n\n"
  3810. "@return energyLevel / datablock.maxEnergy\n"
  3811. "@see setEnergyLevel()\n")
  3812. {
  3813. return object->getEnergyValue();
  3814. }
  3815. DefineEngineMethod( ShapeBase, setDamageLevel, void, ( F32 level ),,
  3816. "@brief Set the object's current damage level.\n\n"
  3817. "@param level new damage level\n"
  3818. "@see getDamageLevel()\n"
  3819. "@see getDamagePercent()\n")
  3820. {
  3821. object->setDamageLevel( level );
  3822. }
  3823. DefineEngineMethod( ShapeBase, getDamageLevel, F32, (),,
  3824. "@brief Get the object's current damage level.\n\n"
  3825. "@return damage level\n"
  3826. "@see setDamageLevel()\n")
  3827. {
  3828. return object->getDamageLevel();
  3829. }
  3830. DefineEngineMethod( ShapeBase, getDamagePercent, F32, (),,
  3831. "@brief Get the object's current damage level as a percentage of maxDamage.\n\n"
  3832. "@return damageLevel / datablock.maxDamage\n"
  3833. "@see setDamageLevel()\n")
  3834. {
  3835. return object->getDamageValue();
  3836. }
  3837. DefineEngineMethod(ShapeBase, getMaxDamage, F32, (),,
  3838. "Get the object's maxDamage level.\n"
  3839. "@return datablock.maxDamage\n")
  3840. {
  3841. return object->getMaxDamage();
  3842. }
  3843. DefineEngineMethod( ShapeBase, setDamageState, bool, ( const char* state ),,
  3844. "@brief Set the object's damage state.\n\n"
  3845. "@param state should be one of \"Enabled\", \"Disabled\", \"Destroyed\"\n"
  3846. "@return true if successful, false if failed\n"
  3847. "@see getDamageState()\n")
  3848. {
  3849. return object->setDamageState( state );
  3850. }
  3851. DefineEngineMethod( ShapeBase, getDamageState, const char*, (),,
  3852. "@brief Get the object's damage state.\n\n"
  3853. "@return the damage state; one of \"Enabled\", \"Disabled\", \"Destroyed\"\n"
  3854. "@see setDamageState()\n")
  3855. {
  3856. return object->getDamageStateName();
  3857. }
  3858. DefineEngineMethod( ShapeBase, isDestroyed, bool, (),,
  3859. "@brief Check if the object is in the Destroyed damage state.\n\n"
  3860. "@return true if damage state is \"Destroyed\", false if not\n"
  3861. "@see isDisabled()\n"
  3862. "@see isEnabled()\n")
  3863. {
  3864. return object->isDestroyed();
  3865. }
  3866. DefineEngineMethod( ShapeBase, isDisabled, bool, (),,
  3867. "@brief Check if the object is in the Disabled or Destroyed damage state.\n\n"
  3868. "@return true if damage state is not \"Enabled\", false if it is\n"
  3869. "@see isDestroyed()\n"
  3870. "@see isEnabled()\n")
  3871. {
  3872. return object->getDamageState() != ShapeBase::Enabled;
  3873. }
  3874. DefineEngineMethod( ShapeBase, isEnabled, bool, (),,
  3875. "@brief Check if the object is in the Enabled damage state.\n\n"
  3876. "@return true if damage state is \"Enabled\", false if not\n"
  3877. "@see isDestroyed()\n"
  3878. "@see isDisabled()\n")
  3879. {
  3880. return object->getDamageState() == ShapeBase::Enabled;
  3881. }
  3882. DefineEngineMethod(ShapeBase, blowUp, void, (),, "@brief Explodes an object into pieces.")
  3883. {
  3884. object->blowUp();
  3885. }
  3886. DefineEngineMethod( ShapeBase, applyDamage, void, ( F32 amount ),,
  3887. "@brief Increment the current damage level by the specified amount.\n\n"
  3888. "@param amount value to add to current damage level\n" )
  3889. {
  3890. object->applyDamage( amount );
  3891. }
  3892. DefineEngineMethod( ShapeBase, applyRepair, void, ( F32 amount ),,
  3893. "@brief Repair damage by the specified amount.\n\n"
  3894. "Note that the damage level is only reduced by repairRate per tick, so it may "
  3895. "take several ticks for the total repair to complete.\n"
  3896. "@param amount total repair value (subtracted from damage level over time)\n" )
  3897. {
  3898. object->applyRepair( amount );
  3899. }
  3900. DefineEngineMethod( ShapeBase, setRepairRate, void, ( F32 rate ),,
  3901. "@brief Set amount to repair damage by each tick.\n\n"
  3902. "Note that this value is separate to the repairRate field in ShapeBaseData. "
  3903. "This value will be subtracted from the damage level each tick, whereas the "
  3904. "ShapeBaseData field limits how much of the applyRepair value is subtracted "
  3905. "each tick. Both repair types can be active at the same time.\n"
  3906. "@param rate value to subtract from damage level each tick (must be > 0)\n"
  3907. "@see getRepairRate()\n")
  3908. {
  3909. if(rate < 0)
  3910. rate = 0;
  3911. object->setRepairRate( rate );
  3912. }
  3913. DefineEngineMethod( ShapeBase, getRepairRate, F32, (),,
  3914. "@brief Get the per-tick repair amount.\n\n"
  3915. "@return the current value to be subtracted from damage level each tick\n"
  3916. "@see setRepairRate\n" )
  3917. {
  3918. return object->getRepairRate();
  3919. }
  3920. DefineEngineMethod( ShapeBase, setRechargeRate, void, ( F32 rate ),,
  3921. "@brief Set the recharge rate.\n\n"
  3922. "The recharge rate is added to the object's current energy level each tick, "
  3923. "up to the maxEnergy level set in the ShapeBaseData datablock.\n"
  3924. "@param rate the recharge rate (per tick)\n"
  3925. "@see getRechargeRate()\n")
  3926. {
  3927. object->setRechargeRate( rate );
  3928. }
  3929. DefineEngineMethod( ShapeBase, getRechargeRate, F32, (),,
  3930. "@brief Get the current recharge rate.\n\n"
  3931. "@return the recharge rate (per tick)\n"
  3932. "@see setRechargeRate()\n")
  3933. {
  3934. return object->getRechargeRate();
  3935. }
  3936. DefineEngineMethod( ShapeBase, getControllingClient, S32, (),,
  3937. "@brief Get the client (if any) that controls this object.\n\n"
  3938. "The controlling client is the one that will send moves to us to act on.\n"
  3939. "@return the ID of the controlling GameConnection, or 0 if this object is not "
  3940. "controlled by any client.\n"
  3941. "@see GameConnection\n")
  3942. {
  3943. if (GameConnection* con = object->getControllingClient())
  3944. return con->getId();
  3945. return 0;
  3946. }
  3947. DefineEngineMethod( ShapeBase, getControllingObject, S32, (),,
  3948. "@brief Get the object (if any) that controls this object.\n\n"
  3949. "@return the ID of the controlling ShapeBase object, or 0 if this object is "
  3950. "not controlled by another object.\n" )
  3951. {
  3952. if (ShapeBase* con = object->getControllingObject())
  3953. return con->getId();
  3954. return 0;
  3955. }
  3956. DefineEngineMethod( ShapeBase, canCloak, bool, (),,
  3957. "@brief Check if this object can cloak.\n\n"
  3958. "@return true\n"
  3959. "@note Not implemented as it always returns true.")
  3960. {
  3961. return true;
  3962. }
  3963. DefineEngineMethod( ShapeBase, setCloaked, void, ( bool cloak ),,
  3964. "@brief Set the cloaked state of this object.\n\n"
  3965. "When an object is cloaked it is not rendered.\n"
  3966. "@param cloak true to cloak the object, false to uncloak\n"
  3967. "@see isCloaked()\n")
  3968. {
  3969. if (object->isServerObject())
  3970. object->setCloakedState( cloak );
  3971. }
  3972. DefineEngineMethod( ShapeBase, isCloaked, bool, (),,
  3973. "@brief Check if this object is cloaked.\n\n"
  3974. "@return true if cloaked, false if not\n"
  3975. "@see setCloaked()\n")
  3976. {
  3977. return object->getCloakedState();
  3978. }
  3979. DefineEngineMethod( ShapeBase, setDamageFlash, void, ( F32 level ),,
  3980. "@brief Set the damage flash level.\n\n"
  3981. "Damage flash may be used as a postfx effect to flash the screen when the "
  3982. "client is damaged.\n"
  3983. "@note Relies on the flash postFx.\n"
  3984. "@param level flash level (0-1)\n"
  3985. "@see getDamageFlash()\n")
  3986. {
  3987. if (object->isServerObject())
  3988. object->setDamageFlash( level );
  3989. }
  3990. DefineEngineMethod( ShapeBase, getDamageFlash, F32, (),,
  3991. "@brief Get the damage flash level.\n\n"
  3992. "@return flash level\n"
  3993. "@see setDamageFlash\n" )
  3994. {
  3995. return object->getDamageFlash();
  3996. }
  3997. DefineEngineMethod( ShapeBase, setWhiteOut, void, ( F32 level ),,
  3998. "@brief Set the white-out level.\n\n"
  3999. "White-out may be used as a postfx effect to brighten the screen in response "
  4000. "to a game event.\n"
  4001. "@note Relies on the flash postFx.\n"
  4002. "@param level flash level (0-1)\n"
  4003. "@see getWhiteOut()\n")
  4004. {
  4005. if (object->isServerObject())
  4006. object->setWhiteOut( level );
  4007. }
  4008. DefineEngineMethod( ShapeBase, getWhiteOut, F32, (),,
  4009. "@brief Get the white-out level.\n\n"
  4010. "@return white-out level\n"
  4011. "@see setWhiteOut\n" )
  4012. {
  4013. return object->getWhiteOut();
  4014. }
  4015. DefineEngineMethod( ShapeBase, getDefaultCameraFov, F32, (),,
  4016. "@brief Returns the default vertical field of view in degrees for this object if used as a camera.\n\n"
  4017. "@return Default FOV\n" )
  4018. {
  4019. if (object->isServerObject())
  4020. return object->getDefaultCameraFov();
  4021. return 0.0;
  4022. }
  4023. DefineEngineMethod( ShapeBase, getCameraFov, F32, (),,
  4024. "@brief Returns the vertical field of view in degrees for this object if used as a camera.\n\n"
  4025. "@return current FOV as defined in ShapeBaseData::cameraDefaultFov\n" )
  4026. {
  4027. if (object->isServerObject())
  4028. return object->getCameraFov();
  4029. return 0.0;
  4030. }
  4031. DefineEngineMethod( ShapeBase, setCameraFov, void, ( F32 fov ),,
  4032. "@brief Set the vertical field of view in degrees for this object if used as a camera.\n\n"
  4033. "@param fov new FOV value\n" )
  4034. {
  4035. if (object->isServerObject())
  4036. object->setCameraFov( fov );
  4037. }
  4038. DefineEngineMethod( ShapeBase, startFade, void, ( S32 time, S32 delay, bool fadeOut ),,
  4039. "@brief Fade the object in or out without removing it from the scene.\n\n"
  4040. "A faded out object is still in the scene and can still be collided with, "
  4041. "so if you want to disable collisions for this shape after it fades out "
  4042. "use setHidden to temporarily remove this shape from the scene.\n"
  4043. "@note Items have the ability to light their surroundings. When an Item with "
  4044. "an active light is fading out, the light it emits is correspondingly "
  4045. "reduced until it goes out. Likewise, when the item fades in, the light is "
  4046. "turned-up till it reaches it's normal brightntess.\n"
  4047. "@param time duration of the fade effect in ms\n"
  4048. "@param delay delay in ms before the fade effect begins\n"
  4049. "@param fadeOut true to fade-out to invisible, false to fade-in to full visibility\n" )
  4050. {
  4051. object->startFade( (F32)time / (F32)1000.0, delay / 1000.0, fadeOut );
  4052. }
  4053. DefineEngineMethod( ShapeBase, setDamageVector, void, ( Point3F vec ),,
  4054. "@brief Set the damage direction vector.\n\n"
  4055. "Currently this is only used to initialise the explosion if this object "
  4056. "is blown up.\n"
  4057. "@param vec damage direction vector\n\n"
  4058. "@tsexample\n"
  4059. "%obj.setDamageVector( \"0 0 1\" );\n"
  4060. "@endtsexample\n" )
  4061. {
  4062. vec.normalize();
  4063. object->setDamageDir( vec );
  4064. }
  4065. DefineEngineMethod( ShapeBase, setShapeName, void, ( const char* name ),,
  4066. "@brief Set the name of this shape.\n\n"
  4067. "@note This is the name of the shape object that is sent to the client, "
  4068. "not the DTS or DAE model filename.\n"
  4069. "@param name new name for the shape\n\n"
  4070. "@see getShapeName()\n")
  4071. {
  4072. object->setShapeName( name );
  4073. }
  4074. DefineEngineMethod( ShapeBase, getShapeName, const char*, (),,
  4075. "@brief Get the name of the shape.\n\n"
  4076. "@note This is the name of the shape object that is sent to the client, "
  4077. "not the DTS or DAE model filename.\n"
  4078. "@return the name of the shape\n\n"
  4079. "@see setShapeName()\n")
  4080. {
  4081. return object->getShapeName();
  4082. }
  4083. DefineEngineMethod( ShapeBase, setSkinName, void, ( const char* name ),,
  4084. "@brief Apply a new skin to this shape.\n\n"
  4085. "'Skinning' the shape effectively renames the material targets, allowing "
  4086. "different materials to be used on different instances of the same model.\n\n"
  4087. "@param name name of the skin to apply\n\n"
  4088. "@see skin\n"
  4089. "@see getSkinName()\n")
  4090. {
  4091. object->setSkinName( name );
  4092. }
  4093. DefineEngineMethod( ShapeBase, getSkinName, const char*, (),,
  4094. "@brief Get the name of the skin applied to this shape.\n\n"
  4095. "@return the name of the skin\n\n"
  4096. "@see skin\n"
  4097. "@see setSkinName()\n")
  4098. {
  4099. return object->getSkinName();
  4100. }
  4101. //----------------------------------------------------------------------------
  4102. void ShapeBase::consoleInit()
  4103. {
  4104. Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec, "Speed to reduce the damage flash effect per tick.\n\n"
  4105. "@see ShapeBase::setDamageFlash()\n"
  4106. "@see ShapeBase::getDamageFlash()\n"
  4107. "@note Relies on the flash postFx.\n"
  4108. "@ingroup gameObjects\n");
  4109. Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec, "Speed to reduce the whiteout effect per tick.\n\n"
  4110. "@see ShapeBase::setWhiteOut()\n"
  4111. "@see ShapeBase::getWhiteOut"
  4112. "@note Relies on the flash postFx.\n"
  4113. "@ingroup gameObjects\n");
  4114. Con::addVariable("SB::FullCorrectionDistance", TypeF32, &sFullCorrectionDistance,
  4115. "@brief Distance at which a weapon's muzzle vector is fully corrected to match where the player is looking.\n\n"
  4116. "When a weapon image has correctMuzzleVector set and the Player is in 1st person, the muzzle vector from the "
  4117. "weapon is modified to match where the player is looking. Beyond the FullCorrectionDistance the muzzle vector "
  4118. "is always corrected. Between FullCorrectionDistance and the player, the weapon's muzzle vector is adjusted so that "
  4119. "the closer the aim point is to the player, the closer the muzzle vector is to the true (non-corrected) one.\n"
  4120. "@ingroup gameObjects\n");
  4121. Con::addVariable("SB::CloakSpeed", TypeF32, &sCloakSpeed,
  4122. "@brief Time to cloak, in seconds.\n\n"
  4123. "@ingroup gameObjects\n");
  4124. }
  4125. void ShapeBase::_updateHiddenMeshes()
  4126. {
  4127. if ( !mShapeInstance )
  4128. return;
  4129. // This may happen at some point in the future... lets
  4130. // detect it so that it can be fixed at that time.
  4131. AssertFatal( mMeshHidden.getSize() == mShapeInstance->mMeshObjects.size(),
  4132. "ShapeBase::_updateMeshVisibility() - Mesh visibility size mismatch!" );
  4133. for ( U32 i = 0; i < mMeshHidden.getSize(); i++ )
  4134. setMeshHidden( i, mMeshHidden.test( i ) );
  4135. }
  4136. void ShapeBase::setMeshHidden( const char *meshName, bool forceHidden )
  4137. {
  4138. setMeshHidden( mDataBlock->mShape->findObject( meshName ), forceHidden );
  4139. }
  4140. void ShapeBase::setMeshHidden( S32 meshIndex, bool forceHidden )
  4141. {
  4142. if ( meshIndex == -1 || meshIndex >= mMeshHidden.getSize() )
  4143. return;
  4144. if ( forceHidden )
  4145. mMeshHidden.set( meshIndex );
  4146. else
  4147. mMeshHidden.clear( meshIndex );
  4148. if ( mShapeInstance )
  4149. mShapeInstance->setMeshForceHidden( meshIndex, forceHidden );
  4150. setMaskBits( MeshHiddenMask );
  4151. }
  4152. void ShapeBase::setAllMeshesHidden( bool forceHidden )
  4153. {
  4154. if ( forceHidden )
  4155. mMeshHidden.set();
  4156. else
  4157. mMeshHidden.clear();
  4158. if ( mShapeInstance )
  4159. {
  4160. for ( U32 i = 0; i < mMeshHidden.getSize(); i++ )
  4161. mShapeInstance->setMeshForceHidden( i, forceHidden );
  4162. }
  4163. setMaskBits( MeshHiddenMask );
  4164. }
  4165. DefineEngineMethod( ShapeBase, setAllMeshesHidden, void, ( bool hide ),,
  4166. "@brief Set the hidden state on all the shape meshes.\n\n"
  4167. "This allows you to hide all meshes in the shape, for example, and then only "
  4168. "enable a few.\n"
  4169. "@param hide new hidden state for all meshes\n\n" )
  4170. {
  4171. object->setAllMeshesHidden( hide );
  4172. }
  4173. DefineEngineMethod( ShapeBase, setMeshHidden, void, ( const char* name, bool hide ),,
  4174. "@brief Set the hidden state on the named shape mesh.\n\n"
  4175. "@param name name of the mesh to hide/show\n"
  4176. "@param hide new hidden state for the mesh\n\n" )
  4177. {
  4178. object->setMeshHidden( name, hide );
  4179. }
  4180. // Some development-handy functions
  4181. #ifndef TORQUE_SHIPPING
  4182. void ShapeBase::dumpMeshVisibility()
  4183. {
  4184. if ( !mShapeInstance )
  4185. return;
  4186. const Vector<TSShapeInstance::MeshObjectInstance> &meshes = mShapeInstance->mMeshObjects;
  4187. for ( U32 i = 0; i < meshes.size(); i++)
  4188. {
  4189. const TSShapeInstance::MeshObjectInstance &mesh = meshes[i];
  4190. const String &meshName = mDataBlock->mShape->getMeshName( i );
  4191. Con::printf( "%d - %s - forceHidden = %s, visibility = %f",
  4192. i,
  4193. meshName.c_str(),
  4194. mesh.forceHidden ? "true" : "false",
  4195. mesh.visible );
  4196. }
  4197. }
  4198. DefineEngineMethod( ShapeBase, dumpMeshVisibility, void, (),,
  4199. "@brief Print a list of visible and hidden meshes in the shape to the console "
  4200. "for debugging purposes.\n\n"
  4201. "@note Only in a SHIPPING build.\n")
  4202. {
  4203. object->dumpMeshVisibility();
  4204. }
  4205. #endif // #ifndef TORQUE_SHIPPING
  4206. //------------------------------------------------------------------------
  4207. //These functions are duplicated in tsStatic and shapeBase.
  4208. //They each function a little differently; but achieve the same purpose of gathering
  4209. //target names/counts without polluting simObject.
  4210. DefineEngineMethod( ShapeBase, getTargetName, const char*, ( S32 index ),,
  4211. "@brief Get the name of the indexed shape material.\n\n"
  4212. "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n"
  4213. "@return the name of the indexed material.\n\n"
  4214. "@see getTargetCount()\n")
  4215. {
  4216. ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
  4217. if(obj)
  4218. {
  4219. // Try to use the client object (so we get the reskinned targets in the Material Editor)
  4220. if ((ShapeBase*)obj->getClientObject())
  4221. obj = (ShapeBase*)obj->getClientObject();
  4222. return obj->getShapeInstance()->getTargetName(index);
  4223. }
  4224. return "";
  4225. }
  4226. DefineEngineMethod( ShapeBase, getTargetCount, S32, (),,
  4227. "@brief Get the number of materials in the shape.\n\n"
  4228. "@return the number of materials in the shape.\n\n"
  4229. "@see getTargetName()\n")
  4230. {
  4231. ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
  4232. if(obj)
  4233. {
  4234. // Try to use the client object (so we get the reskinned targets in the Material Editor)
  4235. if ((ShapeBase*)obj->getClientObject())
  4236. obj = (ShapeBase*)obj->getClientObject();
  4237. if (obj->getShapeInstance() != NULL)
  4238. return obj->getShapeInstance()->getTargetCount();
  4239. }
  4240. return -1;
  4241. }
  4242. DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),,
  4243. "@brief Change one of the materials on the shape.\n\n"
  4244. "This method changes materials per mapTo with others. The material that "
  4245. "is being replaced is mapped to unmapped_mat as a part of this transition.\n"
  4246. "@note Warning, right now this only sort of works. It doesn't do a live "
  4247. "update like it should.\n"
  4248. "@param mapTo the name of the material target to remap (from getTargetName)\n"
  4249. "@param oldMat the old Material that was mapped \n"
  4250. "@param newMat the new Material to map\n\n"
  4251. "@tsexample\n"
  4252. "// remap the first material in the shape\n"
  4253. "%mapTo = %obj.getTargetName( 0 );\n"
  4254. "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n"
  4255. "@endtsexample\n" )
  4256. {
  4257. // if no valid new material, theres no reason for doing this
  4258. if( !newMat )
  4259. {
  4260. Con::errorf("ShapeBase::changeMaterial failed: New material does not exist!");
  4261. return;
  4262. }
  4263. // initilize server/client versions
  4264. ShapeBase *serverObj = object;
  4265. ShapeBase *clientObj = dynamic_cast< ShapeBase* > ( object->getClientObject() );
  4266. // Check the mapTo name exists for this shape
  4267. S32 matIndex = serverObj->getShape()->materialList->getMaterialNameList().find_next(String(mapTo));
  4268. if (matIndex < 0)
  4269. {
  4270. Con::errorf("ShapeBase::changeMaterial failed: Invalid mapTo name '%s'", mapTo);
  4271. return;
  4272. }
  4273. // Lets remap the old material off, so as to let room for our current material room to claim its spot
  4274. if( oldMat )
  4275. oldMat->mMapTo = String("unmapped_mat");
  4276. newMat->mMapTo = mapTo;
  4277. // Map the material by name in the matmgr
  4278. MATMGR->mapMaterial( mapTo, newMat->getName() );
  4279. // Replace instances with the new material being traded in. For ShapeBase
  4280. // class we have to update the server/client objects separately so both
  4281. // represent our changes
  4282. delete serverObj->getShape()->materialList->mMatInstList[matIndex];
  4283. serverObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance();
  4284. if (clientObj)
  4285. {
  4286. delete clientObj->getShape()->materialList->mMatInstList[matIndex];
  4287. clientObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance();
  4288. }
  4289. // Finish up preparing the material instances for rendering
  4290. const GFXVertexFormat *flags = getGFXVertexFormat<GFXVertexPNTTB>();
  4291. FeatureSet features = MATMGR->getDefaultFeatures();
  4292. serverObj->getShape()->materialList->getMaterialInst(matIndex)->init( features, flags );
  4293. if (clientObj)
  4294. clientObj->getShapeInstance()->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
  4295. }
  4296. DefineEngineMethod( ShapeBase, getModelFile, const char *, (),,
  4297. "@brief Get the model filename used by this shape.\n\n"
  4298. "@return the shape filename\n\n" )
  4299. {
  4300. GameBaseData * datablock = object->getDataBlock();
  4301. if( !datablock )
  4302. return String::EmptyString;
  4303. const char *fieldName = StringTable->insert( String("shapeFile") );
  4304. return datablock->getDataField( fieldName, NULL );
  4305. }
  4306. U32 ShapeBase::unique_anim_tag_counter = 1;
  4307. U32 ShapeBase::playBlendAnimation(S32 seq_id, F32 pos, F32 rate)
  4308. {
  4309. BlendThread blend_clip;
  4310. blend_clip.tag = ((unique_anim_tag_counter++) | BLENDED_CLIP);
  4311. blend_clip.thread = 0;
  4312. if (isClientObject())
  4313. {
  4314. blend_clip.thread = mShapeInstance->addThread();
  4315. mShapeInstance->setSequence(blend_clip.thread, seq_id, pos);
  4316. mShapeInstance->setTimeScale(blend_clip.thread, rate);
  4317. }
  4318. blend_clips.push_back(blend_clip);
  4319. return blend_clip.tag;
  4320. }
  4321. void ShapeBase::restoreBlendAnimation(U32 tag)
  4322. {
  4323. for (S32 i = 0; i < blend_clips.size(); i++)
  4324. {
  4325. if (blend_clips[i].tag == tag)
  4326. {
  4327. if (blend_clips[i].thread)
  4328. {
  4329. mShapeInstance->destroyThread(blend_clips[i].thread);
  4330. }
  4331. blend_clips.erase_fast(i);
  4332. break;
  4333. }
  4334. }
  4335. }
  4336. //
  4337. void ShapeBase::restoreAnimation(U32 tag)
  4338. {
  4339. if (!isClientObject())
  4340. return;
  4341. // check if this is a blended clip
  4342. if ((tag & BLENDED_CLIP) != 0)
  4343. {
  4344. restoreBlendAnimation(tag);
  4345. return;
  4346. }
  4347. if (tag != 0 && tag == last_anim_tag)
  4348. {
  4349. anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
  4350. stopThread(0);
  4351. if (saved_seq_id != -1)
  4352. {
  4353. setThreadSequence(0, saved_seq_id);
  4354. setThreadPosition(0, saved_pos);
  4355. setThreadTimeScale(0, saved_rate);
  4356. setThreadDir(0, (saved_rate >= 0));
  4357. playThread(0);
  4358. saved_seq_id = -1;
  4359. saved_pos = 0.0f;
  4360. saved_rate = 1.0f;
  4361. }
  4362. last_anim_tag = 0;
  4363. last_anim_id = -1;
  4364. }
  4365. }
  4366. U32 ShapeBase::getAnimationID(const char* name)
  4367. {
  4368. const TSShape* ts_shape = getShape();
  4369. S32 seq_id = (ts_shape) ? ts_shape->findSequence(name) : -1;
  4370. return (seq_id >= 0) ? (U32) seq_id : BAD_ANIM_ID;
  4371. }
  4372. U32 ShapeBase::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
  4373. {
  4374. if (!isClientObject())
  4375. return 0;
  4376. if (anim_id == BAD_ANIM_ID)
  4377. return 0;
  4378. const TSShape* ts_shape = getShape();
  4379. if (!ts_shape)
  4380. return 0;
  4381. S32 seq_id = (S32) anim_id;
  4382. if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
  4383. return playBlendAnimation(seq_id, pos, rate);
  4384. if (last_anim_tag == 0)
  4385. {
  4386. // try to save state of playing animation
  4387. Thread& st = mScriptThread[0];
  4388. if (st.sequence != -1)
  4389. {
  4390. saved_seq_id = st.sequence;
  4391. saved_pos = st.position;
  4392. saved_rate = st.timescale;
  4393. }
  4394. }
  4395. // START OR TRANSITION TO SEQUENCE HERE
  4396. setThreadSequence(0, seq_id);
  4397. setThreadPosition(0, pos);
  4398. setThreadTimeScale(0, rate);
  4399. setThreadDir(0, (rate >= 0));
  4400. playThread(0);
  4401. if (is_death_anim)
  4402. anim_clip_flags |= IS_DEATH_ANIM;
  4403. else
  4404. anim_clip_flags &= ~IS_DEATH_ANIM;
  4405. anim_clip_flags |= ANIM_OVERRIDDEN;
  4406. last_anim_tag = unique_anim_tag_counter++;
  4407. last_anim_id = anim_id;
  4408. return last_anim_tag;
  4409. }
  4410. F32 ShapeBase::getAnimationDurationByID(U32 anim_id)
  4411. {
  4412. if (anim_id == BAD_ANIM_ID)
  4413. return 0.0f;
  4414. S32 seq_id = (S32) anim_id;
  4415. if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
  4416. return mDataBlock->mShape->sequences[seq_id].duration;
  4417. return 0.0f;
  4418. }
  4419. bool ShapeBase::isBlendAnimation(const char* name)
  4420. {
  4421. U32 anim_id = getAnimationID(name);
  4422. if (anim_id == BAD_ANIM_ID)
  4423. return false;
  4424. S32 seq_id = (S32) anim_id;
  4425. if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
  4426. return mDataBlock->mShape->sequences[seq_id].isBlend();
  4427. return false;
  4428. }
  4429. const char* ShapeBase::getLastClipName(U32 clip_tag)
  4430. {
  4431. if (clip_tag != last_anim_tag)
  4432. return "";
  4433. S32 seq_id = (S32) last_anim_id;
  4434. S32 idx = mDataBlock->mShape->sequences[seq_id].nameIndex;
  4435. if (idx < 0 || idx >= mDataBlock->mShape->names.size())
  4436. return 0;
  4437. return mDataBlock->mShape->names[idx];
  4438. }
  4439. //
  4440. U32 ShapeBase::playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
  4441. {
  4442. return playAnimationByID(getAnimationID(name), pos, rate, trans, hold, wait, is_death_anim);
  4443. }
  4444. F32 ShapeBase::getAnimationDuration(const char* name)
  4445. {
  4446. return getAnimationDurationByID(getAnimationID(name));
  4447. }
  4448. void ShapeBase::setSelectionFlags(U8 flags)
  4449. {
  4450. Parent::setSelectionFlags(flags);
  4451. if (!mShapeInstance || !isClientObject())
  4452. return;
  4453. if (!mShapeInstance->ownMaterialList())
  4454. return;
  4455. TSMaterialList* pMatList = mShapeInstance->getMaterialList();
  4456. for (S32 j = 0; j < pMatList->size(); j++)
  4457. {
  4458. BaseMatInstance * bmi = pMatList->getMaterialInst(j);
  4459. bmi->setSelectionHighlighting(needsSelectionHighlighting());
  4460. }
  4461. }