sphere.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : wwmath *
  23. * *
  24. * $Archive:: /Commando/Code/WWMath/sphere.h $*
  25. * *
  26. * Author:: Greg_h *
  27. * *
  28. * $Modtime:: 3/29/01 10:37a $*
  29. * *
  30. * $Revision:: 18 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * SphereClass::SphereClass -- constructor *
  35. * SphereClass::Init -- assign a new center and radius to this sphere *
  36. * SphereClass::Re_Center -- move the center, update radius to enclose old sphere *
  37. * SphereClass::Add_Sphere -- expands 'this' sphere to enclose the given sphere *
  38. * SphereClass::Transform -- transforms this sphere *
  39. * SphereClass::Volume -- returns the volume of this sphere *
  40. * SphereClass::operator+= -- 'Add' a sphere to this one *
  41. * SphereClass::operator *= -- transform this sphere by the given radius *
  42. * Spheres_Intersect -- test whether two spheres intersect *
  43. * Add_Spheres -- Add two spheres together, creating sphere which encloses both *
  44. * operator + -- Add two spheres together, creating a sphere which encloses both *
  45. * Transform Sphere -- transform a sphere *
  46. * Transform_Sphere -- transform a sphere *
  47. * operator * -- Transform a sphere *
  48. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  49. #if defined(_MSC_VER)
  50. #pragma once
  51. #endif
  52. #ifndef SPHERE_H
  53. #define SPHERE_H
  54. #include "always.h"
  55. #include "vector3.h"
  56. #include "matrix3d.h"
  57. /////////////////////////////////////////////////////////////////////
  58. // SphereClass
  59. //
  60. // Center - center of the sphere.
  61. // Radius - radius of the sphere
  62. //
  63. /////////////////////////////////////////////////////////////////////
  64. class SphereClass
  65. {
  66. public:
  67. inline SphereClass(void) { };
  68. inline SphereClass(const Vector3 & center,float radius) { Init(center,radius); }
  69. inline SphereClass(const Matrix3D& mtx,const Vector3 & center,float radius) { Init(mtx,center,radius); }
  70. inline SphereClass(const Vector3 & center,const SphereClass & s0);
  71. inline SphereClass(const Vector3 *Position, const int VertCount);
  72. inline void Init(const Vector3 & pos,float radius);
  73. inline void Init(const Matrix3D& mtx,const Vector3 & pos,float radius);
  74. inline void Re_Center(const Vector3 & center);
  75. inline void Add_Sphere(const SphereClass & s);
  76. inline void Transform(const Matrix3D & tm);
  77. inline float Volume(void) const;
  78. inline SphereClass & operator += (const SphereClass & s);
  79. inline SphereClass & operator *= (const Matrix3D & m);
  80. Vector3 Center;
  81. float Radius;
  82. };
  83. /***********************************************************************************************
  84. * SphereClass::SphereClass -- constructor *
  85. * *
  86. * INPUT: *
  87. * *
  88. * OUTPUT: *
  89. * *
  90. * WARNINGS: *
  91. * *
  92. * HISTORY: *
  93. * 8/12/98 GTH : Created. *
  94. *=============================================================================================*/
  95. inline SphereClass::SphereClass(const Vector3 & center,const SphereClass & s0)
  96. {
  97. float dist = (s0.Center - center).Length();
  98. Center = center;
  99. Radius = s0.Radius + dist;
  100. }
  101. inline SphereClass::SphereClass(const Vector3 *Position,const int VertCount)
  102. {
  103. int i;
  104. double dx,dy,dz;
  105. // bounding sphere
  106. // Using the algorithm described in Graphics Gems I page 301.
  107. // This algorithm supposedly generates a bounding sphere within
  108. // 5% of the optimal one but is much faster and simpler to implement.
  109. Vector3 xmin(Position[0].X,Position[0].Y,Position[0].Z);
  110. Vector3 xmax(Position[0].X,Position[0].Y,Position[0].Z);
  111. Vector3 ymin(Position[0].X,Position[0].Y,Position[0].Z);
  112. Vector3 ymax(Position[0].X,Position[0].Y,Position[0].Z);
  113. Vector3 zmin(Position[0].X,Position[0].Y,Position[0].Z);
  114. Vector3 zmax(Position[0].X,Position[0].Y,Position[0].Z);
  115. // FIRST PASS:
  116. // finding the 6 minima and maxima points
  117. for (i=1; i<VertCount; i++) {
  118. if (Position[i].X < xmin.X) {
  119. xmin.X = Position[i].X; xmin.Y = Position[i].Y; xmin.Z = Position[i].Z;
  120. }
  121. if (Position[i].X > xmax.X) {
  122. xmax.X = Position[i].X; xmax.Y = Position[i].Y; xmax.Z = Position[i].Z;
  123. }
  124. if (Position[i].Y < ymin.Y) {
  125. ymin.X = Position[i].X; ymin.Y = Position[i].Y; ymin.Z = Position[i].Z;
  126. }
  127. if (Position[i].Y > ymax.Y) {
  128. ymax.X = Position[i].X; ymax.Y = Position[i].Y; ymax.Z = Position[i].Z;
  129. }
  130. if (Position[i].Z < zmin.Z) {
  131. zmin.X = Position[i].X; zmin.Y = Position[i].Y; zmin.Z = Position[i].Z;
  132. }
  133. if (Position[i].Z > zmax.Z) {
  134. zmax.X = Position[i].X; zmax.Y = Position[i].Y; zmax.Z = Position[i].Z;
  135. }
  136. }
  137. // xspan = distance between the 2 points xmin and xmax squared.
  138. // same goes for yspan and zspan.
  139. dx = xmax.X - xmin.X;
  140. dy = xmax.Y - xmin.Y;
  141. dz = xmax.Z - xmin.Z;
  142. double xspan = dx*dx + dy*dy + dz*dz;
  143. dx = ymax.X - ymin.X;
  144. dy = ymax.Y - ymin.Y;
  145. dz = ymax.Z - ymin.Z;
  146. double yspan = dx*dx + dy*dy + dz*dz;
  147. dx = zmax.X - zmin.X;
  148. dy = zmax.Y - zmin.Y;
  149. dz = zmax.Z - zmin.Z;
  150. double zspan = dx*dx + dy*dy + dz*dz;
  151. // Set points dia1 and dia2 to the maximally separated pair
  152. // This will be the diameter of the initial sphere
  153. Vector3 dia1 = xmin;
  154. Vector3 dia2 = xmax;
  155. double maxspan = xspan;
  156. if (yspan > maxspan) {
  157. maxspan = yspan;
  158. dia1 = ymin;
  159. dia2 = ymax;
  160. }
  161. if (zspan > maxspan) {
  162. maxspan = zspan;
  163. dia1 = zmin;
  164. dia2 = zmax;
  165. }
  166. // Compute initial center and radius and radius squared
  167. Vector3 center;
  168. center.X = (dia1.X + dia2.X) / 2.0f;
  169. center.Y = (dia1.Y + dia2.Y) / 2.0f;
  170. center.Z = (dia1.Z + dia2.Z) / 2.0f;
  171. dx = dia2.X - center.X;
  172. dy = dia2.Y - center.Y;
  173. dz = dia2.Z - center.Z;
  174. double radsqr = dx*dx + dy*dy + dz*dz;
  175. double radius = sqrt(radsqr);
  176. // SECOND PASS:
  177. // Increment current sphere if any points fall outside of it.
  178. for (i=0; i<VertCount; i++) {
  179. dx = Position[i].X - center.X;
  180. dy = Position[i].Y - center.Y;
  181. dz = Position[i].Z - center.Z;
  182. double testrad2 = dx*dx + dy*dy + dz*dz;
  183. if (testrad2 > radsqr) {
  184. // this point was outside the old sphere, compute a new
  185. // center point and radius which contains this point
  186. double testrad = sqrt(testrad2);
  187. // adjust center and radius
  188. radius = (radius + testrad) / 2.0;
  189. radsqr = radius * radius;
  190. double oldtonew = testrad - radius;
  191. center.X = (radius * center.X + oldtonew * Position[i].X) / testrad;
  192. center.Y = (radius * center.Y + oldtonew * Position[i].Y) / testrad;
  193. center.Z = (radius * center.Z + oldtonew * Position[i].Z) / testrad;
  194. }
  195. }
  196. Center = center;
  197. Radius = radius;
  198. }
  199. /***********************************************************************************************
  200. * SphereClass::Init -- assign a new center and radius to this sphere *
  201. * *
  202. * INPUT: *
  203. * *
  204. * OUTPUT: *
  205. * *
  206. * WARNINGS: *
  207. * *
  208. * HISTORY: *
  209. * 8/12/98 GTH : Created. *
  210. *=============================================================================================*/
  211. inline void SphereClass::Init(const Vector3 & pos,float radius)
  212. {
  213. Center = pos;
  214. Radius = radius;
  215. }
  216. /***********************************************************************************************
  217. * SphereClass::Init -- assign a new center and radius to this sphere *
  218. * *
  219. * INPUT: *
  220. * *
  221. * OUTPUT: *
  222. * *
  223. * WARNINGS: *
  224. * *
  225. * HISTORY: *
  226. * 8/12/98 GTH : Created. *
  227. *=============================================================================================*/
  228. inline void SphereClass::Init(const Matrix3D& mtx, const Vector3 & pos,float radius)
  229. {
  230. #ifdef ALLOW_TEMPORARIES
  231. Center = mtx * pos;
  232. #else
  233. mtx.mulVector3(pos, Center);
  234. #endif
  235. Radius = radius;
  236. }
  237. /***********************************************************************************************
  238. * SphereClass::Re_Center -- move the center, update radius to enclose old sphere *
  239. * *
  240. * INPUT: *
  241. * *
  242. * OUTPUT: *
  243. * *
  244. * WARNINGS: *
  245. * *
  246. * HISTORY: *
  247. * 8/12/98 GTH : Created. *
  248. *=============================================================================================*/
  249. inline void SphereClass::Re_Center(const Vector3 & center)
  250. {
  251. float dist = (Center - center).Length();
  252. Center = center;
  253. Radius += dist;
  254. }
  255. /***********************************************************************************************
  256. * SphereClass::Add_Sphere -- expands 'this' sphere to enclose the given sphere *
  257. * *
  258. * INPUT: *
  259. * *
  260. * OUTPUT: *
  261. * *
  262. * WARNINGS: *
  263. * *
  264. * HISTORY: *
  265. * 8/12/98 GTH : Created. *
  266. *=============================================================================================*/
  267. inline void SphereClass::Add_Sphere(const SphereClass & s)
  268. {
  269. if (s.Radius == 0.0f) return;
  270. float dist = (s.Center - Center).Length();
  271. if (dist == 0.0f) {
  272. Radius = (Radius > s.Radius) ? Radius : s.Radius;
  273. return;
  274. }
  275. float rnew = (dist + Radius + s.Radius) / 2.0f;
  276. // If rnew is smaller than either of the two sphere radii (it can't be
  277. // smaller than both of them), this means that the smaller sphere is
  278. // completely inside the larger, and the result of adding the two is
  279. // simply the larger sphere. If rnew isn't less than either of them, it is
  280. // the new radius - calculate the new center.
  281. if (rnew < Radius) {
  282. // The existing sphere is the result - do nothing.
  283. } else {
  284. if (rnew < s.Radius) {
  285. // The new sphere is the result:
  286. Init(s.Center, s.Radius);
  287. } else {
  288. // Neither sphere is completely inside the other, so rnew is the new
  289. // radius - calculate the new center
  290. float lerp = (rnew - Radius) / dist;
  291. Vector3 center = (s.Center - Center) * lerp + Center;
  292. Init(center, rnew);
  293. }
  294. }
  295. }
  296. /***********************************************************************************************
  297. * SphereClass::Transform -- transforms this sphere *
  298. * *
  299. * INPUT: *
  300. * *
  301. * OUTPUT: *
  302. * *
  303. * WARNINGS: *
  304. * *
  305. * HISTORY: *
  306. * 3/12/99 GTH : Created. *
  307. *=============================================================================================*/
  308. inline void SphereClass::Transform(const Matrix3D & tm)
  309. {
  310. // warning, assumes Orthogonal matrix
  311. #ifdef ALLOW_TEMPORARIES
  312. Center = tm * Center;
  313. #else
  314. tm.mulVector3(Center, Center);
  315. #endif
  316. }
  317. /***********************************************************************************************
  318. * SphereClass::Volume -- returns the volume of this sphere *
  319. * *
  320. * INPUT: *
  321. * *
  322. * OUTPUT: *
  323. * *
  324. * WARNINGS: *
  325. * *
  326. * HISTORY: *
  327. * 3/22/99 GTH : Created. *
  328. *=============================================================================================*/
  329. inline float SphereClass::Volume(void) const
  330. {
  331. return (4.0 / 3.0) * WWMATH_PI * (Radius * Radius * Radius);
  332. }
  333. /***********************************************************************************************
  334. * SphereClass::operator+= -- 'Add' a sphere to this one *
  335. * *
  336. * Expands 'this' sphere to also enclose the given sphere *
  337. * *
  338. * INPUT: *
  339. * *
  340. * OUTPUT: *
  341. * *
  342. * WARNINGS: *
  343. * *
  344. * HISTORY: *
  345. * 8/12/98 GTH : Created. *
  346. *=============================================================================================*/
  347. inline SphereClass & SphereClass::operator += (const SphereClass & s)
  348. {
  349. Add_Sphere(s);
  350. return *this;
  351. }
  352. /***********************************************************************************************
  353. * SphereClass::operator *= -- transform this sphere by the given radius *
  354. * *
  355. * INPUT: *
  356. * *
  357. * OUTPUT: *
  358. * *
  359. * WARNINGS: *
  360. * *
  361. * HISTORY: *
  362. * 8/12/98 GTH : Created. *
  363. *=============================================================================================*/
  364. inline SphereClass & SphereClass::operator *= (const Matrix3D & m)
  365. {
  366. Init(m, Center, Radius);
  367. return *this;
  368. }
  369. /***********************************************************************************************
  370. * Spheres_Intersect -- test whether two spheres intersect *
  371. * *
  372. * INPUT: *
  373. * *
  374. * OUTPUT: *
  375. * *
  376. * WARNINGS: *
  377. * *
  378. * HISTORY: *
  379. * 8/12/98 GTH : Created. *
  380. *=============================================================================================*/
  381. inline bool Spheres_Intersect(const SphereClass & s0,const SphereClass & s1)
  382. {
  383. Vector3 delta = s0.Center - s1.Center;
  384. float dist2 = Vector3::Dot_Product(delta, delta);
  385. if (dist2 < (s0.Radius + s1.Radius) * (s0.Radius + s1.Radius)) {
  386. return true;
  387. } else {
  388. return false;
  389. }
  390. }
  391. /***********************************************************************************************
  392. * Add_Spheres -- Add two spheres together, creating sphere which encloses both *
  393. * *
  394. * INPUT: *
  395. * *
  396. * OUTPUT: *
  397. * *
  398. * WARNINGS: *
  399. * *
  400. * HISTORY: *
  401. * 8/12/98 GTH : Created. *
  402. *=============================================================================================*/
  403. inline SphereClass Add_Spheres(const SphereClass & s0, const SphereClass & s1)
  404. {
  405. if (s0.Radius == 0.0f) {
  406. return s1;
  407. } else {
  408. SphereClass result(s0);
  409. result.Add_Sphere(s1);
  410. return result;
  411. }
  412. }
  413. /***********************************************************************************************
  414. * operator + -- Add two spheres together, creating a sphere which encloses both *
  415. * *
  416. * INPUT: *
  417. * *
  418. * OUTPUT: *
  419. * *
  420. * WARNINGS: *
  421. * *
  422. * HISTORY: *
  423. * 8/12/98 GTH : Created. *
  424. *=============================================================================================*/
  425. inline SphereClass operator + (const SphereClass & s0,const SphereClass & s1)
  426. {
  427. return Add_Spheres(s0,s1);
  428. }
  429. /***********************************************************************************************
  430. * Transform Sphere -- transform a sphere *
  431. * *
  432. * INPUT: *
  433. * *
  434. * OUTPUT: *
  435. * *
  436. * WARNINGS: *
  437. * *
  438. * HISTORY: *
  439. * 8/12/98 GTH : Created. *
  440. *=============================================================================================*/
  441. inline SphereClass Transform_Sphere(const Matrix3D & m, const SphereClass & s)
  442. {
  443. // Warning, assumes Orthogonal matrix
  444. return SphereClass(m,s.Center,s.Radius);
  445. }
  446. /***********************************************************************************************
  447. * Transform_Sphere -- transform a sphere *
  448. * *
  449. * INPUT: *
  450. * *
  451. * OUTPUT: *
  452. * *
  453. * WARNINGS: *
  454. * *
  455. * HISTORY: *
  456. * 8/12/98 GTH : Created. *
  457. *=============================================================================================*/
  458. inline void Transform_Sphere(const Matrix3D & m, const SphereClass & s,SphereClass & res)
  459. {
  460. // warning, assumes Orthogonal matrix
  461. #ifdef ALLOW_TEMPORARIES
  462. res.Center = m*s.Center;
  463. #else
  464. m.mulVector3(s.Center, res.Center);
  465. #endif
  466. res.Radius = s.Radius;
  467. }
  468. /***********************************************************************************************
  469. * operator * -- Transform a sphere *
  470. * *
  471. * INPUT: *
  472. * *
  473. * OUTPUT: *
  474. * *
  475. * WARNINGS: *
  476. * *
  477. * HISTORY: *
  478. * 8/12/98 GTH : Created. *
  479. *=============================================================================================*/
  480. inline SphereClass operator * (const Matrix3D & m, const SphereClass & s)
  481. {
  482. return Transform_Sphere(m,s);
  483. }
  484. #endif