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