| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- /*
- ** 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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: W3DTankDraw.cpp //////////////////////////////////////////////////////////////////////////
- // Draw turreted tanks
- // Michael S. Booth, October 2001
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include <stdlib.h>
- #include <math.h>
- #include "Common/Thing.h"
- #include "Common/ThingFactory.h"
- #include "Common/GameAudio.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/W3DTankDraw.h"
- #include "WW3D2/matinfo.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- class Matrix3D;
- //-------------------------------------------------------------------------------------------------
- W3DTankDrawModuleData::W3DTankDrawModuleData() :
- m_treadDebrisNameLeft("TrackDebrisDirtLeft"),
- m_treadDebrisNameRight("TrackDebrisDirtRight"),
- m_treadAnimationRate(0.0f),
- m_treadPivotSpeedFraction(0.6f),
- m_treadDriveSpeedFraction(0.3f)
- {
- }
- //-------------------------------------------------------------------------------------------------
- W3DTankDrawModuleData::~W3DTankDrawModuleData()
- {
- }
- //-------------------------------------------------------------------------------------------------
- void W3DTankDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- W3DModelDrawModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "TreadDebrisLeft", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameLeft) },
- { "TreadDebrisRight", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameRight) },
- { "TreadAnimationRate", INI::parseVelocityReal, NULL, offsetof(W3DTankDrawModuleData, m_treadAnimationRate) },
- { "TreadPivotSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadPivotSpeedFraction) },
- { "TreadDriveSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadDriveSpeedFraction) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DTankDraw::W3DTankDraw( Thing *thing, const ModuleData* moduleData )
- : W3DModelDraw( thing, moduleData ),m_prevRenderObj(NULL), m_treadDebrisLeft(NULL), m_treadDebrisRight(NULL)
- {
- m_treadDebrisLeft = NULL;
- m_treadDebrisRight = NULL;
- for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
- m_treads[i].m_robj = NULL;
- m_treadCount=0;
- //Assume all things face along x axis when created.
- m_lastDirection.x=1.0f;
- m_lastDirection.y=0.0f;
- m_lastDirection.z=0.0f;
- createEmitters();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void W3DTankDraw::tossEmitters( void )
- {
- if (m_treadDebrisLeft)
- {
- m_treadDebrisLeft->attachToObject(NULL);
- m_treadDebrisLeft->destroy();
- m_treadDebrisLeft = NULL;
- }
- if (m_treadDebrisRight)
- {
- m_treadDebrisRight->attachToObject(NULL);
- m_treadDebrisRight->destroy();
- m_treadDebrisRight = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void W3DTankDraw::createEmitters( void )
- {
- if (!m_treadDebrisLeft)
- {
- const ParticleSystemTemplate *sysTemplate;
- sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameLeft);
- if (sysTemplate)
- {
- m_treadDebrisLeft = TheParticleSystemManager->createParticleSystem( sysTemplate );
- m_treadDebrisLeft->attachToDrawable(getDrawable());
- // important: mark it as do-not-save, since we'll just re-create it when we reload.
- m_treadDebrisLeft->setSaveable(FALSE);
- // they come into being stopped.
- m_treadDebrisLeft->stop();
- }
- }
- if (!m_treadDebrisRight)
- {
- const ParticleSystemTemplate *sysTemplate;
- sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameRight);
- if (sysTemplate)
- {
- m_treadDebrisRight = TheParticleSystemManager->createParticleSystem( sysTemplate );
- m_treadDebrisRight->attachToDrawable(getDrawable());
- // important: mark it as do-not-save, since we'll just re-create it when we reload.
- m_treadDebrisRight->setSaveable(FALSE);
- // they come into being stopped.
- m_treadDebrisRight->stop();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- W3DTankDraw::~W3DTankDraw()
- {
- for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
- if (m_treads[i].m_robj)
- REF_PTR_RELEASE(m_treads[i].m_robj);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /**
- * Start creating debris from the tank treads
- */
- void W3DTankDraw::startMoveDebris( void )
- {
- if (getDrawable()->isDrawableEffectivelyHidden())
- return;
- if (m_treadDebrisLeft)
- m_treadDebrisLeft->start();
- if (m_treadDebrisRight)
- m_treadDebrisRight->start();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /**
- * Stop creating debris from the tank treads
- */
- void W3DTankDraw::stopMoveDebris( void )
- {
- if (m_treadDebrisLeft)
- m_treadDebrisLeft->stop();
- if (m_treadDebrisRight)
- m_treadDebrisRight->stop();
- }
- //-------------------------------------------------------------------------------------------------
- void W3DTankDraw::setHidden(Bool h)
- {
- W3DModelDraw::setHidden(h);
- if (h)
- {
- stopMoveDebris();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void W3DTankDraw::setFullyObscuredByShroud(Bool fullyObscured)
- {
- if (fullyObscured != getFullyObscuredByShroud())
- {
- if (fullyObscured)
- stopMoveDebris();
- }
- W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
- }
- /**Update uv coordinates on each tread object to simulate movement*/
- void W3DTankDraw::updateTreadPositions(Real uvDelta)
- {
- Real offset_u;
- TreadObjectInfo *pTread=m_treads;
- for (Int i=0; i<m_treadCount; i++)
- {
- 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 W3DTankDraw::updateTreadObjects(void)
- {
- RenderObjClass *robj=getRenderObject();
- //clear all previous tread pointers
- for (Int i=0; i<m_treadCount; i++)
- REF_PTR_RELEASE(m_treads[i].m_robj);
- m_treadCount = 0;
- //Make sure this object has defined a speed for tread scrolling.
- if (getW3DTankDrawModuleData() && getW3DTankDrawModuleData()->m_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; j<mat->Vertex_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);
- 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 W3DTankDraw::onRenderObjRecreated(void)
- {
- updateTreadObjects();
- }
- //-------------------------------------------------------------------------------------------------
- /** Map behavior states into W3D animations. */
- //-------------------------------------------------------------------------------------------------
- void W3DTankDraw::doDrawModule(const Matrix3D* transformMtx)
- {
- const Real DEBRIS_THRESHOLD = 0.00001f;
- Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
- frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
- if (frozen)
- return;
- if (getRenderObject()==NULL) return;
- if (getRenderObject() != m_prevRenderObj) {
- updateTreadObjects();
- }
- // get object from logic
- Object *obj = getDrawable()->getObject();
- if (obj == NULL)
- return;
- // get object physics state
- PhysicsBehavior *physics = obj->getPhysics();
- if (physics == NULL)
- return;
- const Coord3D *vel = physics->getVelocity();
- // 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 );
- //Update movement of treads
- if (m_treadCount)
- {
- PhysicsTurningType turn=physics->getTurning();
- Real offset_u;
- Real treadScrollSpeed=getW3DTankDrawModuleData()->m_treadAnimationRate;
- TreadObjectInfo *pTread=m_treads;
- Real maxSpeed=obj->getAIUpdateInterface()->getCurLocomotorSpeed();
- //For optimization sake, we only do complex tread scrolling when tank
- //is mostly stationary and turning
- if (turn != TURN_NONE && physics->getVelocityMagnitude()/maxSpeed < getW3DTankDrawModuleData()->m_treadPivotSpeedFraction)
- {
- //Check if we have turned enough since last draw to require animation
- Coord3D dir;
- obj->getUnitDirectionVector2D(dir);
- Real angleToGoal = dir.x * m_lastDirection.x + dir.y * m_lastDirection.y;
-
- if (fabs(1.0f-angleToGoal) > 0.00001f) //check if difference in angle cosines is greater than some cutoff.
- {
- if (turn == TURN_NEGATIVE) //turning right
- updateTreadPositions(-treadScrollSpeed);
- else //turning left
- updateTreadPositions(treadScrollSpeed);
- }
- m_lastDirection=dir; //update for next frame
- }
- else
- if (physics->isMotive() && physics->getVelocityMagnitude()/maxSpeed >= getW3DTankDrawModuleData()->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; i<m_treadCount; i++)
- {
- offset_u = pTread->m_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++;
- }
- }
- }
- W3DModelDraw::doDrawModule(transformMtx);
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void W3DTankDraw::crc( Xfer *xfer )
- {
- // extend base class
- W3DModelDraw::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void W3DTankDraw::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 W3DTankDraw::loadPostProcess( void )
- {
- // extend base class
- W3DModelDraw::loadPostProcess();
- // toss any existing ones and re-create 'em (since this module expects 'em to always be around)
- tossEmitters();
- createEmitters();
- } // end loadPostProcess
|