mirror of
https://github.com/tribes2/engine.git
synced 2026-01-22 20:54:53 +00:00
1288 lines
39 KiB
C++
1288 lines
39 KiB
C++
//-----------------------------------------------------------------------------
|
|
// V12 Engine
|
|
//
|
|
// Copyright (c) 2001 GarageGames.Com
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "dgl/dgl.h"
|
|
#include "game/game.h"
|
|
#include "math/mMath.h"
|
|
#include "console/simBase.h"
|
|
#include "console/console.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "collision/clippedPolyList.h"
|
|
#include "collision/planeExtractor.h"
|
|
#include "game/moveManager.h"
|
|
#include "core/bitStream.h"
|
|
#include "core/dnet.h"
|
|
#include "game/gameConnection.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "game/wheeledVehicle.h"
|
|
#include "game/particleEngine.h"
|
|
#include "audio/audio.h"
|
|
#include "game/forceFieldBare.h"
|
|
#include "scenegraph/sceneGraph.h"
|
|
#include "sim/decalManager.h"
|
|
#include "dgl/materialPropertyMap.h"
|
|
#include "terrain/terrData.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
static U32 sCollisionMoveMask = (TerrainObjectType | InteriorObjectType |
|
|
PlayerObjectType | StaticShapeObjectType |
|
|
VehicleObjectType | VehicleBlockerObjectType |
|
|
ForceFieldObjectType | StaticTSObjectType);
|
|
static U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
|
|
static U32 sClientCollisionMask = sCollisionMoveMask;
|
|
|
|
static F32 sWheeledVehicleGravity = -20;
|
|
static F32 sBreakZeroEpsilon = 0.02; // m/s
|
|
static F32 sTireEmitterVerticalScale = 0.7;
|
|
static F32 sTireCollisionExpansion = 0.02; // Expanded size in buildPolyList
|
|
|
|
// Sound
|
|
static F32 sMinSqueelVolume = 0.05;
|
|
static F32 sIdleEngineVolume = 0.2;
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleData);
|
|
|
|
WheeledVehicleData::WheeledVehicleData()
|
|
{
|
|
tire.friction = 0.3;
|
|
tire.restitution = 1;
|
|
tire.radius = 0.6;
|
|
tire.lateralForce = 1000;
|
|
tire.lateralDamping = 100;
|
|
tire.lateralRelaxation = 1;
|
|
tire.longitudinalForce = 1000;
|
|
tire.longitudinalDamping = 100;
|
|
tire.longitudinalRelaxation = 1;
|
|
tire.emitter = 0;
|
|
maxWheelSpeed = 40;
|
|
engineTorque = 1;
|
|
breakTorque = 1;
|
|
staticLoadScale = 1;
|
|
antiSwayForce = 1;
|
|
antiRockForce = 0;
|
|
springForce = 0.6;
|
|
tailLightSequence = -1;
|
|
|
|
stabilizerForce = 0;
|
|
gyroForce = 0;
|
|
gyroDamping = 0;
|
|
|
|
for (S32 i = 0; i < MaxSounds; i++)
|
|
sound[i] = 0;
|
|
}
|
|
|
|
bool WheeledVehicleData::preload(bool server, char errorBuffer[256])
|
|
{
|
|
if (!Parent::preload(server, errorBuffer))
|
|
return false;
|
|
|
|
TSShapeInstance* si = new TSShapeInstance(shape, false);
|
|
|
|
// Resolve objects transmitted from server
|
|
if (!server) {
|
|
for (S32 i = 0; i < MaxSounds; i++)
|
|
if (sound[i])
|
|
Sim::findObject(SimObjectId(sound[i]),sound[i]);
|
|
|
|
if (tire.emitter)
|
|
Sim::findObject(SimObjectId(tire.emitter),tire.emitter);
|
|
}
|
|
|
|
// Extract wheel information from the shape
|
|
TSThread* thread = si->addThread();
|
|
Wheel* wp = wheel;
|
|
for (S32 i = 0; i < MaxWheels; i++) {
|
|
char buff[10];
|
|
|
|
// Spring and ground information have to exist for
|
|
// the wheel to operate at all.
|
|
dSprintf(buff,sizeof(buff),"ground%d",i);
|
|
wp->springNode = shape->findNode(buff);
|
|
dSprintf(buff,sizeof(buff),"spring%d",i);
|
|
wp->springSequence = shape->findSequence(buff);
|
|
if (wp->springSequence != -1 && wp->springNode != -1) {
|
|
|
|
// Extract spring pos & movement
|
|
si->setSequence(thread,wp->springSequence,0);
|
|
si->animate();
|
|
si->mNodeTransforms[wp->springNode].getColumn(3,&wp->spring);
|
|
si->setPos(thread,1);
|
|
si->animate();
|
|
si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
|
|
wp->safePos.set(wp->pos.x, wp->pos.y, wp->pos.z + tire.radius);
|
|
if (!mirrorWheel(wp)) {
|
|
wp->spring.x = wp->spring.y = 0;
|
|
wp->spring.z -= wp->pos.z;
|
|
}
|
|
|
|
// More animation sequences
|
|
dSprintf(buff,sizeof(buff),"turn%d",i);
|
|
wp->steeringSequence = shape->findSequence(buff);
|
|
dSprintf(buff,sizeof(buff),"wheel%d",i);
|
|
wp->rotationSequence = shape->findSequence(buff);
|
|
|
|
//
|
|
wp->springRest = 0.5;
|
|
wp->springForce = springForce;
|
|
wp->springDamping = springDamping;
|
|
wp->steering = (wp->steeringSequence != -1)? Wheel::Forward: Wheel::None;
|
|
wp++;
|
|
}
|
|
}
|
|
wheelCount = wp - wheel;
|
|
|
|
//
|
|
tailLightSequence = shape->findSequence("taillight");
|
|
|
|
// Extract collision planes from shape collision detail level
|
|
if (collisionDetails[0] != -1) {
|
|
MatrixF imat(1);
|
|
SphereF sphere;
|
|
sphere.center = shape->center;
|
|
sphere.radius = shape->radius;
|
|
PlaneExtractorPolyList polyList;
|
|
polyList.mPlaneList = &rigidBody.mPlaneList;
|
|
polyList.setTransform(&imat, Point3F(1,1,1));
|
|
si->buildPolyList(&polyList,collisionDetails[0]);
|
|
}
|
|
|
|
delete si;
|
|
return true;
|
|
}
|
|
|
|
bool WheeledVehicleData::mirrorWheel(Wheel* we)
|
|
{
|
|
we->opposite = -1;
|
|
// Find matching wheel mirrored along Y axis
|
|
for (Wheel* wp = wheel; wp != we; wp++)
|
|
if (mFabs(wp->pos.y - we->pos.y) < 0.5) {
|
|
we->pos.x = -wp->pos.x;
|
|
we->pos.y = wp->pos.y;
|
|
we->pos.z = wp->pos.z;
|
|
we->spring = wp->spring;
|
|
we->opposite = wp - wheel;
|
|
wp->opposite = we - wheel;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void WheeledVehicleData::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addField("jetSound", TypeAudioProfilePtr, Offset(sound[JetSound], WheeledVehicleData));
|
|
addField("engineSound", TypeAudioProfilePtr, Offset(sound[EngineSound], WheeledVehicleData));
|
|
addField("squeelSound", TypeAudioProfilePtr, Offset(sound[SqueelSound], WheeledVehicleData));
|
|
addField("WheelImpactSound", TypeAudioProfilePtr, Offset(sound[WheelImpactSound], WheeledVehicleData));
|
|
|
|
addField("tireRadius", TypeF32, Offset(tire.radius, WheeledVehicleData));
|
|
addField("tireFriction", TypeF32, Offset(tire.friction, WheeledVehicleData));
|
|
addField("tireRestitution", TypeF32, Offset(tire.restitution, WheeledVehicleData));
|
|
addField("tireLateralForce", TypeF32, Offset(tire.lateralForce, WheeledVehicleData));
|
|
addField("tireLateralDamping", TypeF32, Offset(tire.lateralDamping, WheeledVehicleData));
|
|
addField("tireLateralRelaxation", TypeF32, Offset(tire.lateralRelaxation, WheeledVehicleData));
|
|
addField("tireLongitudinalForce", TypeF32, Offset(tire.longitudinalForce, WheeledVehicleData));
|
|
addField("tireLongitudinalDamping", TypeF32, Offset(tire.longitudinalDamping, WheeledVehicleData));
|
|
addField("tireLogitudinalRelaxation", TypeF32, Offset(tire.longitudinalRelaxation, WheeledVehicleData));
|
|
addField("tireEmitter",TypeParticleEmitterDataPtr, Offset(tire.emitter, WheeledVehicleData));
|
|
|
|
addField("maxWheelSpeed", TypeF32, Offset(maxWheelSpeed, WheeledVehicleData));
|
|
addField("engineTorque", TypeF32, Offset(engineTorque, WheeledVehicleData));
|
|
addField("breakTorque", TypeF32, Offset(breakTorque, WheeledVehicleData));
|
|
addField("staticLoadScale", TypeF32, Offset(staticLoadScale, WheeledVehicleData));
|
|
addField("springForce", TypeF32, Offset(springForce, WheeledVehicleData));
|
|
addField("springDamping", TypeF32, Offset(springDamping, WheeledVehicleData));
|
|
addField("antiSwayForce", TypeF32, Offset(antiSwayForce, WheeledVehicleData));
|
|
addField("antiRockForce", TypeF32, Offset(antiRockForce, WheeledVehicleData));
|
|
|
|
addField("stabilizerForce", TypeF32, Offset(stabilizerForce, WheeledVehicleData));
|
|
addField("gyroForce", TypeF32, Offset(gyroForce, WheeledVehicleData));
|
|
addField("gyroDamping", TypeF32, Offset(gyroDamping, WheeledVehicleData));
|
|
}
|
|
|
|
void WheeledVehicleData::packData(BitStream* stream)
|
|
{
|
|
Parent::packData(stream);
|
|
|
|
stream->write(tire.friction);
|
|
stream->write(tire.restitution);
|
|
stream->write(tire.radius);
|
|
stream->write(tire.lateralForce);
|
|
stream->write(tire.lateralDamping);
|
|
stream->write(tire.lateralRelaxation);
|
|
stream->write(tire.longitudinalForce);
|
|
stream->write(tire.longitudinalDamping);
|
|
stream->write(tire.longitudinalRelaxation);
|
|
|
|
if (stream->writeFlag(tire.emitter))
|
|
stream->writeRangedU32(packed? SimObjectId(tire.emitter):
|
|
tire.emitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
|
|
|
for (S32 i = 0; i < MaxSounds; i++)
|
|
if (stream->writeFlag(sound[i]))
|
|
stream->writeRangedU32(packed? SimObjectId(sound[i]):
|
|
sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
|
|
|
stream->write(springForce);
|
|
stream->write(springDamping);
|
|
stream->write(antiSwayForce);
|
|
stream->write(antiRockForce);
|
|
stream->write(maxWheelSpeed);
|
|
stream->write(engineTorque);
|
|
stream->write(breakTorque);
|
|
stream->write(staticLoadScale);
|
|
|
|
stream->write(stabilizerForce);
|
|
stream->write(gyroForce);
|
|
stream->write(gyroDamping);
|
|
}
|
|
|
|
void WheeledVehicleData::unpackData(BitStream* stream)
|
|
{
|
|
Parent::unpackData(stream);
|
|
|
|
stream->read(&tire.friction);
|
|
stream->read(&tire.restitution);
|
|
stream->read(&tire.radius);
|
|
stream->read(&tire.lateralForce);
|
|
stream->read(&tire.lateralDamping);
|
|
stream->read(&tire.lateralRelaxation);
|
|
stream->read(&tire.longitudinalForce);
|
|
stream->read(&tire.longitudinalDamping);
|
|
stream->read(&tire.longitudinalRelaxation);
|
|
|
|
tire.emitter = stream->readFlag()?
|
|
(ParticleEmitterData*) stream->readRangedU32(DataBlockObjectIdFirst,
|
|
DataBlockObjectIdLast): 0;
|
|
|
|
for (S32 i = 0; i < MaxSounds; i++)
|
|
sound[i] = stream->readFlag()?
|
|
(AudioProfile*) stream->readRangedU32(DataBlockObjectIdFirst,
|
|
DataBlockObjectIdLast): 0;
|
|
|
|
stream->read(&springForce);
|
|
stream->read(&springDamping);
|
|
stream->read(&antiSwayForce);
|
|
stream->read(&antiRockForce);
|
|
stream->read(&maxWheelSpeed);
|
|
stream->read(&engineTorque);
|
|
stream->read(&breakTorque);
|
|
stream->read(&staticLoadScale);
|
|
|
|
stream->read(&stabilizerForce);
|
|
stream->read(&gyroForce);
|
|
stream->read(&gyroDamping);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CO_NETOBJECT_V1(WheeledVehicle);
|
|
|
|
WheeledVehicle::WheeledVehicle()
|
|
{
|
|
mGenerateShadow = true;
|
|
|
|
mBraking = false;
|
|
mWheelContact = false;
|
|
mTailLightThread = 0;
|
|
|
|
for (S32 i = 0; i < WheeledVehicleData::MaxWheels; i++) {
|
|
mWheel[i].springThread = 0;
|
|
mWheel[i].steeringThread = 0;
|
|
mWheel[i].rotationThread = 0;
|
|
mWheel[i].Dy = mWheel[i].Dx = 0;
|
|
}
|
|
|
|
mJetSound = 0;
|
|
mEngineSound = 0;
|
|
mSqueelSound = 0;
|
|
|
|
mDataBlock = NULL;
|
|
}
|
|
|
|
WheeledVehicle::~WheeledVehicle()
|
|
{
|
|
if (mJetSound)
|
|
alxStop(mJetSound);
|
|
if (mEngineSound)
|
|
alxStop(mEngineSound);
|
|
if (mSqueelSound)
|
|
alxStop(mSqueelSound);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool WheeledVehicle::onAdd()
|
|
{
|
|
if(!Parent::onAdd())
|
|
return false;
|
|
|
|
addToScene();
|
|
if (isServerObject())
|
|
scriptOnAdd();
|
|
return true;
|
|
}
|
|
|
|
bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr)
|
|
{
|
|
mDataBlock = dynamic_cast<WheeledVehicleData*>(dptr);
|
|
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
|
|
return false;
|
|
|
|
F32 frontStatic = 0;
|
|
F32 backStatic = 0;
|
|
F32 fCount = 0;
|
|
F32 bCount = 0;
|
|
|
|
// Wheel threads
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
WheeledVehicleData::Wheel* wd = &mDataBlock->wheel[i];
|
|
Wheel* wp = &mWheel[i];
|
|
|
|
wp->surface.contact = false;
|
|
wp->surface.object = NULL;
|
|
wp->evel = 0;
|
|
wp->avel = 0;
|
|
wp->apos = 0;
|
|
wp->steeringThread = 0;
|
|
wp->springThread = 0;
|
|
wp->rotationThread = 0;
|
|
|
|
if (wd->steeringSequence != -1) {
|
|
wp->steeringThread = mShapeInstance->addThread();
|
|
mShapeInstance->setSequence(wp->steeringThread,wd->steeringSequence,0);
|
|
}
|
|
if (wd->springSequence != -1) {
|
|
wp->springThread = mShapeInstance->addThread();
|
|
mShapeInstance->setSequence(wp->springThread,wd->springSequence,0);
|
|
}
|
|
if (wd->rotationSequence != -1) {
|
|
wp->rotationThread = mShapeInstance->addThread();
|
|
mShapeInstance->setSequence(wp->rotationThread,wd->rotationSequence,0);
|
|
}
|
|
|
|
// Set springs to currenty gravity
|
|
wp->k = wd->springForce;
|
|
wp->s = wd->springDamping;
|
|
// F32 staticForce = (mMass * -sWheeledVehicleGravity) / mDataBlock->wheelCount;
|
|
// F32 sprungForce = staticForce * 0.8;
|
|
// wp->center = wd->springRest + (sprungForce / wp->k);
|
|
// if (wp->center > 1)
|
|
wp->center = 1;
|
|
wp->extension = wp->center;
|
|
|
|
//
|
|
wp->particleSlip = 0;
|
|
if (mDataBlock->tire.emitter) {
|
|
wp->emitter = new ParticleEmitter;
|
|
wp->emitter->onNewDataBlock(mDataBlock->tire.emitter);
|
|
wp->emitter->registerObject();
|
|
}
|
|
else
|
|
wp->emitter = NULL;
|
|
}
|
|
|
|
//
|
|
if (mDataBlock->tailLightSequence != -1) {
|
|
mTailLightThread = mShapeInstance->addThread();
|
|
mShapeInstance->setSequence(mTailLightThread,mDataBlock->tailLightSequence,0);
|
|
}
|
|
else
|
|
mTailLightThread = 0;
|
|
|
|
// Sounds
|
|
if (mJetSound) {
|
|
alxStop(mJetSound);
|
|
mJetSound = 0;
|
|
}
|
|
if (mEngineSound) {
|
|
alxStop(mEngineSound);
|
|
mEngineSound = 0;
|
|
}
|
|
if (mSqueelSound) {
|
|
alxStop(mSqueelSound);
|
|
mSqueelSound = 0;
|
|
}
|
|
if (isGhost()) {
|
|
if (mDataBlock->sound[WheeledVehicleData::EngineSound])
|
|
mEngineSound = alxPlay(mDataBlock->sound[WheeledVehicleData::EngineSound], &getTransform());
|
|
}
|
|
|
|
scriptOnNewDataBlock();
|
|
return true;
|
|
}
|
|
|
|
void WheeledVehicle::onRemove()
|
|
{
|
|
if (mDataBlock != NULL)
|
|
{
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
if (bool(wp->emitter)) {
|
|
wp->emitter->deleteWhenEmpty();
|
|
wp->emitter = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
scriptOnRemove();
|
|
removeFromScene();
|
|
Parent::onRemove();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::updateWarp()
|
|
{
|
|
updateWheels();
|
|
}
|
|
|
|
void WheeledVehicle::advanceTime(F32 dt)
|
|
{
|
|
Parent::advanceTime(dt);
|
|
|
|
Point3F bz;
|
|
mObjToWorld.getColumn(2,&bz);
|
|
|
|
if (mTailLightThread)
|
|
mShapeInstance->advanceTime(dt,mTailLightThread);
|
|
|
|
// Update wheels
|
|
if (mFrozen == false)
|
|
{
|
|
F32 slipTotal = 0;
|
|
F32 torqueTotal = 0;
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++)
|
|
{
|
|
Wheel* wp = &mWheel[i];
|
|
|
|
// Update angular position
|
|
wp->apos += (wp->avel * dt) / M_2PI;
|
|
wp->apos -= mFloor(wp->apos);
|
|
if (wp->apos < 0)
|
|
wp->apos = 1 - wp->apos;
|
|
|
|
// Keep track of largest slip
|
|
slipTotal += wp->particleSlip;
|
|
torqueTotal += wp->torqueScale;
|
|
|
|
Point3F wv = Parent::getVelocity();
|
|
|
|
// emit dust if moving
|
|
if( wv.len() > 1.0 && wp->surface.object && wp->surface.object->getTypeMask() & TerrainObjectType )
|
|
{
|
|
TerrainBlock* tBlock = static_cast<TerrainBlock*>(wp->surface.object);
|
|
S32 mapIndex = tBlock->mMPMIndex[0];
|
|
|
|
MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
|
|
const MaterialPropertyMap::MapEntry* pEntry = pMatMap->getMapEntryFromIndex(mapIndex);
|
|
|
|
if(pEntry)
|
|
{
|
|
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 );
|
|
|
|
wp->emitter->setColors( colorList );
|
|
}
|
|
Point3F axis = wv;
|
|
axis.normalize();
|
|
|
|
wp->emitter->emitParticles(wp->surface.pos + Point3F( 0.0, 0.0, 0.5 ),true,
|
|
axis, wv, dt * 1000 * (wv.len() / 15.0) );
|
|
}
|
|
}
|
|
|
|
//
|
|
updateJet(dt);
|
|
updateSqueelSound(slipTotal / mDataBlock->wheelCount);
|
|
updateEngineSound(sIdleEngineVolume + (1 - sIdleEngineVolume) *
|
|
(1 - (torqueTotal / mDataBlock->wheelCount)));
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool WheeledVehicle::buildPolyList(AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
|
|
{
|
|
// Parent will take care of body collision.
|
|
Parent::buildPolyList(polyList,box,sphere);
|
|
|
|
// Add wheels.
|
|
Box3F wbox;
|
|
wbox.min.x = -(wbox.max.x = (mDataBlock->tire.radius / 2) + sTireCollisionExpansion);
|
|
wbox.min.y = -(wbox.max.y = mDataBlock->tire.radius + sTireCollisionExpansion);
|
|
wbox.max.z = 2 * mDataBlock->tire.radius;
|
|
wbox.min.z = 0;
|
|
MatrixF mat = mObjToWorld;
|
|
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
WheeledVehicleData::Wheel* wd = &mDataBlock->wheel[i];
|
|
Wheel* wp = &mWheel[i];
|
|
|
|
Point3F sp,vec;
|
|
mObjToWorld.mulP(wd->pos,&sp);
|
|
mObjToWorld.mulV(wd->spring,&vec);
|
|
Point3F ep = sp + (vec * wp->extension);
|
|
mat.setColumn(3,ep);
|
|
polyList->setTransform(&mat,Point3F(1,1,1));
|
|
polyList->addBox(wbox);
|
|
}
|
|
return !polyList->isEmpty();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void WheeledVehicle::processTick(const Move* move)
|
|
{
|
|
Parent::processTick(move);
|
|
|
|
if (isServerObject() && mWaterCoverage > 0.5)
|
|
{
|
|
char buffer1[256], buffer2[256];
|
|
dSprintf(buffer1, 255, "%f %f %f",
|
|
mRigid.state.linPosition.x,
|
|
mRigid.state.linPosition.y,
|
|
mRigid.state.linPosition.z);
|
|
dSprintf(buffer2, 255, "%d", Con::getIntVariable("$DamageType::Water"));
|
|
Con::executef(mDataBlock, 6, "damageObject", scriptThis(), "0", buffer1, "1000", buffer2);
|
|
|
|
}
|
|
}
|
|
|
|
void WheeledVehicle::updateMove(const Move* move)
|
|
{
|
|
Parent::updateMove(move);
|
|
|
|
// Breaking
|
|
F32 wvel = 0;
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++)
|
|
wvel += mWheel[i].avel;
|
|
if (mFabs(wvel * mDataBlock->tire.radius) < sBreakZeroEpsilon)
|
|
wvel = 0;
|
|
if (mBraking) {
|
|
// If the throttle is not set, or is set in the same direction
|
|
// as are wheel velocity, then we are no longer breaking.
|
|
if (!wvel || !mThrottle || (mThrottle > 0 && wvel > 0) || (mThrottle < 0 && wvel < 0))
|
|
mBraking = false;
|
|
}
|
|
else
|
|
// If the throttle is opposite to our wheel velocity, then break.
|
|
if ((mThrottle > 0 && wvel < 0) || (mThrottle < 0 && wvel > 0))
|
|
mBraking = true;
|
|
|
|
if (mTailLightThread)
|
|
mShapeInstance->setTimeScale(mTailLightThread,mBraking? 1: -1);
|
|
|
|
//
|
|
updateWheels();
|
|
|
|
if (mWheelContact)
|
|
mSteering.y = 0;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::updateForces(F32 dt)
|
|
{
|
|
S32 j;
|
|
|
|
MatrixF currMatrix;
|
|
mRigid.state.getTransform(&currMatrix);
|
|
MatrixF currMatrixInv = currMatrix;
|
|
currMatrixInv.inverse();
|
|
|
|
//
|
|
F32 oneOverSprungMass = 1 / (mMass * 0.8);
|
|
F32 maxAvel = mDataBlock->maxWheelSpeed / mDataBlock->tire.radius;
|
|
F32 aMomentum = mMass / mDataBlock->wheelCount;
|
|
|
|
mRigid.state.force.set(0, 0, 0);
|
|
mRigid.state.torque.set(0, 0, 0);
|
|
|
|
// Drag
|
|
{
|
|
mRigid.state.force -= mRigid.state.linVelocity * mDataBlock->minDrag * mOneOverMass;
|
|
mRigid.state.torque -= mRigid.state.angMomentum * mDataBlock->antiRockForce * mOneOverMass;
|
|
}
|
|
|
|
// Body & Steering Vectors
|
|
Point3F bx,by,bz;
|
|
{
|
|
currMatrix.getColumn(0,&bx);
|
|
currMatrix.getColumn(1,&by);
|
|
currMatrix.getColumn(2,&bz);
|
|
}
|
|
|
|
Point3F worldZ(0, 0, 1);
|
|
Point3F worldY(0, 1, 0);
|
|
currMatrix.getColumn(1, &worldY);
|
|
currMatrix.getColumn(2, &worldZ);
|
|
|
|
F32 quadraticSteering = -(mSteering.x * mFabs(mSteering.x));
|
|
F32 cosSteering;
|
|
F32 sinSteering;
|
|
mSinCos(quadraticSteering, sinSteering, cosSteering);
|
|
|
|
// Center of mass in world space
|
|
Point3F massCenter;
|
|
currMatrix.mulP(mDataBlock->massCenter, &massCenter);
|
|
currMatrix.mulP(Point3F(0, 0, 0), &massCenter);
|
|
|
|
Point3F force = Point3F(0,0,0);
|
|
Point3F torque = Point3F(0,0,0);
|
|
|
|
AssertFatal(mDataBlock->wheelCount < 8, "Error, only <= 8 wheels supported");
|
|
Point3F wheelForce[8];
|
|
|
|
// Calculate vertical load for friction. This was marked by TimG as "a hack"
|
|
U32 contactCount = 0;
|
|
F32 verticalLoad = 0;
|
|
{
|
|
for (j = 0; j < mDataBlock->wheelCount; j++) {
|
|
if (mWheel[j].surface.contact)
|
|
contactCount++;
|
|
}
|
|
|
|
if (contactCount != 0) {
|
|
verticalLoad = (mDataBlock->staticLoadScale *
|
|
(mMass * -sWheeledVehicleGravity) / contactCount);
|
|
}
|
|
}
|
|
|
|
// Engine and break torque
|
|
F32 engineTorque,breakTorque,maxBreakVel;
|
|
if (mBraking) {
|
|
breakTorque = mDataBlock->breakTorque * mFabs(mThrottle);
|
|
maxBreakVel = (breakTorque / aMomentum) * dt;
|
|
engineTorque = 0;
|
|
}
|
|
else {
|
|
if (mThrottle) {
|
|
engineTorque = mDataBlock->engineTorque * mThrottle;
|
|
maxBreakVel = breakTorque = 0;
|
|
if (mThrottle > 0 && mJetting)
|
|
// Double the engineTorque to help out the jets
|
|
engineTorque += mDataBlock->engineTorque;
|
|
}
|
|
else {
|
|
// Engine break.
|
|
breakTorque = mDataBlock->engineTorque;
|
|
maxBreakVel = (breakTorque / aMomentum) * dt;
|
|
engineTorque = 0;
|
|
}
|
|
}
|
|
|
|
force = Point3F(0, 0, sWheeledVehicleGravity);
|
|
|
|
// Jet Force
|
|
if (mJetting)
|
|
{
|
|
force += worldY * (mDataBlock->jetForce * mOneOverMass);
|
|
}
|
|
|
|
// Calculate wheel forces
|
|
//
|
|
for (j = 0; j < mDataBlock->wheelCount; j++)
|
|
{
|
|
Wheel* wheel = &mWheel[j];
|
|
WheeledVehicleData::Wheel* wheelData = &mDataBlock->wheel[j];
|
|
|
|
// Zero the force on this wheel
|
|
Point3F& forceVector = wheelForce[j];
|
|
forceVector.set(0, 0, 0);
|
|
|
|
if (wheel->surface.contact) {
|
|
// Wheel in contact with the ground.
|
|
// Forces acting on the body due to this wheel:
|
|
// - Spring forces
|
|
|
|
// Torques acting on the body due to this wheel:
|
|
// - None
|
|
|
|
// First, let's compute the wheels position, and worldspace velocity
|
|
Point3F pos, r, localVel;
|
|
currMatrix.mulP(wheelData->pos, &pos);
|
|
r = pos - massCenter;
|
|
getVelocity(r, &localVel);
|
|
|
|
// Spring forces on this wheel act in the z direction of the body, at the point
|
|
// of contact.
|
|
{
|
|
// Spring force
|
|
F32 spring = wheel->k * (wheel->center - wheel->extension);
|
|
|
|
// Damping in the spring
|
|
F32 damping = wheel->s * -mDot(bz, localVel);
|
|
if (damping < 0)
|
|
damping = 0;
|
|
|
|
// Anti-sway force based on difference in suspension extension
|
|
F32 antiSway = 0;
|
|
if (wheelData->opposite != -1)
|
|
{
|
|
Wheel* oppositeWheel = &mWheel[wheelData->opposite];
|
|
if (oppositeWheel->surface.contact) {
|
|
antiSway = ((oppositeWheel->extension - wheel->extension) *
|
|
mDataBlock->antiSwayForce);
|
|
}
|
|
if (antiSway < 0)
|
|
antiSway = 0;
|
|
}
|
|
|
|
forceVector += bz * ((spring + damping + antiSway) * oneOverSprungMass);
|
|
}
|
|
|
|
// Tire direction vectors perpendicular to surface normal
|
|
Point3F wheelXVec;
|
|
if (wheelData->steering == WheeledVehicleData::Wheel::Forward) {
|
|
wheelXVec = bx * cosSteering;
|
|
wheelXVec += by * sinSteering;
|
|
}
|
|
else if (wheelData->steering == WheeledVehicleData::Wheel::Backward) {
|
|
wheelXVec = bx * cosSteering;
|
|
wheelXVec -= by * sinSteering;
|
|
}
|
|
else {
|
|
wheelXVec = bx;
|
|
}
|
|
|
|
Point3F tireX, tireY;
|
|
mCross(wheel->surface.normal, wheelXVec, &tireY);
|
|
tireY.normalize();
|
|
mCross(tireY, wheel->surface.normal, &tireX);
|
|
tireX.normalize();
|
|
|
|
// Velocity of wheel at surface contact
|
|
Point3F wheelContact = wheel->surface.pos - massCenter;
|
|
Point3F wheelVelocity;
|
|
getVelocity(wheelContact, &wheelVelocity);
|
|
F32 xVelocity = mDot(tireX, wheelVelocity);
|
|
F32 yVelocity = mDot(tireY, wheelVelocity);
|
|
|
|
// Longitudinal deformation force
|
|
F32 ddy = ((wheel->avel * mDataBlock->tire.radius) -
|
|
yVelocity -
|
|
mDataBlock->tire.longitudinalRelaxation * mFabs(wheel->avel) * wheel->Dy);
|
|
|
|
wheel->Dy += ddy * dt;
|
|
|
|
F32 Fy = (mDataBlock->tire.longitudinalForce * wheel->Dy +
|
|
mDataBlock->tire.longitudinalDamping * ddy);
|
|
|
|
// Lateral deformation force
|
|
F32 ddx = (xVelocity -
|
|
(mDataBlock->tire.lateralRelaxation * mFabs(wheel->avel) * wheel->Dx));
|
|
|
|
wheel->Dx += ddx * dt;
|
|
|
|
F32 Fx = -(mDataBlock->tire.lateralForce * wheel->Dx +
|
|
mDataBlock->tire.lateralDamping * ddx);
|
|
|
|
// Vertical load on tire.
|
|
F32 Fz = verticalLoad;
|
|
|
|
// Reduce forces based on friction limit
|
|
F32 sN = mDot(wheel->surface.normal,Point3F(0,0,1));
|
|
if (sN > 0) {
|
|
F32 muS = Fz * mDataBlock->tire.friction * sN;
|
|
F32 muS2 = muS * muS;
|
|
F32 Fn = (Fz * Fz) * muS2;
|
|
F32 Ff = (Fx * Fx + Fy * Fy) * muS2;
|
|
if (Ff > Fn) {
|
|
F32 K = mSqrt(Fn / Ff);
|
|
Fy *= K;
|
|
Fx *= K;
|
|
wheel->Dy *= K;
|
|
wheel->Dx *= K;
|
|
}
|
|
}
|
|
else {
|
|
Fy = Fx = 0;
|
|
}
|
|
|
|
// Apply forces to wheel ground contact point
|
|
forceVector += (tireX * (Fx * mOneOverMass)) + (tireY * (Fy * mOneOverMass));
|
|
|
|
// Wheel angular acceleration from engine torque and tire
|
|
// deformation force.
|
|
wheel->torqueScale = (mFabs(wheel->avel) > maxAvel) ? 0 :
|
|
1 - (mFabs(wheel->avel) / maxAvel);
|
|
|
|
wheel->avel += (((wheel->torqueScale * engineTorque) -
|
|
Fy *
|
|
mDataBlock->tire.radius) / aMomentum) * dt;
|
|
|
|
// Wheel angular acceleration from break torque
|
|
if (maxBreakVel > mFabs(wheel->avel))
|
|
{
|
|
wheel->avel = 0;
|
|
}
|
|
else
|
|
{
|
|
if (wheel->avel > 0)
|
|
wheel->avel -= maxBreakVel;
|
|
else
|
|
wheel->avel += maxBreakVel;
|
|
}
|
|
}
|
|
else {
|
|
// Wheel not in contact with the ground
|
|
// Forces acting on the body due to this wheel:
|
|
// - None
|
|
// Torques acting on the body due to this wheel:
|
|
// - None
|
|
|
|
wheel->torqueScale = 0;
|
|
wheel->particleSlip = 0;
|
|
wheel->Dy += (-mDataBlock->tire.longitudinalRelaxation *
|
|
mFabs(wheel->avel) * wheel->Dy) * dt;
|
|
wheel->Dx += (-mDataBlock->tire.lateralRelaxation *
|
|
mFabs(wheel->avel) * wheel->Dx) * dt;
|
|
}
|
|
}
|
|
|
|
// Sum up the forces
|
|
{
|
|
// Now sum up the torques and forces
|
|
|
|
for (j = 0; j < mDataBlock->wheelCount; j++) {
|
|
WheeledVehicleData::Wheel* wheelData = &mDataBlock->wheel[j];
|
|
Point3F pos, r, t;
|
|
currMatrix.mulP(wheelData->pos, &pos);
|
|
r = pos - massCenter;
|
|
mCross(r, wheelForce[j], &t);
|
|
torque += t;
|
|
force += wheelForce[j];
|
|
}
|
|
}
|
|
|
|
// Container buoyancy & drag
|
|
force += Point3F(0, 0, -mBuoyancy * sWheeledVehicleGravity);
|
|
force -= mRigid.state.linVelocity * mDrag;
|
|
torque -= mRigid.state.angMomentum * mDrag;
|
|
|
|
// Apply forces to body
|
|
mRigid.state.force += force;
|
|
mRigid.state.torque += torque;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
U32 WheeledVehicle::getCollisionMask()
|
|
{
|
|
if (isServerObject())
|
|
return sServerCollisionMask;
|
|
else
|
|
return sClientCollisionMask;
|
|
}
|
|
|
|
|
|
//bool WheeledVehicle::collideBody(const MatrixF& mat,Collision* info)
|
|
//{
|
|
// // Database bounding box
|
|
// Box3F wBox = mObjBox;
|
|
// mat.mul(wBox);
|
|
//
|
|
// // Test the body against the database
|
|
// Box3F box;
|
|
// SphereF sphere;
|
|
// MatrixF imat(1);
|
|
// PlaneExtractorPolyList extractor;
|
|
// sPolyList->mPlaneList.clear();
|
|
// extractor.mPlaneList = &sPolyList->mPlaneList;
|
|
// extractor.setTransform(&mat, Point3F(1,1,1));
|
|
// mShapeInstance->buildPolyList(&extractor,mDataBlock->collisionDetails[0]);
|
|
//
|
|
// sPolyList->clear();
|
|
//
|
|
// // Build list from convex states here...
|
|
// CollisionWorkingList& rList = mConvex.getWorkingList();
|
|
// CollisionWorkingList* pList = rList.wLink.mNext;
|
|
// while (pList != &rList) {
|
|
// Convex* pConvex = pList->mConvex;
|
|
// if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
|
|
// pConvex->getPolyList(sPolyList);
|
|
// }
|
|
// pList = pList->wLink.mNext;
|
|
// }
|
|
//
|
|
// S32 count = sPolyList->mPolyList.size();
|
|
// info->face = count? BodyCollision: 0;
|
|
//
|
|
// // Test the wheels against the database
|
|
// Point3F xvec,yvec,zvec;
|
|
// mat.getColumn(0,&xvec);
|
|
// xvec *= mObjBox.len_x();
|
|
// mat.getColumn(1,&yvec);
|
|
// yvec *= mObjBox.len_y();
|
|
// mat.getColumn(2,&zvec);
|
|
// zvec *= mObjBox.len_z();
|
|
//
|
|
// for (S32 i = 0; i != mDataBlock->wheelCount; i++) {
|
|
// WheeledVehicleData::Wheel* wd = &mDataBlock->wheel[i];
|
|
// Wheel* wp = &mWheel[i];
|
|
//
|
|
// Box3F box;
|
|
// F32 wr = mDataBlock->tire.radius, ww = wr / 2;
|
|
// box.min.set(wd->pos.x - ww,wd->pos.y - wr,wd->pos.z);
|
|
// box.max.set(wd->pos.x + ww,wd->pos.y + wr,wd->pos.z + 2*wr);
|
|
//
|
|
// Point3F min,max;
|
|
// mat.mulP(box.min,&min);
|
|
// mat.mulP(box.max,&max);
|
|
//
|
|
// sPolyList->mPlaneList.clear();
|
|
// sPolyList->mNormal.set(0,0,0);
|
|
// sPolyList->mPlaneList.setSize(6);
|
|
// sPolyList->mPlaneList[0].set(min,xvec);
|
|
// sPolyList->mPlaneList[0].invert();
|
|
// sPolyList->mPlaneList[1].set(max,yvec);
|
|
// sPolyList->mPlaneList[2].set(max,xvec);
|
|
// sPolyList->mPlaneList[3].set(min,yvec);
|
|
// sPolyList->mPlaneList[3].invert();
|
|
// sPolyList->mPlaneList[4].set(min,zvec);
|
|
// sPolyList->mPlaneList[4].invert();
|
|
// sPolyList->mPlaneList[5].set(max,zvec);
|
|
//
|
|
// // Build list from convex states here...
|
|
// CollisionWorkingList& rList = mConvex.getWorkingList();
|
|
// CollisionWorkingList* pList = rList.wLink.mNext;
|
|
// while (pList != &rList) {
|
|
// Convex* pConvex = pList->mConvex;
|
|
// if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
|
|
// pConvex->getPolyList(sPolyList);
|
|
// }
|
|
// pList = pList->wLink.mNext;
|
|
// }
|
|
// }
|
|
//
|
|
// if (sPolyList->mPolyList.size() != count)
|
|
// info->face |= WheelCollision;
|
|
//
|
|
// // Pick best collision point
|
|
// F32 bestDist = 1.0E30;
|
|
// ClippedPolyList::Poly* bestPoly = 0;
|
|
// ClippedPolyList::Vertex* bestVertex;
|
|
//
|
|
// if (sPolyList->mPolyList.size()) {
|
|
// Point3F massCenter;
|
|
// mat.mulP(mDataBlock->massCenter,&massCenter);
|
|
//
|
|
// // Pick surface with best vertex velocity
|
|
// F32 bestVd = -1;
|
|
// ClippedPolyList::Poly* poly = sPolyList->mPolyList.begin();
|
|
// ClippedPolyList::Poly* end = sPolyList->mPolyList.end();
|
|
// for (; poly != end; poly++) {
|
|
// U32* vi = &sPolyList->mIndexList[poly->vertexStart];
|
|
// U32* ve = vi + poly->vertexCount;
|
|
// for (; vi != ve; vi++) {
|
|
// ClippedPolyList::Vertex* ev = &sPolyList->mVertexList[*vi];
|
|
//
|
|
// Point3F v,r;
|
|
// r = ev->point - massCenter;
|
|
// getVelocity(r,&v);
|
|
//
|
|
// F32 dist = mDot(poly->plane,v);
|
|
// if (dist < 0 && dist < bestDist) {
|
|
// bestDist = dist;
|
|
// bestVertex = ev;
|
|
// bestPoly = poly;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// //
|
|
// if (bestPoly) {
|
|
// info->point = bestVertex->point;
|
|
// info->object = bestPoly->object;
|
|
// info->normal = bestPoly->plane;
|
|
// info->material = bestPoly->material;
|
|
// return true;
|
|
// }
|
|
//
|
|
// return false;
|
|
//}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::updateWheels()
|
|
{
|
|
disableCollision();
|
|
static Polyhedron polyh;
|
|
// static ExtrudedPolyList sExtrudedList;
|
|
mWheelContact = false;
|
|
|
|
MatrixF currMatrix;
|
|
mRigid.state.getTransform(&currMatrix);
|
|
|
|
for (S32 i = 0; i != mDataBlock->wheelCount; i++) {
|
|
WheeledVehicleData::Wheel* wheelData = &mDataBlock->wheel[i];
|
|
Wheel* currWheel = &mWheel[i];
|
|
|
|
currWheel->extension = 1;
|
|
|
|
Point3F sp;
|
|
Point3F vec;
|
|
currMatrix.mulP(wheelData->pos,&sp);
|
|
currMatrix.mulV(wheelData->spring,&vec);
|
|
Point3F hp = sp - vec * currWheel->extension;
|
|
Point3F ep = sp + vec * currWheel->extension;
|
|
|
|
MatrixF wmat = currMatrix;
|
|
wmat.setColumn(3,hp);
|
|
F32 wr = mDataBlock->tire.radius, ww = wr / 2;
|
|
Box3F box;
|
|
box.min.set(-ww,-wr,0);
|
|
box.max.set(+ww,+wr,2*wr);
|
|
polyh.buildBox(wmat,box);
|
|
|
|
// DMMTODO: Replace with search through working set...
|
|
//
|
|
CollisionList collisionList;
|
|
if (mContainer->buildCollisionList(polyh,
|
|
hp, ep, vec * 2.0f,
|
|
sClientCollisionMask & ~PlayerObjectType,
|
|
&collisionList)) {
|
|
currWheel->evel = 0;
|
|
|
|
if (collisionList.t > 0.5)
|
|
currWheel->extension *= (collisionList.t - 0.5) * 2.0f;
|
|
else
|
|
currWheel->extension = 0;
|
|
|
|
currWheel->surface.contact = true;
|
|
currWheel->surface.pos = sp + vec * currWheel->extension;
|
|
currWheel->surface.object = collisionList.collision[0].object;
|
|
mWheelContact = true;
|
|
|
|
// Pick flatest surface normal
|
|
F32 dot = 1E30f;
|
|
Collision *collision, *cp = collisionList.collision;
|
|
Collision *ep = cp + collisionList.count;
|
|
for (; cp != ep; cp++) {
|
|
F32 d = mDot(cp->normal,vec);
|
|
if (d < dot) {
|
|
collision = cp;
|
|
dot = d;
|
|
}
|
|
}
|
|
|
|
currWheel->surface.normal = collision->normal;
|
|
currWheel->surface.material = collision->material;
|
|
}
|
|
else {
|
|
// Make sure that we haven't sunk into the ground...
|
|
Point3F safety;
|
|
currMatrix.mulP(wheelData->safePos,&safety);
|
|
|
|
RayInfo rInfo;
|
|
if (mContainer->castRay(safety, sp, sClientCollisionMask & ~PlayerObjectType, &rInfo)) {
|
|
// Actually stuck at this point
|
|
currWheel->evel = 0;
|
|
currWheel->extension = 0;
|
|
currWheel->surface.normal = rInfo.normal;
|
|
currWheel->surface.pos = rInfo.point;
|
|
currWheel->surface.material = rInfo.material;
|
|
currWheel->surface.contact = true;
|
|
currWheel->surface.object = rInfo.object;
|
|
mWheelContact = true;
|
|
} else {
|
|
// Ok, no collision.
|
|
currWheel->surface.contact = false;
|
|
currWheel->surface.object = NULL;
|
|
}
|
|
}
|
|
}
|
|
enableCollision();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::updateWheelThreads()
|
|
{
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
if (wp->springThread) {
|
|
F32 p = wp->extension;
|
|
if (p > wp->center)
|
|
p = wp->center;
|
|
mShapeInstance->setPos(wp->springThread,1 - p);
|
|
}
|
|
if (wp->steeringThread) {
|
|
F32 t = (mSteering.x * mFabs(mSteering.x)) / mDataBlock->maxSteeringAngle;
|
|
mShapeInstance->setPos(wp->steeringThread,0.5 - t * 0.5);
|
|
}
|
|
if (wp->rotationThread)
|
|
mShapeInstance->setPos(wp->rotationThread,wp->apos);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::updateEngineSound(F32 level)
|
|
{
|
|
if (mEngineSound) {
|
|
alxSourceMatrixF(mEngineSound, &getTransform());
|
|
alxSourcef(mEngineSound, AL_GAIN_LINEAR, level);
|
|
}
|
|
}
|
|
|
|
void WheeledVehicle::updateSqueelSound(F32 level)
|
|
{
|
|
if (!mDataBlock->sound[WheeledVehicleData::SqueelSound])
|
|
return;
|
|
// Allocate/Deallocate voice on demand.
|
|
if (level < sMinSqueelVolume) {
|
|
if (mSqueelSound) {
|
|
alxStop(mSqueelSound);
|
|
mSqueelSound = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (!mSqueelSound)
|
|
mSqueelSound = alxPlay(mDataBlock->sound[WheeledVehicleData::SqueelSound], &getTransform());
|
|
|
|
alxSourceMatrixF(mSqueelSound, &getTransform());
|
|
alxSourcef(mSqueelSound, AL_GAIN_LINEAR, level);
|
|
}
|
|
}
|
|
|
|
void WheeledVehicle::updateJet(F32 )
|
|
{
|
|
if (!mDataBlock->sound[WheeledVehicleData::JetSound])
|
|
return;
|
|
// Allocate/Deallocate voice on demand.
|
|
if (!mJetting) {
|
|
if (mJetSound) {
|
|
alxStop(mJetSound);
|
|
mJetSound = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (!mJetSound)
|
|
mJetSound = alxPlay(mDataBlock->sound[WheeledVehicleData::JetSound], &getTransform());
|
|
|
|
alxSourceMatrixF(mJetSound, &getTransform());
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void WheeledVehicle::renderImage(SceneState* state, SceneRenderImage* image)
|
|
{
|
|
updateWheelThreads();
|
|
Parent::renderImage(state, image);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool WheeledVehicle::writePacketData(GameConnection *connection, BitStream *stream)
|
|
{
|
|
bool ret = Parent::writePacketData(connection, stream);
|
|
stream->writeFlag(mBraking);
|
|
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
stream->write(wp->avel);
|
|
stream->write(wp->Dy);
|
|
stream->write(wp->Dx);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void WheeledVehicle::readPacketData(GameConnection *connection, BitStream *stream)
|
|
{
|
|
Parent::readPacketData(connection, stream);
|
|
mBraking = stream->readFlag();
|
|
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
stream->read(&wp->avel);
|
|
stream->read(&wp->Dy);
|
|
stream->read(&wp->Dx);
|
|
}
|
|
|
|
setPosition(mRigid.state.linPosition,mRigid.state.angPosition);
|
|
mDelta.pos = mRigid.state.linPosition;
|
|
mDelta.rot[1] = mRigid.state.angPosition;
|
|
}
|
|
|
|
U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
|
{
|
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
|
|
|
// The rest of the data is part of the control object packet update.
|
|
// If we're controlled by this client, we don't need to send it.
|
|
if( ((GameConnection *) con)->getControlObject() == this && !(mask & InitialUpdateMask))
|
|
return retMask;
|
|
|
|
stream->writeFlag(mBraking);
|
|
|
|
if (stream->writeFlag(mask & PositionMask)) {
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
stream->write(wp->avel);
|
|
stream->write(wp->Dy);
|
|
stream->write(wp->Dx);
|
|
}
|
|
}
|
|
return retMask;
|
|
}
|
|
|
|
void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
|
|
{
|
|
Parent::unpackUpdate(con,stream);
|
|
|
|
if( ((GameConnection *) con)->getControlObject() == this)
|
|
return;
|
|
|
|
mBraking = stream->readFlag();
|
|
|
|
if (stream->readFlag()) {
|
|
for (S32 i = 0; i < mDataBlock->wheelCount; i++) {
|
|
Wheel* wp = &mWheel[i];
|
|
stream->read(&wp->avel);
|
|
stream->read(&wp->Dy);
|
|
stream->read(&wp->Dx);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WheeledVehicle::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
}
|
|
|