Geometry.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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: Geometry.cpp /////////////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, Aug 2002
  25. // Desc:
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_GEOMETRY_NAMES
  29. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  30. #include "Common/Geometry.h"
  31. #include "Common/INI.h"
  32. #include "Common/RandomValue.h"
  33. #include "Common/Xfer.h"
  34. #ifdef _INTERNAL
  35. // for occasional debugging...
  36. //#pragma optimize("", off)
  37. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  38. #endif
  39. ///////////////////////////////////////////////////////////////////////////////////////////////////
  40. ///////////////////////////////////////////////////////////////////////////////////////////////////
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. //=============================================================================
  43. /*static*/ void GeometryInfo::parseGeometryType( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  44. {
  45. ((GeometryInfo*)store)->m_type = (GeometryType)INI::scanIndexList(ini->getNextToken(), GeometryNames);
  46. ((GeometryInfo*)store)->calcBoundingStuff();
  47. }
  48. //=============================================================================
  49. /*static*/ void GeometryInfo::parseGeometryIsSmall( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  50. {
  51. ((GeometryInfo*)store)->m_isSmall = INI::scanBool(ini->getNextToken());
  52. ((GeometryInfo*)store)->calcBoundingStuff();
  53. }
  54. //=============================================================================
  55. /*static*/ void GeometryInfo::parseGeometryHeight( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  56. {
  57. ((GeometryInfo*)store)->m_height = INI::scanReal(ini->getNextToken());
  58. ((GeometryInfo*)store)->calcBoundingStuff();
  59. }
  60. //=============================================================================
  61. /*static*/ void GeometryInfo::parseGeometryMajorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  62. {
  63. ((GeometryInfo*)store)->m_majorRadius = INI::scanReal(ini->getNextToken());
  64. ((GeometryInfo*)store)->calcBoundingStuff();
  65. }
  66. //=============================================================================
  67. /*static*/ void GeometryInfo::parseGeometryMinorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  68. {
  69. ((GeometryInfo*)store)->m_minorRadius = INI::scanReal(ini->getNextToken());
  70. ((GeometryInfo*)store)->calcBoundingStuff();
  71. }
  72. //-----------------------------------------------------------------------------
  73. void GeometryInfo::set(GeometryType type, Bool isSmall, Real height, Real majorRadius, Real minorRadius)
  74. {
  75. m_type = type;
  76. m_isSmall = isSmall;
  77. switch(m_type)
  78. {
  79. case GEOMETRY_SPHERE:
  80. m_majorRadius = majorRadius;
  81. m_minorRadius = majorRadius;
  82. m_height = majorRadius;
  83. break;
  84. case GEOMETRY_CYLINDER:
  85. m_majorRadius = majorRadius;
  86. m_minorRadius = majorRadius;
  87. m_height = height;
  88. break;
  89. case GEOMETRY_BOX:
  90. m_majorRadius = majorRadius;
  91. m_minorRadius = minorRadius;
  92. m_height = height;
  93. break;
  94. };
  95. calcBoundingStuff();
  96. }
  97. //-----------------------------------------------------------------------------
  98. static Real calcDotProduct(const Coord3D& a, const Coord3D& b)
  99. {
  100. return a.x*b.x + a.y*b.y + a.z*b.z;
  101. }
  102. //-----------------------------------------------------------------------------
  103. static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
  104. {
  105. return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
  106. }
  107. //-----------------------------------------------------------------------------
  108. static Real calcPointToLineDistSquared(const Coord3D& pt, const Coord3D& lineStart, const Coord3D& lineEnd)
  109. {
  110. Coord3D line, lineToPt, closest;
  111. line.x = lineEnd.x - lineStart.x;
  112. line.y = lineEnd.y - lineStart.y;
  113. line.z = lineEnd.z - lineStart.z;
  114. lineToPt.x = pt.x - lineStart.x;
  115. lineToPt.y = pt.y - lineStart.y;
  116. lineToPt.z = pt.z - lineStart.z;
  117. Real dot = calcDotProduct(lineToPt, line);
  118. if (dot <= 0.0f)
  119. {
  120. // angle is obtuse
  121. return calcDistSquared(pt, lineStart);
  122. }
  123. Real lineLenSqr = calcDistSquared(lineStart, lineEnd);
  124. DEBUG_ASSERTCRASH(lineLenSqr==calcDotProduct(line,line),("hmm"));
  125. if (lineLenSqr <= dot)
  126. {
  127. return calcDistSquared(pt, lineEnd);
  128. }
  129. Real tmp = dot / lineLenSqr;
  130. closest.x = lineStart.x + tmp * line.x;
  131. closest.y = lineStart.y + tmp * line.y;
  132. closest.z = lineStart.z + tmp * line.z;
  133. return calcDistSquared(pt, closest);
  134. }
  135. //=============================================================================
  136. Bool GeometryInfo::isIntersectedByLineSegment(const Coord3D& loc, const Coord3D& from, const Coord3D& to) const
  137. {
  138. DEBUG_CRASH(("this call does not work properly for nonspheres yet. use with caution."));
  139. /// @todo srj -- treats everything as a sphere for now. fix.
  140. Real distSquared = calcPointToLineDistSquared(loc, from, to);
  141. return distSquared <= sqr(getBoundingSphereRadius());
  142. }
  143. //=============================================================================
  144. // given an object with this geom, located at 'pos', and another obj with the given
  145. // pos & geom, calc the min and max pitches from this to that.
  146. void GeometryInfo::calcPitches(const Coord3D& thisPos, const GeometryInfo& that, const Coord3D& thatPos,
  147. Real& minPitch, Real& maxPitch) const
  148. {
  149. Coord3D thisCenter;
  150. getCenterPosition(thisPos, thisCenter);
  151. Real dxy = sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
  152. Real dz;
  153. /** @todo srj -- this could be better, by calcing it for all the corners, not just top-center
  154. and bottom-center... oh well */
  155. dz = (thatPos.z + that.getMaxHeightAbovePosition()) - thisCenter.z;
  156. maxPitch = atan2(dz, dxy);
  157. dz = (thatPos.z - that.getMaxHeightBelowPosition()) - thisCenter.z;
  158. minPitch = atan2(dz, dxy);
  159. }
  160. //=============================================================================
  161. // given an object with this geom, SET how far above the object's canonical position its max z should extend.
  162. void GeometryInfo::setMaxHeightAbovePosition(Real z)
  163. {
  164. switch(m_type)
  165. {
  166. case GEOMETRY_SPHERE:
  167. m_majorRadius = z;
  168. break;
  169. case GEOMETRY_BOX:
  170. case GEOMETRY_CYLINDER:
  171. m_height = z;
  172. break;
  173. };
  174. calcBoundingStuff();
  175. }
  176. //=============================================================================
  177. // given an object with this geom, how far above the object's canonical position does its max z extend?
  178. Real GeometryInfo::getMaxHeightAbovePosition() const
  179. {
  180. switch(m_type)
  181. {
  182. case GEOMETRY_SPHERE:
  183. return m_majorRadius;
  184. case GEOMETRY_BOX:
  185. case GEOMETRY_CYLINDER:
  186. return m_height;
  187. };
  188. return 0.0f;
  189. }
  190. //=============================================================================
  191. // given an object with this geom, how far below the object's canonical position does its max z extend?
  192. Real GeometryInfo::getMaxHeightBelowPosition() const
  193. {
  194. switch(m_type)
  195. {
  196. case GEOMETRY_SPHERE:
  197. return m_majorRadius;
  198. case GEOMETRY_BOX:
  199. case GEOMETRY_CYLINDER:
  200. return 0.0f;
  201. };
  202. return 0.0f;
  203. }
  204. //=============================================================================
  205. // given an object with this geom, located at 'pos', where is the "center" of the geometry?
  206. Real GeometryInfo::getZDeltaToCenterPosition() const
  207. {
  208. return (m_type == GEOMETRY_SPHERE) ? 0.0f : (m_height * 0.5f);
  209. }
  210. //=============================================================================
  211. // given an object with this geom, located at 'pos', where is the "center" of the geometry?
  212. void GeometryInfo::getCenterPosition(const Coord3D& pos, Coord3D& center) const
  213. {
  214. center = pos;
  215. center.z += getZDeltaToCenterPosition();
  216. }
  217. //=============================================================================
  218. void GeometryInfo::expandFootprint(Real radius)
  219. {
  220. m_majorRadius += radius;
  221. m_minorRadius += radius;
  222. calcBoundingStuff();
  223. }
  224. //=============================================================================
  225. void GeometryInfo::get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds) const
  226. {
  227. switch(m_type)
  228. {
  229. case GEOMETRY_SPHERE:
  230. case GEOMETRY_CYLINDER:
  231. {
  232. bounds.lo.x = geomCenter.x - m_majorRadius;
  233. bounds.lo.y = geomCenter.y - m_majorRadius;
  234. bounds.hi.x = geomCenter.x + m_majorRadius;
  235. bounds.hi.y = geomCenter.y + m_majorRadius;
  236. break;
  237. }
  238. case GEOMETRY_BOX:
  239. {
  240. Real c = (Real)cos(angle);
  241. Real s = (Real)sin(angle);
  242. Real exc = m_majorRadius*c;
  243. Real eyc = m_minorRadius*c;
  244. Real exs = m_majorRadius*s;
  245. Real eys = m_minorRadius*s;
  246. Real x,y;
  247. x = geomCenter.x - exc - eys;
  248. y = geomCenter.y + eyc - exs;
  249. bounds.lo.x = x;
  250. bounds.lo.y = y;
  251. bounds.hi.x = x;
  252. bounds.hi.y = y;
  253. x = geomCenter.x + exc - eys;
  254. y = geomCenter.y + eyc + exs;
  255. if (bounds.lo.x > x) bounds.lo.x = x;
  256. if (bounds.lo.y > y) bounds.lo.y = y;
  257. if (bounds.hi.x < x) bounds.hi.x = x;
  258. if (bounds.hi.y < y) bounds.hi.y = y;
  259. x = geomCenter.x + exc + eys;
  260. y = geomCenter.y - eyc + exs;
  261. if (bounds.lo.x > x) bounds.lo.x = x;
  262. if (bounds.lo.y > y) bounds.lo.y = y;
  263. if (bounds.hi.x < x) bounds.hi.x = x;
  264. if (bounds.hi.y < y) bounds.hi.y = y;
  265. x = geomCenter.x - exc + eys;
  266. y = geomCenter.y - eyc - exs;
  267. if (bounds.lo.x > x) bounds.lo.x = x;
  268. if (bounds.lo.y > y) bounds.lo.y = y;
  269. if (bounds.hi.x < x) bounds.hi.x = x;
  270. if (bounds.hi.y < y) bounds.hi.y = y;
  271. break;
  272. }
  273. }
  274. }
  275. //=============================================================================
  276. void GeometryInfo::clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const
  277. {
  278. switch(m_type)
  279. {
  280. case GEOMETRY_SPHERE:
  281. case GEOMETRY_CYLINDER:
  282. {
  283. Real dx = ptToClip.x - geomCenter.x;
  284. Real dy = ptToClip.y - geomCenter.y;
  285. Real radius = sqrt(sqr(dx) + sqr(dy));
  286. if (radius > m_majorRadius)
  287. {
  288. Real ratio = m_majorRadius / radius;
  289. ptToClip.x = geomCenter.x + dx * ratio;
  290. ptToClip.y = geomCenter.y + dy * ratio;
  291. }
  292. break;
  293. }
  294. case GEOMETRY_BOX:
  295. {
  296. ptToClip.x = clamp(geomCenter.x - m_majorRadius, ptToClip.x, geomCenter.x + m_majorRadius);
  297. ptToClip.y = clamp(geomCenter.y - m_minorRadius, ptToClip.y, geomCenter.y + m_minorRadius);
  298. break;
  299. }
  300. };
  301. }
  302. //=============================================================================
  303. inline Bool isWithin(Real a, Real b, Real c) { return a<=b && b<=c; }
  304. //=============================================================================
  305. Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D& pt) const
  306. {
  307. switch(m_type)
  308. {
  309. case GEOMETRY_SPHERE:
  310. case GEOMETRY_CYLINDER:
  311. {
  312. Real dx = pt.x - geomCenter.x;
  313. Real dy = pt.y - geomCenter.y;
  314. Real radius = sqrt(sqr(dx) + sqr(dy));
  315. return (radius <= m_majorRadius);
  316. break;
  317. }
  318. case GEOMETRY_BOX:
  319. {
  320. return isWithin(geomCenter.x - m_majorRadius, pt.x, geomCenter.x + m_majorRadius) &&
  321. isWithin(geomCenter.y - m_minorRadius, pt.y, geomCenter.y + m_minorRadius);
  322. }
  323. };
  324. return false;
  325. }
  326. //=============================================================================
  327. void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
  328. {
  329. switch(m_type)
  330. {
  331. case GEOMETRY_SPHERE:
  332. case GEOMETRY_CYLINDER:
  333. {
  334. #if 1
  335. // this is a better technique than the more obvious radius-and-angle
  336. // one, below, because the latter tends to clump more towards the center.
  337. Real maxDistSqr = sqr(m_majorRadius);
  338. Real distSqr;
  339. do
  340. {
  341. pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
  342. pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
  343. pt.z = 0.0f;
  344. distSqr = sqr(pt.x) + sqr(pt.y);
  345. } while (distSqr > maxDistSqr);
  346. #else
  347. Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
  348. Real angle = GameLogicRandomValueReal(-PI, PI);
  349. pt.x = radius * Cos(angle);
  350. pt.y = radius * Sin(angle);
  351. pt.z = 0.0f;
  352. #endif
  353. break;
  354. }
  355. case GEOMETRY_BOX:
  356. {
  357. pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
  358. pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
  359. pt.z = 0.0f;
  360. break;
  361. }
  362. };
  363. }
  364. //=============================================================================
  365. Real GeometryInfo::getFootprintArea() const
  366. {
  367. switch(m_type)
  368. {
  369. case GEOMETRY_SPHERE:
  370. case GEOMETRY_CYLINDER:
  371. {
  372. return PI * sqr(m_boundingCircleRadius);
  373. }
  374. case GEOMETRY_BOX:
  375. {
  376. return 4.0f * m_majorRadius * m_minorRadius;
  377. }
  378. };
  379. DEBUG_CRASH(("should never get here"));
  380. return 0.0f;
  381. }
  382. //=============================================================================
  383. void GeometryInfo::calcBoundingStuff()
  384. {
  385. switch(m_type)
  386. {
  387. case GEOMETRY_SPHERE:
  388. {
  389. m_boundingSphereRadius = m_majorRadius;
  390. m_boundingCircleRadius = m_majorRadius;
  391. break;
  392. }
  393. case GEOMETRY_CYLINDER:
  394. {
  395. m_boundingCircleRadius = m_majorRadius;
  396. m_boundingSphereRadius = m_height*0.5;
  397. if (m_boundingSphereRadius < m_majorRadius)
  398. m_boundingSphereRadius = m_majorRadius;
  399. break;
  400. }
  401. case GEOMETRY_BOX:
  402. {
  403. m_boundingCircleRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
  404. m_boundingSphereRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
  405. break;
  406. }
  407. };
  408. }
  409. #if defined(_DEBUG) || defined(_INTERNAL)
  410. //=============================================================================
  411. void GeometryInfo::tweakExtents(ExtentModType extentModType, Real extentModAmount)
  412. {
  413. switch(extentModType)
  414. {
  415. case EXTENTMOD_HEIGHT:
  416. m_height += extentModAmount;
  417. break;
  418. case EXTENTMOD_MAJOR:
  419. m_majorRadius += extentModAmount;
  420. break;
  421. case EXTENTMOD_MINOR:
  422. m_minorRadius += extentModAmount;
  423. break;
  424. case EXTENTMOD_TYPE:
  425. m_type = (GeometryType)((m_type + ((extentModType == EXTENTMOD_TYPE)?1:0)) % GEOMETRY_NUM_TYPES);
  426. break;
  427. }
  428. m_isSmall = false;
  429. calcBoundingStuff();
  430. }
  431. #endif
  432. #if defined(_DEBUG) || defined(_INTERNAL)
  433. //=============================================================================
  434. AsciiString GeometryInfo::getDescriptiveString() const
  435. {
  436. AsciiString msg;
  437. msg.format("%d/%d(%g %g %g)", (Int)m_type, (Int)m_isSmall, m_majorRadius, m_minorRadius, m_height);
  438. return msg;
  439. }
  440. #endif
  441. // ------------------------------------------------------------------------------------------------
  442. /** CRC */
  443. // ------------------------------------------------------------------------------------------------
  444. void GeometryInfo::crc( Xfer *xfer )
  445. {
  446. } // end crc
  447. // ------------------------------------------------------------------------------------------------
  448. /** Xfer method
  449. * Version Info:
  450. * 1: Initial version */
  451. // ------------------------------------------------------------------------------------------------
  452. void GeometryInfo::xfer( Xfer *xfer )
  453. {
  454. // version
  455. XferVersion currentVersion = 1;
  456. XferVersion version = currentVersion;
  457. xfer->xferVersion( &version, currentVersion );
  458. // type
  459. xfer->xferUser( &m_type, sizeof( GeometryType ) );
  460. // is small
  461. xfer->xferBool( &m_isSmall );
  462. // height
  463. xfer->xferReal( &m_height );
  464. // major radius
  465. xfer->xferReal( &m_majorRadius );
  466. // minor radius
  467. xfer->xferReal( &m_minorRadius );
  468. // bouncing circle radius
  469. xfer->xferReal( &m_boundingCircleRadius );
  470. // bounding sphere radius
  471. xfer->xferReal( &m_boundingSphereRadius );
  472. } // end xfer
  473. // ------------------------------------------------------------------------------------------------
  474. /** Load post process */
  475. // ------------------------------------------------------------------------------------------------
  476. void GeometryInfo::loadPostProcess( void )
  477. {
  478. } // end loadPostProcess