flyingVehicle.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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 "platform/platform.h"
  23. #include "T3D/vehicles/flyingVehicle.h"
  24. #include "app/game.h"
  25. #include "math/mMath.h"
  26. #include "console/simBase.h"
  27. #include "console/console.h"
  28. #include "console/consoleTypes.h"
  29. #include "console/engineAPI.h"
  30. #include "collision/clippedPolyList.h"
  31. #include "collision/planeExtractor.h"
  32. #include "core/stream/bitStream.h"
  33. #include "core/dnet.h"
  34. #include "T3D/gameBase/gameConnection.h"
  35. #include "ts/tsShapeInstance.h"
  36. #include "T3D/fx/particleEmitter.h"
  37. #include "sfx/sfxSystem.h"
  38. #include "sfx/sfxProfile.h"
  39. #include "sfx/sfxSource.h"
  40. #include "T3D/missionArea.h"
  41. //----------------------------------------------------------------------------
  42. const static U32 sCollisionMoveMask = ( TerrainObjectType | WaterObjectType |
  43. PlayerObjectType | StaticShapeObjectType |
  44. VehicleObjectType | VehicleBlockerObjectType );
  45. static U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
  46. static U32 sClientCollisionMask = sCollisionMoveMask;
  47. static F32 sFlyingVehicleGravity = -20.0f;
  48. //
  49. const char* FlyingVehicle::sJetSequence[FlyingVehicle::JetAnimCount] =
  50. {
  51. "activateBack",
  52. "maintainBack",
  53. "activateBot",
  54. "maintainBot",
  55. };
  56. const char* FlyingVehicleData::sJetNode[FlyingVehicleData::MaxJetNodes] =
  57. {
  58. "JetNozzle0", // Thrust Forward
  59. "JetNozzle1",
  60. "JetNozzleX", // Thrust Backward
  61. "JetNozzleY",
  62. "JetNozzle2", // Thrust Downward
  63. "JetNozzle3",
  64. "contrail0", // Trail
  65. "contrail1",
  66. "contrail2",
  67. "contrail3",
  68. };
  69. // Convert thrust direction into nodes & emitters
  70. FlyingVehicle::JetActivation FlyingVehicle::sJetActivation[NumThrustDirections] = {
  71. { FlyingVehicleData::ForwardJetNode, FlyingVehicleData::ForwardJetEmitter },
  72. { FlyingVehicleData::BackwardJetNode, FlyingVehicleData::BackwardJetEmitter },
  73. { FlyingVehicleData::DownwardJetNode, FlyingVehicleData::DownwardJetEmitter },
  74. };
  75. //----------------------------------------------------------------------------
  76. IMPLEMENT_CO_DATABLOCK_V1(FlyingVehicleData);
  77. ConsoleDocClass( FlyingVehicleData,
  78. "@brief Defines the properties of a FlyingVehicle.\n\n"
  79. "@ingroup Vehicles\n"
  80. );
  81. FlyingVehicleData::FlyingVehicleData()
  82. {
  83. maneuveringForce = 0;
  84. horizontalSurfaceForce = 0;
  85. verticalSurfaceForce = 0;
  86. autoInputDamping = 1;
  87. steeringForce = 1;
  88. steeringRollForce = 1;
  89. rollForce = 1;
  90. autoAngularForce = 0;
  91. rotationalDrag = 0;
  92. autoLinearForce = 0;
  93. maxAutoSpeed = 0;
  94. hoverHeight = 2;
  95. createHoverHeight = 2;
  96. maxSteeringAngle = M_PI_F;
  97. minTrailSpeed = 1;
  98. maxSpeed = 100;
  99. for (S32 k = 0; k < MaxJetNodes; k++)
  100. jetNode[k] = -1;
  101. for (S32 j = 0; j < MaxJetEmitters; j++)
  102. jetEmitter[j] = 0;
  103. for (S32 i = 0; i < MaxSounds; i++)
  104. sound[i] = 0;
  105. vertThrustMultiple = 1.0;
  106. }
  107. bool FlyingVehicleData::preload(bool server, String &errorStr)
  108. {
  109. if (!Parent::preload(server, errorStr))
  110. return false;
  111. TSShapeInstance* si = new TSShapeInstance(mShape, false);
  112. // Resolve objects transmitted from server
  113. if (!server) {
  114. for (S32 i = 0; i < MaxSounds; i++)
  115. if (sound[i])
  116. Sim::findObject(SimObjectId((uintptr_t)sound[i]),sound[i]);
  117. for (S32 j = 0; j < MaxJetEmitters; j++)
  118. if (jetEmitter[j])
  119. Sim::findObject(SimObjectId((uintptr_t)jetEmitter[j]),jetEmitter[j]);
  120. }
  121. // Extract collision planes from shape collision detail level
  122. if (collisionDetails[0] != -1)
  123. {
  124. MatrixF imat(1);
  125. PlaneExtractorPolyList polyList;
  126. polyList.mPlaneList = &rigidBody.mPlaneList;
  127. polyList.setTransform(&imat, Point3F(1,1,1));
  128. si->animate(collisionDetails[0]);
  129. si->buildPolyList(&polyList,collisionDetails[0]);
  130. }
  131. // Resolve jet nodes
  132. for (S32 j = 0; j < MaxJetNodes; j++)
  133. jetNode[j] = mShape->findNode(sJetNode[j]);
  134. //
  135. maxSpeed = maneuveringForce / minDrag;
  136. delete si;
  137. return true;
  138. }
  139. void FlyingVehicleData::initPersistFields()
  140. {
  141. addField( "jetSound", TYPEID< SFXProfile >(), Offset(sound[JetSound], FlyingVehicleData),
  142. "Looping sound to play while the vehicle is jetting." );
  143. addField( "engineSound", TYPEID< SFXProfile >(), Offset(sound[EngineSound], FlyingVehicleData),
  144. "Looping engine sound." );
  145. addField( "maneuveringForce", TypeF32, Offset(maneuveringForce, FlyingVehicleData),
  146. "@brief Maximum X and Y (horizontal plane) maneuvering force.\n\n"
  147. "The actual force applied depends on the current thrust." );
  148. addField( "horizontalSurfaceForce", TypeF32, Offset(horizontalSurfaceForce, FlyingVehicleData),
  149. "@brief Damping force in the opposite direction to sideways velocity.\n\n"
  150. "Provides \"bite\" into the wind for climbing/diving and turning)." );
  151. addField( "verticalSurfaceForce", TypeF32, Offset(verticalSurfaceForce, FlyingVehicleData),
  152. "@brief Damping force in the opposite direction to vertical velocity.\n\n"
  153. "Controls side slip; lower numbers give more slide." );
  154. addField( "vertThrustMultiple", TypeF32, Offset(vertThrustMultiple, FlyingVehicleData),
  155. "Multiplier applied to the jetForce (defined in VehicleData) when thrusting vertically." );
  156. addField( "steeringForce", TypeF32, Offset(steeringForce, FlyingVehicleData),
  157. "@brief Maximum X and Z (sideways and vertical) steering force.\n\n"
  158. "The actual force applied depends on the current steering input." );
  159. addField( "steeringRollForce", TypeF32, Offset(steeringRollForce, FlyingVehicleData),
  160. "Roll force induced by sideways steering input value (controls how much "
  161. "the vehicle rolls when turning)." );
  162. addField( "rollForce", TypeF32, Offset(rollForce, FlyingVehicleData),
  163. "@brief Damping torque against rolling maneuvers (rotation about the y-axis), "
  164. "proportional to linear velocity.\n\n"
  165. "Acts to adjust roll to a stable position over time as the vehicle moves." );
  166. addField( "rotationalDrag", TypeF32, Offset(rotationalDrag, FlyingVehicleData),
  167. "Rotational drag factor (slows vehicle rotation speed in all axes)." );
  168. addField( "maxAutoSpeed", TypeF32, Offset(maxAutoSpeed, FlyingVehicleData),
  169. "Maximum speed for automatic vehicle control assistance - vehicles "
  170. "travelling at speeds above this value do not get control assitance." );
  171. addField( "autoInputDamping", TypeF32, Offset(autoInputDamping, FlyingVehicleData),
  172. "@brief Scale factor applied to steering input if speed is less than "
  173. "maxAutoSpeed to.improve handling at very low speeds.\n\n"
  174. "Smaller values make steering less sensitive." );
  175. addField( "autoLinearForce", TypeF32, Offset(autoLinearForce, FlyingVehicleData),
  176. "@brief Corrective force applied to slow the vehicle when moving at less than "
  177. "maxAutoSpeed.\n\n"
  178. "The force is inversely proportional to vehicle speed." );
  179. addField( "autoAngularForce", TypeF32, Offset(autoAngularForce, FlyingVehicleData),
  180. "@brief Corrective torque applied to level out the vehicle when moving at less "
  181. "than maxAutoSpeed.\n\n"
  182. "The torque is inversely proportional to vehicle speed." );
  183. addField( "hoverHeight", TypeF32, Offset(hoverHeight, FlyingVehicleData),
  184. "The vehicle's height off the ground when at rest." );
  185. addField( "createHoverHeight", TypeF32, Offset(createHoverHeight, FlyingVehicleData),
  186. "@brief The vehicle's height off the ground when useCreateHeight is active.\n\n"
  187. "This can help avoid problems with spawning the vehicle." );
  188. addField( "forwardJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[ForwardJetEmitter], FlyingVehicleData),
  189. "@brief Emitter to generate particles for forward jet thrust.\n\n"
  190. "Forward jet thrust particles are emitted from model nodes JetNozzle0 "
  191. "and JetNozzle1." );
  192. addField( "backwardJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[BackwardJetEmitter], FlyingVehicleData),
  193. "@brief Emitter to generate particles for backward jet thrust.\n\n"
  194. "Backward jet thrust particles are emitted from model nodes JetNozzleX "
  195. "and JetNozzleY." );
  196. addField( "downJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[DownwardJetEmitter], FlyingVehicleData),
  197. "@brief Emitter to generate particles for downward jet thrust.\n\n"
  198. "Downward jet thrust particles are emitted from model nodes JetNozzle2 "
  199. "and JetNozzle3." );
  200. addField( "trailEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[TrailEmitter], FlyingVehicleData),
  201. "Emitter to generate contrail particles from model nodes contrail0 - contrail3." );
  202. addField( "minTrailSpeed", TypeF32, Offset(minTrailSpeed, FlyingVehicleData),
  203. "Minimum speed at which to start generating contrail particles." );
  204. Parent::initPersistFields();
  205. }
  206. void FlyingVehicleData::packData(BitStream* stream)
  207. {
  208. Parent::packData(stream);
  209. for (S32 i = 0; i < MaxSounds; i++)
  210. {
  211. if (stream->writeFlag(sound[i]))
  212. {
  213. SimObjectId writtenId = packed ? SimObjectId((uintptr_t)sound[i]) : sound[i]->getId();
  214. stream->writeRangedU32(writtenId, DataBlockObjectIdFirst, DataBlockObjectIdLast);
  215. }
  216. }
  217. for (S32 j = 0; j < MaxJetEmitters; j++)
  218. {
  219. if (stream->writeFlag(jetEmitter[j]))
  220. {
  221. SimObjectId writtenId = packed ? SimObjectId((uintptr_t)jetEmitter[j]) : jetEmitter[j]->getId();
  222. stream->writeRangedU32(writtenId, DataBlockObjectIdFirst,DataBlockObjectIdLast);
  223. }
  224. }
  225. stream->write(maneuveringForce);
  226. stream->write(horizontalSurfaceForce);
  227. stream->write(verticalSurfaceForce);
  228. stream->write(autoInputDamping);
  229. stream->write(steeringForce);
  230. stream->write(steeringRollForce);
  231. stream->write(rollForce);
  232. stream->write(autoAngularForce);
  233. stream->write(rotationalDrag);
  234. stream->write(autoLinearForce);
  235. stream->write(maxAutoSpeed);
  236. stream->write(hoverHeight);
  237. stream->write(createHoverHeight);
  238. stream->write(minTrailSpeed);
  239. stream->write(vertThrustMultiple);
  240. }
  241. void FlyingVehicleData::unpackData(BitStream* stream)
  242. {
  243. Parent::unpackData(stream);
  244. for (S32 i = 0; i < MaxSounds; i++) {
  245. sound[i] = NULL;
  246. if (stream->readFlag())
  247. sound[i] = (SFXProfile*)stream->readRangedU32(DataBlockObjectIdFirst,
  248. DataBlockObjectIdLast);
  249. }
  250. for (S32 j = 0; j < MaxJetEmitters; j++) {
  251. jetEmitter[j] = NULL;
  252. if (stream->readFlag())
  253. jetEmitter[j] = (ParticleEmitterData*)stream->readRangedU32(DataBlockObjectIdFirst,
  254. DataBlockObjectIdLast);
  255. }
  256. stream->read(&maneuveringForce);
  257. stream->read(&horizontalSurfaceForce);
  258. stream->read(&verticalSurfaceForce);
  259. stream->read(&autoInputDamping);
  260. stream->read(&steeringForce);
  261. stream->read(&steeringRollForce);
  262. stream->read(&rollForce);
  263. stream->read(&autoAngularForce);
  264. stream->read(&rotationalDrag);
  265. stream->read(&autoLinearForce);
  266. stream->read(&maxAutoSpeed);
  267. stream->read(&hoverHeight);
  268. stream->read(&createHoverHeight);
  269. stream->read(&minTrailSpeed);
  270. stream->read(&vertThrustMultiple);
  271. }
  272. //----------------------------------------------------------------------------
  273. IMPLEMENT_CO_NETOBJECT_V1(FlyingVehicle);
  274. ConsoleDocClass( FlyingVehicle,
  275. "@brief A flying vehicle.\n\n"
  276. "@ingroup Vehicles\n"
  277. );
  278. FlyingVehicle::FlyingVehicle()
  279. {
  280. mSteering.set(0,0);
  281. mThrottle = 0;
  282. mJetting = false;
  283. mJetSound = 0;
  284. mEngineSound = 0;
  285. mBackMaintainOn = false;
  286. mBottomMaintainOn = false;
  287. createHeightOn = false;
  288. for (S32 i = 0; i < JetAnimCount; i++)
  289. mJetThread[i] = 0;
  290. }
  291. FlyingVehicle::~FlyingVehicle()
  292. {
  293. }
  294. //----------------------------------------------------------------------------
  295. bool FlyingVehicle::onAdd()
  296. {
  297. if(!Parent::onAdd())
  298. return false;
  299. addToScene();
  300. if (isServerObject())
  301. scriptOnAdd();
  302. return true;
  303. }
  304. bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
  305. {
  306. mDataBlock = dynamic_cast<FlyingVehicleData*>(dptr);
  307. if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
  308. return false;
  309. // Sounds
  310. if ( isGhost() )
  311. {
  312. // Create the sounds ahead of time. This reduces runtime
  313. // costs and makes the system easier to understand.
  314. SFX_DELETE( mJetSound );
  315. SFX_DELETE( mEngineSound );
  316. if ( mDataBlock->sound[FlyingVehicleData::EngineSound] )
  317. mEngineSound = SFX->createSource( mDataBlock->sound[FlyingVehicleData::EngineSound], &getTransform() );
  318. if ( mDataBlock->sound[FlyingVehicleData::JetSound] )
  319. mJetSound = SFX->createSource( mDataBlock->sound[FlyingVehicleData::JetSound], &getTransform() );
  320. }
  321. // Jet Sequences
  322. for (S32 i = 0; i < JetAnimCount; i++) {
  323. TSShape const* shape = mShapeInstance->getShape();
  324. mJetSeq[i] = shape->findSequence(sJetSequence[i]);
  325. if (mJetSeq[i] != -1) {
  326. if (i == BackActivate || i == BottomActivate) {
  327. mJetThread[i] = mShapeInstance->addThread();
  328. mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0);
  329. mShapeInstance->setTimeScale(mJetThread[i],0);
  330. }
  331. }
  332. else
  333. mJetThread[i] = 0;
  334. }
  335. scriptOnNewDataBlock();
  336. return true;
  337. }
  338. void FlyingVehicle::onRemove()
  339. {
  340. SFX_DELETE( mJetSound );
  341. SFX_DELETE( mEngineSound );
  342. scriptOnRemove();
  343. removeFromScene();
  344. Parent::onRemove();
  345. }
  346. //----------------------------------------------------------------------------
  347. void FlyingVehicle::advanceTime(F32 dt)
  348. {
  349. Parent::advanceTime(dt);
  350. updateEngineSound(1);
  351. updateJet(dt);
  352. }
  353. //----------------------------------------------------------------------------
  354. void FlyingVehicle::updateMove(const Move* move)
  355. {
  356. PROFILE_SCOPE( FlyingVehicle_UpdateMove );
  357. Parent::updateMove(move);
  358. if (move == &NullMove)
  359. mSteering.set(0,0);
  360. F32 speed = mRigid.linVelocity.len();
  361. if (speed < mDataBlock->maxAutoSpeed)
  362. mSteering *= mDataBlock->autoInputDamping;
  363. // Check the mission area to get the factor for the flight ceiling
  364. MissionArea * obj = MissionArea::getServerObject();
  365. mCeilingFactor = 1.0f;
  366. if (obj != NULL)
  367. {
  368. F32 flightCeiling = obj->getFlightCeiling();
  369. F32 ceilingRange = obj->getFlightCeilingRange();
  370. if (mRigid.linPosition.z > flightCeiling)
  371. {
  372. // Thrust starts to fade at the ceiling, and is 0 at ceil + range
  373. if (ceilingRange == 0)
  374. {
  375. mCeilingFactor = 0;
  376. }
  377. else
  378. {
  379. mCeilingFactor = 1.0f - ((mRigid.linPosition.z - flightCeiling) / (flightCeiling + ceilingRange));
  380. if (mCeilingFactor < 0.0f)
  381. mCeilingFactor = 0.0f;
  382. }
  383. }
  384. }
  385. mThrust.x = move->x;
  386. mThrust.y = move->y;
  387. if (mThrust.y != 0.0f)
  388. if (mThrust.y > 0)
  389. mThrustDirection = ThrustForward;
  390. else
  391. mThrustDirection = ThrustBackward;
  392. else
  393. mThrustDirection = ThrustDown;
  394. if (mCeilingFactor != 1.0f)
  395. mJetting = false;
  396. }
  397. //----------------------------------------------------------------------------
  398. void FlyingVehicle::updateForces(F32 /*dt*/)
  399. {
  400. PROFILE_SCOPE( FlyingVehicle_UpdateForces );
  401. MatrixF currPosMat;
  402. mRigid.getTransform(&currPosMat);
  403. mRigid.atRest = false;
  404. Point3F massCenter;
  405. currPosMat.mulP(mDataBlock->massCenter,&massCenter);
  406. Point3F xv,yv,zv;
  407. currPosMat.getColumn(0,&xv);
  408. currPosMat.getColumn(1,&yv);
  409. currPosMat.getColumn(2,&zv);
  410. F32 speed = mRigid.linVelocity.len();
  411. Point3F force = Point3F(0, 0, sFlyingVehicleGravity * mRigid.mass * mGravityMod);
  412. Point3F torque = Point3F(0, 0, 0);
  413. // Drag at any speed
  414. force -= mRigid.linVelocity * mDataBlock->minDrag;
  415. torque -= mRigid.angMomentum * mDataBlock->rotationalDrag;
  416. // Auto-stop at low speeds
  417. if (speed < mDataBlock->maxAutoSpeed) {
  418. F32 autoScale = 1 - speed / mDataBlock->maxAutoSpeed;
  419. // Gyroscope
  420. F32 gf = mDataBlock->autoAngularForce * autoScale;
  421. torque -= xv * gf * mDot(yv,Point3F(0,0,1));
  422. // Manuevering jets
  423. F32 sf = mDataBlock->autoLinearForce * autoScale;
  424. force -= yv * sf * mDot(yv, mRigid.linVelocity);
  425. force -= xv * sf * mDot(xv, mRigid.linVelocity);
  426. }
  427. // Hovering Jet
  428. F32 vf = -sFlyingVehicleGravity * mRigid.mass * mGravityMod;
  429. F32 h = getHeight();
  430. if (h <= 1) {
  431. if (h > 0) {
  432. vf -= vf * h * 0.1;
  433. } else {
  434. vf += mDataBlock->jetForce * -h;
  435. }
  436. }
  437. force += zv * vf;
  438. // Damping "surfaces"
  439. force -= xv * mDot(xv,mRigid.linVelocity) * mDataBlock->horizontalSurfaceForce;
  440. force -= zv * mDot(zv,mRigid.linVelocity) * mDataBlock->verticalSurfaceForce;
  441. // Turbo Jet
  442. if (mJetting) {
  443. if (mThrustDirection == ThrustForward)
  444. force += yv * mDataBlock->jetForce * mCeilingFactor;
  445. else if (mThrustDirection == ThrustBackward)
  446. force -= yv * mDataBlock->jetForce * mCeilingFactor;
  447. else
  448. force += zv * mDataBlock->jetForce * mDataBlock->vertThrustMultiple * mCeilingFactor;
  449. }
  450. // Maneuvering jets
  451. force += yv * (mThrust.y * mDataBlock->maneuveringForce * mCeilingFactor);
  452. force += xv * (mThrust.x * mDataBlock->maneuveringForce * mCeilingFactor);
  453. // Steering
  454. Point2F steering;
  455. steering.x = mSteering.x / mDataBlock->maxSteeringAngle;
  456. steering.x *= mFabs(steering.x);
  457. steering.y = mSteering.y / mDataBlock->maxSteeringAngle;
  458. steering.y *= mFabs(steering.y);
  459. torque -= xv * steering.y * mDataBlock->steeringForce;
  460. torque -= zv * steering.x * mDataBlock->steeringForce;
  461. // Roll
  462. torque += yv * steering.x * mDataBlock->steeringRollForce;
  463. F32 ar = mDataBlock->autoAngularForce * mDot(xv,Point3F(0,0,1));
  464. ar -= mDataBlock->rollForce * mDot(xv, mRigid.linVelocity);
  465. torque += yv * ar;
  466. // Add in force from physical zones...
  467. force += mAppliedForce;
  468. // Container buoyancy & drag
  469. force -= Point3F(0, 0, 1) * (mBuoyancy * sFlyingVehicleGravity * mRigid.mass * mGravityMod);
  470. force -= mRigid.linVelocity * mDrag;
  471. //
  472. mRigid.force = force;
  473. mRigid.torque = torque;
  474. }
  475. //----------------------------------------------------------------------------
  476. F32 FlyingVehicle::getHeight()
  477. {
  478. Point3F sp,ep;
  479. RayInfo collision;
  480. F32 height = (createHeightOn) ? mDataBlock->createHoverHeight : mDataBlock->hoverHeight;
  481. F32 r = 10 + height;
  482. getTransform().getColumn(3, &sp);
  483. ep.x = sp.x;
  484. ep.y = sp.y;
  485. ep.z = sp.z - r;
  486. disableCollision();
  487. if( !mContainer->castRay(sp, ep, sClientCollisionMask, &collision) == true )
  488. collision.t = 1;
  489. enableCollision();
  490. return (r * collision.t - height) / 10;
  491. }
  492. //----------------------------------------------------------------------------
  493. U32 FlyingVehicle::getCollisionMask()
  494. {
  495. if (isServerObject())
  496. return sServerCollisionMask;
  497. else
  498. return sClientCollisionMask;
  499. }
  500. //----------------------------------------------------------------------------
  501. void FlyingVehicle::updateEngineSound(F32 level)
  502. {
  503. if ( !mEngineSound )
  504. return;
  505. if ( !mEngineSound->isPlaying() )
  506. mEngineSound->play();
  507. mEngineSound->setTransform( getTransform() );
  508. mEngineSound->setVelocity( getVelocity() );
  509. mEngineSound->setPitch( level );
  510. }
  511. void FlyingVehicle::updateJet(F32 dt)
  512. {
  513. // Thrust Animation threads
  514. // Back
  515. if (mJetSeq[BackActivate] >=0 ) {
  516. if(!mBackMaintainOn || mThrustDirection != ThrustForward) {
  517. if(mBackMaintainOn) {
  518. mShapeInstance->setPos(mJetThread[BackActivate], 1);
  519. mShapeInstance->destroyThread(mJetThread[BackMaintain]);
  520. mBackMaintainOn = false;
  521. }
  522. mShapeInstance->setTimeScale(mJetThread[BackActivate],
  523. (mThrustDirection == ThrustForward)? 1.0f : -1.0f);
  524. mShapeInstance->advanceTime(dt,mJetThread[BackActivate]);
  525. }
  526. if(mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn &&
  527. mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0) {
  528. mShapeInstance->setPos(mJetThread[BackActivate], 0);
  529. mShapeInstance->setTimeScale(mJetThread[BackActivate], 0);
  530. mJetThread[BackMaintain] = mShapeInstance->addThread();
  531. mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0);
  532. mShapeInstance->setTimeScale(mJetThread[BackMaintain],1);
  533. mBackMaintainOn = true;
  534. }
  535. if(mBackMaintainOn)
  536. mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]);
  537. }
  538. // Thrust Animation threads
  539. // Bottom
  540. if (mJetSeq[BottomActivate] >=0 ) {
  541. if(!mBottomMaintainOn || mThrustDirection != ThrustDown || !mJetting) {
  542. if(mBottomMaintainOn) {
  543. mShapeInstance->setPos(mJetThread[BottomActivate], 1);
  544. mShapeInstance->destroyThread(mJetThread[BottomMaintain]);
  545. mBottomMaintainOn = false;
  546. }
  547. mShapeInstance->setTimeScale(mJetThread[BottomActivate],
  548. (mThrustDirection == ThrustDown && mJetting)? 1.0f : -1.0f);
  549. mShapeInstance->advanceTime(dt,mJetThread[BottomActivate]);
  550. }
  551. if(mJetSeq[BottomMaintain] >= 0 && !mBottomMaintainOn &&
  552. mShapeInstance->getPos(mJetThread[BottomActivate]) >= 1.0) {
  553. mShapeInstance->setPos(mJetThread[BottomActivate], 0);
  554. mShapeInstance->setTimeScale(mJetThread[BottomActivate], 0);
  555. mJetThread[BottomMaintain] = mShapeInstance->addThread();
  556. mShapeInstance->setSequence(mJetThread[BottomMaintain],mJetSeq[BottomMaintain],0);
  557. mShapeInstance->setTimeScale(mJetThread[BottomMaintain],1);
  558. mBottomMaintainOn = true;
  559. }
  560. if(mBottomMaintainOn)
  561. mShapeInstance->advanceTime(dt,mJetThread[BottomMaintain]);
  562. }
  563. // Jet particles
  564. for (S32 j = 0; j < NumThrustDirections; j++) {
  565. JetActivation& jet = sJetActivation[j];
  566. updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter],
  567. jet.node,FlyingVehicleData::MaxDirectionJets);
  568. }
  569. // Trail jets
  570. Point3F yv;
  571. mObjToWorld.getColumn(1,&yv);
  572. F32 speed = mFabs(mDot(yv,mRigid.linVelocity));
  573. F32 trail = 0;
  574. if (speed > mDataBlock->minTrailSpeed) {
  575. trail = dt;
  576. if (speed < mDataBlock->maxSpeed)
  577. trail *= (speed - mDataBlock->minTrailSpeed) / mDataBlock->maxSpeed;
  578. }
  579. updateEmitter(trail,trail,mDataBlock->jetEmitter[FlyingVehicleData::TrailEmitter],
  580. FlyingVehicleData::TrailNode,FlyingVehicleData::MaxTrails);
  581. // Allocate/Deallocate voice on demand.
  582. if ( !mJetSound )
  583. return;
  584. if ( !mJetting )
  585. mJetSound->stop();
  586. else
  587. {
  588. if ( !mJetSound->isPlaying() )
  589. mJetSound->play();
  590. mJetSound->setTransform( getTransform() );
  591. mJetSound->setVelocity( getVelocity() );
  592. }
  593. }
  594. //----------------------------------------------------------------------------
  595. void FlyingVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count)
  596. {
  597. if (!emitter)
  598. return;
  599. for (S32 j = idx; j < idx + count; j++)
  600. if (active) {
  601. if (mDataBlock->jetNode[j] != -1) {
  602. if (!bool(mJetEmitter[j])) {
  603. mJetEmitter[j] = new ParticleEmitter;
  604. mJetEmitter[j]->onNewDataBlock(emitter,false);
  605. mJetEmitter[j]->registerObject();
  606. }
  607. MatrixF mat;
  608. Point3F pos,axis;
  609. mat.mul(getRenderTransform(),
  610. mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]);
  611. mat.getColumn(1,&axis);
  612. mat.getColumn(3,&pos);
  613. mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000));
  614. }
  615. }
  616. else {
  617. for (S32 j = idx; j < idx + count; j++)
  618. if (bool(mJetEmitter[j])) {
  619. mJetEmitter[j]->deleteWhenEmpty();
  620. mJetEmitter[j] = 0;
  621. }
  622. }
  623. }
  624. //----------------------------------------------------------------------------
  625. void FlyingVehicle::writePacketData(GameConnection *connection, BitStream *stream)
  626. {
  627. Parent::writePacketData(connection, stream);
  628. }
  629. void FlyingVehicle::readPacketData(GameConnection *connection, BitStream *stream)
  630. {
  631. Parent::readPacketData(connection, stream);
  632. setPosition(mRigid.linPosition,mRigid.angPosition);
  633. mDelta.pos = mRigid.linPosition;
  634. mDelta.rot[1] = mRigid.angPosition;
  635. }
  636. U32 FlyingVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
  637. {
  638. U32 retMask = Parent::packUpdate(con, mask, stream);
  639. // The rest of the data is part of the control object packet update.
  640. // If we're controlled by this client, we don't need to send it.
  641. if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
  642. return retMask;
  643. stream->writeFlag(createHeightOn);
  644. stream->writeInt(mThrustDirection,NumThrustBits);
  645. return retMask;
  646. }
  647. void FlyingVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
  648. {
  649. Parent::unpackUpdate(con,stream);
  650. if(stream->readFlag())
  651. return;
  652. createHeightOn = stream->readFlag();
  653. mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits));
  654. }
  655. void FlyingVehicle::initPersistFields()
  656. {
  657. Parent::initPersistFields();
  658. }
  659. DefineEngineMethod( FlyingVehicle, useCreateHeight, void, ( bool enabled ),,
  660. "@brief Set whether the vehicle should temporarily use the createHoverHeight "
  661. "specified in the datablock.\n\nThis can help avoid problems with spawning.\n"
  662. "@param enabled true to use the datablock createHoverHeight, false otherwise\n" )
  663. {
  664. object->useCreateHeight( enabled );
  665. }
  666. void FlyingVehicle::useCreateHeight(bool val)
  667. {
  668. createHeightOn = val;
  669. setMaskBits(HoverHeight);
  670. }