Torque3D Documentation / _generateds / hoverVehicle.cpp

hoverVehicle.cpp

Engine/source/T3D/vehicles/hoverVehicle.cpp

More...

Classes:

Public Functions

ConsoleDocClass(HoverVehicle , "@brief A hovering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "A hover vehicle is a vehicle that maintains a specific distance between the " "vehicle and the ground at all times; unlike a flying vehicle which is free " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> ascend and descend at will." "The model used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classhovervehicle/">HoverVehicle</a> has the following <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">requirements:\n</a>" "<dl>" "<dt><a href="/coding/class/structcollision/">Collision</a> mesh</dt><dd>A convex collision mesh at detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> -1.</dd>" "<dt>JetNozzle0-1 nodes</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter nodes used when thrusting " "forward.</dd>" "<dt>JetNozzle2-3 nodes</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter nodes used when thrusting " "downward.</dd>" "<dt>JetNozzleX node</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter node used when thrusting " "backward.</dd>" "<dt>activateBack animation</dt><dd>Non-cyclic animation sequence played " "when the vehicle begins thrusting forwards.</dd>" "<dt>maintainBack animation</dt><dd>Cyclic animation sequence played after " "activateBack when the vehicle continues thrusting forwards.</dd>" "</dl>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
ConsoleDocClass(HoverVehicleData , "@brief Defines the properties of a <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">HoverVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(HoverVehicle , "@brief A hovering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "A hover vehicle is a vehicle that maintains a specific distance between the " "vehicle and the ground at all times; unlike a flying vehicle which is free " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> ascend and descend at will." "The model used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classhovervehicle/">HoverVehicle</a> has the following <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">requirements:\n</a>" "<dl>" "<dt><a href="/coding/class/structcollision/">Collision</a> mesh</dt><dd>A convex collision mesh at detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> -1.</dd>" "<dt>JetNozzle0-1 nodes</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter nodes used when thrusting " "forward.</dd>" "<dt>JetNozzle2-3 nodes</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter nodes used when thrusting " "downward.</dd>" "<dt>JetNozzleX node</dt><dd><a href="/coding/class/structparticle/">Particle</a> emitter node used when thrusting " "backward.</dd>" "<dt>activateBack animation</dt><dd>Non-cyclic animation sequence played " "when the vehicle begins thrusting forwards.</dd>" "<dt>maintainBack animation</dt><dd>Cyclic animation sequence played after " "activateBack when the vehicle continues thrusting forwards.</dd>" "</dl>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

ConsoleDocClass(HoverVehicleData , "@brief Defines the properties of a <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">HoverVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(HoverVehicleData )

IMPLEMENT_CO_NETOBJECT_V1(HoverVehicle )

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