hoverVehicle.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  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/hoverVehicle.h"
  24. #include "core/stream/bitStream.h"
  25. #include "scene/sceneRenderState.h"
  26. #include "collision/clippedPolyList.h"
  27. #include "collision/planeExtractor.h"
  28. #include "T3D/gameBase/moveManager.h"
  29. #include "ts/tsShapeInstance.h"
  30. #include "console/consoleTypes.h"
  31. #include "scene/sceneManager.h"
  32. #include "sfx/sfxSystem.h"
  33. #include "sfx/sfxProfile.h"
  34. #include "sfx/sfxSource.h"
  35. #include "T3D/fx/particleEmitter.h"
  36. #include "math/mathIO.h"
  37. IMPLEMENT_CO_DATABLOCK_V1(HoverVehicleData);
  38. IMPLEMENT_CO_NETOBJECT_V1(HoverVehicle);
  39. ConsoleDocClass( HoverVehicleData,
  40. "@brief Defines the properties of a HoverVehicle.\n\n"
  41. "@ingroup Vehicles\n"
  42. );
  43. ConsoleDocClass( HoverVehicle,
  44. "@brief A hovering vehicle.\n\n"
  45. "A hover vehicle is a vehicle that maintains a specific distance between the "
  46. "vehicle and the ground at all times; unlike a flying vehicle which is free "
  47. "to ascend and descend at will."
  48. "The model used for the HoverVehicle has the following requirements:\n"
  49. "<dl>"
  50. "<dt>Collision mesh</dt><dd>A convex collision mesh at detail size -1.</dd>"
  51. "<dt>JetNozzle0-1 nodes</dt><dd>Particle emitter nodes used when thrusting "
  52. "forward.</dd>"
  53. "<dt>JetNozzle2-3 nodes</dt><dd>Particle emitter nodes used when thrusting "
  54. "downward.</dd>"
  55. "<dt>JetNozzleX node</dt><dd>Particle emitter node used when thrusting "
  56. "backward.</dd>"
  57. "<dt>activateBack animation</dt><dd>Non-cyclic animation sequence played "
  58. "when the vehicle begins thrusting forwards.</dd>"
  59. "<dt>maintainBack animation</dt><dd>Cyclic animation sequence played after "
  60. "activateBack when the vehicle continues thrusting forwards.</dd>"
  61. "</dl>"
  62. "@ingroup Vehicles\n"
  63. );
  64. typedef HoverVehicleData::Sounds hoverSoundsEnum;
  65. DefineEnumType(hoverSoundsEnum);
  66. ImplementEnumType(hoverSoundsEnum, "enum types.\n"
  67. "@ingroup HoverVehicleData\n\n")
  68. { hoverSoundsEnum::JetSound, "JetSound", "..." },
  69. { hoverSoundsEnum::EngineSound, "EngineSound", "..." },
  70. { hoverSoundsEnum::FloatSound, "FloatSound", "..." },
  71. EndImplementEnumType;
  72. namespace {
  73. const U32 sCollisionMoveMask = (TerrainObjectType | PlayerObjectType |
  74. StaticShapeObjectType | VehicleObjectType |
  75. VehicleBlockerObjectType);
  76. const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
  77. const U32 sClientCollisionMask = sCollisionMoveMask;
  78. void nonFilter(SceneObject* object,void *key)
  79. {
  80. SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
  81. object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
  82. }
  83. } // namespace {}
  84. const char* HoverVehicle::sJetSequence[HoverVehicle::JetAnimCount] =
  85. {
  86. "activateBack",
  87. "maintainBack",
  88. };
  89. const char* HoverVehicleData::sJetNode[HoverVehicleData::MaxJetNodes] =
  90. {
  91. "JetNozzle0", // Thrust Forward
  92. "JetNozzle1",
  93. "JetNozzleX", // Thrust Backward
  94. "JetNozzleX",
  95. "JetNozzle2", // Thrust Downward
  96. "JetNozzle3",
  97. };
  98. // Convert thrust direction into nodes & emitters
  99. HoverVehicle::JetActivation HoverVehicle::sJetActivation[NumThrustDirections] = {
  100. { HoverVehicleData::ForwardJetNode, HoverVehicleData::ForwardJetEmitter },
  101. { HoverVehicleData::BackwardJetNode, HoverVehicleData::BackwardJetEmitter },
  102. { HoverVehicleData::DownwardJetNode, HoverVehicleData::DownwardJetEmitter },
  103. };
  104. //--------------------------------------------------------------------------
  105. //--------------------------------------
  106. //
  107. HoverVehicleData::HoverVehicleData()
  108. {
  109. floatingThrustFactor = 0.15f;
  110. mainThrustForce = 0;
  111. reverseThrustForce = 0;
  112. strafeThrustForce = 0;
  113. turboFactor = 1.0f;
  114. stabLenMin = 0.5f;
  115. stabLenMax = 2.0f;
  116. stabSpringConstant = 30;
  117. stabDampingConstant = 10;
  118. gyroDrag = 10;
  119. normalForce = 30;
  120. restorativeForce = 10;
  121. steeringForce = 25;
  122. rollForce = 2.5f;
  123. pitchForce = 2.5f;
  124. dustTrailEmitter = NULL;
  125. dustTrailID = 0;
  126. dustTrailOffset.set( 0.0f, 0.0f, 0.0f );
  127. dustTrailFreqMod = 15.0f;
  128. maxThrustSpeed = 0;
  129. triggerTrailHeight = 2.5f;
  130. floatingGravMag = 1;
  131. brakingForce = 0;
  132. brakingActivationSpeed = 0;
  133. for (S32 k = 0; k < MaxJetNodes; k++)
  134. jetNode[k] = -1;
  135. for (S32 j = 0; j < MaxJetEmitters; j++)
  136. jetEmitter[j] = 0;
  137. for (S32 i = 0; i < MaxSounds; i++)
  138. INIT_SOUNDASSET_ARRAY(HoverSounds, i);
  139. }
  140. HoverVehicleData::~HoverVehicleData()
  141. {
  142. }
  143. //--------------------------------------------------------------------------
  144. void HoverVehicleData::initPersistFields()
  145. {
  146. docsURL;
  147. Parent::initPersistFields();
  148. addGroup("Physics");
  149. addFieldV( "normalForce", TypeRangedF32, Offset(normalForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  150. "Force generated in the ground normal direction when the vehicle is not "
  151. "floating (within stabalizer length from the ground).\n\n"
  152. "@see stabLenMin" );
  153. addFieldV( "stabLenMin", TypeRangedF32, Offset(stabLenMin, HoverVehicleData), &CommonValidators::PositiveFloat,
  154. "Length of the base stabalizer when travelling at minimum speed (0).\n"
  155. "Each tick, the vehicle performs 2 raycasts (from the center back and "
  156. "center front of the vehicle) to check for contact with the ground. The "
  157. "base stabalizer length determines the length of that raycast; if "
  158. "neither raycast hit the ground, the vehicle is floating, stabalizer "
  159. "spring and ground normal forces are not applied.\n\n"
  160. "<img src=\"images/hoverVehicle_forces.png\">\n"
  161. "@see stabSpringConstant" );
  162. addFieldV( "stabLenMax", TypeRangedF32, Offset(stabLenMax, HoverVehicleData), &CommonValidators::PositiveFloat,
  163. "Length of the base stabalizer when travelling at maximum speed "
  164. "(maxThrustSpeed).\n\n@see stabLenMin\n\n@see mainThrustForce" );
  165. addFieldV("vertFactor", TypeRangedF32, Offset(vertFactor, HoverVehicleData), &CommonValidators::PositiveFloat,
  166. "Scalar applied to the vertical portion of the velocity drag acting on "
  167. "the vehicle.\nFor the horizontal (X and Y) components of velocity drag, "
  168. "a factor of 0.25 is applied when the vehicle is floating, and a factor "
  169. "of 1.0 is applied when the vehicle is not floating. This velocity drag "
  170. "is multiplied by the vehicle's dragForce, as defined above, and the "
  171. "result is subtracted from it's movement force.\n"
  172. "@note The vertFactor must be between 0.0 and 1.0 (inclusive).");
  173. addFieldV("stabSpringConstant", TypeRangedF32, Offset(stabSpringConstant, HoverVehicleData), &CommonValidators::PositiveFloat,
  174. "Value used to generate stabalizer spring force. The force generated "
  175. "depends on stabilizer compression, that is how close the vehicle is "
  176. "to the ground proportional to current stabalizer length.\n\n"
  177. "@see stabLenMin");
  178. addFieldV("stabDampingConstant", TypeRangedF32, Offset(stabDampingConstant, HoverVehicleData), &CommonValidators::PositiveFloat,
  179. "Damping spring force acting against changes in the stabalizer length.\n\n"
  180. "@see stabLenMin");
  181. endGroup("Physics");
  182. addGroup("Steering");
  183. addFieldV( "steeringForce", TypeRangedF32, Offset(steeringForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  184. "Yaw (rotation about the Z-axis) force applied when steering in the x-axis direction."
  185. "about the vehicle's Z-axis)" );
  186. addFieldV( "rollForce", TypeRangedF32, Offset(rollForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  187. "Roll (rotation about the Y-axis) force applied when steering in the x-axis direction." );
  188. addFieldV( "pitchForce", TypeRangedF32, Offset(pitchForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  189. "Pitch (rotation about the X-axis) force applied when steering in the y-axis direction." );
  190. addFieldV( "dragForce", TypeRangedF32, Offset(dragForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  191. "Drag force factor that acts opposite to the vehicle velocity.\nAlso "
  192. "used to determnine the vehicle's maxThrustSpeed.\n@see mainThrustForce" );
  193. addFieldV( "mainThrustForce", TypeRangedF32, Offset(mainThrustForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  194. "Force generated by thrusting the vehicle forward.\nAlso used to determine "
  195. "the maxThrustSpeed:\n\n"
  196. "@tsexample\n"
  197. "maxThrustSpeed = (mainThrustForce + strafeThrustForce) / dragForce;\n"
  198. "@endtsexample\n" );
  199. addFieldV( "reverseThrustForce", TypeRangedF32, Offset(reverseThrustForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  200. "Force generated by thrusting the vehicle backward." );
  201. addFieldV( "strafeThrustForce", TypeRangedF32, Offset(strafeThrustForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  202. "Force generated by thrusting the vehicle to one side.\nAlso used to "
  203. "determine the vehicle's maxThrustSpeed.\n@see mainThrustForce" );
  204. addFieldV( "turboFactor", TypeRangedF32, Offset(turboFactor, HoverVehicleData), &CommonValidators::PositiveFloat,
  205. "Scale factor applied to the vehicle's thrust force when jetting." );
  206. addFieldV( "floatingThrustFactor", TypeRangedF32, Offset(floatingThrustFactor, HoverVehicleData), &CommonValidators::PositiveFloat,
  207. "Scalar applied to the vehicle's thrust force when the vehicle is floating.\n"
  208. "@note The floatingThrustFactor must be between 0.0 and 1.0 (inclusive)." );
  209. endGroup("Steering");
  210. addGroup("AutoCorrection");
  211. addFieldV( "gyroDrag", TypeRangedF32, Offset(gyroDrag, HoverVehicleData), &CommonValidators::PositiveFloat,
  212. "Damping torque that acts against the vehicle's current angular momentum." );
  213. addFieldV( "restorativeForce", TypeRangedF32, Offset(restorativeForce, HoverVehicleData), &CommonValidators::PositiveFloat,
  214. "Force generated to stabalize the vehicle (return it to neutral pitch/roll) "
  215. "when the vehicle is floating (more than stabalizer length from the "
  216. "ground.\n\n@see stabLenMin" );
  217. endGroup("AutoCorrection");
  218. addGroup("Particle Effects");
  219. addField( "dustTrailEmitter", TYPEID< ParticleEmitterData >(), Offset(dustTrailEmitter, HoverVehicleData),
  220. "Emitter to generate particles for the vehicle's dust trail.\nThe trail "
  221. "of dust particles is generated only while the vehicle is moving." );
  222. addField( "forwardJetEmitter", TYPEID< ParticleEmitterData >(), Offset(jetEmitter[ForwardJetEmitter], HoverVehicleData),
  223. "Emitter to generate particles for forward jet thrust.\nForward jet "
  224. "thrust particles are emitted from model nodes JetNozzle0 and JetNozzle1." );
  225. addField( "dustTrailOffset", TypePoint3F, Offset(dustTrailOffset, HoverVehicleData),
  226. "\"X Y Z\" offset from the vehicle's origin from which to generate dust "
  227. "trail particles.\nBy default particles are emitted directly beneath the "
  228. "origin of the vehicle model." );
  229. addFieldV( "triggerTrailHeight", TypeRangedF32, Offset(triggerTrailHeight, HoverVehicleData), &CommonValidators::PositiveFloat,
  230. "Maximum height above surface to emit dust trail particles.\nIf the vehicle "
  231. "is less than triggerTrailHeight above a static surface with a material that "
  232. "has 'showDust' set to true, the vehicle will emit particles from the "
  233. "dustTrailEmitter." );
  234. addFieldV( "dustTrailFreqMod", TypeRangedF32, Offset(dustTrailFreqMod, HoverVehicleData), &CommonValidators::PositiveFloat,
  235. "Number of dust trail particles to generate based on vehicle speed.\nThe "
  236. "vehicle's speed is divided by this value to determine how many particles "
  237. "to generate each frame. Lower values give a more dense trail, higher "
  238. "values a more sparse trail." );
  239. endGroup("Sounds");
  240. addGroup("Particle Effects");
  241. INITPERSISTFIELD_SOUNDASSET_ENUMED(HoverSounds, hoverSoundsEnum, Sounds::MaxSounds, HoverVehicleData, "Sounds for hover vehicle.");
  242. endGroup("Sounds");
  243. }
  244. //--------------------------------------------------------------------------
  245. bool HoverVehicleData::onAdd()
  246. {
  247. if(!Parent::onAdd())
  248. return false;
  249. return true;
  250. }
  251. bool HoverVehicleData::preload(bool server, String &errorStr)
  252. {
  253. if (Parent::preload(server, errorStr) == false)
  254. return false;
  255. if (dragForce <= 0.01f) {
  256. Con::warnf("HoverVehicleData::preload: dragForce must be at least 0.01");
  257. dragForce = 0.01f;
  258. }
  259. if (vertFactor < 0.0f || vertFactor > 1.0f) {
  260. Con::warnf("HoverVehicleData::preload: vert factor must be [0, 1]");
  261. vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f;
  262. }
  263. if (floatingThrustFactor < 0.0f || floatingThrustFactor > 1.0f) {
  264. Con::warnf("HoverVehicleData::preload: floatingThrustFactor must be [0, 1]");
  265. floatingThrustFactor = floatingThrustFactor < 0.0f ? 0.0f : 1.0f;
  266. }
  267. maxThrustSpeed = (mainThrustForce + strafeThrustForce) / dragForce;
  268. massCenter = Point3F(0, 0, 0);
  269. // Resolve objects transmitted from server
  270. if (!server) {
  271. for (S32 i = 0; i < MaxSounds; i++)
  272. {
  273. if (!isHoverSoundsValid(i))
  274. {
  275. //return false; -TODO: trigger asset download
  276. }
  277. }
  278. for (S32 j = 0; j < MaxJetEmitters; j++)
  279. if (jetEmitter[j])
  280. Sim::findObject(SimObjectId((uintptr_t)jetEmitter[j]),jetEmitter[j]);
  281. }
  282. if( !dustTrailEmitter && dustTrailID != 0 )
  283. {
  284. if( !Sim::findObject( dustTrailID, dustTrailEmitter ) )
  285. {
  286. Con::errorf( ConsoleLogEntry::General, "HoverVehicleData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID );
  287. }
  288. }
  289. // Resolve jet nodes
  290. for (S32 j = 0; j < MaxJetNodes; j++)
  291. jetNode[j] = mShape->findNode(sJetNode[j]);
  292. return true;
  293. }
  294. //--------------------------------------------------------------------------
  295. void HoverVehicleData::packData(BitStream* stream)
  296. {
  297. Parent::packData(stream);
  298. stream->write(dragForce);
  299. stream->write(vertFactor);
  300. stream->write(floatingThrustFactor);
  301. stream->write(mainThrustForce);
  302. stream->write(reverseThrustForce);
  303. stream->write(strafeThrustForce);
  304. stream->write(turboFactor);
  305. stream->write(stabLenMin);
  306. stream->write(stabLenMax);
  307. stream->write(stabSpringConstant);
  308. stream->write(stabDampingConstant);
  309. stream->write(gyroDrag);
  310. stream->write(normalForce);
  311. stream->write(restorativeForce);
  312. stream->write(steeringForce);
  313. stream->write(rollForce);
  314. stream->write(pitchForce);
  315. mathWrite(*stream, dustTrailOffset);
  316. stream->write(triggerTrailHeight);
  317. stream->write(dustTrailFreqMod);
  318. for (S32 i = 0; i < MaxSounds; i++)
  319. {
  320. PACKDATA_SOUNDASSET_ARRAY(HoverSounds, i);
  321. }
  322. for (S32 j = 0; j < MaxJetEmitters; j++)
  323. {
  324. if (stream->writeFlag(jetEmitter[j]))
  325. {
  326. SimObjectId writtenId = mPacked ? SimObjectId((uintptr_t)jetEmitter[j]) : jetEmitter[j]->getId();
  327. stream->writeRangedU32(writtenId, DataBlockObjectIdFirst,DataBlockObjectIdLast);
  328. }
  329. }
  330. if (stream->writeFlag( dustTrailEmitter ))
  331. {
  332. stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  333. }
  334. stream->write(floatingGravMag);
  335. stream->write(brakingForce);
  336. stream->write(brakingActivationSpeed);
  337. }
  338. void HoverVehicleData::unpackData(BitStream* stream)
  339. {
  340. Parent::unpackData(stream);
  341. stream->read(&dragForce);
  342. stream->read(&vertFactor);
  343. stream->read(&floatingThrustFactor);
  344. stream->read(&mainThrustForce);
  345. stream->read(&reverseThrustForce);
  346. stream->read(&strafeThrustForce);
  347. stream->read(&turboFactor);
  348. stream->read(&stabLenMin);
  349. stream->read(&stabLenMax);
  350. stream->read(&stabSpringConstant);
  351. stream->read(&stabDampingConstant);
  352. stream->read(&gyroDrag);
  353. stream->read(&normalForce);
  354. stream->read(&restorativeForce);
  355. stream->read(&steeringForce);
  356. stream->read(&rollForce);
  357. stream->read(&pitchForce);
  358. mathRead(*stream, &dustTrailOffset);
  359. stream->read(&triggerTrailHeight);
  360. stream->read(&dustTrailFreqMod);
  361. for (S32 i = 0; i < MaxSounds; i++)
  362. {
  363. UNPACKDATA_SOUNDASSET_ARRAY(HoverSounds, i);
  364. }
  365. for (S32 j = 0; j < MaxJetEmitters; j++) {
  366. jetEmitter[j] = NULL;
  367. if (stream->readFlag())
  368. jetEmitter[j] = (ParticleEmitterData*)(uintptr_t)stream->readRangedU32(DataBlockObjectIdFirst,
  369. DataBlockObjectIdLast);
  370. }
  371. if( stream->readFlag() )
  372. {
  373. dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
  374. }
  375. stream->read(&floatingGravMag);
  376. stream->read(&brakingForce);
  377. stream->read(&brakingActivationSpeed);
  378. }
  379. //--------------------------------------------------------------------------
  380. //--------------------------------------
  381. //
  382. HoverVehicle::HoverVehicle()
  383. {
  384. mDataBlock = NULL;
  385. // Todo: ScopeAlways?
  386. mNetFlags.set(Ghostable);
  387. mFloating = false;
  388. mThrustLevel = 0.0f;
  389. mForwardThrust = 0.0f;
  390. mReverseThrust = 0.0f;
  391. mLeftThrust = 0.0f;
  392. mRightThrust = 0.0f;
  393. mJetSound = NULL;
  394. mEngineSound = NULL;
  395. mFloatSound = NULL;
  396. mThrustDirection = HoverVehicle::ThrustForward;
  397. mDustTrailEmitter = NULL;
  398. mBackMaintainOn = false;
  399. for (S32 i = 0; i < JetAnimCount; i++)
  400. {
  401. mJetSeq[i] = -1;
  402. mJetThread[i] = NULL;
  403. }
  404. }
  405. HoverVehicle::~HoverVehicle()
  406. {
  407. //
  408. }
  409. //--------------------------------------------------------------------------
  410. bool HoverVehicle::onAdd()
  411. {
  412. if(!Parent::onAdd())
  413. return false;
  414. addToScene();
  415. if( !isServerObject() )
  416. {
  417. if( mDataBlock->dustTrailEmitter )
  418. {
  419. mDustTrailEmitter = new ParticleEmitter;
  420. mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false );
  421. if( !mDustTrailEmitter->registerObject() )
  422. {
  423. Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
  424. delete mDustTrailEmitter;
  425. mDustTrailEmitter = NULL;
  426. }
  427. }
  428. // Jet Sequences
  429. for (S32 i = 0; i < JetAnimCount; i++) {
  430. TSShape const* shape = mShapeInstance->getShape();
  431. mJetSeq[i] = shape->findSequence(sJetSequence[i]);
  432. if (mJetSeq[i] != -1) {
  433. if (i == BackActivate) {
  434. mJetThread[i] = mShapeInstance->addThread();
  435. mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0);
  436. mShapeInstance->setTimeScale(mJetThread[i],0);
  437. }
  438. }
  439. else
  440. mJetThread[i] = 0;
  441. }
  442. }
  443. return true;
  444. }
  445. void HoverVehicle::onRemove()
  446. {
  447. SFX_DELETE( mJetSound );
  448. SFX_DELETE( mEngineSound );
  449. SFX_DELETE( mFloatSound );
  450. removeFromScene();
  451. Parent::onRemove();
  452. }
  453. bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
  454. {
  455. mDataBlock = dynamic_cast<HoverVehicleData*>(dptr);
  456. if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
  457. return false;
  458. if (isGhost())
  459. {
  460. // Create the sounds ahead of time. This reduces runtime
  461. // costs and makes the system easier to understand.
  462. SFX_DELETE( mEngineSound );
  463. SFX_DELETE( mFloatSound );
  464. SFX_DELETE( mJetSound );
  465. if ( mDataBlock->getHoverSounds(HoverVehicleData::EngineSound) )
  466. mEngineSound = SFX->createSource( mDataBlock->getHoverSoundsProfile(HoverVehicleData::EngineSound), &getTransform() );
  467. if ( !mDataBlock->getHoverSounds(HoverVehicleData::FloatSound) )
  468. mFloatSound = SFX->createSource( mDataBlock->getHoverSoundsProfile(HoverVehicleData::FloatSound), &getTransform() );
  469. if ( mDataBlock->getHoverSounds(HoverVehicleData::JetSound) )
  470. mJetSound = SFX->createSource( mDataBlock->getHoverSoundsProfile(HoverVehicleData::JetSound), &getTransform() );
  471. }
  472. // Todo: Uncomment if this is a "leaf" class
  473. scriptOnNewDataBlock();
  474. return true;
  475. }
  476. //--------------------------------------------------------------------------
  477. void HoverVehicle::advanceTime(F32 dt)
  478. {
  479. Parent::advanceTime(dt);
  480. // Update jetsound...
  481. if ( mJetSound )
  482. {
  483. if ( mJetting )
  484. {
  485. if ( !mJetSound->isPlaying() )
  486. mJetSound->play();
  487. mJetSound->setTransform( getTransform() );
  488. }
  489. else
  490. mJetSound->stop();
  491. }
  492. // Update engine sound...
  493. if ( mEngineSound )
  494. {
  495. if ( !mEngineSound->isPlaying() )
  496. mEngineSound->play();
  497. mEngineSound->setTransform( getTransform() );
  498. F32 denom = mDataBlock->mainThrustForce + mDataBlock->strafeThrustForce;
  499. F32 factor = getMin(mThrustLevel, denom) / denom;
  500. F32 vol = 0.25 + factor * 0.75;
  501. mEngineSound->setVolume( vol );
  502. }
  503. // Are we floating? If so, start the floating sound...
  504. if ( mFloatSound )
  505. {
  506. if ( mFloating )
  507. {
  508. if ( !mFloatSound->isPlaying() )
  509. mFloatSound->play();
  510. mFloatSound->setTransform( getTransform() );
  511. }
  512. else
  513. mFloatSound->stop();
  514. }
  515. updateJet(dt);
  516. updateDustTrail( dt );
  517. }
  518. //--------------------------------------------------------------------------
  519. U32 HoverVehicle::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
  520. {
  521. U32 retMask = Parent::packUpdate(con, mask, stream);
  522. //
  523. stream->writeInt(mThrustDirection,NumThrustBits);
  524. return retMask;
  525. }
  526. void HoverVehicle::unpackUpdate(NetConnection* con, BitStream* stream)
  527. {
  528. Parent::unpackUpdate(con, stream);
  529. mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits));
  530. //
  531. }
  532. //--------------------------------------------------------------------------
  533. void HoverVehicle::updateMove(const Move* move)
  534. {
  535. Parent::updateMove(move);
  536. mForwardThrust = mThrottle > 0.0f ? mThrottle : 0.0f;
  537. mReverseThrust = mThrottle < 0.0f ? -mThrottle : 0.0f;
  538. mLeftThrust = move->x < 0.0f ? -move->x : 0.0f;
  539. mRightThrust = move->x > 0.0f ? move->x : 0.0f;
  540. mThrustDirection = (!move->y)? ThrustDown: (move->y > 0)? ThrustForward: ThrustBackward;
  541. }
  542. F32 HoverVehicle::getBaseStabilizerLength() const
  543. {
  544. F32 base = mDataBlock->stabLenMin;
  545. F32 lengthDiff = mDataBlock->stabLenMax - mDataBlock->stabLenMin;
  546. F32 velLength = mRigid.linVelocity.len();
  547. F32 minVel = getMin(velLength, mDataBlock->maxThrustSpeed);
  548. F32 velDiff = mDataBlock->maxThrustSpeed - minVel;
  549. // Protect against divide by zero.
  550. F32 velRatio = mDataBlock->maxThrustSpeed != 0.0f ? ( velDiff / mDataBlock->maxThrustSpeed ) : 0.0f;
  551. F32 inc = lengthDiff * ( 1.0 - velRatio );
  552. base += inc;
  553. return base;
  554. }
  555. struct StabPoint
  556. {
  557. Point3F osPoint; //
  558. Point3F wsPoint; //
  559. F32 extension;
  560. Point3F wsExtension; //
  561. Point3F wsVelocity; //
  562. };
  563. void HoverVehicle::updateForces(F32 /*dt*/)
  564. {
  565. PROFILE_SCOPE( HoverVehicle_UpdateForces );
  566. Point3F gravForce(0, 0, mRigid.mass * mNetGravity);
  567. MatrixF currTransform;
  568. mRigid.getTransform(&currTransform);
  569. mRigid.atRest = false;
  570. mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce +
  571. mReverseThrust * mDataBlock->reverseThrustForce +
  572. mLeftThrust * mDataBlock->strafeThrustForce +
  573. mRightThrust * mDataBlock->strafeThrustForce);
  574. Point3F thrustForce = ((Point3F( 0, 1, 0) * (mForwardThrust * mDataBlock->mainThrustForce)) +
  575. (Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) +
  576. (Point3F(-1, 0, 0) * (mLeftThrust * mDataBlock->strafeThrustForce)) +
  577. (Point3F( 1, 0, 0) * (mRightThrust * mDataBlock->strafeThrustForce)));
  578. currTransform.mulV(thrustForce);
  579. if (mJetting)
  580. thrustForce *= mDataBlock->turboFactor;
  581. Point3F torque(0, 0, 0);
  582. Point3F force(0, 0, 0);
  583. Point3F vel = mRigid.linVelocity;
  584. F32 baseStabLen = getBaseStabilizerLength();
  585. Point3F stabExtend(0, 0, -baseStabLen);
  586. currTransform.mulV(stabExtend);
  587. StabPoint stabPoints[2];
  588. stabPoints[0].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  589. mObjBox.maxExtents.y,
  590. (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
  591. stabPoints[1].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  592. mObjBox.minExtents.y,
  593. (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
  594. U32 j, i;
  595. for (i = 0; i < 2; i++) {
  596. currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint);
  597. stabPoints[i].wsExtension = stabExtend;
  598. stabPoints[i].extension = baseStabLen;
  599. stabPoints[i].wsVelocity = mRigid.linVelocity;
  600. }
  601. RayInfo rinfo;
  602. mFloating = true;
  603. bool reallyFloating = true;
  604. F32 compression[2] = { 0.0f, 0.0f };
  605. F32 normalMod[2] = { 0.0f, 0.0f };
  606. bool normalSet[2] = { false, false };
  607. Point3F normal[2];
  608. for (j = 0; j < 2; j++) {
  609. if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0,
  610. TerrainObjectType |
  611. WaterObjectType, &rinfo))
  612. {
  613. reallyFloating = false;
  614. if (rinfo.t <= 0.5) {
  615. // Ok, stab is in contact with the ground, let's calc the forces...
  616. compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen;
  617. }
  618. normalSet[j] = true;
  619. normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0));
  620. normal[j] = rinfo.normal;
  621. }
  622. if ( pointInWater( stabPoints[j].wsPoint ) )
  623. compression[j] = baseStabLen;
  624. }
  625. for (j = 0; j < 2; j++) {
  626. if (compression[j] != 0.0) {
  627. mFloating = false;
  628. // Spring force and damping
  629. Point3F springForce = -stabPoints[j].wsExtension;
  630. springForce.normalize();
  631. springForce *= compression[j] * mDataBlock->stabSpringConstant;
  632. Point3F springDamping = -stabPoints[j].wsExtension;
  633. springDamping.normalize();
  634. springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant;
  635. force += springForce + springDamping;
  636. }
  637. }
  638. // Gravity
  639. if (reallyFloating == false)
  640. force += gravForce;
  641. else
  642. force += gravForce * mDataBlock->floatingGravMag;
  643. // Braking
  644. F32 vellen = mRigid.linVelocity.len();
  645. if (mThrottle == 0.0f &&
  646. mLeftThrust == 0.0f &&
  647. mRightThrust == 0.0f &&
  648. vellen != 0.0f &&
  649. vellen < mDataBlock->brakingActivationSpeed)
  650. {
  651. Point3F dir = mRigid.linVelocity;
  652. dir.normalize();
  653. dir.neg();
  654. force += dir * mDataBlock->brakingForce;
  655. }
  656. // Gyro Drag
  657. torque = -mRigid.angMomentum * mDataBlock->gyroDrag;
  658. // Move to proper normal
  659. Point3F sn, r;
  660. currTransform.getColumn(2, &sn);
  661. if (normalSet[0] || normalSet[1]) {
  662. if (normalSet[0] && normalSet[1]) {
  663. F32 dot = mDot(normal[0], normal[1]);
  664. if (dot > 0.999) {
  665. // Just pick the first normal. They're too close to call
  666. if ((sn - normal[0]).lenSquared() > 0.00001) {
  667. mCross(sn, normal[0], &r);
  668. torque += r * mDataBlock->normalForce * normalMod[0];
  669. }
  670. } else {
  671. Point3F rotAxis;
  672. mCross(normal[0], normal[1], &rotAxis);
  673. rotAxis.normalize();
  674. F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1]));
  675. AngAxisF aa(rotAxis, angle);
  676. QuatF q(aa);
  677. MatrixF tempMat(true);
  678. q.setMatrix(&tempMat);
  679. Point3F newNormal;
  680. tempMat.mulV(normal[1], &newNormal);
  681. if ((sn - newNormal).lenSquared() > 0.00001) {
  682. mCross(sn, newNormal, &r);
  683. torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5));
  684. }
  685. }
  686. } else {
  687. Point3F useNormal;
  688. F32 useMod;
  689. if (normalSet[0]) {
  690. useNormal = normal[0];
  691. useMod = normalMod[0];
  692. } else {
  693. useNormal = normal[1];
  694. useMod = normalMod[1];
  695. }
  696. if ((sn - useNormal).lenSquared() > 0.00001) {
  697. mCross(sn, useNormal, &r);
  698. torque += r * mDataBlock->normalForce * useMod;
  699. }
  700. }
  701. } else {
  702. if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) {
  703. mCross(sn, Point3F(0, 0, 1), &r);
  704. torque += r * mDataBlock->restorativeForce;
  705. }
  706. }
  707. Point3F sn2;
  708. currTransform.getColumn(0, &sn);
  709. currTransform.getColumn(1, &sn2);
  710. mCross(sn, sn2, &r);
  711. r.normalize();
  712. torque -= r * (mSteering.x * mDataBlock->steeringForce);
  713. currTransform.getColumn(0, &sn);
  714. currTransform.getColumn(2, &sn2);
  715. mCross(sn, sn2, &r);
  716. r.normalize();
  717. torque -= r * (mSteering.x * mDataBlock->rollForce);
  718. currTransform.getColumn(1, &sn);
  719. currTransform.getColumn(2, &sn2);
  720. mCross(sn, sn2, &r);
  721. r.normalize();
  722. torque -= r * (mSteering.y * mDataBlock->pitchForce);
  723. // Apply drag
  724. Point3F vDrag = mRigid.linVelocity;
  725. if (!mFloating) {
  726. vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
  727. } else {
  728. vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor));
  729. }
  730. force -= vDrag * mDataBlock->dragForce;
  731. force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce;
  732. // Add in physical zone force
  733. force += mAppliedForce;
  734. force -= mRigid.linVelocity * mDrag;
  735. torque -= mRigid.angMomentum * mDrag;
  736. mRigid.force = force;
  737. mRigid.torque = torque;
  738. }
  739. //--------------------------------------------------------------------------
  740. U32 HoverVehicle::getCollisionMask()
  741. {
  742. if (isServerObject())
  743. return sServerCollisionMask;
  744. else
  745. return sClientCollisionMask;
  746. }
  747. void HoverVehicle::updateDustTrail( F32 dt )
  748. {
  749. // Check to see if we're moving.
  750. VectorF velocityVector = getVelocity();
  751. F32 velocity = velocityVector.len();
  752. if( velocity > 2.0 )
  753. {
  754. velocityVector.normalize();
  755. emitDust( mDustTrailEmitter, mDataBlock->triggerTrailHeight, mDataBlock->dustTrailOffset,
  756. ( U32 )( dt * 1000 * ( velocity / mDataBlock->dustTrailFreqMod ) ),
  757. velocityVector );
  758. }
  759. }
  760. void HoverVehicle::updateJet(F32 dt)
  761. {
  762. if (mJetThread[BackActivate] == NULL)
  763. return;
  764. // Thrust Animation threads
  765. // Back
  766. if (mJetSeq[BackActivate] >=0 ) {
  767. if (!mBackMaintainOn || mThrustDirection != ThrustForward) {
  768. if (mBackMaintainOn) {
  769. mShapeInstance->setPos(mJetThread[BackActivate], 1);
  770. mShapeInstance->destroyThread(mJetThread[BackMaintain]);
  771. mBackMaintainOn = false;
  772. }
  773. mShapeInstance->setTimeScale(mJetThread[BackActivate],
  774. (mThrustDirection == ThrustForward)? 1.0f : -1.0f);
  775. mShapeInstance->advanceTime(dt,mJetThread[BackActivate]);
  776. }
  777. }
  778. if (mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn &&
  779. mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0f)
  780. {
  781. mShapeInstance->setPos(mJetThread[BackActivate], 0);
  782. mShapeInstance->setTimeScale(mJetThread[BackActivate], 0);
  783. mJetThread[BackMaintain] = mShapeInstance->addThread();
  784. mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0);
  785. mShapeInstance->setTimeScale(mJetThread[BackMaintain],1);
  786. mBackMaintainOn = true;
  787. }
  788. if(mBackMaintainOn)
  789. mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]);
  790. // Jet particles
  791. for (S32 j = 0; j < NumThrustDirections; j++) {
  792. JetActivation& jet = sJetActivation[j];
  793. updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter],
  794. jet.node,HoverVehicleData::MaxDirectionJets);
  795. }
  796. }
  797. void HoverVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count)
  798. {
  799. if (!emitter)
  800. return;
  801. for (S32 j = idx; j < idx + count; j++)
  802. if (active) {
  803. if (mDataBlock->jetNode[j] != -1) {
  804. if (!bool(mJetEmitter[j])) {
  805. mJetEmitter[j] = new ParticleEmitter;
  806. mJetEmitter[j]->onNewDataBlock( emitter, false );
  807. mJetEmitter[j]->registerObject();
  808. }
  809. MatrixF mat;
  810. Point3F pos,axis;
  811. mat.mul(getRenderTransform(),
  812. mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]);
  813. mat.getColumn(1,&axis);
  814. mat.getColumn(3,&pos);
  815. mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000.0f));
  816. }
  817. }
  818. else {
  819. for (S32 k = idx; k < idx + count; k++)
  820. if (bool(mJetEmitter[k])) {
  821. mJetEmitter[k]->deleteWhenEmpty();
  822. mJetEmitter[k] = 0;
  823. }
  824. }
  825. }