turretShape.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "T3D/turret/turretShape.h"
  23. #include "console/console.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "core/stream/bitStream.h"
  27. #include "math/mMath.h"
  28. #include "math/mathIO.h"
  29. #include "ts/tsShapeInstance.h"
  30. #include "T3D/fx/cameraFXMgr.h"
  31. #include "T3D/gameBase/gameConnection.h"
  32. #include "T3D/physics/physicsBody.h"
  33. //----------------------------------------------------------------------------
  34. // Client prediction
  35. // Trigger objects that are not normally collided with.
  36. static U32 sTriggerMask = ItemObjectType |
  37. TriggerObjectType |
  38. CorpseObjectType;
  39. //----------------------------------------------------------------------------
  40. ImplementEnumType( TurretShapeFireLinkType,
  41. "@brief How the weapons are linked to triggers for this TurretShape.\n\n"
  42. "@ingroup gameObjects\n\n")
  43. { TurretShapeData::FireTogether, "FireTogether", "All weapons fire under trigger 0.\n" },
  44. { TurretShapeData::GroupedFire, "GroupedFire", "Weapon mounts 0,2 fire under trigger 0, mounts 1,3 fire under trigger 1.\n" },
  45. { TurretShapeData::IndividualFire, "IndividualFire", "Each weapon mount fires under its own trigger 0-3.\n" },
  46. EndImplementEnumType;
  47. IMPLEMENT_CO_DATABLOCK_V1(TurretShapeData);
  48. ConsoleDocClass( TurretShapeData,
  49. "@brief Defines properties for a TurretShape object.\n\n"
  50. "@see TurretShape\n"
  51. "@see TurretShapeData\n"
  52. "@ingroup gameObjects\n"
  53. );
  54. IMPLEMENT_CALLBACK( TurretShapeData, onMountObject, void, ( SceneObject* turret, SceneObject* obj, S32 node ),( turret, obj, node ),
  55. "@brief Informs the TurretShapeData object that a player is mounting it.\n\n"
  56. "@param turret The TurretShape object.\n"
  57. "@param obj The player that is mounting.\n"
  58. "@param node The node the player is mounting to.\n"
  59. "@note Server side only.\n"
  60. );
  61. IMPLEMENT_CALLBACK( TurretShapeData, onUnmountObject, void, ( SceneObject* turret, SceneObject* obj ),( turret, obj ),
  62. "@brief Informs the TurretShapeData object that a player is unmounting it.\n\n"
  63. "@param turret The TurretShape object.\n"
  64. "@param obj The player that is unmounting.\n"
  65. "@note Server side only.\n"
  66. );
  67. IMPLEMENT_CALLBACK( TurretShapeData, onStickyCollision, void, ( TurretShape* obj ),( obj ),
  68. "@brief Informs the TurretData object that it is now sticking to another object.\n\n"
  69. "This callback is only called if the TurretData::sticky property for this Turret is true.\n"
  70. "@param obj The Turret object that is colliding.\n"
  71. "@note Server side only.\n"
  72. "@see TurretShape, TurretData\n"
  73. );
  74. TurretShapeData::TurretShapeData()
  75. {
  76. weaponLinkType = FireTogether;
  77. shadowEnable = true;
  78. zRotOnly = false;
  79. startLoaded = true;
  80. friction = 0;
  81. elasticity = 0;
  82. sticky = false;
  83. gravityMod = 1.0;
  84. maxVelocity = 25.0f;
  85. density = 2;
  86. drag = 0.5;
  87. cameraOffset = 0;
  88. maxHeading = 180.0f;
  89. minPitch = 90.0f;
  90. maxPitch = 90.0f;
  91. headingRate = -1;
  92. pitchRate = -1;
  93. headingNode = -1;
  94. pitchNode = -1;
  95. for (U32 i=0; i<NumMirrorDirectionNodes; ++i)
  96. {
  97. pitchNodes[i] = -1;
  98. headingNodes[i] = -1;
  99. }
  100. for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
  101. {
  102. weaponMountNode[i] = -1;
  103. }
  104. }
  105. void TurretShapeData::initPersistFields()
  106. {
  107. addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData),
  108. "@brief Should the turret allow only z rotations.\n\n"
  109. "True indicates that the turret may only be rotated on its z axis, just like the Item class. "
  110. "This keeps the turret always upright regardless of the surface it lands on.\n");
  111. addField( "weaponLinkType", TYPEID< TurretShapeData::FireLinkType >(), Offset(weaponLinkType, TurretShapeData),
  112. "@brief Set how the mounted weapons are linked and triggered.\n\n"
  113. "<ul><li>FireTogether: All weapons fire under trigger 0.</li>"
  114. "<li>GroupedFire: Weapon mounts 0,2 fire under trigger 0, mounts 1,3 fire under trigger 1.</li>"
  115. "<li>IndividualFire: Each weapon mount fires under its own trigger 0-3.</li></ul>\n"
  116. "@see TurretShapeFireLinkType");
  117. addField("startLoaded", TypeBool, Offset(startLoaded, TurretShapeData),
  118. "@brief Does the turret's mounted weapon(s) start in a loaded state.\n\n"
  119. "True indicates that all mounted weapons start in a loaded state.\n"
  120. "@see ShapeBase::setImageLoaded()");
  121. addField("cameraOffset", TypeF32, Offset(cameraOffset, TurretShapeData),
  122. "Vertical (Z axis) height of the camera above the turret." );
  123. addField("maxHeading", TypeF32, Offset(maxHeading, TurretShapeData),
  124. "@brief Maximum number of degrees to rotate from center.\n\n"
  125. "A value of 180 or more degrees indicates the turret may rotate completely around.\n");
  126. addField("minPitch", TypeF32, Offset(minPitch, TurretShapeData),
  127. "@brief Minimum number of degrees to rotate down from straight ahead.\n\n");
  128. addField("maxPitch", TypeF32, Offset(maxPitch, TurretShapeData),
  129. "@brief Maximum number of degrees to rotate up from straight ahead.\n\n");
  130. addField("headingRate", TypeF32, Offset(headingRate, TurretShapeData),
  131. "@brief Degrees per second rotation.\n\n"
  132. "A value of 0 means no rotation is allowed. A value less than 0 means the rotation is instantaneous.\n");
  133. addField("pitchRate", TypeF32, Offset(pitchRate, TurretShapeData),
  134. "@brief Degrees per second rotation.\n\n"
  135. "A value of 0 means no rotation is allowed. A value less than 0 means the rotation is instantaneous.\n");
  136. Parent::initPersistFields();
  137. }
  138. void TurretShapeData::packData(BitStream* stream)
  139. {
  140. Parent::packData(stream);
  141. stream->writeFlag(zRotOnly);
  142. stream->writeInt(weaponLinkType,NumFireLinkTypeBits);
  143. stream->write(cameraOffset);
  144. stream->write(maxHeading);
  145. stream->write(minPitch);
  146. stream->write(maxPitch);
  147. stream->write(headingRate);
  148. stream->write(pitchRate);
  149. }
  150. void TurretShapeData::unpackData(BitStream* stream)
  151. {
  152. Parent::unpackData(stream);
  153. zRotOnly = stream->readFlag();
  154. weaponLinkType = (FireLinkType)stream->readInt(NumFireLinkTypeBits);
  155. stream->read(&cameraOffset);
  156. stream->read(&maxHeading);
  157. stream->read(&minPitch);
  158. stream->read(&maxPitch);
  159. stream->read(&headingRate);
  160. stream->read(&pitchRate);
  161. }
  162. bool TurretShapeData::preload(bool server, String &errorStr)
  163. {
  164. if (!Parent::preload(server, errorStr))
  165. return false;
  166. // We have mShape at this point. Resolve nodes.
  167. headingNode = mShape->findNode("heading");
  168. pitchNode = mShape->findNode("pitch");
  169. // Find any mirror pitch nodes
  170. for (U32 i = 0; i < NumMirrorDirectionNodes; ++i)
  171. {
  172. char name[32];
  173. dSprintf(name, 31, "pitch%d", i+1);
  174. pitchNodes[i] = mShape->findNode(name);
  175. dSprintf(name, 31, "heading%d", i+1);
  176. headingNodes[i] = mShape->findNode(name);
  177. }
  178. // Resolve weapon mount point node indexes
  179. for (U32 i = 0; i < ShapeBase::MaxMountedImages; i++) {
  180. char fullName[256];
  181. dSprintf(fullName,sizeof(fullName),"weaponMount%d",i);
  182. weaponMountNode[i] = mShape->findNode(fullName);
  183. }
  184. // Recoil animations
  185. recoilSequence[0] = mShape->findSequence("light_recoil");
  186. recoilSequence[1] = mShape->findSequence("medium_recoil");
  187. recoilSequence[2] = mShape->findSequence("heavy_recoil");
  188. // Optional sequences used when the turret rotates
  189. pitchSequence = mShape->findSequence("pitch");
  190. headingSequence = mShape->findSequence("heading");
  191. return true;
  192. }
  193. //----------------------------------------------------------------------------
  194. IMPLEMENT_CO_NETOBJECT_V1(TurretShape);
  195. ConsoleDocClass( TurretShape,
  196. "@ingroup gameObjects\n"
  197. );
  198. TurretShape::TurretShape()
  199. {
  200. mTypeMask |= VehicleObjectType | DynamicShapeObjectType;
  201. mDataBlock = 0;
  202. allowManualRotation = true;
  203. allowManualFire = true;
  204. mTurretDelta.rot = Point3F(0.0f, 0.0f, 0.0f);
  205. mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);
  206. mTurretDelta.dt = 1;
  207. mRot = mTurretDelta.rot;
  208. mPitchAllowed = true;
  209. mHeadingAllowed = true;
  210. mPitchRate = -1;
  211. mHeadingRate = -1;
  212. mPitchUp = 0;
  213. mPitchDown = 0;
  214. mHeadingMax = mDegToRad(180.0f);
  215. mRespawn = false;
  216. mPitchThread = 0;
  217. mHeadingThread = 0;
  218. mSubclassTurretShapeHandlesScene = false;
  219. // For the Item class
  220. mSubclassItemHandlesScene = true;
  221. }
  222. TurretShape::~TurretShape()
  223. {
  224. }
  225. //----------------------------------------------------------------------------
  226. void TurretShape::initPersistFields()
  227. {
  228. addField("respawn", TypeBool, Offset(mRespawn, TurretShape),
  229. "@brief Respawn the turret after it has been destroyed.\n\n"
  230. "If true, the turret will respawn after it is destroyed.\n");
  231. Parent::initPersistFields();
  232. }
  233. bool TurretShape::onAdd()
  234. {
  235. if( !Parent::onAdd() )
  236. return false;
  237. // Add this object to the scene
  238. if (!mSubclassTurretShapeHandlesScene)
  239. {
  240. addToScene();
  241. }
  242. if (isServerObject() && !mSubclassTurretShapeHandlesScene)
  243. {
  244. scriptOnAdd();
  245. }
  246. return true;
  247. }
  248. void TurretShape::onRemove()
  249. {
  250. Parent::onRemove();
  251. if (!mSubclassTurretShapeHandlesScene)
  252. {
  253. scriptOnRemove();
  254. // Remove this object from the scene
  255. removeFromScene();
  256. }
  257. }
  258. bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload)
  259. {
  260. mDataBlock = dynamic_cast<TurretShapeData*>(dptr);
  261. if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
  262. return false;
  263. // Mark these nodes for control by code only (will not animate in a sequence)
  264. if (mDataBlock->headingNode != -1)
  265. mShapeInstance->setNodeAnimationState(mDataBlock->headingNode, TSShapeInstance::MaskNodeHandsOff);
  266. if (mDataBlock->pitchNode != -1)
  267. mShapeInstance->setNodeAnimationState(mDataBlock->pitchNode, TSShapeInstance::MaskNodeHandsOff);
  268. for (U32 i=0; i<TurretShapeData::NumMirrorDirectionNodes; ++i)
  269. {
  270. if (mDataBlock->pitchNodes[i] != -1)
  271. {
  272. mShapeInstance->setNodeAnimationState(mDataBlock->pitchNodes[i], TSShapeInstance::MaskNodeHandsOff);
  273. }
  274. if (mDataBlock->headingNodes[i] != -1)
  275. {
  276. mShapeInstance->setNodeAnimationState(mDataBlock->headingNodes[i], TSShapeInstance::MaskNodeHandsOff);
  277. }
  278. }
  279. if (mIsZero(mDataBlock->pitchRate))
  280. {
  281. mPitchAllowed = false;
  282. }
  283. else
  284. {
  285. mPitchAllowed = true;
  286. if (mDataBlock->pitchRate > 0)
  287. {
  288. mPitchRate = mDegToRad(mDataBlock->pitchRate);
  289. }
  290. else
  291. {
  292. mPitchRate = -1;
  293. }
  294. }
  295. if (mIsZero(mDataBlock->headingRate))
  296. {
  297. mHeadingAllowed = false;
  298. }
  299. else
  300. {
  301. mHeadingAllowed = true;
  302. if (mDataBlock->headingRate > 0)
  303. {
  304. mHeadingRate = mDegToRad(mDataBlock->headingRate);
  305. }
  306. else
  307. {
  308. mHeadingRate = -1;
  309. }
  310. }
  311. mPitchUp = -mDegToRad(mDataBlock->maxPitch);
  312. mPitchDown = mDegToRad(mDataBlock->minPitch);
  313. mHeadingMax = mDegToRad(mDataBlock->maxHeading);
  314. // Create Recoil thread if any recoil sequences are specified.
  315. // Note that the server player does not play this animation.
  316. mRecoilThread = 0;
  317. if (isGhost())
  318. for (U32 s = 0; s < TurretShapeData::NumRecoilSequences; s++)
  319. if (mDataBlock->recoilSequence[s] != -1) {
  320. mRecoilThread = mShapeInstance->addThread();
  321. mShapeInstance->setSequence(mRecoilThread, mDataBlock->recoilSequence[s], 0);
  322. mShapeInstance->setTimeScale(mRecoilThread, 0);
  323. break;
  324. }
  325. // Reset the image state driven animation thread. This will be properly built
  326. // in onImageStateAnimation() when needed.
  327. mImageStateThread = 0;
  328. // Optional rotation threads. These only play on the client.
  329. mPitchThread = 0;
  330. mHeadingThread = 0;
  331. if (isGhost())
  332. {
  333. if (mDataBlock->pitchSequence != -1)
  334. {
  335. mPitchThread = mShapeInstance->addThread();
  336. mShapeInstance->setSequence(mPitchThread, mDataBlock->pitchSequence, 0);
  337. mShapeInstance->setTimeScale(mPitchThread, 0);
  338. }
  339. if (mDataBlock->headingSequence != -1)
  340. {
  341. mHeadingThread = mShapeInstance->addThread();
  342. mShapeInstance->setSequence(mHeadingThread, mDataBlock->headingSequence, 0);
  343. mShapeInstance->setTimeScale(mHeadingThread, 0);
  344. }
  345. }
  346. if (!mSubclassTurretShapeHandlesScene)
  347. {
  348. scriptOnNewDataBlock();
  349. }
  350. return true;
  351. }
  352. //----------------------------------------------------------------------------
  353. void TurretShape::updateAnimation(F32 dt)
  354. {
  355. if (mRecoilThread)
  356. mShapeInstance->advanceTime(dt,mRecoilThread);
  357. if (mImageStateThread)
  358. mShapeInstance->advanceTime(dt,mImageStateThread);
  359. // Update any pitch and heading threads
  360. if (mPitchThread)
  361. {
  362. F32 d = mPitchDown - mPitchUp;
  363. if (!mIsZero(d))
  364. {
  365. F32 pos = (mRot.x - mPitchUp) / d;
  366. mShapeInstance->setPos(mPitchThread, mClampF(pos, 0.0f, 1.0f));
  367. }
  368. }
  369. if (mHeadingThread)
  370. {
  371. F32 pos = 0.0f;
  372. if (mHeadingMax < mDegToRad(180.0f))
  373. {
  374. F32 d = mHeadingMax * 2.0f;
  375. if (!mIsZero(d))
  376. {
  377. pos = (mRot.z + mHeadingMax) / d;
  378. }
  379. }
  380. else
  381. {
  382. pos = mRot.z / M_2PI;
  383. if (pos < 0.0f)
  384. {
  385. // We don't want negative rotations to simply mirror the
  386. // positive rotations but to animate into them as if -0.0
  387. // is equivalent to 1.0.
  388. pos = mFmod(pos, 1.0f) + 1.0f;
  389. }
  390. if (pos > 1.0f)
  391. {
  392. pos = mFmod(pos, 1.0f);
  393. }
  394. }
  395. mShapeInstance->setPos(mHeadingThread, mClampF(pos, 0.0f, 1.0f));
  396. }
  397. }
  398. //----------------------------------------------------------------------------
  399. void TurretShape::onImage(U32 imageSlot, bool unmount)
  400. {
  401. // Clear out any previous image state animation
  402. if (mImageStateThread)
  403. {
  404. mShapeInstance->destroyThread(mImageStateThread);
  405. mImageStateThread = 0;
  406. }
  407. }
  408. void TurretShape::onImageRecoil( U32, ShapeBaseImageData::StateData::RecoilState state )
  409. {
  410. if ( mRecoilThread )
  411. {
  412. if ( state != ShapeBaseImageData::StateData::NoRecoil )
  413. {
  414. S32 stateIndex = state - ShapeBaseImageData::StateData::LightRecoil;
  415. if ( mDataBlock->recoilSequence[stateIndex] != -1 )
  416. {
  417. mShapeInstance->setSequence( mRecoilThread, mDataBlock->recoilSequence[stateIndex], 0 );
  418. mShapeInstance->setTimeScale( mRecoilThread, 1 );
  419. }
  420. }
  421. }
  422. }
  423. void TurretShape::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue)
  424. {
  425. if (isGhost())
  426. {
  427. S32 seqIndex = mShapeInstance->getShape()->findSequence(seqName);
  428. if (seqIndex != -1)
  429. {
  430. if (!mImageStateThread)
  431. {
  432. mImageStateThread = mShapeInstance->addThread();
  433. }
  434. mShapeInstance->setSequence( mImageStateThread, seqIndex, 0 );
  435. F32 timeScale = (scaleToState && stateTimeOutValue) ?
  436. mShapeInstance->getDuration(mImageStateThread) / stateTimeOutValue : 1.0f;
  437. mShapeInstance->setTimeScale( mImageStateThread, direction ? timeScale : -timeScale );
  438. }
  439. }
  440. }
  441. //----------------------------------------------------------------------------
  442. const char* TurretShape::getStateName()
  443. {
  444. if (mDamageState != Enabled)
  445. return "Dead";
  446. if (isMounted())
  447. return "Mounted";
  448. return "Ready";
  449. }
  450. void TurretShape::updateDamageLevel()
  451. {
  452. if (!isGhost())
  453. setDamageState((mDamage >= mDataBlock->maxDamage)? Destroyed: Enabled);
  454. if (mDamageThread)
  455. mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel);
  456. }
  457. //----------------------------------------------------------------------------
  458. void TurretShape::processTick(const Move* move)
  459. {
  460. // Image Triggers
  461. if (getAllowManualFire() && move && mDamageState == Enabled)
  462. {
  463. switch(mDataBlock->weaponLinkType)
  464. {
  465. case TurretShapeData::FireTogether:
  466. {
  467. setImageTriggerState(0,move->trigger[0]);
  468. setImageTriggerState(1,move->trigger[0]);
  469. setImageTriggerState(2,move->trigger[0]);
  470. setImageTriggerState(3,move->trigger[0]);
  471. setImageAltTriggerState(0,move->trigger[1]);
  472. setImageAltTriggerState(1,move->trigger[1]);
  473. setImageAltTriggerState(2,move->trigger[1]);
  474. setImageAltTriggerState(3,move->trigger[1]);
  475. break;
  476. }
  477. case TurretShapeData::GroupedFire:
  478. {
  479. setImageTriggerState(0,move->trigger[0]);
  480. setImageTriggerState(1,move->trigger[1]);
  481. setImageTriggerState(2,move->trigger[0]);
  482. setImageTriggerState(3,move->trigger[1]);
  483. break;
  484. }
  485. case TurretShapeData::IndividualFire:
  486. {
  487. setImageTriggerState(0,move->trigger[0]);
  488. setImageTriggerState(1,move->trigger[1]);
  489. setImageTriggerState(2,move->trigger[2]);
  490. setImageTriggerState(3,move->trigger[3]);
  491. break;
  492. }
  493. }
  494. }
  495. Parent::processTick(move);
  496. // Change our type based on our rest state
  497. if (mAtRest)
  498. {
  499. // At rest so we're static
  500. mTypeMask &= ~DynamicShapeObjectType;
  501. mTypeMask |= StaticObjectType | StaticShapeObjectType;
  502. }
  503. else
  504. {
  505. // Not at rest so we're dynamic
  506. mTypeMask &= ~StaticObjectType;
  507. mTypeMask &= ~StaticShapeObjectType;
  508. mTypeMask |= DynamicShapeObjectType;
  509. }
  510. if (!isGhost())
  511. updateAnimation(TickSec);
  512. updateMove(move);
  513. }
  514. void TurretShape::interpolateTick(F32 dt)
  515. {
  516. Parent::interpolateTick(dt);
  517. if (isMounted()) {
  518. MatrixF mat;
  519. mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &mat );
  520. ShapeBase::setRenderTransform(mat);
  521. }
  522. // Orientation
  523. Point3F rot = mTurretDelta.rot + mTurretDelta.rotVec * dt;
  524. // Make sure we don't interpolate past the limits
  525. _applyLimits(rot);
  526. _setRotation(rot);
  527. }
  528. void TurretShape::advanceTime(F32 dt)
  529. {
  530. // If there were any ShapeBase script threads that
  531. // have played, then we need to update all code
  532. // controlled nodes. This is done before the Parent
  533. // call as script threads may play and be destroyed
  534. // before our code is called.
  535. bool updateNodes = false;
  536. for (U32 i = 0; i < MaxScriptThreads; i++)
  537. {
  538. Thread& st = mScriptThread[i];
  539. if (st.thread)
  540. {
  541. updateNodes = true;
  542. break;
  543. }
  544. }
  545. Parent::advanceTime(dt);
  546. updateAnimation(dt);
  547. _setRotation(mRot);
  548. }
  549. void TurretShape::setTransform( const MatrixF& mat )
  550. {
  551. if (mDataBlock && mDataBlock->zRotOnly)
  552. {
  553. // Allow Item::setTransform() to do the work
  554. Parent::setTransform( mat );
  555. }
  556. else
  557. {
  558. // Do the transform work here to avoid Item's restriction on rotation
  559. ShapeBase::setTransform( mat );
  560. if ( !mStatic )
  561. {
  562. mAtRest = false;
  563. mAtRestCounter = 0;
  564. }
  565. if ( mPhysicsRep )
  566. mPhysicsRep->setTransform( getTransform() );
  567. setMaskBits( Item::RotationMask | Item::PositionMask | Item::NoWarpMask );
  568. }
  569. }
  570. void TurretShape::updateMove(const Move* move)
  571. {
  572. PROFILE_SCOPE( TurretShape_UpdateMove );
  573. if (!move)
  574. return;
  575. Point3F vec, pos;
  576. // Update orientation
  577. mTurretDelta.rotVec = mRot;
  578. VectorF rotVec(0, 0, 0);
  579. if (getAllowManualRotation())
  580. {
  581. if (mPitchAllowed)
  582. {
  583. rotVec.x = move->pitch * 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script;
  584. if (mPitchRate > 0)
  585. {
  586. rotVec.x *= mPitchRate * TickSec;
  587. }
  588. }
  589. if (mHeadingAllowed)
  590. {
  591. rotVec.z = move->yaw * 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script
  592. if (mHeadingRate > 0)
  593. {
  594. rotVec.z *= mHeadingRate * TickSec;
  595. }
  596. }
  597. }
  598. mRot.x += rotVec.x;
  599. mRot.z += rotVec.z;
  600. _applyLimits(mRot);
  601. if (isServerObject())
  602. {
  603. // As this ends up animating shape nodes, we have no sense of a transform and
  604. // render transform. Therefore we treat this as the true transform and leave the
  605. // client shape node changes to interpolateTick() as the render transform. Otherwise
  606. // on the client we'll have this node change from processTick() and then backstepping
  607. // and catching up to the true node change in interpolateTick(), which causes the
  608. // turret to stutter.
  609. _setRotation( mRot );
  610. }
  611. else
  612. {
  613. // If on the client, calc delta for backstepping
  614. mTurretDelta.rot = mRot;
  615. mTurretDelta.rotVec = mTurretDelta.rotVec - mTurretDelta.rot;
  616. }
  617. setMaskBits(TurretUpdateMask);
  618. }
  619. bool TurretShape::getNodeTransform(S32 node, MatrixF& mat)
  620. {
  621. if (node == -1)
  622. return false;
  623. MatrixF nodeTransform = mShapeInstance->mNodeTransforms[node];
  624. const Point3F& scale = getScale();
  625. // The position of the node needs to be scaled.
  626. Point3F position = nodeTransform.getPosition();
  627. position.convolve( scale );
  628. nodeTransform.setPosition( position );
  629. mat.mul(mObjToWorld, nodeTransform);
  630. return true;
  631. }
  632. bool TurretShape::getWorldNodeTransform(S32 node, MatrixF& mat)
  633. {
  634. MatrixF nodeMat;
  635. if (!getNodeTransform(node, nodeMat))
  636. return false;
  637. nodeMat.affineInverse();
  638. mat = nodeMat;
  639. return true;
  640. }
  641. void TurretShape::_setRotation(const Point3F& rot)
  642. {
  643. _updateNodes(rot);
  644. mShapeInstance->animate();
  645. mRot = rot;
  646. }
  647. void TurretShape::_updateNodes(const Point3F& rot)
  648. {
  649. EulerF xRot(rot.x, 0.0f, 0.0f);
  650. EulerF zRot(0.0f, 0.0f, rot.z);
  651. // Set heading
  652. S32 node = mDataBlock->headingNode;
  653. if (node != -1)
  654. {
  655. MatrixF* mat = &mShapeInstance->mNodeTransforms[node];
  656. Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node];
  657. Quat16 defaultRot = mShapeInstance->getShape()->defaultRotations[node];
  658. QuatF qrot(zRot);
  659. qrot *= defaultRot.getQuatF();
  660. qrot.setMatrix( mat );
  661. mat->setColumn(3, defaultPos);
  662. }
  663. // Set pitch
  664. node = mDataBlock->pitchNode;
  665. if (node != -1)
  666. {
  667. MatrixF* mat = &mShapeInstance->mNodeTransforms[node];
  668. Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node];
  669. Quat16 defaultRot = mShapeInstance->getShape()->defaultRotations[node];
  670. QuatF qrot(xRot);
  671. qrot *= defaultRot.getQuatF();
  672. qrot.setMatrix( mat );
  673. mat->setColumn(3, defaultPos);
  674. }
  675. // Now the mirror direction nodes, if any
  676. for (U32 i=0; i<TurretShapeData::NumMirrorDirectionNodes; ++i)
  677. {
  678. node = mDataBlock->pitchNodes[i];
  679. if (node != -1)
  680. {
  681. MatrixF* mat = &mShapeInstance->mNodeTransforms[node];
  682. Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node];
  683. Quat16 defaultRot = mShapeInstance->getShape()->defaultRotations[node];
  684. QuatF qrot(xRot);
  685. qrot *= defaultRot.getQuatF();
  686. qrot.setMatrix( mat );
  687. mat->setColumn(3, defaultPos);
  688. }
  689. node = mDataBlock->headingNodes[i];
  690. if (node != -1)
  691. {
  692. MatrixF* mat = &mShapeInstance->mNodeTransforms[node];
  693. Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node];
  694. Quat16 defaultRot = mShapeInstance->getShape()->defaultRotations[node];
  695. QuatF qrot(zRot);
  696. qrot *= defaultRot.getQuatF();
  697. qrot.setMatrix( mat );
  698. mat->setColumn(3, defaultPos);
  699. }
  700. }
  701. mShapeInstance->setDirty(TSShapeInstance::TransformDirty);
  702. }
  703. void TurretShape::_applyLimits(Point3F& rot)
  704. {
  705. rot.x = mClampF(rot.x, mPitchUp, mPitchDown);
  706. if (mHeadingMax < mDegToRad(180.0f))
  707. {
  708. rot.z = mClampF(rot.z, -mHeadingMax, mHeadingMax);
  709. }
  710. }
  711. bool TurretShape::_outsideLimits(Point3F& rot)
  712. {
  713. if (rot.x < mPitchUp || rot.x > mPitchDown)
  714. return true;
  715. if (mHeadingMax < mDegToRad(180.0f))
  716. {
  717. if (rot.z < -mHeadingMax || rot.z > mHeadingMax)
  718. return true;
  719. }
  720. return false;
  721. }
  722. //----------------------------------------------------------------------------
  723. void TurretShape::mountObject( SceneObject *obj, S32 node, const MatrixF &xfm )
  724. {
  725. Parent::mountObject(obj, node, xfm);
  726. if (isClientObject())
  727. {
  728. if (obj)
  729. {
  730. GameConnection* conn = obj->getControllingClient();
  731. if (conn)
  732. {
  733. // Allow the client to set up any action maps, HUD, etc.
  734. Con::executef("turretMountCallback", Con::getIntArg(getId()), Con::getIntArg(obj->getId()), Con::getIntArg(true));
  735. }
  736. }
  737. }
  738. else
  739. {
  740. mDataBlock->onMountObject_callback( this, obj, node );
  741. }
  742. }
  743. void TurretShape::unmountObject( SceneObject *obj )
  744. {
  745. Parent::unmountObject(obj);
  746. if (isClientObject())
  747. {
  748. if (obj)
  749. {
  750. GameConnection* conn = obj->getControllingClient();
  751. if (conn)
  752. {
  753. // Allow the client to set up any action maps, HUD, etc.
  754. Con::executef("turretMountCallback", Con::getIntArg(getId()), Con::getIntArg(obj->getId()), Con::getIntArg(false));
  755. }
  756. }
  757. }
  758. else
  759. {
  760. mDataBlock->onUnmountObject_callback( this, obj );
  761. }
  762. }
  763. void TurretShape::onUnmount(SceneObject*,S32)
  764. {
  765. // Make sure the client get's the final server pos of this turret.
  766. setMaskBits(PositionMask);
  767. }
  768. //----------------------------------------------------------------------------
  769. void TurretShape::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
  770. {
  771. *min = mDataBlock->cameraMinDist;
  772. *max = mDataBlock->cameraMaxDist;
  773. off->set(0,0,mDataBlock->cameraOffset);
  774. rot->identity();
  775. }
  776. void TurretShape::getCameraTransform(F32* pos,MatrixF* mat)
  777. {
  778. // Returns camera to world space transform
  779. // Handles first person / third person camera position
  780. if (isServerObject() && mShapeInstance)
  781. mShapeInstance->animateNodeSubtrees(true);
  782. if (*pos == 0) {
  783. getRenderEyeTransform(mat);
  784. return;
  785. }
  786. // Get the shape's camera parameters.
  787. F32 min,max;
  788. MatrixF rot;
  789. Point3F offset;
  790. getCameraParameters(&min,&max,&offset,&rot);
  791. // Start with the current eye position
  792. MatrixF eye;
  793. getRenderEyeTransform(&eye);
  794. // Build a transform that points along the eye axis
  795. // but where the Z axis is always up.
  796. {
  797. MatrixF cam(1);
  798. VectorF x,y,z(0,0,1);
  799. eye.getColumn(1, &y);
  800. mCross(y, z, &x);
  801. x.normalize();
  802. mCross(x, y, &z);
  803. z.normalize();
  804. cam.setColumn(0,x);
  805. cam.setColumn(1,y);
  806. cam.setColumn(2,z);
  807. mat->mul(cam,rot);
  808. }
  809. // Camera is positioned straight back along the eye's -Y axis.
  810. // A ray is cast to make sure the camera doesn't go through
  811. // anything solid.
  812. VectorF vp,vec;
  813. vp.x = vp.z = 0;
  814. vp.y = -(max - min) * *pos;
  815. eye.mulV(vp,&vec);
  816. // Use the camera node as the starting position if it exists.
  817. Point3F osp,sp;
  818. if (mDataBlock->cameraNode != -1)
  819. {
  820. mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
  821. getRenderTransform().mulP(osp,&sp);
  822. }
  823. else
  824. eye.getColumn(3,&sp);
  825. // Make sure we don't hit ourself...
  826. disableCollision();
  827. if (isMounted())
  828. getObjectMount()->disableCollision();
  829. // Cast the ray into the container database to see if we're going
  830. // to hit anything.
  831. RayInfo collision;
  832. Point3F ep = sp + vec + offset;
  833. if (mContainer->castRay(sp, ep,
  834. ~(WaterObjectType | GameBaseObjectType | DefaultObjectType | sTriggerMask),
  835. &collision) == true) {
  836. // Shift the collision point back a little to try and
  837. // avoid clipping against the front camera plane.
  838. F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1;
  839. if (t > 0.0f)
  840. ep = sp + offset + (vec * t);
  841. else
  842. eye.getColumn(3,&ep);
  843. }
  844. mat->setColumn(3,ep);
  845. // Re-enable our collision.
  846. if (isMounted())
  847. getObjectMount()->enableCollision();
  848. enableCollision();
  849. // Apply Camera FX.
  850. mat->mul( gCamFXMgr.getTrans() );
  851. }
  852. //----------------------------------------------------------------------------
  853. void TurretShape::writePacketData(GameConnection *connection, BitStream *stream)
  854. {
  855. // Update client regardless of status flags.
  856. Parent::writePacketData(connection, stream);
  857. stream->writeSignedFloat(mRot.x / M_2PI_F, 7);
  858. stream->writeSignedFloat(mRot.z / M_2PI_F, 7);
  859. }
  860. void TurretShape::readPacketData(GameConnection *connection, BitStream *stream)
  861. {
  862. Parent::readPacketData(connection, stream);
  863. Point3F rot(0.0f, 0.0f, 0.0f);
  864. rot.x = stream->readSignedFloat(7) * M_2PI_F;
  865. rot.z = stream->readSignedFloat(7) * M_2PI_F;
  866. _setRotation(rot);
  867. mTurretDelta.rot = rot;
  868. mTurretDelta.rotVec.set(0.0f, 0.0f, 0.0f);
  869. }
  870. U32 TurretShape::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
  871. {
  872. // Handle rotation ourselves (so it is not locked to the Z axis like for Items)
  873. U32 retMask = Parent::packUpdate( connection, mask & (~Item::RotationMask), stream );
  874. if (stream->writeFlag(mask & InitialUpdateMask)) {
  875. stream->writeFlag(mRespawn);
  876. }
  877. if ( stream->writeFlag( mask & Item::RotationMask ) )
  878. {
  879. QuatF rot( mObjToWorld );
  880. mathWrite( *stream, rot );
  881. }
  882. // The rest of the data is part of the control object packet update.
  883. // If we're controlled by this client, we don't need to send it.
  884. if(stream->writeFlag((NetConnection*)getControllingClient() == connection && !(mask & InitialUpdateMask)))
  885. return 0;
  886. if (stream->writeFlag(mask & TurretUpdateMask))
  887. {
  888. stream->writeSignedFloat(mRot.x / M_2PI_F, 7);
  889. stream->writeSignedFloat(mRot.z / M_2PI_F, 7);
  890. stream->write(allowManualRotation);
  891. stream->write(allowManualFire);
  892. }
  893. return retMask;
  894. }
  895. void TurretShape::unpackUpdate(NetConnection *connection, BitStream *stream)
  896. {
  897. Parent::unpackUpdate(connection,stream);
  898. // InitialUpdateMask
  899. if (stream->readFlag()) {
  900. mRespawn = stream->readFlag();
  901. }
  902. // Item::RotationMask
  903. if ( stream->readFlag() )
  904. {
  905. QuatF rot;
  906. mathRead( *stream, &rot );
  907. Point3F pos = mObjToWorld.getPosition();
  908. rot.setMatrix( &mObjToWorld );
  909. mObjToWorld.setPosition( pos );
  910. }
  911. // controlled by the client?
  912. if(stream->readFlag())
  913. return;
  914. // TurretUpdateMask
  915. if (stream->readFlag())
  916. {
  917. Point3F rot(0.0f, 0.0f, 0.0f);
  918. rot.x = stream->readSignedFloat(7) * M_2PI_F;
  919. rot.z = stream->readSignedFloat(7) * M_2PI_F;
  920. _setRotation(rot);
  921. // New delta for client side interpolation
  922. mTurretDelta.rot = rot;
  923. mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);
  924. stream->read(&allowManualRotation);
  925. stream->read(&allowManualFire);
  926. }
  927. }
  928. //----------------------------------------------------------------------------
  929. void TurretShape::getWeaponMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat )
  930. {
  931. // Returns mount point to world space transform
  932. if ( index >= 0 && index < ShapeBase::MaxMountedImages) {
  933. S32 ni = mDataBlock->weaponMountNode[index];
  934. if (ni != -1) {
  935. MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni];
  936. mountTransform.mul( xfm );
  937. const Point3F& scale = getScale();
  938. // The position of the mount point needs to be scaled.
  939. Point3F position = mountTransform.getPosition();
  940. position.convolve( scale );
  941. mountTransform.setPosition( position );
  942. // Also we would like the object to be scaled to the model.
  943. outMat->mul(mObjToWorld, mountTransform);
  944. return;
  945. }
  946. }
  947. // Then let SceneObject handle it.
  948. GrandParent::getMountTransform( index, xfm, outMat );
  949. }
  950. void TurretShape::getRenderWeaponMountTransform( F32 delta, S32 mountPoint, const MatrixF &xfm, MatrixF *outMat )
  951. {
  952. // Returns mount point to world space transform
  953. if ( mountPoint >= 0 && mountPoint < ShapeBase::MaxMountedImages) {
  954. S32 ni = mDataBlock->weaponMountNode[mountPoint];
  955. if (ni != -1) {
  956. MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni];
  957. mountTransform.mul( xfm );
  958. const Point3F& scale = getScale();
  959. // The position of the mount point needs to be scaled.
  960. Point3F position = mountTransform.getPosition();
  961. position.convolve( scale );
  962. mountTransform.setPosition( position );
  963. // Also we would like the object to be scaled to the model.
  964. mountTransform.scale( scale );
  965. outMat->mul(getRenderTransform(), mountTransform);
  966. return;
  967. }
  968. }
  969. // Then let SceneObject handle it.
  970. GrandParent::getRenderMountTransform( delta, mountPoint, xfm, outMat );
  971. }
  972. void TurretShape::getImageTransform(U32 imageSlot,MatrixF* mat)
  973. {
  974. // Image transform in world space
  975. MountedImage& image = mMountedImageList[imageSlot];
  976. if (image.dataBlock) {
  977. ShapeBaseImageData& data = *image.dataBlock;
  978. MatrixF nmat;
  979. if (data.useEyeOffset && isFirstPerson()) {
  980. getEyeTransform(&nmat);
  981. mat->mul(nmat,data.eyeOffset);
  982. }
  983. else {
  984. getWeaponMountTransform( imageSlot, MatrixF::Identity, &nmat );
  985. mat->mul(nmat,data.mountTransform[getImageShapeIndex(image)]);
  986. }
  987. }
  988. else
  989. *mat = mObjToWorld;
  990. }
  991. void TurretShape::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEyeOffset )
  992. {
  993. // Image transform in world space
  994. MountedImage& image = mMountedImageList[imageSlot];
  995. if (image.dataBlock)
  996. {
  997. ShapeBaseImageData& data = *image.dataBlock;
  998. MatrixF nmat;
  999. if ( !noEyeOffset && data.useEyeOffset && isFirstPerson() )
  1000. {
  1001. getRenderEyeTransform(&nmat);
  1002. mat->mul(nmat,data.eyeOffset);
  1003. }
  1004. else
  1005. {
  1006. getRenderWeaponMountTransform( 0.0f, imageSlot, MatrixF::Identity, &nmat );
  1007. mat->mul(nmat,data.mountTransform[getImageShapeIndex(image)]);
  1008. }
  1009. }
  1010. else
  1011. *mat = getRenderTransform();
  1012. }
  1013. void TurretShape::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
  1014. {
  1015. // Same as ShapeBase::getImageTransform() other than getRenderWeaponMountTransform() below
  1016. // Image transform in world space
  1017. MountedImage& image = mMountedImageList[imageSlot];
  1018. if (image.dataBlock)
  1019. {
  1020. if (node != -1)
  1021. {
  1022. ShapeBaseImageData& data = *image.dataBlock;
  1023. U32 shapeIndex = getImageShapeIndex(image);
  1024. MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node];
  1025. MatrixF mmat;
  1026. if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1)
  1027. {
  1028. // We need to animate, even on the server, to make sure the nodes are in the correct location.
  1029. image.shapeInstance[shapeIndex]->animate();
  1030. MatrixF emat;
  1031. getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
  1032. MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
  1033. mountTransform.affineInverse();
  1034. mmat.mul(emat, mountTransform);
  1035. }
  1036. else if (data.useEyeOffset && isFirstPerson())
  1037. {
  1038. MatrixF emat;
  1039. getEyeTransform(&emat);
  1040. mmat.mul(emat,data.eyeOffset);
  1041. }
  1042. else
  1043. {
  1044. MatrixF emat;
  1045. getWeaponMountTransform( imageSlot, MatrixF::Identity, &emat );
  1046. mmat.mul(emat,data.mountTransform[shapeIndex]);
  1047. }
  1048. mat->mul(mmat, nmat);
  1049. }
  1050. else
  1051. getImageTransform(imageSlot,mat);
  1052. }
  1053. else
  1054. *mat = mObjToWorld;
  1055. }
  1056. void TurretShape::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
  1057. {
  1058. // Same as ShapeBase::getRenderImageTransform() other than getRenderWeaponMountTransform() below
  1059. // Image transform in world space
  1060. MountedImage& image = mMountedImageList[imageSlot];
  1061. if (image.dataBlock)
  1062. {
  1063. if (node != -1)
  1064. {
  1065. ShapeBaseImageData& data = *image.dataBlock;
  1066. U32 shapeIndex = getImageShapeIndex(image);
  1067. MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node];
  1068. MatrixF mmat;
  1069. if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 )
  1070. {
  1071. MatrixF emat;
  1072. getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
  1073. MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
  1074. mountTransform.affineInverse();
  1075. mmat.mul(emat, mountTransform);
  1076. }
  1077. else if ( data.useEyeOffset && isFirstPerson() )
  1078. {
  1079. MatrixF emat;
  1080. getRenderEyeTransform(&emat);
  1081. mmat.mul(emat,data.eyeOffset);
  1082. }
  1083. else
  1084. {
  1085. MatrixF emat;
  1086. getRenderWeaponMountTransform( 0.0f, imageSlot, MatrixF::Identity, &emat );
  1087. mmat.mul(emat,data.mountTransform[shapeIndex]);
  1088. }
  1089. mat->mul(mmat, nmat);
  1090. }
  1091. else
  1092. getRenderImageTransform(imageSlot,mat);
  1093. }
  1094. else
  1095. *mat = getRenderTransform();
  1096. }
  1097. //----------------------------------------------------------------------------
  1098. void TurretShape::prepRenderImage( SceneRenderState *state )
  1099. {
  1100. // Skip the Item class rendering
  1101. _prepRenderImage( state, true, true );
  1102. }
  1103. void TurretShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex )
  1104. {
  1105. Parent::prepBatchRender( state, mountedImageIndex );
  1106. if ( !gShowBoundingBox )
  1107. return;
  1108. //if ( mountedImageIndex != -1 )
  1109. //{
  1110. // ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  1111. // ri->renderDelegate.bind( this, &Vehicle::_renderMuzzleVector );
  1112. // ri->objectIndex = mountedImageIndex;
  1113. // ri->type = RenderPassManager::RIT_Editor;
  1114. // state->getRenderPass()->addInst( ri );
  1115. // return;
  1116. //}
  1117. //ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  1118. //ri->renderDelegate.bind( this, &Vehicle::_renderMassAndContacts );
  1119. //ri->type = RenderPassManager::RIT_Editor;
  1120. //state->getRenderPass()->addInst( ri );
  1121. }
  1122. //----------------------------------------------------------------------------
  1123. DefineEngineMethod( TurretShape, getAllowManualRotation, bool, (),,
  1124. "@brief Get if the turret is allowed to rotate through moves.\n\n"
  1125. "@return True if the turret is allowed to rotate through moves.\n" )
  1126. {
  1127. return object->getAllowManualRotation();
  1128. }
  1129. DefineEngineMethod( TurretShape, setAllowManualRotation, void, (bool allow),,
  1130. "@brief Set if the turret is allowed to rotate through moves.\n\n"
  1131. "@param allow If true then the turret may be rotated through moves.\n")
  1132. {
  1133. return object->setAllowManualRotation(allow);
  1134. }
  1135. DefineEngineMethod( TurretShape, getAllowManualFire, bool, (),,
  1136. "@brief Get if the turret is allowed to fire through moves.\n\n"
  1137. "@return True if the turret is allowed to fire through moves.\n" )
  1138. {
  1139. return object->getAllowManualFire();
  1140. }
  1141. DefineEngineMethod( TurretShape, setAllowManualFire, void, (bool allow),,
  1142. "@brief Set if the turret is allowed to fire through moves.\n\n"
  1143. "@param allow If true then the turret may be fired through moves.\n")
  1144. {
  1145. return object->setAllowManualFire(allow);
  1146. }
  1147. DefineEngineMethod( TurretShape, getState, const char*, (),,
  1148. "@brief Get the name of the turret's current state.\n\n"
  1149. "The state is one of the following:\n\n<ul>"
  1150. "<li>Dead - The TurretShape is destroyed.</li>"
  1151. "<li>Mounted - The TurretShape is mounted to an object such as a vehicle.</li>"
  1152. "<li>Ready - The TurretShape is free to move. The usual state.</li></ul>\n"
  1153. "@return The current state; one of: \"Dead\", \"Mounted\", \"Ready\"\n" )
  1154. {
  1155. return object->getStateName();
  1156. }
  1157. DefineEngineMethod( TurretShape, getTurretEulerRotation, Point3F, (),,
  1158. "@brief Get Euler rotation of this turret's heading and pitch nodes.\n\n"
  1159. "@return the orientation of the turret's heading and pitch nodes in the "
  1160. "form of rotations around the X, Y and Z axes in degrees.\n" )
  1161. {
  1162. Point3F euler = object->getTurretRotation();
  1163. // Convert to degrees.
  1164. euler.x = mRadToDeg( euler.x );
  1165. euler.y = mRadToDeg( euler.y );
  1166. euler.z = mRadToDeg( euler.z );
  1167. return euler;
  1168. }
  1169. DefineEngineMethod( TurretShape, setTurretEulerRotation, void, ( Point3F rot ),,
  1170. "@brief Set Euler rotation of this turret's heading and pitch nodes in degrees.\n\n"
  1171. "@param rot The rotation in degrees. The pitch is the X component and the "
  1172. "heading is the Z component. The Y component is ignored.\n")
  1173. {
  1174. object->setTurretRotation( rot );
  1175. }
  1176. DefineEngineMethod( TurretShape, doRespawn, bool, (),,
  1177. "@brief Does the turret respawn after it has been destroyed.\n\n"
  1178. "@returns True if the turret respawns.\n")
  1179. {
  1180. return object->doRespawn();
  1181. }