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