Torque3D Documentation / _generateds / flyingVehicle.cpp

flyingVehicle.cpp

Engine/source/T3D/vehicles/flyingVehicle.cpp

More...

Public Functions

ConsoleDocClass(FlyingVehicle , "@brief A flying <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
ConsoleDocClass(FlyingVehicleData , "@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">FlyingVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
DefineEngineMethod(FlyingVehicle , useCreateHeight , void , (bool enabled) , "@brief Set whether the vehicle should temporarily use the createHoverHeight " "specified in the datablock.\n\nThis can help avoid problems with <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spawning.\n</a>" "@param enabled true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the datablock createHoverHeight, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n</a>" )

Detailed Description

Public Variables

U32 sClientCollisionMask 
const U32 sCollisionMoveMask 
U32 sServerCollisionMask 

Public Functions

ConsoleDocClass(FlyingVehicle , "@brief A flying <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

ConsoleDocClass(FlyingVehicleData , "@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">FlyingVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

DefineEngineMethod(FlyingVehicle , useCreateHeight , void , (bool enabled) , "@brief Set whether the vehicle should temporarily use the createHoverHeight " "specified in the datablock.\n\nThis can help avoid problems with <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spawning.\n</a>" "@param enabled true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the datablock createHoverHeight, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(FlyingVehicleData )

IMPLEMENT_CO_NETOBJECT_V1(FlyingVehicle )

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