/* ** Command & Conquer Generals Zero Hour(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: W3DTankTruckDraw.cpp // Draw TankTrucks. Actually, this draws quad cannon which has both treads and wheels. // Author: Mark Wilczynski, August 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 "GameLogic/Weapon.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Module/PhysicsUpdate.h" #include "GameLogic/Module/BodyModule.h" #include "GameLogic/ScriptEngine.h" #include "GameLogic/Module/AIUpdate.h" #include "GameClient/Drawable.h" #include "GameClient/ParticleSys.h" #include "W3DDevice/GameClient/W3DGameClient.h" #include "W3DDevice/GameClient/Module/W3DTankTruckDraw.h" #include "WW3D2/matinfo.h" //#define SHOW_TANK_DEBRIS //------------------------------------------------------------------------------------------------- W3DTankTruckDrawModuleData::W3DTankTruckDrawModuleData(): m_treadDebrisNameLeft("TrackDebrisDirtLeft"), m_treadDebrisNameRight("TrackDebrisDirtRight"), m_treadAnimationRate(0.0f), m_treadPivotSpeedFraction(0.6f), m_treadDriveSpeedFraction(0.3f) { } //------------------------------------------------------------------------------------------------- W3DTankTruckDrawModuleData::~W3DTankTruckDrawModuleData() { } //------------------------------------------------------------------------------------------------- void W3DTankTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p) { W3DModelDrawModuleData::buildFieldParse(p); static const FieldParse dataFieldParse[] = { { "Dust", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_dustEffectName) }, { "DirtSpray", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_dirtEffectName) }, { "PowerslideSpray", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_powerslideEffectName) }, { "LeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_frontLeftTireBoneName) }, { "RightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_frontRightTireBoneName) }, { "LeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_rearLeftTireBoneName) }, { "RightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_rearRightTireBoneName) }, { "MidLeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midFrontLeftTireBoneName) }, { "MidRightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midFrontRightTireBoneName) }, { "MidLeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midRearLeftTireBoneName) }, { "MidRightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midRearRightTireBoneName) }, { "TireRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_rotationSpeedMultiplier) }, { "PowerslideRotationAddition", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_powerslideRotationAddition) }, { "TreadDebrisLeft", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDebrisNameLeft) }, { "TreadDebrisRight", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDebrisNameRight) }, { "TreadAnimationRate", INI::parseVelocityReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadAnimationRate) }, { "TreadPivotSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadPivotSpeedFraction) }, { "TreadDriveSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDriveSpeedFraction) }, { 0, 0, 0, 0 } }; p.add(dataFieldParse); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- W3DTankTruckDraw::W3DTankTruckDraw( 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_frontRightTireBone(0), m_frontLeftTireBone(0), m_rearLeftTireBone(0),m_rearRightTireBone(0), m_prevRenderObj(NULL) { //Truck Data m_landingSound = *(thing->getTemplate()->getPerUnitSound("TruckLandingSound")); m_powerslideSound = *(thing->getTemplate()->getPerUnitSound("TruckPowerslideSound")); //Tank data m_treadDebrisLeft = NULL; m_treadDebrisRight = NULL; for (Int i=0; ifindTemplate(getW3DTankTruckDrawModuleData()->m_treadDebrisNameLeft); if (sysTemplate) { m_treadDebrisLeft = TheParticleSystemManager->createParticleSystem( sysTemplate ); m_treadDebrisLeft->attachToDrawable(getDrawable()); DEBUG_CRASH(("test me, may not work (srj)")); // important: mark it as do-not-save, since we'll just re-create it when we reload. m_treadDebrisLeft->setSaveable(FALSE); } sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_treadDebrisNameRight); if (sysTemplate) { m_treadDebrisRight = TheParticleSystemManager->createParticleSystem( sysTemplate ); m_treadDebrisRight->attachToDrawable(getDrawable()); DEBUG_CRASH(("test me, may not work (srj)")); // important: mark it as do-not-save, since we'll just re-create it when we reload. m_treadDebrisRight->setSaveable(FALSE); } } #endif } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- W3DTankTruckDraw::~W3DTankTruckDraw() { tossEmitters(); for (Int i=0; iisDrawableEffectivelyHidden()) return; if (m_treadDebrisLeft) m_treadDebrisLeft->start(); if (m_treadDebrisRight) m_treadDebrisRight->start(); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /** * Stop creating debris from the tank treads */ void W3DTankTruckDraw::stopMoveDebris( void ) { if (m_treadDebrisLeft) m_treadDebrisLeft->stop(); if (m_treadDebrisRight) m_treadDebrisRight->stop(); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void W3DTankTruckDraw::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 W3DTankTruckDraw::setFullyObscuredByShroud(Bool fullyObscured) { if (fullyObscured != getFullyObscuredByShroud()) { if (fullyObscured) tossEmitters(); else createEmitters(); } W3DModelDraw::setFullyObscuredByShroud(fullyObscured); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /** * Start creating debris from the tank treads */ void W3DTankTruckDraw::createEmitters( void ) { if (getDrawable()->isDrawableEffectivelyHidden()) return; if (getW3DTankTruckDrawModuleData()) { const ParticleSystemTemplate *sysTemplate; if (!m_dustEffect) { sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->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 (!getW3DTankTruckDrawModuleData()->m_dustEffectName.isEmpty()) { DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n", getW3DTankTruckDrawModuleData()->m_dustEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str())); } } } if (!m_dirtEffect) { sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->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 (!getW3DTankTruckDrawModuleData()->m_dirtEffectName.isEmpty()) { DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n", getW3DTankTruckDrawModuleData()->m_dirtEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str())); } } } if (!m_powerslideEffect) { sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->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 (!getW3DTankTruckDrawModuleData()->m_powerslideEffectName.isEmpty()) { DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n", getW3DTankTruckDrawModuleData()->m_powerslideEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str())); } } } } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /** * Stop creating debris from the tank treads */ void W3DTankTruckDraw::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 W3DTankTruckDraw::updateBones( void ) { if( getW3DTankTruckDrawModuleData() ) { //Front tires if( !getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() ) { m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.str()); DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name())); m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_frontRightTireBoneName.str()); DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name())); if (!m_frontRightTireBone ) { m_frontLeftTireBone = 0; } } //Rear tires if( !getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.isEmpty() ) { m_rearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.str()); DEBUG_ASSERTCRASH(m_rearLeftTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.str(), getRenderObject()->Get_Name())); m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_rearRightTireBoneName.str()); DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name())); if (!m_rearRightTireBone) { m_rearLeftTireBone = 0; } } //midFront tires if( !getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() ) { m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.str()); DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name())); m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midFrontRightTireBoneName.str()); DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name())); if (!m_midFrontRightTireBone ) { m_midFrontLeftTireBone = 0; } } //midRear tires if( !getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() ) { m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.str()); DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name())); m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midRearRightTireBoneName.str()); DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name())); if (!m_midRearRightTireBone) { m_midRearLeftTireBone = 0; } } } m_prevRenderObj = getRenderObject(); } //------------------------------------------------------------------------------------------------- void W3DTankTruckDraw::setHidden(Bool h) { W3DModelDraw::setHidden(h); if (h) { enableEmitters(false); #ifdef SHOW_TANK_DEBRIS stopMoveDebris(); #endif } } /**Update uv coordinates on each tread object to simulate movement*/ void W3DTankTruckDraw::updateTreadPositions(Real uvDelta) { Real offset_u; TreadObjectInfo *pTread=m_treads; for (Int i=0; im_type == TREAD_MIDDLE) //this tread needs to scroll backwards offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta; else if (pTread->m_type == TREAD_LEFT) //this tread needs to scroll forwards offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta; else if (pTread->m_type == TREAD_RIGHT) //this tread needs to scroll backwards offset_u = pTread->m_materialSettings.customUVOffset.X - uvDelta; // ensure coordinates of offset are in [0, 1] range: offset_u = offset_u - WWMath::Floor(offset_u); pTread->m_materialSettings.customUVOffset.Set(offset_u,0); pTread++; } } /**Grab pointers to the sub-meshes for each tread*/ void W3DTankTruckDraw::updateTreadObjects(void) { RenderObjClass *robj=getRenderObject(); //clear all previous tread pointers for (Int i=0; im_treadAnimationRate && robj) { for (Int i=0; i < robj->Get_Num_Sub_Objects() && m_treadCount < MAX_TREADS_PER_TANK; i++) { RenderObjClass *subObj=robj->Get_Sub_Object(i); const char *meshName; //Check if subobject name starts with "TREADS". if (subObj && subObj->Class_ID() == RenderObjClass::CLASSID_MESH && subObj->Get_Name() && ( (meshName=strchr(subObj->Get_Name(),'.') ) != 0 && *(meshName++)) &&_strnicmp(meshName,"TREADS", 6) == 0) { //check if sub-object has the correct material to do texture scrolling. MaterialInfoClass *mat=subObj->Get_Material_Info(); if (mat) { for (Int j=0; jVertex_Material_Count(); j++) { VertexMaterialClass *vmaterial=mat->Peek_Vertex_Material(j); LinearOffsetTextureMapperClass *mapper=(LinearOffsetTextureMapperClass *)vmaterial->Peek_Mapper(); if (mapper && mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET) { mapper->Set_UV_Offset_Delta(Vector2(0,0)); //disable automatic scrolling subObj->Add_Ref(); //increase reference since we're storing the pointer m_treads[m_treadCount].m_robj=subObj; m_treads[m_treadCount].m_type = TREAD_MIDDLE; //default type subObj->Set_User_Data(&m_treads[m_treadCount].m_materialSettings); //tell W3D about custom material settings m_treads[m_treadCount].m_materialSettings.customUVOffset=Vector2(0,0); //Commented out since on vehicles with wheels, it makes no sense to turn with treads. /* switch (meshName[6]) //check next character after 'TREADS' { case 'L': case 'l': m_treads[m_treadCount].m_type = TREAD_LEFT; break; case 'R': case 'r': m_treads[m_treadCount].m_type = TREAD_RIGHT; break; }*/ m_treadCount++; } } REF_PTR_RELEASE(mat); } } REF_PTR_RELEASE(subObj); } } m_prevRenderObj = robj; } //------------------------------------------------------------------------------------------------- void W3DTankTruckDraw::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; updateBones(); updateTreadObjects(); } //------------------------------------------------------------------------------------------------- /** Map behavior states into W3D animations. */ //------------------------------------------------------------------------------------------------- void W3DTankTruckDraw::doDrawModule(const Matrix3D* transformMtx) { W3DModelDraw::doDrawModule(transformMtx); if (!TheGlobalData->m_showClientPhysics) return; 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) { updateBones(); updateTreadObjects(); } // 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! if (wheelInfo && (m_frontLeftTireBone || m_rearLeftTireBone)) { static Real rotation = 0; const Real rotationFactor = getW3DTankTruckDrawModuleData()->m_rotationSpeedMultiplier; m_frontWheelRotation += rotationFactor*speed; if (m_isPowersliding) { m_rearWheelRotation += rotationFactor*(speed+getW3DTankTruckDrawModuleData()->m_powerslideRotationAddition); } else { m_rearWheelRotation += rotationFactor*speed; } Matrix3D wheelXfrm(1); if (m_frontLeftTireBone) { 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) { 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); getRenderObject()->Capture_Bone( m_rearRightTireBone ); getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm ); } if (m_midFrontLeftTireBone) { 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) { 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 ); } } 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.setPosition(obj->getPosition()); 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()); } //Tank update #ifdef SHOW_TANK_DEBRIS const Real DEBRIS_THRESHOLD = 0.00001f; // if tank is moving, kick up dust and debris Real velMag = vel->x*vel->x + vel->y*vel->y; // only care about moving on the ground if (velMag > DEBRIS_THRESHOLD && !getDrawable()->isDrawableEffectivelyHidden() && !getFullyObscuredByShroud()) startMoveDebris(); else stopMoveDebris(); // kick debris higher the faster we move Coord3D velMult; velMag = (Real)sqrt( velMag ); velMult.x = 0.5f * velMag + 0.1f; if (velMult.x > 1.0f) velMult.x = 1.0f; velMult.y = velMult.x; velMult.z = velMag + 0.1f; if (velMult.z > 1.0f) velMult.z = 1.0f; m_treadDebrisLeft->setVelocityMultiplier( &velMult ); m_treadDebrisRight->setVelocityMultiplier( &velMult ); m_treadDebrisLeft->setBurstCountMultiplier( velMult.z ); m_treadDebrisRight->setBurstCountMultiplier( velMult.z ); #endif //Update movement of treads if (m_treadCount) { Real offset_u; Real treadScrollSpeed=getW3DTankTruckDrawModuleData()->m_treadAnimationRate; TreadObjectInfo *pTread=m_treads; Real maxSpeed=obj->getAIUpdateInterface()->getCurLocomotorSpeed(); /* Commented out because these vehicles are presumed not to turn via treads. PhysicsTurningType turn=physics->getTurning(); //For optimization sake, we only do complex tread scrolling when tank //is mostly stationary and turning if ((turn=physics->getTurning()) != TURN_NONE && physics->getSpeed()/maxSpeed < getW3DTankTruckDrawModuleData()->m_treadPivotSpeedFraction) { if (turn == TURN_NEGATIVE) //turning right updateTreadPositions(-treadScrollSpeed); else //turning left updateTreadPositions(treadScrollSpeed); } else*/ if (physics->isMotive() && physics->getVelocityMagnitude()/maxSpeed >= getW3DTankTruckDrawModuleData()->m_treadDriveSpeedFraction) { //do simple scrolling based only on speed when tank is moving straight at high speed. //we stop scrolling when tank slows down to reduce the appearance of sliding //tread scrolling speed was not directly tied into tank velocity because it looked odd //under certain situations when tank moved sideways. for (Int i=0; im_materialSettings.customUVOffset.X - treadScrollSpeed; // ensure coordinates of offset are in [0, 1] range: offset_u = offset_u - WWMath::Floor(offset_u); pTread->m_materialSettings.customUVOffset.Set(offset_u,0); pTread++; } } } } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void W3DTankTruckDraw::crc( Xfer *xfer ) { // extend base class W3DModelDraw::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void W3DTankTruckDraw::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 W3DTankTruckDraw::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