Thing.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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: Thing.cpp ////////////////////////////////////////////////////////////
  24. // Created: Colin Day, May 2001
  25. //
  26. // Desc: Things are the base class for objects and drawables, objects
  27. // are logic side representations while drawables are client
  28. // side. Common data will be held in the Thing defined here
  29. // and systems that need to work with both of them will work with
  30. // "Things"
  31. //
  32. //-----------------------------------------------------------------------------
  33. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  34. #include "Common/PerfTimer.h"
  35. #include "Common/Thing.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/ThingFactory.h"
  38. #include "Common/GlobalData.h"
  39. #include "Common/NameKeyGenerator.h"
  40. #include "Common/Player.h"
  41. #include "Common/PlayerList.h"
  42. #include "Common/Team.h"
  43. #include "Lib/Trig.h"
  44. #include "GameLogic/TerrainLogic.h"
  45. #ifdef _INTERNAL
  46. // for occasional debugging...
  47. //#pragma optimize("", off)
  48. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  49. #endif
  50. //=============================================================================
  51. /** Constructor */
  52. //=============================================================================
  53. Thing::Thing( const ThingTemplate *thingTemplate )
  54. {
  55. // sanity
  56. if( thingTemplate == NULL )
  57. {
  58. // cannot create thing without template
  59. DEBUG_CRASH(( "no template" ));
  60. return;
  61. } // end if
  62. m_template = thingTemplate;
  63. #if defined(_DEBUG) || defined(_INTERNAL)
  64. m_templateName = thingTemplate->getName();
  65. #endif
  66. m_transform.Make_Identity();
  67. m_cachedPos.zero();
  68. m_cachedAngle = 0.0f;
  69. m_cachedDirVector.zero();
  70. m_cachedAltitudeAboveTerrain = 0;
  71. m_cachedAltitudeAboveTerrainOrWater = 0;
  72. m_cacheFlags = 0;
  73. }
  74. //=============================================================================
  75. /** Destructor */
  76. //=============================================================================
  77. Thing::~Thing()
  78. {
  79. }
  80. //DECLARE_PERF_TIMER(ThingMatrixStuff)
  81. //=============================================================================
  82. const ThingTemplate *Thing::getTemplate() const
  83. {
  84. return m_template;
  85. }
  86. //=============================================================================
  87. const Coord3D* Thing::getUnitDirectionVector2D() const
  88. {
  89. //USE_PERF_TIMER(ThingMatrixStuff)
  90. if (!(m_cacheFlags & VALID_DIRVECTOR))
  91. {
  92. Real angle = getOrientation();
  93. m_cachedDirVector.x = Cos( angle );
  94. m_cachedDirVector.y = Sin( angle );
  95. m_cachedDirVector.z = 0;
  96. m_cacheFlags |= VALID_DIRVECTOR;
  97. }
  98. return &m_cachedDirVector;
  99. }
  100. //=============================================================================
  101. void Thing::getUnitDirectionVector2D(Coord3D& dir) const
  102. {
  103. dir = *getUnitDirectionVector2D();
  104. }
  105. //=============================================================================
  106. void Thing::getUnitDirectionVector3D(Coord3D& dir) const
  107. {
  108. Vector3 vdir = m_transform.Get_X_Vector();
  109. vdir.Normalize();
  110. dir.x = vdir.X;
  111. dir.y = vdir.Y;
  112. dir.z = vdir.Z;
  113. }
  114. //=============================================================================
  115. // the nice thing about this is that we don't have to recalc out cached terrain stuff.
  116. void Thing::setPositionZ( Real z )
  117. {
  118. //USE_PERF_TIMER(ThingMatrixStuff)
  119. if( !m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
  120. {
  121. Real oldAngle = m_cachedAngle;
  122. Coord3D oldPos = m_cachedPos;
  123. Matrix3D oldMtx = m_transform;
  124. m_transform.Set_Z_Translation( z );
  125. m_cachedPos.z = z;
  126. if (m_cacheFlags & VALID_ALTITUDE_TERRAIN)
  127. {
  128. m_cachedAltitudeAboveTerrain += (z - oldPos.z);
  129. }
  130. if (m_cacheFlags & VALID_ALTITUDE_SEALEVEL)
  131. {
  132. m_cachedAltitudeAboveTerrainOrWater += (z - oldPos.z);
  133. }
  134. reactToTransformChange(&oldMtx, &oldPos, oldAngle);
  135. }
  136. else
  137. {
  138. Matrix3D mtx;
  139. const Bool stickToGround = true; // yes, set the "z" pos
  140. Coord3D pos = m_cachedPos;
  141. pos.z = z;
  142. TheTerrainLogic->alignOnTerrain(getOrientation(), pos, stickToGround, mtx );
  143. setTransformMatrix(&mtx);
  144. }
  145. DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
  146. }
  147. //=============================================================================
  148. void Thing::setPosition( const Coord3D *pos )
  149. {
  150. //USE_PERF_TIMER(ThingMatrixStuff)
  151. if( !m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
  152. {
  153. Real oldAngle = m_cachedAngle;
  154. Coord3D oldPos = m_cachedPos;
  155. Matrix3D oldMtx = m_transform;
  156. //DEBUG_ASSERTCRASH(!(_isnan(pos->x) || _isnan(pos->y) || _isnan(pos->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
  157. m_transform.Set_X_Translation( pos->x );
  158. m_transform.Set_Y_Translation( pos->y );
  159. m_transform.Set_Z_Translation( pos->z );
  160. m_cachedPos = *pos;
  161. m_cacheFlags &= ~(VALID_ALTITUDE_TERRAIN | VALID_ALTITUDE_SEALEVEL); // but don't clear the dir flags.
  162. reactToTransformChange(&oldMtx, &oldPos, oldAngle);
  163. }
  164. else
  165. {
  166. Matrix3D mtx;
  167. const Bool stickToGround = true; // yes, set the "z" pos
  168. TheTerrainLogic->alignOnTerrain(getOrientation(), *pos, stickToGround, mtx );
  169. setTransformMatrix(&mtx);
  170. }
  171. DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
  172. }
  173. //=============================================================================
  174. void Thing::setOrientation( Real angle )
  175. {
  176. //USE_PERF_TIMER(ThingMatrixStuff)
  177. Coord3D u, x, y, z, pos;
  178. // setOrientation always forces us straight up in the Z axis,
  179. // or aligned with the terrain if we have the magic flag set.
  180. // don't want this? call setTransformMatrix instead.
  181. Real oldAngle = m_cachedAngle;
  182. Coord3D oldPos = m_cachedPos;
  183. Matrix3D oldMtx = m_transform;
  184. pos.x = m_transform.Get_X_Translation();
  185. pos.y = m_transform.Get_Y_Translation();
  186. pos.z = m_transform.Get_Z_Translation();
  187. if( m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
  188. {
  189. Matrix3D mtx;
  190. const Bool stickToGround = true; // yes, set the "z" pos
  191. TheTerrainLogic->alignOnTerrain(angle, pos, stickToGround, m_transform );
  192. }
  193. else
  194. {
  195. z.x = 0.0f;
  196. z.y = 0.0f;
  197. z.z = 1.0f;
  198. u.x = Cos(angle);
  199. u.y = Sin(angle);
  200. u.z = 0.0f;
  201. y.crossProduct( &z, &u, &y );
  202. x.crossProduct( &y, &z, &x );
  203. m_transform.Set( x.x, y.x, z.x, pos.x,
  204. x.y, y.y, z.y, pos.y,
  205. x.z, y.z, z.z, pos.z );
  206. }
  207. //DEBUG_ASSERTCRASH(-PI <= angle && angle <= PI, ("Please pass only normalized (-PI..PI) angles to setOrientation (%f).\n", angle));
  208. m_cachedAngle = normalizeAngle(angle);
  209. m_cachedPos = pos;
  210. m_cacheFlags &= ~VALID_DIRVECTOR; // but don't clear the altitude flags.
  211. reactToTransformChange(&oldMtx, &oldPos, oldAngle);
  212. DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
  213. }
  214. //=============================================================================
  215. /** Set the world transformation matrix */
  216. //=============================================================================
  217. void Thing::setTransformMatrix( const Matrix3D *mx )
  218. {
  219. //USE_PERF_TIMER(ThingMatrixStuff)
  220. Real oldAngle = m_cachedAngle;
  221. Coord3D oldPos = m_cachedPos;
  222. Matrix3D oldMtx = m_transform;
  223. m_transform = *mx;
  224. m_cachedPos.x = m_transform.Get_X_Translation();
  225. m_cachedPos.y = m_transform.Get_Y_Translation();
  226. m_cachedPos.z = m_transform.Get_Z_Translation();
  227. m_cachedAngle = m_transform.Get_Z_Rotation();
  228. m_cacheFlags = 0;
  229. reactToTransformChange(&oldMtx, &oldPos, oldAngle);
  230. DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
  231. }
  232. //-------------------------------------------------------------------------------------------------
  233. Bool Thing::isKindOf(KindOfType t) const
  234. {
  235. return getTemplate()->isKindOf(t);
  236. }
  237. //-------------------------------------------------------------------------------------------------
  238. Bool Thing::isKindOfMulti(const KindOfMaskType& mustBeSet, const KindOfMaskType& mustBeClear) const
  239. {
  240. return getTemplate()->isKindOfMulti(mustBeSet, mustBeClear);
  241. }
  242. // ------------------------------------------------------------------------------------------------
  243. Bool Thing::isAnyKindOf( const KindOfMaskType& anyKindOf ) const
  244. {
  245. return getTemplate()->isAnyKindOf( anyKindOf );
  246. }
  247. // ------------------------------------------------------------------------------------------------
  248. Real Thing::calculateHeightAboveTerrain() const
  249. {
  250. //USE_PERF_TIMER(ThingMatrixStuff)
  251. const Coord3D* pos = getPosition();
  252. Real terrainZ = TheTerrainLogic->getGroundHeight( pos->x, pos->y );
  253. Real myZ = pos->z;
  254. return myZ - terrainZ;
  255. }
  256. //-------------------------------------------------------------------------------------------------
  257. Real Thing::getHeightAboveTerrain() const
  258. {
  259. if (!(m_cacheFlags & VALID_ALTITUDE_TERRAIN))
  260. {
  261. m_cachedAltitudeAboveTerrain = calculateHeightAboveTerrain();
  262. m_cacheFlags |= VALID_ALTITUDE_TERRAIN;
  263. }
  264. return m_cachedAltitudeAboveTerrain;
  265. }
  266. //-------------------------------------------------------------------------------------------------
  267. Real Thing::getHeightAboveTerrainOrWater() const
  268. {
  269. //USE_PERF_TIMER(ThingMatrixStuff)
  270. if (!(m_cacheFlags & VALID_ALTITUDE_SEALEVEL))
  271. {
  272. const Coord3D* pos = getPosition();
  273. Real waterZ;
  274. if (TheTerrainLogic->isUnderwater(pos->x, pos->y, &waterZ))
  275. {
  276. m_cachedAltitudeAboveTerrainOrWater = pos->z - waterZ;
  277. }
  278. else
  279. {
  280. m_cachedAltitudeAboveTerrainOrWater = getHeightAboveTerrain();
  281. }
  282. m_cacheFlags |= VALID_ALTITUDE_SEALEVEL;
  283. }
  284. return m_cachedAltitudeAboveTerrainOrWater;
  285. }
  286. //=============================================================================
  287. /** If we treat this as airborne, then they slide down slopes. This checks whether
  288. they are high enough that we should let them act like they're flying. jba. */
  289. //=============================================================================
  290. Bool Thing::isSignificantlyAboveTerrain() const
  291. {
  292. // If it's high enough that it will take more than 3 frames to return to the ground,
  293. // then it's significantly airborne. jba
  294. return (getHeightAboveTerrain() > -(3*3)*TheGlobalData->m_gravity);
  295. }
  296. //-------------------------------------------------------------------------------------------------
  297. void Thing::convertBonePosToWorldPos(const Coord3D* bonePos, const Matrix3D* boneTransform, Coord3D* worldPos, Matrix3D* worldTransform) const
  298. {
  299. if (worldTransform)
  300. {
  301. #ifdef ALLOW_TEMPORARIES
  302. *worldTransform = m_transform * (*boneTransform);
  303. #else
  304. worldTransform->mul(m_transform, *boneTransform);
  305. #endif
  306. }
  307. if (worldPos)
  308. {
  309. Vector3 vector;
  310. vector.X = bonePos->x;
  311. vector.Y = bonePos->y;
  312. vector.Z = bonePos->z;
  313. m_transform.Transform_Vector(m_transform, vector, &vector);
  314. worldPos->x = vector.X;
  315. worldPos->y = vector.Y;
  316. worldPos->z = vector.Z;
  317. }
  318. }
  319. // ------------------------------------------------------------------------------------------------
  320. /** Push the 'in' parameter through our transformation matrix and store in 'out' */
  321. // ------------------------------------------------------------------------------------------------
  322. void Thing::transformPoint( const Coord3D *in, Coord3D *out )
  323. {
  324. // santiy
  325. if( in == NULL || out == NULL )
  326. return;
  327. // for conversion
  328. Vector3 vectorIn;
  329. Vector3 vectorOut;
  330. ///@ todo this is dumb and we should not have to convert types
  331. // convert to Vector3 datatypes
  332. vectorIn.X = in->x;
  333. vectorIn.Y = in->y;
  334. vectorIn.Z = in->z;
  335. // do the transform
  336. m_transform.Transform_Vector( m_transform, vectorIn, &vectorOut );
  337. // store converted vector in 'out'
  338. out->x = vectorOut.X;
  339. out->y = vectorOut.Y;
  340. out->z = vectorOut.Z;
  341. } // end transformPoint