W3DTankDraw.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: W3DTankDraw.cpp //////////////////////////////////////////////////////////////////////////
  24. // Draw turreted tanks
  25. // Michael S. Booth, October 2001
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include <stdlib.h>
  29. #include <math.h>
  30. #include "Common/Thing.h"
  31. #include "Common/ThingFactory.h"
  32. #include "Common/GameAudio.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "Common/Xfer.h"
  35. #include "GameLogic/Weapon.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Module/PhysicsUpdate.h"
  38. #include "GameLogic/Module/BodyModule.h"
  39. #include "GameLogic/ScriptEngine.h"
  40. #include "GameLogic/Module/AIUpdate.h"
  41. #include "GameClient/Drawable.h"
  42. #include "GameClient/ParticleSys.h"
  43. #include "W3DDevice/GameClient/W3DGameClient.h"
  44. #include "W3DDevice/GameClient/Module/W3DTankDraw.h"
  45. #include "WW3D2/matinfo.h"
  46. #ifdef _INTERNAL
  47. // for occasional debugging...
  48. //#pragma optimize("", off)
  49. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  50. #endif
  51. class Matrix3D;
  52. //-------------------------------------------------------------------------------------------------
  53. W3DTankDrawModuleData::W3DTankDrawModuleData() :
  54. m_treadDebrisNameLeft("TrackDebrisDirtLeft"),
  55. m_treadDebrisNameRight("TrackDebrisDirtRight"),
  56. m_treadAnimationRate(0.0f),
  57. m_treadPivotSpeedFraction(0.6f),
  58. m_treadDriveSpeedFraction(0.3f)
  59. {
  60. }
  61. //-------------------------------------------------------------------------------------------------
  62. W3DTankDrawModuleData::~W3DTankDrawModuleData()
  63. {
  64. }
  65. //-------------------------------------------------------------------------------------------------
  66. void W3DTankDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
  67. {
  68. W3DModelDrawModuleData::buildFieldParse(p);
  69. static const FieldParse dataFieldParse[] =
  70. {
  71. { "TreadDebrisLeft", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameLeft) },
  72. { "TreadDebrisRight", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameRight) },
  73. { "TreadAnimationRate", INI::parseVelocityReal, NULL, offsetof(W3DTankDrawModuleData, m_treadAnimationRate) },
  74. { "TreadPivotSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadPivotSpeedFraction) },
  75. { "TreadDriveSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadDriveSpeedFraction) },
  76. { 0, 0, 0, 0 }
  77. };
  78. p.add(dataFieldParse);
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. //-------------------------------------------------------------------------------------------------
  82. W3DTankDraw::W3DTankDraw( Thing *thing, const ModuleData* moduleData )
  83. : W3DModelDraw( thing, moduleData ),m_prevRenderObj(NULL), m_treadDebrisLeft(NULL), m_treadDebrisRight(NULL)
  84. {
  85. m_treadDebrisLeft = NULL;
  86. m_treadDebrisRight = NULL;
  87. for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
  88. m_treads[i].m_robj = NULL;
  89. m_treadCount=0;
  90. //Assume all things face along x axis when created.
  91. m_lastDirection.x=1.0f;
  92. m_lastDirection.y=0.0f;
  93. m_lastDirection.z=0.0f;
  94. createEmitters();
  95. }
  96. //-------------------------------------------------------------------------------------------------
  97. //-------------------------------------------------------------------------------------------------
  98. void W3DTankDraw::tossEmitters( void )
  99. {
  100. if (m_treadDebrisLeft)
  101. {
  102. m_treadDebrisLeft->attachToObject(NULL);
  103. m_treadDebrisLeft->destroy();
  104. m_treadDebrisLeft = NULL;
  105. }
  106. if (m_treadDebrisRight)
  107. {
  108. m_treadDebrisRight->attachToObject(NULL);
  109. m_treadDebrisRight->destroy();
  110. m_treadDebrisRight = NULL;
  111. }
  112. }
  113. //-------------------------------------------------------------------------------------------------
  114. //-------------------------------------------------------------------------------------------------
  115. void W3DTankDraw::createEmitters( void )
  116. {
  117. if (!m_treadDebrisLeft)
  118. {
  119. const ParticleSystemTemplate *sysTemplate;
  120. sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameLeft);
  121. if (sysTemplate)
  122. {
  123. m_treadDebrisLeft = TheParticleSystemManager->createParticleSystem( sysTemplate );
  124. m_treadDebrisLeft->attachToDrawable(getDrawable());
  125. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  126. m_treadDebrisLeft->setSaveable(FALSE);
  127. // they come into being stopped.
  128. m_treadDebrisLeft->stop();
  129. }
  130. }
  131. if (!m_treadDebrisRight)
  132. {
  133. const ParticleSystemTemplate *sysTemplate;
  134. sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameRight);
  135. if (sysTemplate)
  136. {
  137. m_treadDebrisRight = TheParticleSystemManager->createParticleSystem( sysTemplate );
  138. m_treadDebrisRight->attachToDrawable(getDrawable());
  139. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  140. m_treadDebrisRight->setSaveable(FALSE);
  141. // they come into being stopped.
  142. m_treadDebrisRight->stop();
  143. }
  144. }
  145. }
  146. //-------------------------------------------------------------------------------------------------
  147. //-------------------------------------------------------------------------------------------------
  148. W3DTankDraw::~W3DTankDraw()
  149. {
  150. for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
  151. if (m_treads[i].m_robj)
  152. REF_PTR_RELEASE(m_treads[i].m_robj);
  153. }
  154. //-------------------------------------------------------------------------------------------------
  155. //-------------------------------------------------------------------------------------------------
  156. /**
  157. * Start creating debris from the tank treads
  158. */
  159. void W3DTankDraw::startMoveDebris( void )
  160. {
  161. if (getDrawable()->isDrawableEffectivelyHidden())
  162. return;
  163. if (m_treadDebrisLeft)
  164. m_treadDebrisLeft->start();
  165. if (m_treadDebrisRight)
  166. m_treadDebrisRight->start();
  167. }
  168. //-------------------------------------------------------------------------------------------------
  169. //-------------------------------------------------------------------------------------------------
  170. /**
  171. * Stop creating debris from the tank treads
  172. */
  173. void W3DTankDraw::stopMoveDebris( void )
  174. {
  175. if (m_treadDebrisLeft)
  176. m_treadDebrisLeft->stop();
  177. if (m_treadDebrisRight)
  178. m_treadDebrisRight->stop();
  179. }
  180. //-------------------------------------------------------------------------------------------------
  181. void W3DTankDraw::setHidden(Bool h)
  182. {
  183. W3DModelDraw::setHidden(h);
  184. if (h)
  185. {
  186. stopMoveDebris();
  187. }
  188. }
  189. //-------------------------------------------------------------------------------------------------
  190. void W3DTankDraw::setFullyObscuredByShroud(Bool fullyObscured)
  191. {
  192. if (fullyObscured != getFullyObscuredByShroud())
  193. {
  194. if (fullyObscured)
  195. stopMoveDebris();
  196. }
  197. W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
  198. }
  199. /**Update uv coordinates on each tread object to simulate movement*/
  200. void W3DTankDraw::updateTreadPositions(Real uvDelta)
  201. {
  202. Real offset_u;
  203. TreadObjectInfo *pTread=m_treads;
  204. for (Int i=0; i<m_treadCount; i++)
  205. {
  206. if (pTread->m_type == TREAD_LEFT) //this tread needs to scroll forwards
  207. offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta;
  208. else
  209. if (pTread->m_type == TREAD_RIGHT) //this tread needs to scroll backwards
  210. offset_u = pTread->m_materialSettings.customUVOffset.X - uvDelta;
  211. // ensure coordinates of offset are in [0, 1] range:
  212. offset_u = offset_u - WWMath::Floor(offset_u);
  213. pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
  214. pTread++;
  215. }
  216. }
  217. /**Grab pointers to the sub-meshes for each tread*/
  218. void W3DTankDraw::updateTreadObjects(void)
  219. {
  220. RenderObjClass *robj=getRenderObject();
  221. //clear all previous tread pointers
  222. for (Int i=0; i<m_treadCount; i++)
  223. REF_PTR_RELEASE(m_treads[i].m_robj);
  224. m_treadCount = 0;
  225. //Make sure this object has defined a speed for tread scrolling.
  226. if (getW3DTankDrawModuleData() && getW3DTankDrawModuleData()->m_treadAnimationRate && robj)
  227. {
  228. for (Int i=0; i < robj->Get_Num_Sub_Objects() && m_treadCount < MAX_TREADS_PER_TANK; i++)
  229. {
  230. RenderObjClass *subObj=robj->Get_Sub_Object(i);
  231. const char *meshName;
  232. //Check if subobject name starts with "TREADS".
  233. if (subObj && subObj->Class_ID() == RenderObjClass::CLASSID_MESH && subObj->Get_Name()
  234. && ( (meshName=strchr(subObj->Get_Name(),'.') ) != 0 && *(meshName++))
  235. &&_strnicmp(meshName,"TREADS", 6) == 0)
  236. { //check if sub-object has the correct material to do texture scrolling.
  237. MaterialInfoClass *mat=subObj->Get_Material_Info();
  238. if (mat)
  239. { for (Int j=0; j<mat->Vertex_Material_Count(); j++)
  240. {
  241. VertexMaterialClass *vmaterial=mat->Peek_Vertex_Material(j);
  242. LinearOffsetTextureMapperClass *mapper=(LinearOffsetTextureMapperClass *)vmaterial->Peek_Mapper();
  243. if (mapper && mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET)
  244. { mapper->Set_UV_Offset_Delta(Vector2(0,0)); //disable automatic scrolling
  245. subObj->Add_Ref(); //increase reference since we're storing the pointer
  246. m_treads[m_treadCount].m_robj=subObj;
  247. m_treads[m_treadCount].m_type = TREAD_MIDDLE; //default type
  248. subObj->Set_User_Data(&m_treads[m_treadCount].m_materialSettings); //tell W3D about custom material settings
  249. m_treads[m_treadCount].m_materialSettings.customUVOffset=Vector2(0,0);
  250. switch (meshName[6]) //check next character after 'TREADS'
  251. {
  252. case 'L':
  253. case 'l': m_treads[m_treadCount].m_type = TREAD_LEFT;
  254. break;
  255. case 'R':
  256. case 'r': m_treads[m_treadCount].m_type = TREAD_RIGHT;
  257. break;
  258. }
  259. m_treadCount++;
  260. }
  261. }
  262. REF_PTR_RELEASE(mat);
  263. }
  264. }
  265. REF_PTR_RELEASE(subObj);
  266. }
  267. }
  268. m_prevRenderObj = robj;
  269. }
  270. //-------------------------------------------------------------------------------------------------
  271. void W3DTankDraw::onRenderObjRecreated(void)
  272. {
  273. updateTreadObjects();
  274. }
  275. //-------------------------------------------------------------------------------------------------
  276. /** Map behavior states into W3D animations. */
  277. //-------------------------------------------------------------------------------------------------
  278. void W3DTankDraw::doDrawModule(const Matrix3D* transformMtx)
  279. {
  280. const Real DEBRIS_THRESHOLD = 0.00001f;
  281. Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
  282. frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
  283. if (frozen)
  284. return;
  285. if (getRenderObject()==NULL) return;
  286. if (getRenderObject() != m_prevRenderObj) {
  287. updateTreadObjects();
  288. }
  289. // get object from logic
  290. Object *obj = getDrawable()->getObject();
  291. if (obj == NULL)
  292. return;
  293. // get object physics state
  294. PhysicsBehavior *physics = obj->getPhysics();
  295. if (physics == NULL)
  296. return;
  297. const Coord3D *vel = physics->getVelocity();
  298. // if tank is moving, kick up dust and debris
  299. Real velMag = vel->x*vel->x + vel->y*vel->y; // only care about moving on the ground
  300. if (velMag > DEBRIS_THRESHOLD && !getDrawable()->isDrawableEffectivelyHidden() && !getFullyObscuredByShroud())
  301. startMoveDebris();
  302. else
  303. stopMoveDebris();
  304. // kick debris higher the faster we move
  305. Coord3D velMult;
  306. velMag = (Real)sqrt( velMag );
  307. velMult.x = 0.5f * velMag + 0.1f;
  308. if (velMult.x > 1.0f)
  309. velMult.x = 1.0f;
  310. velMult.y = velMult.x;
  311. velMult.z = velMag + 0.1f;
  312. if (velMult.z > 1.0f)
  313. velMult.z = 1.0f;
  314. m_treadDebrisLeft->setVelocityMultiplier( &velMult );
  315. m_treadDebrisRight->setVelocityMultiplier( &velMult );
  316. m_treadDebrisLeft->setBurstCountMultiplier( velMult.z );
  317. m_treadDebrisRight->setBurstCountMultiplier( velMult.z );
  318. //Update movement of treads
  319. if (m_treadCount)
  320. {
  321. PhysicsTurningType turn=physics->getTurning();
  322. Real offset_u;
  323. Real treadScrollSpeed=getW3DTankDrawModuleData()->m_treadAnimationRate;
  324. TreadObjectInfo *pTread=m_treads;
  325. Real maxSpeed=obj->getAIUpdateInterface()->getCurLocomotorSpeed();
  326. //For optimization sake, we only do complex tread scrolling when tank
  327. //is mostly stationary and turning
  328. if (turn != TURN_NONE && physics->getVelocityMagnitude()/maxSpeed < getW3DTankDrawModuleData()->m_treadPivotSpeedFraction)
  329. {
  330. //Check if we have turned enough since last draw to require animation
  331. Coord3D dir;
  332. obj->getUnitDirectionVector2D(dir);
  333. Real angleToGoal = dir.x * m_lastDirection.x + dir.y * m_lastDirection.y;
  334. if (fabs(1.0f-angleToGoal) > 0.00001f) //check if difference in angle cosines is greater than some cutoff.
  335. {
  336. if (turn == TURN_NEGATIVE) //turning right
  337. updateTreadPositions(-treadScrollSpeed);
  338. else //turning left
  339. updateTreadPositions(treadScrollSpeed);
  340. }
  341. m_lastDirection=dir; //update for next frame
  342. }
  343. else
  344. if (physics->isMotive() && physics->getVelocityMagnitude()/maxSpeed >= getW3DTankDrawModuleData()->m_treadDriveSpeedFraction)
  345. { //do simple scrolling based only on speed when tank is moving straight at high speed.
  346. //we stop scrolling when tank slows down to reduce the appearance of sliding
  347. //tread scrolling speed was not directly tied into tank velocity because it looked odd
  348. //under certain situations when tank moved sideways.
  349. for (Int i=0; i<m_treadCount; i++)
  350. {
  351. offset_u = pTread->m_materialSettings.customUVOffset.X - treadScrollSpeed;
  352. // ensure coordinates of offset are in [0, 1] range:
  353. offset_u = offset_u - WWMath::Floor(offset_u);
  354. pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
  355. pTread++;
  356. }
  357. }
  358. }
  359. W3DModelDraw::doDrawModule(transformMtx);
  360. }
  361. // ------------------------------------------------------------------------------------------------
  362. /** CRC */
  363. // ------------------------------------------------------------------------------------------------
  364. void W3DTankDraw::crc( Xfer *xfer )
  365. {
  366. // extend base class
  367. W3DModelDraw::crc( xfer );
  368. } // end crc
  369. // ------------------------------------------------------------------------------------------------
  370. /** Xfer method
  371. * Version Info:
  372. * 1: Initial version */
  373. // ------------------------------------------------------------------------------------------------
  374. void W3DTankDraw::xfer( Xfer *xfer )
  375. {
  376. // version
  377. XferVersion currentVersion = 1;
  378. XferVersion version = currentVersion;
  379. xfer->xferVersion( &version, currentVersion );
  380. // extend base class
  381. W3DModelDraw::xfer( xfer );
  382. // John A and Mark W say there is no data to save here
  383. } // end xfer
  384. // ------------------------------------------------------------------------------------------------
  385. /** Load post process */
  386. // ------------------------------------------------------------------------------------------------
  387. void W3DTankDraw::loadPostProcess( void )
  388. {
  389. // extend base class
  390. W3DModelDraw::loadPostProcess();
  391. // toss any existing ones and re-create 'em (since this module expects 'em to always be around)
  392. tossEmitters();
  393. createEmitters();
  394. } // end loadPostProcess