W3DTruckDraw.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. /*
  2. ** Command & Conquer Generals(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. if( getW3DTruckDrawModuleData() )
  247. {
  248. //Front tires
  249. if( !getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() )
  250. {
  251. m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str());
  252. DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  253. m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str());
  254. DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name()));
  255. if (!m_frontRightTireBone )
  256. {
  257. m_frontLeftTireBone = 0;
  258. }
  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. m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str());
  266. DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name()));
  267. if (!m_rearRightTireBone)
  268. {
  269. m_rearLeftTireBone = 0;
  270. }
  271. }
  272. //midFront tires
  273. if( !getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() )
  274. {
  275. m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str());
  276. DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  277. m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str());
  278. DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name()));
  279. if (!m_midFrontRightTireBone )
  280. {
  281. m_midFrontLeftTireBone = 0;
  282. }
  283. }
  284. //midRear tires
  285. if( !getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() )
  286. {
  287. m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str());
  288. DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  289. m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str());
  290. DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name()));
  291. if (!m_midRearRightTireBone)
  292. {
  293. m_midRearLeftTireBone = 0;
  294. }
  295. }
  296. //midMid tires
  297. if( !getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.isEmpty() )
  298. {
  299. m_midMidLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str());
  300. DEBUG_ASSERTCRASH(m_midMidLeftTireBone, ("Missing mid-mid-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str(), getRenderObject()->Get_Name()));
  301. m_midMidRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str());
  302. DEBUG_ASSERTCRASH(m_midMidRightTireBone, ("Missing mid-mid-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str(), getRenderObject()->Get_Name()));
  303. if (!m_midMidRightTireBone)
  304. {
  305. m_midMidLeftTireBone = 0;
  306. }
  307. }
  308. //Cab
  309. if( !getW3DTruckDrawModuleData()->m_cabBoneName.isEmpty() )
  310. {
  311. m_cabBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_cabBoneName.str());
  312. DEBUG_ASSERTCRASH(m_cabBone, ("Missing cab bone %s in model %s\n", getW3DTruckDrawModuleData()->m_cabBoneName.str(), getRenderObject()->Get_Name()));
  313. m_trailerBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_trailerBoneName.str());
  314. }
  315. }
  316. m_prevRenderObj = getRenderObject();
  317. m_prevNumBones = m_prevRenderObj->Get_Num_Bones();
  318. }
  319. //-------------------------------------------------------------------------------------------------
  320. void W3DTruckDraw::setHidden(Bool h)
  321. {
  322. W3DModelDraw::setHidden(h);
  323. if (h)
  324. {
  325. enableEmitters(false);
  326. }
  327. }
  328. //-------------------------------------------------------------------------------------------------
  329. void W3DTruckDraw::onRenderObjRecreated(void)
  330. {
  331. //DEBUG_LOG(("Old obj %x, newObj %x, new bones %d, old bones %d\n",
  332. // m_prevRenderObj, getRenderObject(), getRenderObject()->Get_Num_Bones(),
  333. // m_prevNumBones));
  334. m_prevRenderObj = NULL;
  335. m_frontLeftTireBone = 0;
  336. m_frontRightTireBone = 0;
  337. m_rearLeftTireBone = 0;
  338. m_rearRightTireBone = 0;
  339. m_midFrontLeftTireBone = 0;
  340. m_midFrontRightTireBone = 0;
  341. m_midRearLeftTireBone = 0;
  342. m_midRearRightTireBone = 0;
  343. m_midMidLeftTireBone = 0;
  344. m_midMidRightTireBone = 0;
  345. updateBones();
  346. }
  347. //-------------------------------------------------------------------------------------------------
  348. /** Rotate and position wheels and other truck parts. */
  349. //-------------------------------------------------------------------------------------------------
  350. void W3DTruckDraw::doDrawModule(const Matrix3D* transformMtx)
  351. {
  352. W3DModelDraw::doDrawModule(transformMtx);
  353. if (!TheGlobalData->m_showClientPhysics)
  354. return;
  355. const W3DTruckDrawModuleData *moduleData = getW3DTruckDrawModuleData();
  356. if (moduleData==NULL) return; // shouldn't ever happen.
  357. Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
  358. frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
  359. if (frozen)
  360. return;
  361. const Real ACCEL_THRESHOLD = 0.01f;
  362. const Real SIZE_CAP = 2.0f;
  363. // get object from logic
  364. Object *obj = getDrawable()->getObject();
  365. if (obj == NULL)
  366. return;
  367. if (getRenderObject()==NULL) return;
  368. if (getRenderObject() != m_prevRenderObj) {
  369. DEBUG_LOG(("W3DTruckDraw::doDrawModule - shouldn't update bones. jba\n"));
  370. updateBones();
  371. }
  372. // get object physics state
  373. PhysicsBehavior *physics = obj->getPhysics();
  374. if (physics == NULL)
  375. return;
  376. const Coord3D *vel = physics->getVelocity();
  377. Real speed = physics->getVelocityMagnitude();
  378. const TWheelInfo *wheelInfo = getDrawable()->getWheelInfo(); // note, can return null!
  379. AIUpdateInterface *ai = obj->getAI();
  380. if (m_cabBone && wheelInfo) {
  381. Matrix3D cabXfrm(1);
  382. cabXfrm.Make_Identity();
  383. Real desiredAngle = wheelInfo->m_wheelAngle*moduleData->m_cabRotationFactor;
  384. // Check goal angle.
  385. if (ai && ai->getPath())
  386. {
  387. Coord3D pointOnPath;
  388. ai->getPath()->peekCachedPointOnPath(pointOnPath);
  389. Real angleToGoal = ThePartitionManager->getRelativeAngle2D( obj, &pointOnPath );
  390. //DEBUG_LOG(("To goal %f, desired %f ", 180*angleToGoal/PI, 180*desiredAngle/PI));
  391. if (angleToGoal<0) {
  392. if (desiredAngle<angleToGoal) desiredAngle=angleToGoal;
  393. if (desiredAngle>0) desiredAngle = 0;
  394. } else {
  395. if (desiredAngle>angleToGoal) desiredAngle = angleToGoal;
  396. if (desiredAngle<0) desiredAngle = 0;
  397. }
  398. //DEBUG_LOG(("final desired %f ", 180*desiredAngle/PI));
  399. }
  400. Real deltaAngle = desiredAngle - m_curCabRotation;
  401. deltaAngle *= moduleData->m_rotationDampingFactor;
  402. m_curCabRotation += deltaAngle;
  403. cabXfrm.Rotate_Z(m_curCabRotation);
  404. getRenderObject()->Capture_Bone( m_cabBone );
  405. getRenderObject()->Control_Bone( m_cabBone, cabXfrm );
  406. if (m_trailerBone && wheelInfo) {
  407. desiredAngle = -wheelInfo->m_wheelAngle*moduleData->m_trailerRotationFactor;
  408. Real deltaAngle = desiredAngle - m_curTrailerRotation;
  409. deltaAngle *= moduleData->m_rotationDampingFactor;
  410. m_curTrailerRotation += deltaAngle;
  411. cabXfrm.Make_Identity();
  412. cabXfrm.Rotate_Z(m_curTrailerRotation);
  413. getRenderObject()->Capture_Bone( m_trailerBone );
  414. getRenderObject()->Control_Bone( m_trailerBone, cabXfrm );
  415. }
  416. }
  417. if (m_frontLeftTireBone || m_rearLeftTireBone)
  418. {
  419. Real powerslideRotationAddition = moduleData->m_powerslideRotationAddition;
  420. if (ai) {
  421. Locomotor *loco = ai->getCurLocomotor();
  422. if (loco) {
  423. if (loco->isMovingBackwards()) {
  424. speed = -speed; // rotate wheels backwards. jba.
  425. powerslideRotationAddition = -powerslideRotationAddition;
  426. }
  427. }
  428. }
  429. const Real rotationFactor = moduleData->m_rotationSpeedMultiplier;
  430. m_frontWheelRotation += rotationFactor*speed;
  431. if (m_isPowersliding)
  432. {
  433. m_rearWheelRotation += rotationFactor*(speed + powerslideRotationAddition);
  434. }
  435. else
  436. {
  437. m_rearWheelRotation += rotationFactor*speed;
  438. }
  439. // For now, just use the same values for mid wheels -- may want to do independent calcs later...
  440. m_midFrontWheelRotation = m_frontWheelRotation;
  441. m_midRearWheelRotation = m_rearWheelRotation;
  442. Matrix3D wheelXfrm(1);
  443. if (m_frontLeftTireBone && wheelInfo)
  444. {
  445. wheelXfrm.Make_Identity();
  446. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
  447. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  448. wheelXfrm.Rotate_Y(m_frontWheelRotation);
  449. getRenderObject()->Capture_Bone( m_frontLeftTireBone );
  450. getRenderObject()->Control_Bone( m_frontLeftTireBone, wheelXfrm );
  451. wheelXfrm.Make_Identity();
  452. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
  453. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  454. wheelXfrm.Rotate_Y(m_frontWheelRotation);
  455. getRenderObject()->Capture_Bone( m_frontRightTireBone );
  456. getRenderObject()->Control_Bone( m_frontRightTireBone, wheelXfrm );
  457. }
  458. if (m_rearLeftTireBone && wheelInfo)
  459. {
  460. wheelXfrm.Make_Identity();
  461. wheelXfrm.Rotate_Y(m_rearWheelRotation);
  462. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  463. getRenderObject()->Capture_Bone( m_rearLeftTireBone );
  464. getRenderObject()->Control_Bone( m_rearLeftTireBone, wheelXfrm );
  465. wheelXfrm.Make_Identity();
  466. wheelXfrm.Rotate_Y(m_rearWheelRotation);
  467. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  468. //@todo TROUBLE HERE, THE BONE INDICES DO NOT MATCH THE RENDEROBJECTS BONES, SOMETIMES
  469. getRenderObject()->Capture_Bone( m_rearRightTireBone );
  470. getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm );
  471. }
  472. if (m_midFrontLeftTireBone && wheelInfo)
  473. {
  474. wheelXfrm.Make_Identity();
  475. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
  476. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  477. wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
  478. getRenderObject()->Capture_Bone( m_midFrontLeftTireBone );
  479. getRenderObject()->Control_Bone( m_midFrontLeftTireBone, wheelXfrm );
  480. wheelXfrm.Make_Identity();
  481. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
  482. wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
  483. wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
  484. getRenderObject()->Capture_Bone( m_midFrontRightTireBone );
  485. getRenderObject()->Control_Bone( m_midFrontRightTireBone, wheelXfrm );
  486. }
  487. if (m_midRearLeftTireBone && wheelInfo)
  488. {
  489. wheelXfrm.Make_Identity();
  490. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  491. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  492. getRenderObject()->Capture_Bone( m_midRearLeftTireBone );
  493. getRenderObject()->Control_Bone( m_midRearLeftTireBone, wheelXfrm );
  494. wheelXfrm.Make_Identity();
  495. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  496. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  497. getRenderObject()->Capture_Bone( m_midRearRightTireBone );
  498. getRenderObject()->Control_Bone( m_midRearRightTireBone, wheelXfrm );
  499. }
  500. if (m_midMidLeftTireBone && wheelInfo)
  501. {
  502. wheelXfrm.Make_Identity();
  503. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  504. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
  505. getRenderObject()->Capture_Bone( m_midMidLeftTireBone );
  506. getRenderObject()->Control_Bone( m_midMidLeftTireBone, wheelXfrm );
  507. wheelXfrm.Make_Identity();
  508. wheelXfrm.Rotate_Y(m_midRearWheelRotation);
  509. wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
  510. getRenderObject()->Capture_Bone( m_midMidRightTireBone );
  511. getRenderObject()->Control_Bone( m_midMidRightTireBone, wheelXfrm );
  512. }
  513. }
  514. Bool wasPowersliding = m_isPowersliding;
  515. m_isPowersliding = false;
  516. if (physics->isMotive() && !obj->isSignificantlyAboveTerrain()) {
  517. enableEmitters(true);
  518. Coord3D accel = *physics->getAcceleration();
  519. accel.z = 0; // ignore gravitational force.
  520. Bool accelerating = accel.length()>ACCEL_THRESHOLD;
  521. //DEBUG_LOG(("Accel %f, speed %f\n", accel.length(), speed));
  522. if (accelerating) {
  523. Real dot = accel.x*vel->x + accel.y*vel->y;
  524. if (dot<0) {
  525. accelerating = false; // decelerating, actually.
  526. }
  527. }
  528. if (m_dustEffect) {
  529. // Need more dust the faster we go.
  530. if (speed>SIZE_CAP) {
  531. speed = SIZE_CAP;
  532. }
  533. m_dustEffect->setSizeMultiplier(speed);
  534. }
  535. if (m_dirtEffect) {
  536. if (wheelInfo && wheelInfo->m_framesAirborne>3) {
  537. Real factor = 1 + wheelInfo->m_framesAirborne/16;
  538. if (factor>2.0) factor = 2.0;
  539. m_dustEffect->setSizeMultiplier(factor*SIZE_CAP);
  540. m_dustEffect->trigger();
  541. m_landingSound.setObjectID(obj->getID());
  542. TheAudio->addAudioEvent(&m_landingSound);
  543. } else {
  544. if (!accelerating || speed>2.0f) {
  545. m_dirtEffect->stop();
  546. }
  547. }
  548. }
  549. if (m_powerslideEffect) {
  550. if (physics->getTurning() == TURN_NONE) {
  551. m_powerslideEffect->stop();
  552. } else {
  553. m_isPowersliding = true;
  554. m_powerslideEffect->start();
  555. }
  556. }
  557. if (m_dirtEffect) {
  558. if (!accelerating || speed>2.0f) {
  559. m_dirtEffect->stop();
  560. }
  561. }
  562. }
  563. else
  564. enableEmitters(false);
  565. m_wasAirborne = obj->isSignificantlyAboveTerrain();
  566. if(!wasPowersliding && m_isPowersliding) {
  567. // start sound
  568. m_powerslideSound.setObjectID(obj->getID());
  569. m_powerslideSound.setPlayingHandle(TheAudio->addAudioEvent(&m_powerslideSound));
  570. } else if (wasPowersliding && !m_isPowersliding) {
  571. TheAudio->removeAudioEvent(m_powerslideSound.getPlayingHandle());
  572. }
  573. }
  574. // ------------------------------------------------------------------------------------------------
  575. /** CRC */
  576. // ------------------------------------------------------------------------------------------------
  577. void W3DTruckDraw::crc( Xfer *xfer )
  578. {
  579. // extend base class
  580. W3DModelDraw::crc( xfer );
  581. } // end crc
  582. // ------------------------------------------------------------------------------------------------
  583. /** Xfer method
  584. * Version Info:
  585. * 1: Initial version */
  586. // ------------------------------------------------------------------------------------------------
  587. void W3DTruckDraw::xfer( Xfer *xfer )
  588. {
  589. // version
  590. XferVersion currentVersion = 1;
  591. XferVersion version = currentVersion;
  592. xfer->xferVersion( &version, currentVersion );
  593. // extend base class
  594. W3DModelDraw::xfer( xfer );
  595. // John A and Mark W say there is no data to save here
  596. } // end xfer
  597. // ------------------------------------------------------------------------------------------------
  598. /** Load post process */
  599. // ------------------------------------------------------------------------------------------------
  600. void W3DTruckDraw::loadPostProcess( void )
  601. {
  602. // extend base class
  603. W3DModelDraw::loadPostProcess();
  604. // toss any existing ones (no need to re-create; we'll do that on demand)
  605. tossEmitters();
  606. } // end loadPostProcess