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