tge/engine/game/vehicles/hoverVehicle.cc
2017-04-17 06:17:10 -06:00

877 lines
29 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/vehicles/hoverVehicle.h"
#include "core/bitStream.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneState.h"
#include "collision/clippedPolyList.h"
#include "collision/planeExtractor.h"
#include "game/moveManager.h"
#include "ts/tsShapeInstance.h"
#include "console/consoleTypes.h"
#include "terrain/terrData.h"
#include "sceneGraph/sceneGraph.h"
#include "audio/audioDataBlock.h"
#include "game/fx/particleEngine.h"
#include "math/mathIO.h"
#include "dgl/materialPropertyMap.h"
#include "terrain/waterBlock.h"
IMPLEMENT_CO_DATABLOCK_V1(HoverVehicleData);
IMPLEMENT_CO_NETOBJECT_V1(HoverVehicle);
namespace {
const U32 sIntergrationsPerTick = 1;
const F32 sHoverVehicleGravity = -20;
const U32 sCollisionMoveMask = (TerrainObjectType | InteriorObjectType |
PlayerObjectType | StaticTSObjectType |
StaticShapeObjectType | VehicleObjectType |
VehicleBlockerObjectType);
const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
const U32 sClientCollisionMask = sCollisionMoveMask;
void nonFilter(SceneObject* object,void *key)
{
Container::CallbackInfo* info = reinterpret_cast<Container::CallbackInfo*>(key);
object->buildPolyList(info->polyList,info->boundingBox,info->boundingSphere);
}
} // namespace {}
const char* HoverVehicle::sJetSequence[HoverVehicle::JetAnimCount] =
{
"activateBack",
"maintainBack",
};
const char* HoverVehicleData::sJetNode[HoverVehicleData::MaxJetNodes] =
{
"JetNozzle0", // Thrust Forward
"JetNozzle1",
"JetNozzleX", // Thrust Backward
"JetNozzleX",
"JetNozzle2", // Thrust Downward
"JetNozzle3",
};
// Convert thrust direction into nodes & emitters
HoverVehicle::JetActivation HoverVehicle::sJetActivation[NumThrustDirections] = {
{ HoverVehicleData::ForwardJetNode, HoverVehicleData::ForwardJetEmitter },
{ HoverVehicleData::BackwardJetNode, HoverVehicleData::BackwardJetEmitter },
{ HoverVehicleData::DownwardJetNode, HoverVehicleData::DownwardJetEmitter },
};
//--------------------------------------------------------------------------
//--------------------------------------
//
HoverVehicleData::HoverVehicleData()
{
dragForce = 0;
vertFactor = 0.25;
floatingThrustFactor = 0.15;
mainThrustForce = 0;
reverseThrustForce = 0;
strafeThrustForce = 0;
turboFactor = 1.0;
stabLenMin = 0.5;
stabLenMax = 2.0;
stabSpringConstant = 30;
stabDampingConstant = 10;
gyroDrag = 10;
normalForce = 30;
restorativeForce = 10;
steeringForce = 25;
rollForce = 2.5;
pitchForce = 2.5;
dustTrailEmitter = NULL;
dustTrailID = 0;
dustTrailOffset.set( 0.0, 0.0, 0.0 );
dustTrailFreqMod = 15.0;
triggerTrailHeight = 2.5;
floatingGravMag = 1;
brakingForce = 0;
brakingActivationSpeed = 0;
for (S32 k = 0; k < MaxJetNodes; k++)
jetNode[k] = -1;
for (S32 j = 0; j < MaxJetEmitters; j++)
jetEmitter[j] = 0;
for (S32 i = 0; i < MaxSounds; i++)
sound[i] = 0;
}
HoverVehicleData::~HoverVehicleData()
{
}
//--------------------------------------------------------------------------
void HoverVehicleData::initPersistFields()
{
Parent::initPersistFields();
addField("dragForce", TypeF32, Offset(dragForce, HoverVehicleData));
addField("vertFactor", TypeF32, Offset(vertFactor, HoverVehicleData));
addField("floatingThrustFactor", TypeF32, Offset(floatingThrustFactor, HoverVehicleData));
addField("mainThrustForce", TypeF32, Offset(mainThrustForce, HoverVehicleData));
addField("reverseThrustForce", TypeF32, Offset(reverseThrustForce, HoverVehicleData));
addField("strafeThrustForce", TypeF32, Offset(strafeThrustForce, HoverVehicleData));
addField("turboFactor", TypeF32, Offset(turboFactor, HoverVehicleData));
addField("stabLenMin", TypeF32, Offset(stabLenMin, HoverVehicleData));
addField("stabLenMax", TypeF32, Offset(stabLenMax, HoverVehicleData));
addField("stabSpringConstant", TypeF32, Offset(stabSpringConstant, HoverVehicleData));
addField("stabDampingConstant", TypeF32, Offset(stabDampingConstant, HoverVehicleData));
addField("gyroDrag", TypeF32, Offset(gyroDrag, HoverVehicleData));
addField("normalForce", TypeF32, Offset(normalForce, HoverVehicleData));
addField("restorativeForce", TypeF32, Offset(restorativeForce, HoverVehicleData));
addField("steeringForce", TypeF32, Offset(steeringForce, HoverVehicleData));
addField("rollForce", TypeF32, Offset(rollForce, HoverVehicleData));
addField("pitchForce", TypeF32, Offset(pitchForce, HoverVehicleData));
addField("jetSound", TypeAudioProfilePtr, Offset(sound[JetSound], HoverVehicleData));
addField("engineSound", TypeAudioProfilePtr, Offset(sound[EngineSound], HoverVehicleData));
addField("floatSound", TypeAudioProfilePtr, Offset(sound[FloatSound], HoverVehicleData));
addField("dustTrailEmitter", TypeParticleEmitterDataPtr, Offset(dustTrailEmitter, HoverVehicleData));
addField("dustTrailOffset", TypePoint3F, Offset(dustTrailOffset, HoverVehicleData));
addField("triggerTrailHeight", TypeF32, Offset(triggerTrailHeight, HoverVehicleData));
addField("dustTrailFreqMod", TypeF32, Offset(dustTrailFreqMod, HoverVehicleData));
addField("floatingGravMag", TypeF32, Offset(floatingGravMag, HoverVehicleData));
addField("brakingForce", TypeF32, Offset(brakingForce, HoverVehicleData));
addField("brakingActivationSpeed", TypeF32, Offset(brakingActivationSpeed, HoverVehicleData));
addField("forwardJetEmitter",TypeParticleEmitterDataPtr, Offset(jetEmitter[ForwardJetEmitter], HoverVehicleData));
}
//--------------------------------------------------------------------------
bool HoverVehicleData::onAdd()
{
if(!Parent::onAdd())
return false;
return true;
}
bool HoverVehicleData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if (dragForce <= 0.01f) {
Con::warnf("HoverVehicleData::preload: dragForce must be at least 0.01");
dragForce = 0.01f;
}
if (vertFactor < 0.0f || vertFactor > 1.0f) {
Con::warnf("HoverVehicleData::preload: vert factor must be [0, 1]");
vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f;
}
if (floatingThrustFactor < 0.0f || floatingThrustFactor > 1.0f) {
Con::warnf("HoverVehicleData::preload: floatingThrustFactor must be [0, 1]");
floatingThrustFactor = floatingThrustFactor < 0.0f ? 0.0f : 1.0f;
}
maxThrustSpeed = (mainThrustForce + strafeThrustForce) / dragForce;
massCenter = Point3F(0, 0, 0);
// Resolve objects transmitted from server
if (!server) {
for (S32 i = 0; i < MaxSounds; i++)
if (sound[i])
Sim::findObject(SimObjectId(sound[i]),sound[i]);
for (S32 j = 0; j < MaxJetEmitters; j++)
if (jetEmitter[j])
Sim::findObject(SimObjectId(jetEmitter[j]),jetEmitter[j]);
}
if( !dustTrailEmitter && dustTrailID != 0 )
{
if( !Sim::findObject( dustTrailID, dustTrailEmitter ) )
{
Con::errorf( ConsoleLogEntry::General, "HoverVehicleData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID );
}
}
// Resolve jet nodes
for (S32 j = 0; j < MaxJetNodes; j++)
jetNode[j] = shape->findNode(sJetNode[j]);
return true;
}
//--------------------------------------------------------------------------
void HoverVehicleData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(dragForce);
stream->write(vertFactor);
stream->write(floatingThrustFactor);
stream->write(mainThrustForce);
stream->write(reverseThrustForce);
stream->write(strafeThrustForce);
stream->write(turboFactor);
stream->write(stabLenMin);
stream->write(stabLenMax);
stream->write(stabSpringConstant);
stream->write(stabDampingConstant);
stream->write(gyroDrag);
stream->write(normalForce);
stream->write(restorativeForce);
stream->write(steeringForce);
stream->write(rollForce);
stream->write(pitchForce);
mathWrite(*stream, dustTrailOffset);
stream->write(triggerTrailHeight);
stream->write(dustTrailFreqMod);
for (S32 i = 0; i < MaxSounds; i++)
if (stream->writeFlag(sound[i]))
stream->writeRangedU32(packed? SimObjectId(sound[i]):
sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
for (S32 j = 0; j < MaxJetEmitters; j++)
{
if (stream->writeFlag(jetEmitter[j]))
{
SimObjectId writtenId = packed ? SimObjectId(jetEmitter[j]) : jetEmitter[j]->getId();
stream->writeRangedU32(writtenId, DataBlockObjectIdFirst,DataBlockObjectIdLast);
}
}
if (stream->writeFlag( dustTrailEmitter ))
{
stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
stream->write(floatingGravMag);
stream->write(brakingForce);
stream->write(brakingActivationSpeed);
}
void HoverVehicleData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&dragForce);
stream->read(&vertFactor);
stream->read(&floatingThrustFactor);
stream->read(&mainThrustForce);
stream->read(&reverseThrustForce);
stream->read(&strafeThrustForce);
stream->read(&turboFactor);
stream->read(&stabLenMin);
stream->read(&stabLenMax);
stream->read(&stabSpringConstant);
stream->read(&stabDampingConstant);
stream->read(&gyroDrag);
stream->read(&normalForce);
stream->read(&restorativeForce);
stream->read(&steeringForce);
stream->read(&rollForce);
stream->read(&pitchForce);
mathRead(*stream, &dustTrailOffset);
stream->read(&triggerTrailHeight);
stream->read(&dustTrailFreqMod);
for (S32 i = 0; i < MaxSounds; i++)
sound[i] = stream->readFlag()?
(AudioProfile*) stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast): 0;
for (S32 j = 0; j < MaxJetEmitters; j++) {
jetEmitter[j] = NULL;
if (stream->readFlag())
jetEmitter[j] = (ParticleEmitterData*)stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
if( stream->readFlag() )
{
dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
}
stream->read(&floatingGravMag);
stream->read(&brakingForce);
stream->read(&brakingActivationSpeed);
}
//--------------------------------------------------------------------------
//--------------------------------------
//
HoverVehicle::HoverVehicle()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable);
mFloating = false;
mForwardThrust = 0;
mReverseThrust = 0;
mLeftThrust = 0;
mRightThrust = 0;
mJetSound = NULL_AUDIOHANDLE;
mEngineSound = NULL_AUDIOHANDLE;
mFloatSound = NULL_AUDIOHANDLE;
mDustTrailEmitter = NULL;
mBackMaintainOn = false;
for (S32 i = 0; i < JetAnimCount; i++)
mJetThread[i] = 0;
}
HoverVehicle::~HoverVehicle()
{
//
}
//--------------------------------------------------------------------------
bool HoverVehicle::onAdd()
{
if(!Parent::onAdd())
return false;
addToScene();
if( !isServerObject() )
{
if( mDataBlock->dustTrailEmitter )
{
mDustTrailEmitter = new ParticleEmitter;
mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter );
if( !mDustTrailEmitter->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
delete mDustTrailEmitter;
mDustTrailEmitter = NULL;
}
}
// Jet Sequences
for (S32 i = 0; i < JetAnimCount; i++) {
TSShape const* shape = mShapeInstance->getShape();
mJetSeq[i] = shape->findSequence(sJetSequence[i]);
if (mJetSeq[i] != -1) {
if (i == BackActivate) {
mJetThread[i] = mShapeInstance->addThread();
mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0);
mShapeInstance->setTimeScale(mJetThread[i],0);
}
}
else
mJetThread[i] = 0;
}
}
if (isServerObject())
scriptOnAdd();
return true;
}
void HoverVehicle::onRemove()
{
scriptOnRemove();
removeFromScene();
if (mJetSound != NULL_AUDIOHANDLE)
alxStop(mJetSound);
if (mEngineSound != NULL_AUDIOHANDLE)
alxStop(mEngineSound);
if (mFloatSound != NULL_AUDIOHANDLE)
alxStop(mFloatSound);
Parent::onRemove();
}
bool HoverVehicle::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<HoverVehicleData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
// Todo: Uncomment if this is a "leaf" class
scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
void HoverVehicle::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
// Update jetsound...
if (mJetting) {
if (mJetSound == NULL_AUDIOHANDLE && mDataBlock->sound[HoverVehicleData::JetSound] != NULL)
mJetSound = alxPlay(mDataBlock->sound[HoverVehicleData::JetSound], &getTransform());
if (mJetSound != NULL_AUDIOHANDLE)
alxSourceMatrixF(mJetSound, &getTransform());
} else {
if (mJetSound != NULL_AUDIOHANDLE) {
alxStop(mJetSound);
mJetSound = NULL_AUDIOHANDLE;
}
}
// Update engine sound...
if (mEngineSound == NULL_AUDIOHANDLE && mDataBlock->sound[HoverVehicleData::EngineSound] != NULL)
mEngineSound = alxPlay(mDataBlock->sound[HoverVehicleData::EngineSound], &getTransform());
if (mEngineSound != NULL_AUDIOHANDLE) {
alxSourceMatrixF(mEngineSound, &getTransform());
F32 denom = mDataBlock->mainThrustForce + mDataBlock->strafeThrustForce;
F32 factor = getMin(mThrustLevel, denom) / denom;
F32 vol = 0.25 + factor * 0.75;
alxSourcef(mEngineSound, AL_GAIN_LINEAR, vol);
}
// Are we floating? If so, start the floating sound...
if (mFloating) {
if (mFloatSound == NULL_AUDIOHANDLE && mDataBlock->sound[HoverVehicleData::FloatSound] != NULL)
mFloatSound = alxPlay(mDataBlock->sound[HoverVehicleData::FloatSound], &getTransform());
if (mFloatSound != NULL_AUDIOHANDLE)
alxSourceMatrixF(mFloatSound, &getTransform());
} else {
if (mFloatSound != NULL_AUDIOHANDLE) {
alxStop(mFloatSound);
mFloatSound = NULL_AUDIOHANDLE;
}
}
updateJet(dt);
updateDustTrail( dt );
}
//--------------------------------------------------------------------------
U32 HoverVehicle::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
//
stream->writeInt(mThrustDirection,NumThrustBits);
return retMask;
}
void HoverVehicle::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits));
//
}
//--------------------------------------------------------------------------
void HoverVehicle::updateMove(const Move* move)
{
Parent::updateMove(move);
mForwardThrust = mThrottle > 0.0f ? mThrottle : 0.0f;
mReverseThrust = mThrottle < 0.0f ? -mThrottle : 0.0f;
mLeftThrust = move->x < 0.0f ? -move->x : 0.0f;
mRightThrust = move->x > 0.0f ? move->x : 0.0f;
mThrustDirection = (!move->y)? ThrustDown: (move->y > 0)? ThrustForward: ThrustBackward;
}
F32 HoverVehicle::getBaseStabilizerLength() const
{
F32 base = mDataBlock->stabLenMin;
F32 lengthDiff = mDataBlock->stabLenMax - mDataBlock->stabLenMin;
F32 velLength = mRigid.linVelocity.len();
F32 minVel = getMin(velLength, mDataBlock->maxThrustSpeed);
F32 velDiff = mDataBlock->maxThrustSpeed - minVel;
F32 velRatio = velDiff / mDataBlock->maxThrustSpeed;
F32 inc = lengthDiff * ( 1.0 - velRatio );
base += inc;
return base;
}
struct StabPoint
{
Point3F osPoint; //
Point3F wsPoint; //
F32 extension;
Point3F wsExtension; //
Point3F wsVelocity; //
};
void HoverVehicle::updateForces(F32 /*dt*/)
{
Point3F gravForce(0, 0, sHoverVehicleGravity * mRigid.mass * mGravityMod);
MatrixF currTransform;
mRigid.getTransform(&currTransform);
mRigid.atRest = false;
mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce +
mReverseThrust * mDataBlock->reverseThrustForce +
mLeftThrust * mDataBlock->strafeThrustForce +
mRightThrust * mDataBlock->strafeThrustForce);
Point3F thrustForce = ((Point3F( 0, 1, 0) * (mForwardThrust * mDataBlock->mainThrustForce)) +
(Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) +
(Point3F(-1, 0, 0) * (mLeftThrust * mDataBlock->strafeThrustForce)) +
(Point3F( 1, 0, 0) * (mRightThrust * mDataBlock->strafeThrustForce)));
currTransform.mulV(thrustForce);
if (mJetting)
thrustForce *= mDataBlock->turboFactor;
Point3F torque(0, 0, 0);
Point3F force(0, 0, 0);
Point3F vel = mRigid.linVelocity;
F32 baseStabLen = getBaseStabilizerLength();
Point3F stabExtend(0, 0, -baseStabLen);
currTransform.mulV(stabExtend);
StabPoint stabPoints[2];
stabPoints[0].osPoint = Point3F((mObjBox.min.x + mObjBox.max.x) * 0.5,
mObjBox.max.y,
(mObjBox.min.z + mObjBox.max.z) * 0.5);
stabPoints[1].osPoint = Point3F((mObjBox.min.x + mObjBox.max.x) * 0.5,
mObjBox.min.y,
(mObjBox.min.z + mObjBox.max.z) * 0.5);
U32 j, i;
for (i = 0; i < 2; i++) {
currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint);
stabPoints[i].wsExtension = stabExtend;
stabPoints[i].extension = baseStabLen;
stabPoints[i].wsVelocity = mRigid.linVelocity;
}
RayInfo rinfo;
mFloating = true;
bool reallyFloating = true;
F32 compression[2] = { 0.0f, 0.0f };
F32 normalMod[2] = { 0.0f, 0.0f };
bool normalSet[2] = { false, false };
Point3F normal[2];
for (j = 0; j < 2; j++) {
if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0,
TerrainObjectType | InteriorObjectType | WaterObjectType, &rinfo)) {
reallyFloating = false;
if (rinfo.t <= 0.5) {
// Ok, stab is in contact with the ground, let's calc the forces...
compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen;
}
normalSet[j] = true;
normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0));
normal[j] = rinfo.normal;
}
// Check the waterblock directly
SimpleQueryList sql;
mSceneManager->getWaterObjectList(sql);
for (U32 i = 0; i < sql.mList.size(); i++)
{
WaterBlock* pBlock = static_cast<WaterBlock*>(sql.mList[i]);
if (pBlock->isPointSubmerged(stabPoints[j].wsPoint))
{
compression[j] = baseStabLen;
break;
}
}
}
for (j = 0; j < 2; j++) {
if (compression[j] != 0.0) {
mFloating = false;
// Spring force and damping
Point3F springForce = -stabPoints[j].wsExtension;
springForce.normalize();
springForce *= compression[j] * mDataBlock->stabSpringConstant;
Point3F springDamping = -stabPoints[j].wsExtension;
springDamping.normalize();
springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant;
force += springForce + springDamping;
}
}
// Gravity
if (reallyFloating == false)
force += gravForce;
else
force += gravForce * mDataBlock->floatingGravMag;
// Braking
F32 vellen = mRigid.linVelocity.len();
if (mThrottle == 0.0f &&
mLeftThrust == 0.0f &&
mRightThrust == 0.0f &&
vellen != 0.0f &&
vellen < mDataBlock->brakingActivationSpeed)
{
Point3F dir = mRigid.linVelocity;
dir.normalize();
dir.neg();
force += dir * mDataBlock->brakingForce;
}
// Gyro Drag
torque = -mRigid.angMomentum * mDataBlock->gyroDrag;
// Move to proper normal
Point3F sn, r;
currTransform.getColumn(2, &sn);
if (normalSet[0] || normalSet[1]) {
if (normalSet[0] && normalSet[1]) {
F32 dot = mDot(normal[0], normal[1]);
if (dot > 0.999) {
// Just pick the first normal. They're too close to call
if ((sn - normal[0]).lenSquared() > 0.00001) {
mCross(sn, normal[0], &r);
torque += r * mDataBlock->normalForce * normalMod[0];
}
} else {
Point3F rotAxis;
mCross(normal[0], normal[1], &rotAxis);
rotAxis.normalize();
F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1]));
AngAxisF aa(rotAxis, angle);
QuatF q(aa);
MatrixF tempMat(true);
q.setMatrix(&tempMat);
Point3F newNormal;
tempMat.mulV(normal[1], &newNormal);
if ((sn - newNormal).lenSquared() > 0.00001) {
mCross(sn, newNormal, &r);
torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5));
}
}
} else {
Point3F useNormal;
F32 useMod;
if (normalSet[0]) {
useNormal = normal[0];
useMod = normalMod[0];
} else {
useNormal = normal[1];
useMod = normalMod[1];
}
if ((sn - useNormal).lenSquared() > 0.00001) {
mCross(sn, useNormal, &r);
torque += r * mDataBlock->normalForce * useMod;
}
}
} else {
if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) {
mCross(sn, Point3F(0, 0, 1), &r);
torque += r * mDataBlock->restorativeForce;
}
}
Point3F sn2;
currTransform.getColumn(0, &sn);
currTransform.getColumn(1, &sn2);
mCross(sn, sn2, &r);
r.normalize();
torque -= r * (mSteering.x * mDataBlock->steeringForce);
currTransform.getColumn(0, &sn);
currTransform.getColumn(2, &sn2);
mCross(sn, sn2, &r);
r.normalize();
torque -= r * (mSteering.x * mDataBlock->rollForce);
currTransform.getColumn(1, &sn);
currTransform.getColumn(2, &sn2);
mCross(sn, sn2, &r);
r.normalize();
torque -= r * (mSteering.y * mDataBlock->pitchForce);
// Apply drag
Point3F vDrag = mRigid.linVelocity;
if (!mFloating) {
vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
} else {
vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor));
}
force -= vDrag * mDataBlock->dragForce;
force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce;
// Add in physical zone force
force += mAppliedForce;
// Container buoyancy & drag
force += Point3F(0, 0,-mBuoyancy * sHoverVehicleGravity * mRigid.mass * mGravityMod);
force -= mRigid.linVelocity * mDrag;
torque -= mRigid.angMomentum * mDrag;
mRigid.force = force;
mRigid.torque = torque;
}
//--------------------------------------------------------------------------
U32 HoverVehicle::getCollisionMask()
{
if (isServerObject())
return sServerCollisionMask;
else
return sClientCollisionMask;
}
void HoverVehicle::updateDustTrail( F32 dt )
{
if( !mDustTrailEmitter ) return;
// check if close to ground
Point3F startPos = getPosition();
Point3F endPos = startPos + Point3F( 0.0, 0.0, -mDataBlock->triggerTrailHeight );
RayInfo rayInfo;
if( !getContainer()->castRay( startPos, endPos, TerrainObjectType, &rayInfo ) )
{
return;
}
VectorF vel = getVelocity();
TerrainBlock* tBlock = static_cast<TerrainBlock*>(rayInfo.object);
S32 mapIndex = tBlock->mMPMIndex[0];
MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
const MaterialPropertyMap::MapEntry* pEntry = pMatMap->getMapEntryFromIndex(mapIndex);
// emit dust if moving
if( vel.len() > 2.0 && pEntry)
{
VectorF axis = vel;
axis.normalize();
S32 x;
ColorF colorList[ParticleEngine::PC_COLOR_KEYS];
for(x = 0; x < 2; ++x)
colorList[x].set( pEntry->puffColor[x].red, pEntry->puffColor[x].green, pEntry->puffColor[x].blue, pEntry->puffColor[x].alpha );
for(x = 2; x < ParticleEngine::PC_COLOR_KEYS; ++x)
colorList[x].set( 1.0, 1.0, 1.0, 0.0 );
mDustTrailEmitter->setColors( colorList );
Point3F contactPoint = rayInfo.point + mDataBlock->dustTrailOffset;
mDustTrailEmitter->emitParticles( contactPoint , true, axis, vel, (U32)(dt * 1000 * (vel.len() / mDataBlock->dustTrailFreqMod)));
}
}
void HoverVehicle::updateJet(F32 dt)
{
if (mJetThread[BackActivate] == NULL)
return;
F32 pos = mShapeInstance->getPos(mJetThread[BackActivate]);
F32 scale = mShapeInstance->getTimeScale(mJetThread[BackActivate]);
// Thrust Animation threads
// Back
if (mJetSeq[BackActivate] >=0 ) {
if (!mBackMaintainOn || mThrustDirection != ThrustForward) {
if (mBackMaintainOn) {
mShapeInstance->setPos(mJetThread[BackActivate], 1);
mShapeInstance->destroyThread(mJetThread[BackMaintain]);
mBackMaintainOn = false;
}
mShapeInstance->setTimeScale(mJetThread[BackActivate],
(mThrustDirection == ThrustForward)? 1: -1);
mShapeInstance->advanceTime(dt,mJetThread[BackActivate]);
}
}
if (mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn &&
mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0)
{
mShapeInstance->setPos(mJetThread[BackActivate], 0);
mShapeInstance->setTimeScale(mJetThread[BackActivate], 0);
mJetThread[BackMaintain] = mShapeInstance->addThread();
mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0);
mShapeInstance->setTimeScale(mJetThread[BackMaintain],1);
mBackMaintainOn = true;
}
if(mBackMaintainOn)
mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]);
// Jet particles
for (S32 j = 0; j < NumThrustDirections; j++) {
JetActivation& jet = sJetActivation[j];
updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter],
jet.node,HoverVehicleData::MaxDirectionJets);
}
}
void HoverVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count)
{
if (!emitter)
return;
for (S32 j = idx; j < idx + count; j++)
if (active) {
if (mDataBlock->jetNode[j] != -1) {
if (!bool(mJetEmitter[j])) {
mJetEmitter[j] = new ParticleEmitter;
mJetEmitter[j]->onNewDataBlock(emitter);
mJetEmitter[j]->registerObject();
}
MatrixF mat;
Point3F pos,axis;
mat.mul(getRenderTransform(),
mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]);
mat.getColumn(1,&axis);
mat.getColumn(3,&pos);
mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000));
}
}
else {
for (S32 j = idx; j < idx + count; j++)
if (bool(mJetEmitter[j])) {
mJetEmitter[j]->deleteWhenEmpty();
mJetEmitter[j] = 0;
}
}
}