flyingVehicle.cpp 28 KB

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