turretShape.cpp 42 KB

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