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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle that maintains <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific distance between the " "vehicle and the ground at all times; unlike <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle that maintains <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific distance between the " "vehicle and the ground at all times; unlike <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 {
 73
 74const U32 sCollisionMoveMask = (TerrainObjectType     | PlayerObjectType  | 
 75                                StaticShapeObjectType | VehicleObjectType | 
 76                                VehicleBlockerObjectType);
 77
 78const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
 79const U32 sClientCollisionMask = sCollisionMoveMask;
 80
 81void nonFilter(SceneObject* object,void *key)
 82{
 83   SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
 84   object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
 85}
 86
 87} // namespace {}
 88
 89const char* HoverVehicle::sJetSequence[HoverVehicle::JetAnimCount] =
 90{
 91   "activateBack",
 92   "maintainBack",
 93};
 94
 95const char* HoverVehicleData::sJetNode[HoverVehicleData::MaxJetNodes] =
 96{
 97   "JetNozzle0",  // Thrust Forward
 98   "JetNozzle1",
 99   "JetNozzleX",  // Thrust Backward
100   "JetNozzleX",
101   "JetNozzle2",  // Thrust Downward
102   "JetNozzle3",
103};
104
105// Convert thrust direction into nodes & emitters
106HoverVehicle::JetActivation HoverVehicle::sJetActivation[NumThrustDirections] = {
107   { HoverVehicleData::ForwardJetNode,  HoverVehicleData::ForwardJetEmitter },
108   { HoverVehicleData::BackwardJetNode, HoverVehicleData::BackwardJetEmitter },
109   { HoverVehicleData::DownwardJetNode, HoverVehicleData::DownwardJetEmitter },
110};
111
112//--------------------------------------------------------------------------
113//--------------------------------------
114//
115HoverVehicleData::HoverVehicleData()
116{
117   dragForce            = 0;
118   vertFactor           = 0.25f;
119   floatingThrustFactor = 0.15f;
120
121   mainThrustForce    = 0;
122   reverseThrustForce = 0;
123   strafeThrustForce  = 0;
124   turboFactor        = 1.0f;
125
126   stabLenMin = 0.5f;
127   stabLenMax = 2.0f;
128   stabSpringConstant  = 30;
129   stabDampingConstant = 10;
130
131   gyroDrag = 10;
132   normalForce = 30;
133   restorativeForce = 10;
134   steeringForce = 25;
135   rollForce = 2.5f;
136   pitchForce = 2.5f;
137
138   dustTrailEmitter = NULL;
139   dustTrailID = 0;
140   dustTrailOffset.set( 0.0f, 0.0f, 0.0f );
141   dustTrailFreqMod = 15.0f;
142   maxThrustSpeed = 0;
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(mPacked ? 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 = mPacked ? 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*)(uintptr_t)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*)(uintptr_t)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   mDataBlock = NULL;
441   // Todo: ScopeAlways?
442   mNetFlags.set(Ghostable);
443
444   mFloating      = false;
445   mThrustLevel   = 0.0f;
446   mForwardThrust = 0.0f;
447   mReverseThrust = 0.0f;
448   mLeftThrust    = 0.0f;
449   mRightThrust   = 0.0f;
450
451   mJetSound    = NULL;
452   mEngineSound = NULL;
453   mFloatSound  = NULL;
454   mThrustDirection = HoverVehicle::ThrustForward;
455   mDustTrailEmitter = NULL;
456
457   mBackMaintainOn = false;
458   for (S32 i = 0; i < JetAnimCount; i++)
459   {
460      mJetSeq[i] = -1;
461      mJetThread[i] = NULL;
462   }
463}
464
465HoverVehicle::~HoverVehicle()
466{
467   //
468}
469
470//--------------------------------------------------------------------------
471bool HoverVehicle::onAdd()
472{
473   if(!Parent::onAdd())
474      return false;
475
476   addToScene();
477
478
479   if( !isServerObject() )
480   {
481      if( mDataBlock->dustTrailEmitter )
482      {
483         mDustTrailEmitter = new ParticleEmitter;
484         mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false );
485         if( !mDustTrailEmitter->registerObject() )
486         {
487            Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
488            delete mDustTrailEmitter;
489            mDustTrailEmitter = NULL;
490         }
491      }
492      // Jet Sequences
493      for (S32 i = 0; i < JetAnimCount; i++) {
494         TSShape const* shape = mShapeInstance->getShape();
495         mJetSeq[i] = shape->findSequence(sJetSequence[i]);
496         if (mJetSeq[i] != -1) {
497            if (i == BackActivate) {
498               mJetThread[i] = mShapeInstance->addThread();
499               mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0);
500               mShapeInstance->setTimeScale(mJetThread[i],0);
501            }
502         }
503         else
504            mJetThread[i] = 0;
505      }
506   }
507
508
509   if (isServerObject())
510      scriptOnAdd();
511
512   return true;
513}
514
515
516void HoverVehicle::onRemove()
517{
518   SFX_DELETE( mJetSound );
519   SFX_DELETE( mEngineSound );
520   SFX_DELETE( mFloatSound );
521
522   scriptOnRemove();
523   removeFromScene();
524   Parent::onRemove();
525}
526
527
528bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
529{
530   mDataBlock = dynamic_cast<HoverVehicleData*>(dptr);
531   if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
532      return false;
533
534   if (isGhost()) 
535   {
536      // Create the sounds ahead of time.  This reduces runtime
537      // costs and makes the system easier to understand.
538
539      SFX_DELETE( mEngineSound );
540      SFX_DELETE( mFloatSound );
541      SFX_DELETE( mJetSound );
542
543      if ( mDataBlock->sound[HoverVehicleData::EngineSound] )
544         mEngineSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::EngineSound], &getTransform() );
545
546      if ( !mDataBlock->sound[HoverVehicleData::FloatSound] )
547         mFloatSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::FloatSound], &getTransform() );
548
549      if ( mDataBlock->sound[HoverVehicleData::JetSound] )
550         mJetSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::JetSound], &getTransform() );
551   }
552
553   // Todo: Uncomment if this is a "leaf" class
554   scriptOnNewDataBlock();
555
556   return true;
557}
558
559
560
561//--------------------------------------------------------------------------
562void HoverVehicle::advanceTime(F32 dt)
563{
564   Parent::advanceTime(dt);
565
566   // Update jetsound...
567   if ( mJetSound ) 
568   {
569      if ( mJetting )
570      {
571         if ( !mJetSound->isPlaying() )
572            mJetSound->play();
573
574         mJetSound->setTransform( getTransform() );
575      }
576      else 
577         mJetSound->stop();
578   }
579
580   // Update engine sound...
581   if ( mEngineSound )
582   {
583      if ( !mEngineSound->isPlaying() )
584         mEngineSound->play();
585
586      mEngineSound->setTransform( getTransform() );
587
588      F32 denom  = mDataBlock->mainThrustForce + mDataBlock->strafeThrustForce;
589      F32 factor = getMin(mThrustLevel, denom) / denom;
590      F32 vol = 0.25 + factor * 0.75;
591      mEngineSound->setVolume( vol );
592   }
593
594   // Are we floating?  If so, start the floating sound...
595   if ( mFloatSound )
596   {
597      if ( mFloating )
598      {
599         if ( !mFloatSound->isPlaying() )
600            mFloatSound->play();
601
602         mFloatSound->setTransform( getTransform() );
603      }
604      else
605         mFloatSound->stop();
606   }
607
608   updateJet(dt);
609   updateDustTrail( dt );
610}
611
612
613//--------------------------------------------------------------------------
614
615U32 HoverVehicle::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
616{
617   U32 retMask = Parent::packUpdate(con, mask, stream);
618
619   //
620   stream->writeInt(mThrustDirection,NumThrustBits);
621
622   return retMask;
623}
624
625void HoverVehicle::unpackUpdate(NetConnection* con, BitStream* stream)
626{
627   Parent::unpackUpdate(con, stream);
628
629   mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits));
630   //
631}
632
633
634//--------------------------------------------------------------------------
635void HoverVehicle::updateMove(const Move* move)
636{
637   Parent::updateMove(move);
638
639   mForwardThrust = mThrottle > 0.0f ?  mThrottle : 0.0f;
640   mReverseThrust = mThrottle < 0.0f ? -mThrottle : 0.0f;
641   mLeftThrust    = move->x   < 0.0f ? -move->x   : 0.0f;
642   mRightThrust   = move->x   > 0.0f ?  move->x   : 0.0f;
643
644   mThrustDirection = (!move->y)? ThrustDown: (move->y > 0)? ThrustForward: ThrustBackward;
645}
646
647F32 HoverVehicle::getBaseStabilizerLength() const
648{
649   F32 base = mDataBlock->stabLenMin;
650   F32 lengthDiff = mDataBlock->stabLenMax - mDataBlock->stabLenMin;
651   F32 velLength  = mRigid.linVelocity.len();
652   F32 minVel     = getMin(velLength, mDataBlock->maxThrustSpeed);
653   F32 velDiff    = mDataBlock->maxThrustSpeed - minVel;
654   // Protect against divide by zero.
655   F32 velRatio   = mDataBlock->maxThrustSpeed != 0.0f ? ( velDiff / mDataBlock->maxThrustSpeed ) : 0.0f;
656   F32 inc        = lengthDiff * ( 1.0 - velRatio );
657   base += inc;
658
659   return base;
660}
661
662
663struct StabPoint
664{
665   Point3F osPoint;           //
666   Point3F wsPoint;           //
667   F32     extension;
668   Point3F wsExtension;       //
669   Point3F wsVelocity;        //
670};
671
672
673void HoverVehicle::updateForces(F32 /*dt*/)
674{
675   PROFILE_SCOPE( HoverVehicle_UpdateForces );
676
677   Point3F gravForce(0, 0, mRigid.mass * mNetGravity);
678
679   MatrixF currTransform;
680   mRigid.getTransform(&currTransform);
681   mRigid.atRest = false;
682
683   mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce    +
684                   mReverseThrust * mDataBlock->reverseThrustForce +
685                   mLeftThrust    * mDataBlock->strafeThrustForce  +
686                   mRightThrust   * mDataBlock->strafeThrustForce);
687
688   Point3F thrustForce = ((Point3F( 0,  1, 0) * (mForwardThrust * mDataBlock->mainThrustForce))    +
689                          (Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) +
690                          (Point3F(-1,  0, 0) * (mLeftThrust    * mDataBlock->strafeThrustForce))  +
691                          (Point3F( 1,  0, 0) * (mRightThrust   * mDataBlock->strafeThrustForce)));
692   currTransform.mulV(thrustForce);
693   if (mJetting)
694      thrustForce *= mDataBlock->turboFactor;
695
696   Point3F torque(0, 0, 0);
697   Point3F force(0, 0, 0);
698
699   Point3F vel = mRigid.linVelocity;
700   F32 baseStabLen = getBaseStabilizerLength();
701   Point3F stabExtend(0, 0, -baseStabLen);
702   currTransform.mulV(stabExtend);
703
704   StabPoint stabPoints[2];
705   stabPoints[0].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
706                                   mObjBox.maxExtents.y,
707                                   (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
708   stabPoints[1].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
709                                   mObjBox.minExtents.y,
710                                   (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
711   U32 j, i;
712   for (i = 0; i < 2; i++) {
713      currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint);
714      stabPoints[i].wsExtension = stabExtend;
715      stabPoints[i].extension   = baseStabLen;
716      stabPoints[i].wsVelocity  = mRigid.linVelocity;
717   }
718
719   RayInfo rinfo;
720
721   mFloating = true;
722   bool reallyFloating = true;
723   F32 compression[2] = { 0.0f, 0.0f };
724   F32  normalMod[2]  = { 0.0f, 0.0f };
725   bool normalSet[2]  = { false, false };
726   Point3F normal[2];
727
728   for (j = 0; j < 2; j++) {
729      if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0,
730                                  TerrainObjectType | 
731                                  WaterObjectType, &rinfo)) 
732      {
733         reallyFloating = false;
734
735         if (rinfo.t <= 0.5) {
736            // Ok, stab is in contact with the ground, let's calc the forces...
737            compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen;
738         }
739         normalSet[j] = true;
740         normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0));
741
742         normal[j] = rinfo.normal;
743      }
744      
745      if ( pointInWater( stabPoints[j].wsPoint ) )
746         compression[j] = baseStabLen;
747   }
748
749   for (j = 0; j < 2; j++) {
750      if (compression[j] != 0.0) {
751         mFloating = false;
752
753         // Spring force and damping
754         Point3F springForce = -stabPoints[j].wsExtension;
755         springForce.normalize();
756         springForce *= compression[j] * mDataBlock->stabSpringConstant;
757
758         Point3F springDamping = -stabPoints[j].wsExtension;
759         springDamping.normalize();
760         springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant;
761
762         force += springForce + springDamping;
763      }
764   }
765
766   // Gravity
767   if (reallyFloating == false)
768      force += gravForce;
769   else
770      force += gravForce * mDataBlock->floatingGravMag;
771
772   // Braking
773   F32 vellen = mRigid.linVelocity.len();
774   if (mThrottle == 0.0f &&
775       mLeftThrust == 0.0f &&
776       mRightThrust == 0.0f &&
777       vellen != 0.0f &&
778       vellen < mDataBlock->brakingActivationSpeed)
779   {
780      Point3F dir = mRigid.linVelocity;
781      dir.normalize();
782      dir.neg();
783      force += dir *  mDataBlock->brakingForce;
784   }
785
786   // Gyro Drag
787   torque = -mRigid.angMomentum * mDataBlock->gyroDrag;
788
789   // Move to proper normal
790   Point3F sn, r;
791   currTransform.getColumn(2, &sn);
792   if (normalSet[0] || normalSet[1]) {
793      if (normalSet[0] && normalSet[1]) {
794         F32 dot = mDot(normal[0], normal[1]);
795         if (dot > 0.999) {
796            // Just pick the first normal.  They're too close to call
797            if ((sn - normal[0]).lenSquared() > 0.00001) {
798               mCross(sn, normal[0], &r);
799               torque += r * mDataBlock->normalForce * normalMod[0];
800            }
801         } else {
802            Point3F rotAxis;
803            mCross(normal[0], normal[1], &rotAxis);
804            rotAxis.normalize();
805
806            F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1]));
807            AngAxisF aa(rotAxis, angle);
808            QuatF q(aa);
809            MatrixF tempMat(true);
810            q.setMatrix(&tempMat);
811            Point3F newNormal;
812            tempMat.mulV(normal[1], &newNormal);
813
814            if ((sn - newNormal).lenSquared() > 0.00001) {
815               mCross(sn, newNormal, &r);
816               torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5));
817            }
818         }
819      } else {
820         Point3F useNormal;
821         F32     useMod;
822         if (normalSet[0]) {
823            useNormal = normal[0];
824            useMod    = normalMod[0];
825         } else {
826            useNormal = normal[1];
827            useMod    = normalMod[1];
828         }
829
830         if ((sn - useNormal).lenSquared() > 0.00001) {
831            mCross(sn, useNormal, &r);
832            torque += r * mDataBlock->normalForce * useMod;
833         }
834      }
835   } else {
836      if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) {
837         mCross(sn, Point3F(0, 0, 1), &r);
838         torque += r * mDataBlock->restorativeForce;
839      }
840   }
841
842   Point3F sn2;
843   currTransform.getColumn(0, &sn);
844   currTransform.getColumn(1, &sn2);
845   mCross(sn, sn2, &r);
846   r.normalize();
847   torque -= r * (mSteering.x * mDataBlock->steeringForce);
848
849   currTransform.getColumn(0, &sn);
850   currTransform.getColumn(2, &sn2);
851   mCross(sn, sn2, &r);
852   r.normalize();
853   torque -= r * (mSteering.x * mDataBlock->rollForce);
854
855   currTransform.getColumn(1, &sn);
856   currTransform.getColumn(2, &sn2);
857   mCross(sn, sn2, &r);
858   r.normalize();
859   torque -= r * (mSteering.y * mDataBlock->pitchForce);
860
861   // Apply drag
862   Point3F vDrag = mRigid.linVelocity;
863   if (!mFloating) {
864      vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
865   } else {
866      vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor));
867   }
868   force -= vDrag * mDataBlock->dragForce;
869
870   force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce;
871
872   // Add in physical zone force
873   force += mAppliedForce;
874
875   force  -= mRigid.linVelocity * mDrag;
876   torque -= mRigid.angMomentum * mDrag;
877
878   mRigid.force  = force;
879   mRigid.torque = torque;
880}
881
882
883//--------------------------------------------------------------------------
884U32 HoverVehicle::getCollisionMask()
885{
886   if (isServerObject())
887      return sServerCollisionMask;
888   else
889      return sClientCollisionMask;
890}
891
892void HoverVehicle::updateDustTrail( F32 dt )
893{
894   // Check to see if we're moving.
895
896   VectorF velocityVector = getVelocity();
897   F32 velocity = velocityVector.len();
898
899   if( velocity > 2.0 )
900   {
901      velocityVector.normalize();
902      emitDust( mDustTrailEmitter, mDataBlock->triggerTrailHeight, mDataBlock->dustTrailOffset,
903                ( U32 )( dt * 1000 * ( velocity / mDataBlock->dustTrailFreqMod ) ),
904                velocityVector );
905   }
906}
907
908void HoverVehicle::updateJet(F32 dt)
909{
910   if (mJetThread[BackActivate] == NULL)
911      return;
912
913   // Thrust Animation threads
914   //  Back
915   if (mJetSeq[BackActivate] >=0 ) {
916      if (!mBackMaintainOn || mThrustDirection != ThrustForward) {
917         if (mBackMaintainOn) {
918            mShapeInstance->setPos(mJetThread[BackActivate], 1);
919            mShapeInstance->destroyThread(mJetThread[BackMaintain]);
920            mBackMaintainOn = false;
921         }
922         mShapeInstance->setTimeScale(mJetThread[BackActivate],
923                                      (mThrustDirection == ThrustForward)? 1.0f : -1.0f);
924         mShapeInstance->advanceTime(dt,mJetThread[BackActivate]);
925      }
926   }
927
928   if (mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn &&
929       mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0f)
930   {
931      mShapeInstance->setPos(mJetThread[BackActivate], 0);
932      mShapeInstance->setTimeScale(mJetThread[BackActivate], 0);
933      mJetThread[BackMaintain] = mShapeInstance->addThread();
934      mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0);
935      mShapeInstance->setTimeScale(mJetThread[BackMaintain],1);
936      mBackMaintainOn = true;
937   }
938
939   if(mBackMaintainOn)
940      mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]);
941
942   // Jet particles
943   for (S32 j = 0; j < NumThrustDirections; j++) {
944      JetActivation& jet = sJetActivation[j];
945      updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter],
946                    jet.node,HoverVehicleData::MaxDirectionJets);
947   }
948}
949
950void HoverVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count)
951{
952   if (!emitter)
953      return;
954   for (S32 j = idx; j < idx + count; j++)
955      if (active) {
956         if (mDataBlock->jetNode[j] != -1) {
957            if (!bool(mJetEmitter[j])) {
958               mJetEmitter[j] = new ParticleEmitter;
959               mJetEmitter[j]->onNewDataBlock( emitter, false );
960               mJetEmitter[j]->registerObject();
961            }
962            MatrixF mat;
963            Point3F pos,axis;
964            mat.mul(getRenderTransform(),
965                    mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]);
966            mat.getColumn(1,&axis);
967            mat.getColumn(3,&pos);
968            mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000.0f));
969         }
970      }
971      else {
972         for (S32 k = idx; k < idx + count; k++)
973            if (bool(mJetEmitter[k])) {
974               mJetEmitter[k]->deleteWhenEmpty();
975               mJetEmitter[k] = 0;
976            }
977      }
978}
979