W3DTruckDraw.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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: W3DTruckDraw.cpp
  24. // Draw Trucks. Actually, this draws rocket buggies.
  25. // Author: John Ahlquist, March 2002
  26. #include <stdlib.h>
  27. #include <math.h>
  28. #include "Common/Thing.h"
  29. #include "Common/ThingFactory.h"
  30. #include "Common/GameAudio.h"
  31. #include "Common/GlobalData.h"
  32. #include "Common/ThingTemplate.h"
  33. #include "Common/Xfer.h"
  34. #include "GameClient/Drawable.h"
  35. #include "GameClient/ParticleSys.h"
  36. #include "GameLogic/AIPathfind.h"
  37. #include "GameLogic/Weapon.h"
  38. #include "GameLogic/GameLogic.h" // for logic frame count
  39. #include "GameLogic/PartitionManager.h"
  40. #include "GameLogic/Locomotor.h"
  41. #include "GameLogic/Module/PhysicsUpdate.h"
  42. #include "GameLogic/Module/BodyModule.h"
  43. #include "GameLogic/Module/AIUpdate.h"
  44. #include "GameLogic/ScriptEngine.h"
  45. #include "W3DDevice/GameClient/W3DGameClient.h"
  46. #include "W3DDevice/GameClient/Module/W3DTruckDraw.h"
  47. #ifdef _INTERNAL
  48. // for occasional debugging...
  49. //#pragma optimize("", off)
  50. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  51. #endif
  52. //-------------------------------------------------------------------------------------------------
  53. W3DTruckDrawModuleData::W3DTruckDrawModuleData()
  54. {
  55. }
  56. //-------------------------------------------------------------------------------------------------
  57. W3DTruckDrawModuleData::~W3DTruckDrawModuleData()
  58. {
  59. }
  60. //-------------------------------------------------------------------------------------------------
  61. void W3DTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
  62. {
  63. W3DModelDrawModuleData::buildFieldParse(p);
  64. static const FieldParse dataFieldParse[] =
  65. {
  66. { "Dust", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dustEffectName) },
  67. { "DirtSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dirtEffectName) },
  68. { "PowerslideSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideEffectName) },
  69. { "LeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontLeftTireBoneName) },
  70. { "RightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontRightTireBoneName) },
  71. { "LeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearLeftTireBoneName) },
  72. { "RightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearRightTireBoneName) },
  73. { "MidLeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontLeftTireBoneName) },
  74. { "MidRightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontRightTireBoneName) },
  75. { "MidLeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearLeftTireBoneName) },
  76. { "MidRightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearRightTireBoneName) },
  77. { "MidLeftMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidLeftTireBoneName) },
  78. { "MidRightMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidRightTireBoneName) },
  79. { "TireRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationSpeedMultiplier) },
  80. { "PowerslideRotationAddition", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideRotationAddition) },
  81. { "CabBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_cabBoneName) },
  82. { "TrailerBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_trailerBoneName) },
  83. { "CabRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_cabRotationFactor) },
  84. { "TrailerRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_trailerRotationFactor) },
  85. { "RotationDamping", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationDampingFactor) },
  86. { 0, 0, 0, 0 }
  87. };
  88. p.add(dataFieldParse);
  89. }
  90. //-------------------------------------------------------------------------------------------------
  91. //-------------------------------------------------------------------------------------------------
  92. W3DTruckDraw::W3DTruckDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData ),
  93. m_dirtEffect(NULL), m_dustEffect(NULL), m_powerslideEffect(NULL), m_effectsInitialized(false),
  94. m_wasAirborne(false), m_isPowersliding(false),
  95. m_frontWheelRotation(0), m_rearWheelRotation(0), m_midFrontWheelRotation(0), m_midRearWheelRotation(0),
  96. m_frontRightTireBone(0), m_frontLeftTireBone(0), m_rearLeftTireBone(0),m_rearRightTireBone(0),
  97. m_midFrontRightTireBone(0), m_midFrontLeftTireBone(0), m_midRearLeftTireBone(0),m_midRearRightTireBone(0),
  98. m_midMidRightTireBone(0), m_midMidLeftTireBone(0), m_prevRenderObj(NULL)
  99. {
  100. const AudioEventRTS * event;
  101. event = thing->getTemplate()->getPerUnitSound("TruckLandingSound");
  102. if (event) {
  103. m_landingSound = *event;
  104. }
  105. event = thing->getTemplate()->getPerUnitSound("TruckPowerslideSound");
  106. if (event) {
  107. m_powerslideSound = *event;
  108. }
  109. }
  110. //-------------------------------------------------------------------------------------------------
  111. //-------------------------------------------------------------------------------------------------
  112. W3DTruckDraw::~W3DTruckDraw()
  113. {
  114. tossEmitters();
  115. }
  116. //-------------------------------------------------------------------------------------------------
  117. //-------------------------------------------------------------------------------------------------
  118. void W3DTruckDraw::tossEmitters()
  119. {
  120. if (m_dustEffect)
  121. {
  122. m_dustEffect->attachToObject(NULL);
  123. m_dustEffect->destroy();
  124. m_dustEffect = NULL;
  125. }
  126. if (m_dirtEffect)
  127. {
  128. m_dirtEffect->attachToObject(NULL);
  129. m_dirtEffect->destroy();
  130. m_dirtEffect = NULL;
  131. }
  132. if (m_powerslideEffect)
  133. {
  134. m_powerslideEffect->attachToObject(NULL);
  135. m_powerslideEffect->destroy();
  136. m_powerslideEffect = NULL;
  137. }
  138. }
  139. //-------------------------------------------------------------------------------------------------
  140. void W3DTruckDraw::setFullyObscuredByShroud(Bool fullyObscured)
  141. {
  142. if (fullyObscured != getFullyObscuredByShroud())
  143. {
  144. if (fullyObscured)
  145. tossEmitters();
  146. else
  147. createEmitters();
  148. }
  149. W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
  150. }
  151. //-------------------------------------------------------------------------------------------------
  152. //-------------------------------------------------------------------------------------------------
  153. /**
  154. * Start creating debris from the tank treads
  155. */
  156. void W3DTruckDraw::createEmitters( void )
  157. {
  158. if (getDrawable()->isDrawableEffectivelyHidden())
  159. return;
  160. if (getW3DTruckDrawModuleData())
  161. {
  162. const ParticleSystemTemplate *sysTemplate;
  163. if (!m_dustEffect) {
  164. sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dustEffectName);
  165. if (sysTemplate)
  166. {
  167. m_dustEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
  168. m_dustEffect->attachToObject(getDrawable()->getObject());
  169. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  170. m_dustEffect->setSaveable(FALSE);
  171. } else {
  172. if (!getW3DTruckDrawModuleData()->m_dustEffectName.isEmpty()) {
  173. DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
  174. getW3DTruckDrawModuleData()->m_dustEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
  175. }
  176. }
  177. }
  178. if (!m_dirtEffect) {
  179. sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dirtEffectName);
  180. if (sysTemplate)
  181. {
  182. m_dirtEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
  183. m_dirtEffect->attachToObject(getDrawable()->getObject());
  184. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  185. m_dirtEffect->setSaveable(FALSE);
  186. } else {
  187. if (!getW3DTruckDrawModuleData()->m_dirtEffectName.isEmpty()) {
  188. DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
  189. getW3DTruckDrawModuleData()->m_dirtEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
  190. }
  191. }
  192. }
  193. if (!m_powerslideEffect) {
  194. sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_powerslideEffectName);
  195. if (sysTemplate)
  196. {
  197. m_powerslideEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
  198. m_powerslideEffect->attachToObject(getDrawable()->getObject());
  199. // important: mark it as do-not-save, since we'll just re-create it when we reload.
  200. m_powerslideEffect->setSaveable(FALSE);
  201. } else {
  202. if (!getW3DTruckDrawModuleData()->m_powerslideEffectName.isEmpty()) {
  203. DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
  204. getW3DTruckDrawModuleData()->m_powerslideEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
  205. }
  206. }
  207. }
  208. }
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. //-------------------------------------------------------------------------------------------------
  212. /**
  213. * Stop creating debris from the tank treads
  214. */
  215. void W3DTruckDraw::enableEmitters( Bool enable )
  216. {
  217. // don't check... if we are hidden the first time thru, then we'll never create the emitters.
  218. // eg, if we are loading a game and the unit is in a tunnel, he'll never get emitteres even when he exits.
  219. //if (!m_effectsInitialized)
  220. {
  221. createEmitters();
  222. m_effectsInitialized=true;
  223. }
  224. if (m_dustEffect)
  225. {
  226. if (enable)
  227. m_dustEffect->start();
  228. else
  229. m_dustEffect->stop();
  230. }
  231. if (m_dirtEffect)
  232. {
  233. if (enable)
  234. m_dirtEffect->start();
  235. else
  236. m_dirtEffect->stop();
  237. }
  238. if (m_powerslideEffect)
  239. {
  240. if (!enable)
  241. m_powerslideEffect->stop();
  242. }
  243. }
  244. //-------------------------------------------------------------------------------------------------
  245. void W3DTruckDraw::updateBones( void )
  246. {
  247. if( getW3DTruckDrawModuleData() )
  248. {
  249. //Front tires
  250. if( !getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() )
  251. {
  252. m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str());
  253. DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  254. }
  255. if( !getW3DTruckDrawModuleData()->m_frontRightTireBoneName.isEmpty() )
  256. {
  257. m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str());
  258. DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name()));
  259. }
  260. //Rear tires
  261. if( !getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.isEmpty() )
  262. {
  263. m_rearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str());
  264. DEBUG_ASSERTCRASH(m_rearLeftTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  265. }
  266. if( !getW3DTruckDrawModuleData()->m_rearRightTireBoneName.isEmpty() )
  267. {
  268. m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str());
  269. DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name()));
  270. }
  271. //midFront tires
  272. if( !getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() )
  273. {
  274. m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str());
  275. DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  276. m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str());
  277. DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name()));
  278. if (!m_midFrontRightTireBone )
  279. {
  280. m_midFrontLeftTireBone = 0;
  281. }
  282. }
  283. //midRear tires
  284. if( !getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() )
  285. {
  286. m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str());
  287. DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  288. m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str());
  289. DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name()));
  290. if (!m_midRearRightTireBone)
  291. {
  292. m_midRearLeftTireBone = 0;
  293. }
  294. }
  295. //midMid tires
  296. if( !getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.isEmpty() )
  297. {
  298. m_midMidLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str());
  299. DEBUG_ASSERTCRASH(m_midMidLeftTireBone, ("Missing mid-mid-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  300. m_midMidRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str());
  301. DEBUG_ASSERTCRASH(m_midMidRightTireBone, ("Missing mid-mid-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str(), getRenderObject()->Get_Name()));
  302. if (!m_midMidRightTireBone)
  303. {
  304. m_midMidLeftTireBone = 0;
  305. }
  306. }
  307. //Cab
  308. if( !getW3DTruckDrawModuleData()->m_cabBoneName.isEmpty() )
  309. {
  310. m_cabBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_cabBoneName.str());
  311. DEBUG_ASSERTCRASH(m_cabBone, ("Missing cab bone %s in model %s\n", getW3DTruckDrawModuleData()->m_cabBoneName.str(), getRenderObject()->Get_Name()));
  312. m_trailerBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_trailerBoneName.str());
  313. }
  314. }
  315. m_prevRenderObj = getRenderObject();
  316. m_prevNumBones = m_prevRenderObj->Get_Num_Bones();
  317. }
  318. //-------------------------------------------------------------------------------------------------
  319. void W3DTruckDraw::setHidden(Bool h)
  320. {
  321. W3DModelDraw::setHidden(h);
  322. if (h)
  323. {
  324. enableEmitters(false);
  325. }
  326. }
  327. //-------------------------------------------------------------------------------------------------
  328. void W3DTruckDraw::onRenderObjRecreated(void)
  329. {
  330. //DEBUG_LOG(("Old obj %x, newObj %x, new bones %d, old bones %d\n",
  331. // m_prevRenderObj, getRenderObject(), getRenderObject()->Get_Num_Bones(),
  332. // m_prevNumBones));
  333. m_prevRenderObj = NULL;
  334. m_frontLeftTireBone = 0;
  335. m_frontRightTireBone = 0;
  336. m_rearLeftTireBone = 0;
  337. m_rearRightTireBone = 0;
  338. m_midFrontLeftTireBone = 0;
  339. m_midFrontRightTireBone = 0;
  340. m_midRearLeftTireBone = 0;
  341. m_midRearRightTireBone = 0;
  342. m_midMidLeftTireBone = 0;
  343. m_midMidRightTireBone = 0;
  344. updateBones();
  345. }
  346. //-------------------------------------------------------------------------------------------------
  347. /** Rotate and position wheels and other truck parts. */
  348. //-------------------------------------------------------------------------------------------------
  349. void W3DTruckDraw::doDrawModule(const Matrix3D* transformMtx)
  350. {
  351. W3DModelDraw::doDrawModule(transformMtx);
  352. if (!TheGlobalData->m_showClientPhysics)
  353. return;
  354. const W3DTruckDrawModuleData *moduleData = getW3DTruckDrawModuleData();
  355. if (moduleData==NULL) return; // shouldn't ever happen.
  356. Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
  357. frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
  358. if (frozen)
  359. return;
  360. const Real ACCEL_THRESHOLD = 0.01f;
  361. const Real SIZE_CAP = 2.0f;
  362. // get object from logic
  363. Object *obj = getDrawable()->getObject();
  364. if (obj == NULL)
  365. return;
  366. if (getRenderObject()==NULL) return;
  367. if (getRenderObject() != m_prevRenderObj) {
  368. DEBUG_LOG(("W3DTruckDraw::doDrawModule - shouldn't update bones. jba\n"));
  369. updateBones();
  370. }
  371. // get object physics state
  372. PhysicsBehavior *physics = obj->getPhysics();
  373. if (physics == NULL)
  374. return;
  375. const Coord3D *vel = physics->getVelocity();
  376. Real speed = physics->getVelocityMagnitude();
  377. const TWheelInfo *wheelInfo = getDrawable()->getWheelInfo(); // note, can return null!
  378. AIUpdateInterface *ai = obj->getAI();
  379. if (m_cabBone && wheelInfo) {
  380. Matrix3D cabXfrm(1);
  381. cabXfrm.Make_Identity();
  382. Real desiredAngle = wheelInfo->m_wheelAngle*moduleData->m_cabRotationFactor;
  383. // Check goal angle.
  384. if (ai && ai->getPath())
  385. {
  386. Coord3D pointOnPath;
  387. ai->getPath()->peekCachedPointOnPath(pointOnPath);
  388. Real angleToGoal = ThePartitionManager->getRelativeAngle2D( obj, &pointOnPath );
  389. //DEBUG_LOG(("To goal %f, desired %f ", 180*angleToGoal/PI, 180*desiredAngle/PI));
  390. if (angleToGoal<0) {
  391. if (desiredAngle<angleToGoal) desiredAngle=angleToGoal;
  392. if (desiredAngle>0) desiredAngle = 0;
  393. } else {
  394. if (desiredAngle>angleToGoal) desiredAngle = angleToGoal;
  395. if (desiredAngle<0) desiredAngle = 0;
  396. }
  397. //DEBUG_LOG(("final desired %f ", 180*desiredAngle/PI));
  398. }
  399. Real deltaAngle = desiredAngle - m_curCabRotation;
  400. deltaAngle *= moduleData->m_rotationDampingFactor;
  401. m_curCabRotation += deltaAngle;
  402. cabXfrm.Rotate_Z(m_curCabRotation);
  403. getRenderObject()->Capture_Bone( m_cabBone );
  404. getRenderObject()->Control_Bone( m_cabBone, cabXfrm );
  405. if (m_trailerBone && wheelInfo) {
  406. desiredAngle = -wheelInfo->m_wheelAngle*moduleData->m_trailerRotationFactor;
  407. Real deltaAngle = desiredAngle - m_curTrailerRotation;
  408. deltaAngle *= moduleData->m_rotationDampingFactor;
  409. m_curTrailerRotation += deltaAngle;
  410. cabXfrm.Make_Identity();
  411. cabXfrm.Rotate_Z(m_curTrailerRotation);
  412. getRenderObject()->Capture_Bone( m_trailerBone );
  413. getRenderObject()->Control_Bone( m_trailerBone, cabXfrm );
  414. }
  415. }
  416. if (m_frontLeftTireBone || m_rearLeftTireBone)
  417. {
  418. Real powerslideRotationAddition = moduleData->m_powerslideRotationAddition;
  419. if (ai) {
  420. Locomotor *loco = ai->getCurLocomotor();
  421. if (loco) {
  422. if (loco->isMovingBackwards()) {
  423. speed = -speed; // rotate wheels backwards. jba.
  424. powerslideRotationAddition = -powerslideRotationAddition;
  425. }
  426. }
  427. }
  428. const Real rotationFactor = moduleData->m_rotationSpeedMultiplier;
  429. m_frontWheelRotation += rotationFactor*speed;
  430. if (m_isPowersliding)
  431. {
  432. m_rearWheelRotation += rotationFactor*(speed + powerslideRotationAddition);
  433. }
  434. else
  435. {
  436. m_rearWheelRotation += rotationFactor*speed;
  437. }
  438. // For now, just use the same values for mid wheels -- may want to do independent calcs later...
  439. m_midFrontWheelRotation = m_frontWheelRotation;
  440. m_midRearWheelRotation = m_rearWheelRotation;
  441. Matrix3D wheelXfrm(1);
  442. if (m_frontLeftTireBone && wheelInfo)
  443. {
  444. wheelXfrm.Make_Identity();
  445. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
  446. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  447. wheelXfrm.Rotate_Y(m_frontWheelRotation);
  448. getRenderObject()->Capture_Bone( m_frontLeftTireBone );
  449. getRenderObject()->Control_Bone( m_frontLeftTireBone, wheelXfrm );
  450. wheelXfrm.Make_Identity();
  451. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
  452. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  453. wheelXfrm.Rotate_Y(m_frontWheelRotation);
  454. getRenderObject()->Capture_Bone( m_frontRightTireBone );
  455. getRenderObject()->Control_Bone( m_frontRightTireBone, wheelXfrm );
  456. }
  457. if (m_rearLeftTireBone && wheelInfo)
  458. {
  459. wheelXfrm.Make_Identity();
  460. wheelXfrm.Rotate_Y(m_rearWheelRotation);
  461. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  462. getRenderObject()->Capture_Bone( m_rearLeftTireBone );
  463. getRenderObject()->Control_Bone( m_rearLeftTireBone, wheelXfrm );
  464. wheelXfrm.Make_Identity();
  465. wheelXfrm.Rotate_Y(m_rearWheelRotation);
  466. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  467. //@todo TROUBLE HERE, THE BONE INDICES DO NOT MATCH THE RENDEROBJECTS BONES, SOMETIMES
  468. getRenderObject()->Capture_Bone( m_rearRightTireBone );
  469. getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm );
  470. }
  471. if (m_midFrontLeftTireBone && wheelInfo)
  472. {
  473. wheelXfrm.Make_Identity();
  474. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
  475. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  476. wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
  477. getRenderObject()->Capture_Bone( m_midFrontLeftTireBone );
  478. getRenderObject()->Control_Bone( m_midFrontLeftTireBone, wheelXfrm );
  479. wheelXfrm.Make_Identity();
  480. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
  481. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  482. wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
  483. getRenderObject()->Capture_Bone( m_midFrontRightTireBone );
  484. getRenderObject()->Control_Bone( m_midFrontRightTireBone, wheelXfrm );
  485. }
  486. if (m_midRearLeftTireBone && wheelInfo)
  487. {
  488. wheelXfrm.Make_Identity();
  489. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  490. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  491. getRenderObject()->Capture_Bone( m_midRearLeftTireBone );
  492. getRenderObject()->Control_Bone( m_midRearLeftTireBone, wheelXfrm );
  493. wheelXfrm.Make_Identity();
  494. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  495. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  496. getRenderObject()->Capture_Bone( m_midRearRightTireBone );
  497. getRenderObject()->Control_Bone( m_midRearRightTireBone, wheelXfrm );
  498. }
  499. if (m_midMidLeftTireBone && wheelInfo)
  500. {
  501. wheelXfrm.Make_Identity();
  502. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  503. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  504. getRenderObject()->Capture_Bone( m_midMidLeftTireBone );
  505. getRenderObject()->Control_Bone( m_midMidLeftTireBone, wheelXfrm );
  506. wheelXfrm.Make_Identity();
  507. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  508. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  509. getRenderObject()->Capture_Bone( m_midMidRightTireBone );
  510. getRenderObject()->Control_Bone( m_midMidRightTireBone, wheelXfrm );
  511. }
  512. }
  513. Bool wasPowersliding = m_isPowersliding;
  514. m_isPowersliding = false;
  515. if (physics->isMotive() && !obj->isSignificantlyAboveTerrain()) {
  516. enableEmitters(true);
  517. Coord3D accel = *physics->getAcceleration();
  518. accel.z = 0; // ignore gravitational force.
  519. Bool accelerating = accel.length()>ACCEL_THRESHOLD;
  520. //DEBUG_LOG(("Accel %f, speed %f\n", accel.length(), speed));
  521. if (accelerating) {
  522. Real dot = accel.x*vel->x + accel.y*vel->y;
  523. if (dot<0) {
  524. accelerating = false; // decelerating, actually.
  525. }
  526. }
  527. if (m_dustEffect) {
  528. // Need more dust the faster we go.
  529. if (speed>SIZE_CAP) {
  530. speed = SIZE_CAP;
  531. }
  532. m_dustEffect->setSizeMultiplier(speed);
  533. }
  534. if (m_dirtEffect) {
  535. if (wheelInfo && wheelInfo->m_framesAirborne>3) {
  536. Real factor = 1 + wheelInfo->m_framesAirborne/16;
  537. if (factor>2.0) factor = 2.0;
  538. m_dustEffect->setSizeMultiplier(factor*SIZE_CAP);
  539. m_dustEffect->trigger();
  540. m_landingSound.setObjectID(obj->getID());
  541. TheAudio->addAudioEvent(&m_landingSound);
  542. } else {
  543. if (!accelerating || speed>2.0f) {
  544. m_dirtEffect->stop();
  545. }
  546. }
  547. }
  548. if (m_powerslideEffect) {
  549. if (physics->getTurning() == TURN_NONE) {
  550. m_powerslideEffect->stop();
  551. } else {
  552. m_isPowersliding = true;
  553. m_powerslideEffect->start();
  554. }
  555. }
  556. if (m_dirtEffect) {
  557. if (!accelerating || speed>2.0f) {
  558. m_dirtEffect->stop();
  559. }
  560. }
  561. }
  562. else
  563. enableEmitters(false);
  564. m_wasAirborne = obj->isSignificantlyAboveTerrain();
  565. if(!wasPowersliding && m_isPowersliding) {
  566. // start sound
  567. m_powerslideSound.setObjectID(obj->getID());
  568. m_powerslideSound.setPlayingHandle(TheAudio->addAudioEvent(&m_powerslideSound));
  569. } else if (wasPowersliding && !m_isPowersliding) {
  570. TheAudio->removeAudioEvent(m_powerslideSound.getPlayingHandle());
  571. }
  572. }
  573. // ------------------------------------------------------------------------------------------------
  574. /** CRC */
  575. // ------------------------------------------------------------------------------------------------
  576. void W3DTruckDraw::crc( Xfer *xfer )
  577. {
  578. // extend base class
  579. W3DModelDraw::crc( xfer );
  580. } // end crc
  581. // ------------------------------------------------------------------------------------------------
  582. /** Xfer method
  583. * Version Info:
  584. * 1: Initial version */
  585. // ------------------------------------------------------------------------------------------------
  586. void W3DTruckDraw::xfer( Xfer *xfer )
  587. {
  588. // version
  589. XferVersion currentVersion = 1;
  590. XferVersion version = currentVersion;
  591. xfer->xferVersion( &version, currentVersion );
  592. // extend base class
  593. W3DModelDraw::xfer( xfer );
  594. // John A and Mark W say there is no data to save here
  595. } // end xfer
  596. // ------------------------------------------------------------------------------------------------
  597. /** Load post process */
  598. // ------------------------------------------------------------------------------------------------
  599. void W3DTruckDraw::loadPostProcess( void )
  600. {
  601. // extend base class
  602. W3DModelDraw::loadPostProcess();
  603. // toss any existing ones (no need to re-create; we'll do that on demand)
  604. tossEmitters();
  605. } // end loadPostProcess