/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTruckDraw.cpp
// Draw Trucks. Actually, this draws rocket buggies.
// Author: John Ahlquist, March 2002
#include
#include
#include "Common/Thing.h"
#include "Common/ThingFactory.h"
#include "Common/GameAudio.h"
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameClient/ParticleSys.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GameLogic.h" // for logic frame count
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Locomotor.h"
#include "GameLogic/Module/PhysicsUpdate.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameLogic/ScriptEngine.h"
#include "W3DDevice/GameClient/W3DGameClient.h"
#include "W3DDevice/GameClient/Module/W3DTruckDraw.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
W3DTruckDrawModuleData::W3DTruckDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
W3DTruckDrawModuleData::~W3DTruckDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "Dust", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dustEffectName) },
{ "DirtSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dirtEffectName) },
{ "PowerslideSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideEffectName) },
{ "LeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontLeftTireBoneName) },
{ "RightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontRightTireBoneName) },
{ "LeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearLeftTireBoneName) },
{ "RightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearRightTireBoneName) },
{ "MidLeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontLeftTireBoneName) },
{ "MidRightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontRightTireBoneName) },
{ "MidLeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearLeftTireBoneName) },
{ "MidRightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearRightTireBoneName) },
{ "MidLeftMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidLeftTireBoneName) },
{ "MidRightMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidRightTireBoneName) },
{ "TireRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationSpeedMultiplier) },
{ "PowerslideRotationAddition", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideRotationAddition) },
{ "CabBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_cabBoneName) },
{ "TrailerBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_trailerBoneName) },
{ "CabRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_cabRotationFactor) },
{ "TrailerRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_trailerRotationFactor) },
{ "RotationDamping", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationDampingFactor) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTruckDraw::W3DTruckDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData ),
m_dirtEffect(NULL), m_dustEffect(NULL), m_powerslideEffect(NULL), m_effectsInitialized(false),
m_wasAirborne(false), m_isPowersliding(false),
m_frontWheelRotation(0), m_rearWheelRotation(0), m_midFrontWheelRotation(0), m_midRearWheelRotation(0),
m_frontRightTireBone(0), m_frontLeftTireBone(0), m_rearLeftTireBone(0),m_rearRightTireBone(0),
m_midFrontRightTireBone(0), m_midFrontLeftTireBone(0), m_midRearLeftTireBone(0),m_midRearRightTireBone(0),
m_midMidRightTireBone(0), m_midMidLeftTireBone(0), m_prevRenderObj(NULL)
{
const AudioEventRTS * event;
event = thing->getTemplate()->getPerUnitSound("TruckLandingSound");
if (event) {
m_landingSound = *event;
}
event = thing->getTemplate()->getPerUnitSound("TruckPowerslideSound");
if (event) {
m_powerslideSound = *event;
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTruckDraw::~W3DTruckDraw()
{
tossEmitters();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::tossEmitters()
{
if (m_dustEffect)
{
m_dustEffect->attachToObject(NULL);
m_dustEffect->destroy();
m_dustEffect = NULL;
}
if (m_dirtEffect)
{
m_dirtEffect->attachToObject(NULL);
m_dirtEffect->destroy();
m_dirtEffect = NULL;
}
if (m_powerslideEffect)
{
m_powerslideEffect->attachToObject(NULL);
m_powerslideEffect->destroy();
m_powerslideEffect = NULL;
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (fullyObscured != getFullyObscuredByShroud())
{
if (fullyObscured)
tossEmitters();
else
createEmitters();
}
W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Start creating debris from the tank treads
*/
void W3DTruckDraw::createEmitters( void )
{
if (getDrawable()->isDrawableEffectivelyHidden())
return;
if (getW3DTruckDrawModuleData())
{
const ParticleSystemTemplate *sysTemplate;
if (!m_dustEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dustEffectName);
if (sysTemplate)
{
m_dustEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dustEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dustEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_dustEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_dustEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_dirtEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dirtEffectName);
if (sysTemplate)
{
m_dirtEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dirtEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dirtEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_dirtEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_dirtEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_powerslideEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_powerslideEffectName);
if (sysTemplate)
{
m_powerslideEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_powerslideEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_powerslideEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_powerslideEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_powerslideEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Stop creating debris from the tank treads
*/
void W3DTruckDraw::enableEmitters( Bool enable )
{
// don't check... if we are hidden the first time thru, then we'll never create the emitters.
// eg, if we are loading a game and the unit is in a tunnel, he'll never get emitteres even when he exits.
//if (!m_effectsInitialized)
{
createEmitters();
m_effectsInitialized=true;
}
if (m_dustEffect)
{
if (enable)
m_dustEffect->start();
else
m_dustEffect->stop();
}
if (m_dirtEffect)
{
if (enable)
m_dirtEffect->start();
else
m_dirtEffect->stop();
}
if (m_powerslideEffect)
{
if (!enable)
m_powerslideEffect->stop();
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::updateBones( void ) {
if( getW3DTruckDrawModuleData() )
{
//Front tires
if( !getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() )
{
m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_frontRightTireBone )
{
m_frontLeftTireBone = 0;
}
}
//Rear tires
if( !getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.isEmpty() )
{
m_rearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearLeftTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_rearRightTireBone)
{
m_rearLeftTireBone = 0;
}
}
//midFront tires
if( !getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() )
{
m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midFrontRightTireBone )
{
m_midFrontLeftTireBone = 0;
}
}
//midRear tires
if( !getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() )
{
m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midRearRightTireBone)
{
m_midRearLeftTireBone = 0;
}
}
//midMid tires
if( !getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.isEmpty() )
{
m_midMidLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midMidLeftTireBone, ("Missing mid-mid-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midMidRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midMidRightTireBone, ("Missing mid-mid-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midMidRightTireBone)
{
m_midMidLeftTireBone = 0;
}
}
//Cab
if( !getW3DTruckDrawModuleData()->m_cabBoneName.isEmpty() )
{
m_cabBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_cabBoneName.str());
DEBUG_ASSERTCRASH(m_cabBone, ("Missing cab bone %s in model %s\n", getW3DTruckDrawModuleData()->m_cabBoneName.str(), getRenderObject()->Get_Name()));
m_trailerBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_trailerBoneName.str());
}
}
m_prevRenderObj = getRenderObject();
m_prevNumBones = m_prevRenderObj->Get_Num_Bones();
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::setHidden(Bool h)
{
W3DModelDraw::setHidden(h);
if (h)
{
enableEmitters(false);
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::onRenderObjRecreated(void)
{
//DEBUG_LOG(("Old obj %x, newObj %x, new bones %d, old bones %d\n",
// m_prevRenderObj, getRenderObject(), getRenderObject()->Get_Num_Bones(),
// m_prevNumBones));
m_prevRenderObj = NULL;
m_frontLeftTireBone = 0;
m_frontRightTireBone = 0;
m_rearLeftTireBone = 0;
m_rearRightTireBone = 0;
m_midFrontLeftTireBone = 0;
m_midFrontRightTireBone = 0;
m_midRearLeftTireBone = 0;
m_midRearRightTireBone = 0;
m_midMidLeftTireBone = 0;
m_midMidRightTireBone = 0;
updateBones();
}
//-------------------------------------------------------------------------------------------------
/** Rotate and position wheels and other truck parts. */
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::doDrawModule(const Matrix3D* transformMtx)
{
W3DModelDraw::doDrawModule(transformMtx);
if (!TheGlobalData->m_showClientPhysics)
return;
const W3DTruckDrawModuleData *moduleData = getW3DTruckDrawModuleData();
if (moduleData==NULL) return; // shouldn't ever happen.
Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
if (frozen)
return;
const Real ACCEL_THRESHOLD = 0.01f;
const Real SIZE_CAP = 2.0f;
// get object from logic
Object *obj = getDrawable()->getObject();
if (obj == NULL)
return;
if (getRenderObject()==NULL) return;
if (getRenderObject() != m_prevRenderObj) {
DEBUG_LOG(("W3DTruckDraw::doDrawModule - shouldn't update bones. jba\n"));
updateBones();
}
// get object physics state
PhysicsBehavior *physics = obj->getPhysics();
if (physics == NULL)
return;
const Coord3D *vel = physics->getVelocity();
Real speed = physics->getVelocityMagnitude();
const TWheelInfo *wheelInfo = getDrawable()->getWheelInfo(); // note, can return null!
AIUpdateInterface *ai = obj->getAI();
if (m_cabBone && wheelInfo) {
Matrix3D cabXfrm(1);
cabXfrm.Make_Identity();
Real desiredAngle = wheelInfo->m_wheelAngle*moduleData->m_cabRotationFactor;
// Check goal angle.
if (ai && ai->getPath())
{
Coord3D pointOnPath;
ai->getPath()->peekCachedPointOnPath(pointOnPath);
Real angleToGoal = ThePartitionManager->getRelativeAngle2D( obj, &pointOnPath );
//DEBUG_LOG(("To goal %f, desired %f ", 180*angleToGoal/PI, 180*desiredAngle/PI));
if (angleToGoal<0) {
if (desiredAngle0) desiredAngle = 0;
} else {
if (desiredAngle>angleToGoal) desiredAngle = angleToGoal;
if (desiredAngle<0) desiredAngle = 0;
}
//DEBUG_LOG(("final desired %f ", 180*desiredAngle/PI));
}
Real deltaAngle = desiredAngle - m_curCabRotation;
deltaAngle *= moduleData->m_rotationDampingFactor;
m_curCabRotation += deltaAngle;
cabXfrm.Rotate_Z(m_curCabRotation);
getRenderObject()->Capture_Bone( m_cabBone );
getRenderObject()->Control_Bone( m_cabBone, cabXfrm );
if (m_trailerBone && wheelInfo) {
desiredAngle = -wheelInfo->m_wheelAngle*moduleData->m_trailerRotationFactor;
Real deltaAngle = desiredAngle - m_curTrailerRotation;
deltaAngle *= moduleData->m_rotationDampingFactor;
m_curTrailerRotation += deltaAngle;
cabXfrm.Make_Identity();
cabXfrm.Rotate_Z(m_curTrailerRotation);
getRenderObject()->Capture_Bone( m_trailerBone );
getRenderObject()->Control_Bone( m_trailerBone, cabXfrm );
}
}
if (m_frontLeftTireBone || m_rearLeftTireBone)
{
Real powerslideRotationAddition = moduleData->m_powerslideRotationAddition;
if (ai) {
Locomotor *loco = ai->getCurLocomotor();
if (loco) {
if (loco->isMovingBackwards()) {
speed = -speed; // rotate wheels backwards. jba.
powerslideRotationAddition = -powerslideRotationAddition;
}
}
}
const Real rotationFactor = moduleData->m_rotationSpeedMultiplier;
m_frontWheelRotation += rotationFactor*speed;
if (m_isPowersliding)
{
m_rearWheelRotation += rotationFactor*(speed + powerslideRotationAddition);
}
else
{
m_rearWheelRotation += rotationFactor*speed;
}
// For now, just use the same values for mid wheels -- may want to do independent calcs later...
m_midFrontWheelRotation = m_frontWheelRotation;
m_midRearWheelRotation = m_rearWheelRotation;
Matrix3D wheelXfrm(1);
if (m_frontLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontLeftTireBone );
getRenderObject()->Control_Bone( m_frontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontRightTireBone );
getRenderObject()->Control_Bone( m_frontRightTireBone, wheelXfrm );
}
if (m_rearLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_rearLeftTireBone );
getRenderObject()->Control_Bone( m_rearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
//@todo TROUBLE HERE, THE BONE INDICES DO NOT MATCH THE RENDEROBJECTS BONES, SOMETIMES
getRenderObject()->Capture_Bone( m_rearRightTireBone );
getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm );
}
if (m_midFrontLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontLeftTireBone );
getRenderObject()->Control_Bone( m_midFrontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontRightTireBone );
getRenderObject()->Control_Bone( m_midFrontRightTireBone, wheelXfrm );
}
if (m_midRearLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_midRearLeftTireBone );
getRenderObject()->Control_Bone( m_midRearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_midRearRightTireBone );
getRenderObject()->Control_Bone( m_midRearRightTireBone, wheelXfrm );
}
if (m_midMidLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_midMidLeftTireBone );
getRenderObject()->Control_Bone( m_midMidLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_midMidRightTireBone );
getRenderObject()->Control_Bone( m_midMidRightTireBone, wheelXfrm );
}
}
Bool wasPowersliding = m_isPowersliding;
m_isPowersliding = false;
if (physics->isMotive() && !obj->isSignificantlyAboveTerrain()) {
enableEmitters(true);
Coord3D accel = *physics->getAcceleration();
accel.z = 0; // ignore gravitational force.
Bool accelerating = accel.length()>ACCEL_THRESHOLD;
//DEBUG_LOG(("Accel %f, speed %f\n", accel.length(), speed));
if (accelerating) {
Real dot = accel.x*vel->x + accel.y*vel->y;
if (dot<0) {
accelerating = false; // decelerating, actually.
}
}
if (m_dustEffect) {
// Need more dust the faster we go.
if (speed>SIZE_CAP) {
speed = SIZE_CAP;
}
m_dustEffect->setSizeMultiplier(speed);
}
if (m_dirtEffect) {
if (wheelInfo && wheelInfo->m_framesAirborne>3) {
Real factor = 1 + wheelInfo->m_framesAirborne/16;
if (factor>2.0) factor = 2.0;
m_dustEffect->setSizeMultiplier(factor*SIZE_CAP);
m_dustEffect->trigger();
m_landingSound.setObjectID(obj->getID());
TheAudio->addAudioEvent(&m_landingSound);
} else {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
if (m_powerslideEffect) {
if (physics->getTurning() == TURN_NONE) {
m_powerslideEffect->stop();
} else {
m_isPowersliding = true;
m_powerslideEffect->start();
}
}
if (m_dirtEffect) {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
else
enableEmitters(false);
m_wasAirborne = obj->isSignificantlyAboveTerrain();
if(!wasPowersliding && m_isPowersliding) {
// start sound
m_powerslideSound.setObjectID(obj->getID());
m_powerslideSound.setPlayingHandle(TheAudio->addAudioEvent(&m_powerslideSound));
} else if (wasPowersliding && !m_isPowersliding) {
TheAudio->removeAudioEvent(m_powerslideSound.getPlayingHandle());
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// John A and Mark W say there is no data to save here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
// toss any existing ones (no need to re-create; we'll do that on demand)
tossEmitters();
} // end loadPostProcess