quat.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  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. /* $Header: /Commando/Code/wwmath/quat.cpp 38 8/28/01 10:26a Greg_h $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Voxel Technology *
  24. * *
  25. * File Name : QUAT.CPP *
  26. * *
  27. * Programmer : Greg Hjelstrom *
  28. * *
  29. * Start Date : 02/24/97 *
  30. * *
  31. * Last Update : February 28, 1997 [GH] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * Quaternion::Quaternion -- constructor *
  36. * Quaternion::Set -- Set the quaternion *
  37. * Quaternion::operator= -- Assignment operator *
  38. * Quaternion::Make_Closest -- Use nearest representation to the given quaternion. *
  39. * Trackball -- Computes a "trackball" quaternion given 2D mouse coordinates *
  40. * Axis_To_Quat -- Creates a quaternion given an axis and angle of rotation *
  41. * Slerp -- Spherical Linear interpolation! *
  42. * Build_Quaternion -- Creates a quaternion from a Matrix *
  43. * Build_Matrix -- Creates a Matrix from a Quaternion *
  44. * Normalize -- normalizes a quaternion *
  45. * Quaternion::Quaternion -- constructor *
  46. * Slerp_Setup -- Get ready to call "Cached_Slerp" *
  47. * Cached_Slerp -- Quaternion slerping, optimized with cached values *
  48. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  49. #include "quat.h"
  50. #include "matrix3d.h"
  51. #include "matrix4.h"
  52. #include "wwmath.h"
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <math.h>
  56. #include <assert.h>
  57. #define SLERP_EPSILON 0.001
  58. static int _nxt[3] = { 1 , 2 , 0 };
  59. // ------------------------------------------------------------
  60. // local functions
  61. // ------------------------------------------------------------
  62. static float project_to_sphere(float,float,float);
  63. /***********************************************************************************************
  64. * Quaternion::Quaternion -- constructor *
  65. * *
  66. * constructs a quaternion from the given axis and angle of rotation (in RADIANS of course) *
  67. * *
  68. * INPUT: *
  69. * axis - axis of the rotation *
  70. * angle - rotation angle *
  71. * *
  72. * OUTPUT: *
  73. * *
  74. * WARNINGS: *
  75. * *
  76. * HISTORY: *
  77. * 12/10/97 GTH : Created. *
  78. *=============================================================================================*/
  79. Quaternion::Quaternion(const Vector3 & axis,float angle)
  80. {
  81. float s = WWMath::Sin(angle/2);
  82. float c = WWMath::Cos(angle/2);
  83. X = s * axis.X;
  84. Y = s * axis.Y;
  85. Z = s * axis.Z;
  86. W = c;
  87. }
  88. /***********************************************************************************************
  89. * Quaternion::Normalize -- Normalize to a unit quaternion *
  90. * *
  91. * INPUT: *
  92. * *
  93. * OUTPUT: *
  94. * *
  95. * WARNINGS: *
  96. * *
  97. * HISTORY: *
  98. * 02/24/1997 GH : Created. *
  99. *=============================================================================================*/
  100. void Quaternion::Normalize()
  101. {
  102. float len2=X * X + Y * Y + Z * Z + W * W;
  103. if (0.0f == len2) {
  104. return;
  105. } else {
  106. float inv_mag = WWMath::Inv_Sqrt(len2);
  107. X *= inv_mag;
  108. Y *= inv_mag;
  109. Z *= inv_mag;
  110. W *= inv_mag;
  111. }
  112. }
  113. /***********************************************************************************************
  114. * Q::Make_Closest -- Use nearest representation to the given quaternion. *
  115. * *
  116. * INPUT: *
  117. * *
  118. * OUTPUT: *
  119. * *
  120. * WARNINGS: *
  121. * *
  122. * HISTORY: *
  123. * 02/28/1997 GH : Created. *
  124. *=============================================================================================*/
  125. Quaternion & Quaternion::Make_Closest(const Quaternion & qto)
  126. {
  127. float cos_t = qto.X * X + qto.Y * Y + qto.Z * Z + qto.W * W;
  128. // if we are on opposite hemisphere from qto, negate ourselves
  129. if (cos_t < 0.0) {
  130. X = -X;
  131. Y = -Y;
  132. Z = -Z;
  133. W = -W;
  134. }
  135. return *this;
  136. }
  137. /***********************************************************************************************
  138. * Trackball -- Computes a "trackball" quaternion given 2D mouse coordinates *
  139. * *
  140. * INPUT: *
  141. * x0,y0 - x1,y1 - "normalized" mouse coordinates for the mouse movement *
  142. * sphsize - size of the trackball sphere *
  143. * *
  144. * OUTPUT: *
  145. * a quaternion representing the rotation of a trackball *
  146. * *
  147. * WARNINGS: *
  148. * *
  149. * HISTORY: *
  150. * 02/28/1997 GH : Created. *
  151. *=============================================================================================*/
  152. Quaternion Trackball(float x0, float y0, float x1, float y1, float sphsize)
  153. {
  154. Vector3 a;
  155. Vector3 p1;
  156. Vector3 p2;
  157. Vector3 d;
  158. float phi,t;
  159. if ((x0 == x1) && (y0 == y1)) {
  160. return Quaternion(0.0f, 0.0f, 0.0f, 1.0f); // Zero rotation
  161. }
  162. // Compute z coordinates for projection of p1 and p2 to
  163. // deformed sphere
  164. p1[0] = x0;
  165. p1[1] = y0;
  166. p1[2] = project_to_sphere(sphsize, x0, y0);
  167. p2[0] = x1;
  168. p2[1] = y1;
  169. p2[2] = project_to_sphere(sphsize, x1, y1);
  170. // Find their cross product
  171. Vector3::Cross_Product(p2,p1,&a);
  172. // Compute how much to rotate
  173. d = p1 - p2;
  174. t = d.Length() / (2.0f * sphsize);
  175. // Avoid problems with out of control values
  176. if (t > 1.0f) t = 1.0f;
  177. if (t < -1.0f) t = -1.0f;
  178. phi = 2.0f * WWMath::Asin(t);
  179. return Axis_To_Quat(a, phi);
  180. }
  181. /***********************************************************************************************
  182. * Axis_To_Quat -- Creates a quaternion given an axis and angle of rotation *
  183. * *
  184. * INPUT: *
  185. * *
  186. * OUTPUT: *
  187. * *
  188. * WARNINGS: *
  189. * *
  190. * HISTORY: *
  191. * 02/28/1997 GH : Created. *
  192. *=============================================================================================*/
  193. Quaternion Axis_To_Quat(const Vector3 &a, float phi)
  194. {
  195. Quaternion q;
  196. Vector3 tmp = a;
  197. tmp.Normalize();
  198. q[0] = tmp[0];
  199. q[1] = tmp[1];
  200. q[2] = tmp[2];
  201. q.Scale(WWMath::Sin(phi / 2.0f));
  202. q[3] = WWMath::Cos(phi / 2.0f);
  203. return q;
  204. }
  205. /***********************************************************************************************
  206. * Slerp -- Spherical Linear interpolation! *
  207. * *
  208. * INPUT: *
  209. * p - start quaternion *
  210. * q - end quaternion *
  211. * alpha - interpolating parameter *
  212. * *
  213. * OUTPUT: *
  214. * *
  215. * WARNINGS: *
  216. * *
  217. * HISTORY: *
  218. * 02/28/1997 GH : Created. *
  219. *=============================================================================================*/
  220. #if 0
  221. #pragma warning (disable : 4725)
  222. #define ARC_TABLE_SIZE_MASK 1023
  223. #define SIN_TABLE_SIZE_MASK 1023
  224. void __cdecl Fast_Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
  225. {
  226. float float_epsilon2=WWMATH_EPSILON * WWMATH_EPSILON;
  227. float HalfOfArcTableSize=float(ARC_TABLE_SIZE/2);
  228. float HalfOfSinTableSize=float(SIN_TABLE_SIZE/2);
  229. const unsigned ARC_TABLE_SIZE_PER_2=ARC_TABLE_SIZE/2;
  230. float beta; // complementary interploation parameter
  231. float theta; // angle between p and q
  232. __asm {
  233. mov esi, p
  234. mov edi, q
  235. fld1 // we'll need 1.0 and 0.0 later
  236. // ----------------------------------------------------------------------------
  237. // cos theta = dot product of p and q
  238. // cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
  239. // if q is on opposite hemisphere from A, use -B instead
  240. // if (cos_t < 0.0) {
  241. // cos_t = -cos_t;
  242. // qflip = true;
  243. // }
  244. // else {
  245. // qflip = false;
  246. // }
  247. // ----------------------------------------------------------------------------
  248. fld dword ptr [esi] // p.X
  249. fmul dword ptr [edi] // p.X*q.X
  250. fld dword ptr [esi+08h] // p.Y
  251. fmul dword ptr [edi+08h] // p.Y*q.Y
  252. fld dword ptr [esi+04h] // p.Z
  253. fmul dword ptr [edi+04h] // p.Z*q.Z
  254. fld dword ptr [edi+0ch] // p.W
  255. fmul dword ptr [esi+0ch] // p.W*q.W
  256. faddp st(2), st(0) // y+=w
  257. faddp st(2), st(0) // x+=z
  258. faddp st(1),st(0) // x+z + y+w
  259. fst beta
  260. fabs
  261. mov ebx,beta
  262. and ebx,0x80000000
  263. // ----------------------------------------------------------------------------
  264. // if q is very close to p, just linearly interpolate
  265. // between the two.
  266. // if (1.0 - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
  267. // beta = 1.0 - alpha;
  268. // }
  269. // ----------------------------------------------------------------------------
  270. fld st(0) // duplicate st(0), which contains cos_t
  271. fsubr st(0),st(2) // st(2) contains 1.0
  272. fcomp float_epsilon2
  273. fnstsw ax
  274. test ah, 01h
  275. je normal_slerp
  276. fld alpha
  277. fsubr st(0),st(1) // st(1) contains 1.0
  278. fstp beta
  279. jmp done_slerp
  280. normal_slerp:
  281. // ----------------------------------------------------------------------------
  282. // normal slerp!
  283. // else {
  284. // theta = WWMath::Acos(cos_t);
  285. // sin_t = WWMath::Sin(theta);
  286. // oo_sin_t = 1.0 / sin_t;
  287. // beta = WWMath::Sin(theta - alpha*theta) * oo_sin_t;
  288. // alpha = WWMath::Sin(alpha*theta) * oo_sin_t;
  289. // }
  290. // if (qflip) {
  291. // alpha = -alpha;
  292. // }
  293. // ----------------------------------------------------------------------------
  294. fld HalfOfSinTableSize
  295. fld HalfOfArcTableSize
  296. fmul st(0),st(2) // cos_t * (ARC_TABLE_SIZE/2)
  297. fistp theta // convert to integer
  298. mov eax,theta
  299. add eax,ARC_TABLE_SIZE_PER_2
  300. jns no_neg
  301. xor eax,eax
  302. jmp contin
  303. no_neg:
  304. cmp eax,ARC_TABLE_SIZE
  305. jl contin // Note: Use Setcc/Movcc here!!!
  306. mov eax,ARC_TABLE_SIZE_MASK
  307. contin:
  308. fld dword ptr[_FastAcosTable+eax*4]
  309. fst theta
  310. fmul st(0),st(1) // theta * (sin_table_size/2)
  311. fadd st(0),st(1) // theta * (sin_table_size/2) + (sin_table_size/2)
  312. fistp beta // conver to integer
  313. mov ecx,SIN_TABLE_SIZE_MASK
  314. mov eax,beta
  315. and eax,ecx // & SIN_TABLE_SIZE_MASK
  316. fld dword ptr[_FastInvSinTable+eax*4] // 1.0f/sin(theta)
  317. fld theta
  318. fmul alpha // theta*alpha
  319. fld st(0) // duplicate stack head as we need theta*alpha later
  320. fsubr theta // theta-theta*alpha
  321. fmul st(0),st(3) // (theta-theta*alpha)*HalfOfSinTableSize
  322. fadd st(0),st(3) // (theta-theta*alpha)*HalfOfSinTableSize+HalfOfSinTableSize
  323. fistp beta // convert to integer
  324. mov eax,beta
  325. and eax,ecx // & SIN_TABLE_SIZE_MASK
  326. fld dword ptr[_FastSinTable+eax*4] // sin(theta-theta*alpha)
  327. fmul st(0),st(2) // sin(theta-theta*alpha) * oo_sin_t
  328. fstp beta
  329. fmul st(0),st(2) // (theta*alpha)*HalfOfSinTableSize
  330. fadd st(0),st(2) // (theta*alpha)*HalfOfSinTableSize+HalfOfSinTableSize
  331. fistp theta // convert to integer
  332. mov eax,theta
  333. and eax,ecx // & SIN_TABLE_SIZE_MASK
  334. fld dword ptr[_FastSinTable+eax*4] // sin(theta*alpha)
  335. fmul st(0),st(1) // oo_sin_t
  336. fstp alpha
  337. fstp st(0) // pop oo_sin_t
  338. fstp st(0) // pop HalfOfSinTableSize
  339. done_slerp:
  340. test ebx, ebx
  341. je no_negative
  342. fld alpha
  343. fchs
  344. fstp alpha
  345. no_negative:
  346. // ----------------------------------------------------------------------------
  347. // res.X = beta*p.X + alpha*q.X;
  348. // res.Y = beta*p.Y + alpha*q.Y;
  349. // res.Z = beta*p.Z + alpha*q.Z;
  350. // res.W = beta*p.W + alpha*q.W;
  351. // ----------------------------------------------------------------------------
  352. fstp st(0) // pop cos_t
  353. fstp st(0) // pop 1.0
  354. fld alpha
  355. fld dword ptr [edi+4] // q.Y
  356. fmul st(0),st(1) // alpha*q.Y
  357. fld dword ptr [edi+8] // q.Z
  358. fmul st(0),st(2) // alpha*q.Z
  359. fld dword ptr [edi+12] // q.W
  360. fmul st(0),st(3) // alpha*q.W
  361. fld dword ptr [edi] // q.X
  362. fmulp st(4),st // alpha*q.X
  363. fld beta
  364. fld dword ptr [esi+4] // p.Y
  365. fmul st(0),st(1) // beta*p.Y
  366. fld dword ptr [esi+8] // p.Z
  367. fmul st(0),st(2) // beta*p.Z
  368. fld dword ptr [esi] // p.X
  369. fmul st(0),st(3) // beta*p.X
  370. fxch st(3) // move beta to top of stack
  371. fmul dword ptr [esi+12] // beta*p.W
  372. faddp st(4),st // w
  373. faddp st(4),st // z
  374. faddp st(4),st // y
  375. faddp st(4),st // x
  376. mov ecx, res
  377. fstp [ecx+12] // w
  378. fstp [ecx+8] // z
  379. fstp [ecx+4] // y
  380. fstp [ecx] // x
  381. }
  382. }
  383. #else
  384. void __cdecl Fast_Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
  385. {
  386. float beta; // complementary interploation parameter
  387. float theta; // angle between p and q
  388. float cos_t; // sine, cosine of theta
  389. float oo_sin_t;
  390. int qflip; // use flip of q?
  391. // cos theta = dot product of p and q
  392. cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
  393. // if q is on opposite hemisphere from A, use -B instead
  394. if (cos_t < 0.0f) {
  395. cos_t = -cos_t;
  396. qflip = true;
  397. } else {
  398. qflip = false;
  399. }
  400. if (1.0f - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
  401. // if q is very close to p, just linearly interpolate
  402. // between the two.
  403. beta = 1.0f - alpha;
  404. } else {
  405. theta = WWMath::Fast_Acos(cos_t);
  406. float sin_t = WWMath::Fast_Sin(theta);
  407. oo_sin_t = 1.0f / sin_t;
  408. beta = WWMath::Fast_Sin(theta - alpha*theta) * oo_sin_t;
  409. alpha = WWMath::Fast_Sin(alpha*theta) * oo_sin_t;
  410. }
  411. if (qflip) {
  412. alpha = -alpha;
  413. }
  414. res.X = beta*p.X + alpha*q.X;
  415. res.Y = beta*p.Y + alpha*q.Y;
  416. res.Z = beta*p.Z + alpha*q.Z;
  417. res.W = beta*p.W + alpha*q.W;
  418. }
  419. #endif // MSC_VER
  420. void Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
  421. {
  422. float beta; // complementary interploation parameter
  423. float theta; // angle between p and q
  424. //float sin_t
  425. float cos_t; // sine, cosine of theta
  426. float oo_sin_t;
  427. int qflip; // use flip of q?
  428. // cos theta = dot product of p and q
  429. cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
  430. // if q is on opposite hemisphere from A, use -B instead
  431. if (cos_t < 0.0f) {
  432. cos_t = -cos_t;
  433. qflip = true;
  434. } else {
  435. qflip = false;
  436. }
  437. if (1.0f - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
  438. // if q is very close to p, just linearly interpolate
  439. // between the two.
  440. beta = 1.0f - alpha;
  441. } else {
  442. // normal slerp!
  443. theta = WWMath::Acos(cos_t);
  444. float sin_t = WWMath::Sin(theta);
  445. oo_sin_t = 1.0f / sin_t;
  446. beta = WWMath::Sin(theta - alpha*theta) * oo_sin_t;
  447. alpha = WWMath::Sin(alpha*theta) * oo_sin_t;
  448. }
  449. if (qflip) {
  450. alpha = -alpha;
  451. }
  452. res.X = beta*p.X + alpha*q.X;
  453. res.Y = beta*p.Y + alpha*q.Y;
  454. res.Z = beta*p.Z + alpha*q.Z;
  455. res.W = beta*p.W + alpha*q.W;
  456. }
  457. /***********************************************************************************************
  458. * Slerp_Setup -- Get ready to call "Cached_Slerp" *
  459. * *
  460. * INPUT: *
  461. * *
  462. * OUTPUT: *
  463. * *
  464. * WARNINGS: *
  465. * *
  466. * HISTORY: *
  467. * 2/27/98 GTH : Created. *
  468. *=============================================================================================*/
  469. void Slerp_Setup(const Quaternion & p,const Quaternion & q,SlerpInfoStruct * slerpinfo)
  470. {
  471. float cos_t;
  472. assert(slerpinfo != NULL);
  473. // cos theta = dot product of p and q
  474. cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
  475. // if q is on opposite hemisphere from A, use -B instead
  476. if (cos_t < 0.0f) {
  477. cos_t = -cos_t;
  478. slerpinfo->Flip = true;
  479. } else {
  480. slerpinfo->Flip = false;
  481. }
  482. if (1.0f - cos_t < SLERP_EPSILON) {
  483. slerpinfo->Linear = true;
  484. slerpinfo->Theta = 0.0f;
  485. slerpinfo->SinT = 0.0f;
  486. } else {
  487. slerpinfo->Linear = false;
  488. slerpinfo->Theta = WWMath::Acos(cos_t);
  489. slerpinfo->SinT = WWMath::Sin(slerpinfo->Theta);
  490. }
  491. }
  492. /***********************************************************************************************
  493. * Cached_Slerp -- Quaternion slerping, optimized with cached values *
  494. * *
  495. * INPUT: *
  496. * *
  497. * OUTPUT: *
  498. * *
  499. * WARNINGS: *
  500. * *
  501. * HISTORY: *
  502. * 2/27/98 GTH : Created. *
  503. *=============================================================================================*/
  504. Quaternion Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo)
  505. {
  506. float beta; // complementary interploation parameter
  507. float oo_sin_t;
  508. if (slerpinfo->Linear) {
  509. // if q is very close to p, just linearly interpolate
  510. // between the two.
  511. beta = 1.0f - alpha;
  512. } else {
  513. // normal slerp!
  514. oo_sin_t = 1.0f / slerpinfo->Theta;
  515. beta = WWMath::Sin(slerpinfo->Theta - alpha*slerpinfo->Theta) * oo_sin_t;
  516. alpha = WWMath::Sin(alpha*slerpinfo->Theta) * oo_sin_t;
  517. }
  518. if (slerpinfo->Flip) {
  519. alpha = -alpha;
  520. }
  521. Quaternion res;
  522. res.X = beta*p.X + alpha*q.X;
  523. res.Y = beta*p.Y + alpha*q.Y;
  524. res.Z = beta*p.Z + alpha*q.Z;
  525. res.W = beta*p.W + alpha*q.W;
  526. return res;
  527. }
  528. void Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo,Quaternion * set_q)
  529. {
  530. float beta; // complementary interploation parameter
  531. float oo_sin_t;
  532. if (slerpinfo->Linear) {
  533. // if q is very close to p, just linearly interpolate
  534. // between the two.
  535. beta = 1.0f - alpha;
  536. } else {
  537. // normal slerp!
  538. oo_sin_t = 1.0f / slerpinfo->Theta;
  539. beta = WWMath::Sin(slerpinfo->Theta - alpha*slerpinfo->Theta) * oo_sin_t;
  540. alpha = WWMath::Sin(alpha*slerpinfo->Theta) * oo_sin_t;
  541. }
  542. if (slerpinfo->Flip) {
  543. alpha = -alpha;
  544. }
  545. set_q->X = beta*p.X + alpha*q.X;
  546. set_q->Y = beta*p.Y + alpha*q.Y;
  547. set_q->Z = beta*p.Z + alpha*q.Z;
  548. set_q->W = beta*p.W + alpha*q.W;
  549. }
  550. /***********************************************************************************************
  551. * Build_Quaternion -- Creates a quaternion from a Matrix *
  552. * *
  553. * INPUT: *
  554. * *
  555. * OUTPUT: *
  556. * *
  557. * WARNINGS: *
  558. * Matrix MUST NOT have scaling! *
  559. * *
  560. * HISTORY: *
  561. * 02/28/1997 GH : Created. *
  562. *=============================================================================================*/
  563. Quaternion Build_Quaternion(const Matrix3D & mat)
  564. {
  565. float tr,s;
  566. int i,j,k;
  567. Quaternion q;
  568. // sum the diagonal of the rotation matrix
  569. tr = mat[0][0] + mat[1][1] + mat[2][2];
  570. if (tr > 0.0f) {
  571. s = sqrt(tr + 1.0);
  572. q[3] = s * 0.5;
  573. s = 0.5 / s;
  574. q[0] = (mat[2][1] - mat[1][2]) * s;
  575. q[1] = (mat[0][2] - mat[2][0]) * s;
  576. q[2] = (mat[1][0] - mat[0][1]) * s;
  577. } else {
  578. i=0;
  579. if (mat[1][1] > mat[0][0]) i = 1;
  580. if (mat[2][2] > mat[i][i]) i = 2;
  581. j = _nxt[i];
  582. k = _nxt[j];
  583. s = sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1.0);
  584. q[i] = s * 0.5;
  585. if (s != 0.0) {
  586. s = 0.5 / s;
  587. }
  588. q[3] = ( mat[k][j] - mat[j][k] ) * s;
  589. q[j] = ( mat[j][i] + mat[i][j] ) * s;
  590. q[k] = ( mat[k][i] + mat[i][k] ) * s;
  591. }
  592. return q;
  593. }
  594. Quaternion Build_Quaternion(const Matrix3 & mat)
  595. {
  596. float tr,s;
  597. int i,j,k;
  598. Quaternion q;
  599. // sum the diagonal of the rotation matrix
  600. tr = mat[0][0] + mat[1][1] + mat[2][2];
  601. if (tr > 0.0) {
  602. s = sqrt(tr + 1.0);
  603. q[3] = s * 0.5;
  604. s = 0.5 / s;
  605. q[0] = (mat[2][1] - mat[1][2]) * s;
  606. q[1] = (mat[0][2] - mat[2][0]) * s;
  607. q[2] = (mat[1][0] - mat[0][1]) * s;
  608. } else {
  609. i = 0;
  610. if (mat[1][1] > mat[0][0]) i = 1;
  611. if (mat[2][2] > mat[i][i]) i = 2;
  612. j = _nxt[i];
  613. k = _nxt[j];
  614. s = sqrt( (mat[i][i] - (mat[j][j]+mat[k][k])) + 1.0);
  615. q[i] = s * 0.5;
  616. if (s != 0.0) {
  617. s = 0.5/s;
  618. }
  619. q[3] = ( mat[k][j] - mat[j][k] ) * s;
  620. q[j] = ( mat[j][i] + mat[i][j] ) * s;
  621. q[k] = ( mat[k][i] + mat[i][k] ) * s;
  622. }
  623. return q;
  624. }
  625. Quaternion Build_Quaternion(const Matrix4 & mat)
  626. {
  627. float tr,s;
  628. int i,j,k;
  629. Quaternion q;
  630. // sum the diagonal of the rotation matrix
  631. tr = mat[0][0] + mat[1][1] + mat[2][2];
  632. if (tr > 0.0) {
  633. s = sqrt(tr + 1.0);
  634. q[3] = s * 0.5;
  635. s = 0.5 / s;
  636. q[0] = (mat[2][1] - mat[1][2]) * s;
  637. q[1] = (mat[0][2] - mat[2][0]) * s;
  638. q[2] = (mat[1][0] - mat[0][1]) * s;
  639. } else {
  640. i = 0;
  641. if (mat[1][1] > mat[0][0]) i = 1;
  642. if (mat[2][2] > mat[i][i]) i = 2;
  643. j = _nxt[i];
  644. k = _nxt[j];
  645. s = sqrt( (mat[i][i] - (mat[j][j]+mat[k][k])) + 1.0);
  646. q[i] = s * 0.5;
  647. if (s != 0.0) {
  648. s = 0.5/s;
  649. }
  650. q[3] = ( mat[k][j] - mat[j][k] ) * s;
  651. q[j] = ( mat[j][i] + mat[i][j] ) * s;
  652. q[k] = ( mat[k][i] + mat[i][k] ) * s;
  653. }
  654. return q;
  655. }
  656. /***********************************************************************************************
  657. * Build_Matrix -- Creates a Matrix from a Quaternion *
  658. * *
  659. * INPUT: *
  660. * *
  661. * OUTPUT: *
  662. * *
  663. * WARNINGS: *
  664. * *
  665. * HISTORY: *
  666. * 02/28/1997 GH : Created. *
  667. *=============================================================================================*/
  668. Matrix3 Build_Matrix3(const Quaternion & q)
  669. {
  670. Matrix3 m;
  671. m[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
  672. m[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
  673. m[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
  674. m[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
  675. m[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
  676. m[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
  677. m[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
  678. m[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
  679. m[2][2] =(float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
  680. return m;
  681. }
  682. Matrix4 Build_Matrix4(const Quaternion & q)
  683. {
  684. Matrix4 m;
  685. // initialize the rotation sub-matrix
  686. m[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
  687. m[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
  688. m[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
  689. m[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
  690. m[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
  691. m[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
  692. m[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
  693. m[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
  694. m[2][2] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
  695. // no translation
  696. m[0][3] = m[1][3] = m[2][3] = 0.0f;
  697. // last row
  698. m[3][0] = m[3][1] = m[3][2] = 0.0f;
  699. m[3][3] = 1.0f;
  700. return m;
  701. }
  702. void Quaternion::Rotate_X(float theta)
  703. {
  704. // TODO: optimize this
  705. *this = (*this) * Quaternion(Vector3(1,0,0),theta);
  706. }
  707. void Quaternion::Rotate_Y(float theta)
  708. {
  709. // TODO: optimize this
  710. *this = (*this) * Quaternion(Vector3(0,1,0),theta);
  711. }
  712. void Quaternion::Rotate_Z(float theta)
  713. {
  714. // TODO: optimize this
  715. *this = (*this) * Quaternion(Vector3(0,0,1),theta);
  716. }
  717. float project_to_sphere(float r, float x, float y)
  718. {
  719. const float SQRT2 = 1.41421356f;
  720. float t, z;
  721. float d = WWMath::Sqrt(x * x + y * y);
  722. if (d < r * (SQRT2/(2.0f))) // inside sphere
  723. z = WWMath::Sqrt(r * r - d * d);
  724. else { // on hyperbola
  725. t = r / SQRT2;
  726. z = t * t / d;
  727. }
  728. return z;
  729. }
  730. void Quaternion::Randomize(void)
  731. {
  732. X = ((float) (rand() & 0xFFFF)) / 65536.0f;
  733. Y = ((float) (rand() & 0xFFFF)) / 65536.0f;
  734. Z = ((float) (rand() & 0xFFFF)) / 65536.0f;
  735. W = ((float) (rand() & 0xFFFF)) / 65536.0f;
  736. Normalize();
  737. }