flyingVehicle.cpp
Engine/source/T3D/vehicles/flyingVehicle.cpp
Public Variables
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