curve.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. ** Command & Conquer Renegade(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:: /VSS_Sync/wwmath/curve.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Vss_sync $*
  29. * *
  30. * $Modtime:: 6/13/01 2:18p $*
  31. * *
  32. * $Revision:: 9 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "curve.h"
  38. #include "wwdebug.h"
  39. #include "persistfactory.h"
  40. #include "wwmathids.h"
  41. #include "wwhack.h"
  42. /*
  43. ** Force-Link this module because the linker can't detect that we actually need it...
  44. */
  45. DECLARE_FORCE_LINK(curve);
  46. /*
  47. ** Persist factories and chunk-id's used to save and load.
  48. */
  49. SimplePersistFactoryClass<LinearCurve3DClass,WWMATH_CHUNKID_LINEARCURVE3D> _LinearCurve3DFactory;
  50. SimplePersistFactoryClass<LinearCurve1DClass,WWMATH_CHUNKID_LINEARCURVE1D> _LinearCurve1DFactory;
  51. enum
  52. {
  53. // ID's used by Curve3D
  54. CURVE3D_CHUNK_VARIABLES = 0x00020651,
  55. CURVE3D_CHUNK_KEYS,
  56. CURVE3D_VARIABLE_ISLOOPING = 0x00,
  57. CURVE3D_VARIABLE_KEYCOUNT,
  58. // ID's used by LinearCurve3D
  59. LINEARCURVE3D_CHUNK_CURVE3D = 0x00020653,
  60. // ID's used by Curve1D
  61. CURVE1D_CHUNK_VARIABLES = 0x00020655,
  62. CURVE1D_CHUNK_KEYS,
  63. CURVE1D_VARIABLE_ISLOOPING = 0x00,
  64. CURVE1D_VARIABLE_KEYCOUNT,
  65. // ID's used by LinearCurve1D
  66. LINEARCURVE1D_CHUNK_CURVE1D = 0x00020657,
  67. };
  68. /***********************************************************************************************
  69. **
  70. ** Curve3DCLass Implementation
  71. **
  72. ***********************************************************************************************/
  73. Curve3DClass::Curve3DClass(void) :
  74. IsLooping(false)
  75. {
  76. }
  77. Curve3DClass::Curve3DClass(const Curve3DClass & that)
  78. {
  79. *this = that;
  80. }
  81. Curve3DClass::~Curve3DClass(void)
  82. {
  83. }
  84. Curve3DClass & Curve3DClass::operator = (const Curve3DClass & that)
  85. {
  86. IsLooping = that.IsLooping;
  87. Keys = that.Keys;
  88. return *this;
  89. }
  90. bool Curve3DClass::Is_Looping(void)
  91. {
  92. return IsLooping;
  93. }
  94. void Curve3DClass::Set_Looping(bool onoff)
  95. {
  96. IsLooping = onoff;
  97. }
  98. float Curve3DClass::Get_Start_Time(void)
  99. {
  100. if (Keys.Count() > 0) {
  101. return Keys[0].Time;
  102. } else {
  103. return 0.0f;
  104. }
  105. }
  106. float Curve3DClass::Get_End_Time(void)
  107. {
  108. if (Keys.Count() > 0) {
  109. return Keys[Keys.Count() - 1].Time;
  110. } else {
  111. return 0.0f;
  112. }
  113. }
  114. int Curve3DClass::Key_Count(void)
  115. {
  116. return Keys.Count();
  117. }
  118. void Curve3DClass::Get_Key(int i,Vector3 * set_point,float * set_t)
  119. {
  120. assert(i >= 0);
  121. assert(i < Keys.Count());
  122. if (set_point != NULL) {
  123. *set_point = Keys[i].Point;
  124. }
  125. if (set_t != NULL) {
  126. *set_t = Keys[i].Time;
  127. }
  128. }
  129. void Curve3DClass::Set_Key(int i,const Vector3 & point)
  130. {
  131. assert(i >= 0);
  132. assert(i < Keys.Count());
  133. Keys[i].Point = point;
  134. }
  135. int Curve3DClass::Add_Key(const Vector3 & point,float t)
  136. {
  137. int idx = 0;
  138. while (idx < Keys.Count() && Keys[idx].Time < t) {
  139. idx++;
  140. }
  141. KeyClass newkey;
  142. newkey.Point = point;
  143. newkey.Time = t;
  144. Keys.Insert(idx,newkey);
  145. return idx;
  146. }
  147. void Curve3DClass::Remove_Key(int i)
  148. {
  149. assert(i >= 0);
  150. assert(i < Keys.Count());
  151. Keys.Delete(i);
  152. }
  153. void Curve3DClass::Clear_Keys(void)
  154. {
  155. Keys.Clear();
  156. }
  157. void Curve3DClass::Find_Interval(float time,int * i0,int * i1,float * t)
  158. {
  159. WWASSERT(time >= Keys[0].Time);
  160. WWASSERT(time <= Keys[Keys.Count()-1].Time);
  161. int i=0;
  162. while (time > Keys[i+1].Time) {
  163. i++;
  164. }
  165. *i0 = i;
  166. *i1 = i+1;
  167. *t = (time - Keys[i].Time) / (Keys[i+1].Time - Keys[i].Time);
  168. }
  169. bool Curve3DClass::Save(ChunkSaveClass & csave)
  170. {
  171. int keycount = Keys.Count();
  172. csave.Begin_Chunk(CURVE3D_CHUNK_VARIABLES);
  173. WRITE_MICRO_CHUNK(csave,CURVE3D_VARIABLE_ISLOOPING,IsLooping);
  174. WRITE_MICRO_CHUNK(csave,CURVE3D_VARIABLE_KEYCOUNT,keycount);
  175. csave.End_Chunk();
  176. // Saving the keys, Note that if the format of a key changes we'll
  177. // need a new chunk. (I didn't wrap each variable in its own chunk)
  178. csave.Begin_Chunk(CURVE3D_CHUNK_KEYS);
  179. for (int i=0; i<keycount; i++) {
  180. csave.Write(&(Keys[i].Point),sizeof(Keys[i].Point));
  181. csave.Write(&(Keys[i].Time),sizeof(Keys[i].Time));
  182. }
  183. csave.End_Chunk();
  184. return true;
  185. }
  186. bool Curve3DClass::Load(ChunkLoadClass & cload)
  187. {
  188. int i;
  189. int keycount = 0;
  190. KeyClass newkey;
  191. // reset the curve
  192. Keys.Delete_All();
  193. // read in the chunks
  194. while (cload.Open_Chunk()) {
  195. switch(cload.Cur_Chunk_ID())
  196. {
  197. case CURVE3D_CHUNK_VARIABLES:
  198. while (cload.Open_Micro_Chunk()) {
  199. switch(cload.Cur_Micro_Chunk_ID()) {
  200. READ_MICRO_CHUNK(cload,CURVE3D_VARIABLE_ISLOOPING,IsLooping);
  201. READ_MICRO_CHUNK(cload,CURVE3D_VARIABLE_KEYCOUNT,keycount);
  202. }
  203. cload.Close_Micro_Chunk();
  204. }
  205. break;
  206. case CURVE3D_CHUNK_KEYS:
  207. for (i=0; i<keycount; i++) {
  208. cload.Read(&(newkey.Point),sizeof(newkey.Point));
  209. cload.Read(&(newkey.Time),sizeof(newkey.Time));
  210. Keys.Add(newkey);
  211. }
  212. break;
  213. default:
  214. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  215. break;
  216. }
  217. cload.Close_Chunk();
  218. }
  219. return true;
  220. }
  221. /***********************************************************************************************
  222. **
  223. ** LinearCurve3DClass Implementation
  224. ** Linear curve, linearly interpolates the keys
  225. **
  226. ***********************************************************************************************/
  227. void LinearCurve3DClass::Evaluate(float time,Vector3 * set_val)
  228. {
  229. if (time < Keys[0].Time) {
  230. *set_val = Keys[0].Point;
  231. return;
  232. }
  233. if (time >= Keys[Keys.Count() - 1].Time) {
  234. *set_val = Keys[Keys.Count() - 1].Point;
  235. return;
  236. }
  237. int i0,i1;
  238. float t;
  239. Find_Interval(time,&i0,&i1,&t);
  240. *set_val = Keys[i0].Point + t * (Keys[i1].Point - Keys[i0].Point);
  241. }
  242. const PersistFactoryClass & LinearCurve3DClass::Get_Factory(void) const
  243. {
  244. return _LinearCurve3DFactory;
  245. }
  246. bool LinearCurve3DClass::Save(ChunkSaveClass & csave)
  247. {
  248. csave.Begin_Chunk(LINEARCURVE3D_CHUNK_CURVE3D);
  249. Curve3DClass::Save(csave);
  250. csave.End_Chunk();
  251. return true;
  252. }
  253. bool LinearCurve3DClass::Load(ChunkLoadClass & cload)
  254. {
  255. while (cload.Open_Chunk()) {
  256. switch(cload.Cur_Chunk_ID())
  257. {
  258. case LINEARCURVE3D_CHUNK_CURVE3D:
  259. Curve3DClass::Load(cload);
  260. break;
  261. default:
  262. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  263. break;
  264. }
  265. cload.Close_Chunk();
  266. }
  267. return true;
  268. }
  269. /***********************************************************************************************
  270. **
  271. ** Curve1DClass
  272. **
  273. ***********************************************************************************************/
  274. Curve1DClass::Curve1DClass(void) :
  275. IsLooping(false)
  276. {
  277. }
  278. Curve1DClass::Curve1DClass(const Curve1DClass & that)
  279. {
  280. *this = that;
  281. }
  282. Curve1DClass::~Curve1DClass(void)
  283. {
  284. }
  285. Curve1DClass & Curve1DClass::operator = (const Curve1DClass & that)
  286. {
  287. IsLooping = that.IsLooping;
  288. Keys = that.Keys;
  289. return *this;
  290. }
  291. bool Curve1DClass::Is_Looping(void)
  292. {
  293. return IsLooping;
  294. }
  295. void Curve1DClass::Set_Looping(bool onoff)
  296. {
  297. IsLooping = onoff;
  298. }
  299. float Curve1DClass::Get_Start_Time(void)
  300. {
  301. if (Keys.Count() > 0) {
  302. return Keys[0].Time;
  303. } else {
  304. return 0.0f;
  305. }
  306. }
  307. float Curve1DClass::Get_End_Time(void)
  308. {
  309. if (Keys.Count() > 0) {
  310. return Keys[Keys.Count() - 1].Time;
  311. } else {
  312. return 0.0f;
  313. }
  314. }
  315. int Curve1DClass::Key_Count(void)
  316. {
  317. return Keys.Count();
  318. }
  319. void Curve1DClass::Get_Key(int i,float * set_point,float * set_t,unsigned int * extra)
  320. {
  321. assert(i >= 0);
  322. assert(i < Keys.Count());
  323. if (set_point != NULL) {
  324. *set_point = Keys[i].Point;
  325. }
  326. if (set_t != NULL) {
  327. *set_t = Keys[i].Time;
  328. }
  329. if (extra != NULL) {
  330. *extra = Keys[i].Extra;
  331. }
  332. }
  333. void Curve1DClass::Set_Key(int i,float point,unsigned int extra)
  334. {
  335. assert(i >= 0);
  336. assert(i < Keys.Count());
  337. Keys[i].Point = point;
  338. Keys[i].Extra = extra;
  339. }
  340. int Curve1DClass::Add_Key(float point,float t,unsigned int extra)
  341. {
  342. int idx = 0;
  343. while (idx < Keys.Count() && Keys[idx].Time < t) {
  344. idx++;
  345. }
  346. KeyClass newkey;
  347. newkey.Point = point;
  348. newkey.Time = t;
  349. newkey.Extra = extra;
  350. Keys.Insert(idx,newkey);
  351. return idx;
  352. }
  353. void Curve1DClass::Remove_Key(int i)
  354. {
  355. assert(i >= 0);
  356. assert(i < Keys.Count());
  357. Keys.Delete(i);
  358. }
  359. void Curve1DClass::Clear_Keys(void)
  360. {
  361. Keys.Clear();
  362. }
  363. void Curve1DClass::Find_Interval(float time,int * i0,int * i1,float * t)
  364. {
  365. if (IsLooping) {
  366. if (time < Keys[0].Time) {
  367. *i0 = Keys.Count() - 1;
  368. *i1 = 0;
  369. float interval = 1.0f - Keys[*i0].Time + Keys[*i1].Time;
  370. *t = (1.0f - Keys[*i0].Time + time) / interval;
  371. return;
  372. }
  373. else if (time > Keys[Keys.Count() - 1].Time) {
  374. *i0 = Keys.Count() - 1;
  375. *i1 = 0;
  376. float interval = 1.0f - Keys[*i0].Time + Keys[*i1].Time;
  377. *t = (time - Keys[*i0].Time) / interval;
  378. return;
  379. }
  380. }
  381. else {
  382. WWASSERT(time >= Keys[0].Time);
  383. WWASSERT(time <= Keys[Keys.Count()-1].Time);
  384. }
  385. int i=0;
  386. while (time > Keys[i+1].Time) {
  387. i++;
  388. }
  389. *i0 = i;
  390. *i1 = i+1;
  391. *t = (time - Keys[i].Time) / (Keys[i+1].Time - Keys[i].Time);
  392. }
  393. bool Curve1DClass::Save(ChunkSaveClass & csave)
  394. {
  395. int keycount = Keys.Count();
  396. csave.Begin_Chunk(CURVE1D_CHUNK_VARIABLES);
  397. WRITE_MICRO_CHUNK(csave,CURVE1D_VARIABLE_ISLOOPING,IsLooping);
  398. WRITE_MICRO_CHUNK(csave,CURVE1D_VARIABLE_KEYCOUNT,keycount);
  399. csave.End_Chunk();
  400. // Saving the keys, Note that if the format of a key changes we'll
  401. // need a new chunk. (I didn't wrap each variable in its own chunk)
  402. csave.Begin_Chunk(CURVE1D_CHUNK_KEYS);
  403. for (int i=0; i<keycount; i++) {
  404. csave.Write(&(Keys[i].Point),sizeof(Keys[i].Point));
  405. csave.Write(&(Keys[i].Time),sizeof(Keys[i].Time));
  406. csave.Write(&(Keys[i].Extra),sizeof(Keys[i].Extra));
  407. }
  408. csave.End_Chunk();
  409. return true;
  410. }
  411. bool Curve1DClass::Load(ChunkLoadClass & cload)
  412. {
  413. int i;
  414. int keycount = 0;
  415. KeyClass newkey;
  416. // reset the curve
  417. Keys.Delete_All();
  418. // read in the chunks
  419. while (cload.Open_Chunk()) {
  420. switch(cload.Cur_Chunk_ID())
  421. {
  422. case CURVE1D_CHUNK_VARIABLES:
  423. while (cload.Open_Micro_Chunk()) {
  424. switch(cload.Cur_Micro_Chunk_ID()) {
  425. READ_MICRO_CHUNK(cload,CURVE1D_VARIABLE_ISLOOPING,IsLooping);
  426. READ_MICRO_CHUNK(cload,CURVE1D_VARIABLE_KEYCOUNT,keycount);
  427. }
  428. cload.Close_Micro_Chunk();
  429. }
  430. break;
  431. case CURVE1D_CHUNK_KEYS:
  432. for (i=0; i<keycount; i++) {
  433. cload.Read(&(newkey.Point),sizeof(newkey.Point));
  434. cload.Read(&(newkey.Time),sizeof(newkey.Time));
  435. cload.Read(&(newkey.Extra),sizeof(newkey.Extra));
  436. Keys.Add(newkey);
  437. }
  438. break;
  439. default:
  440. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  441. break;
  442. }
  443. cload.Close_Chunk();
  444. }
  445. return true;
  446. }
  447. /***********************************************************************************************
  448. **
  449. ** LinearCurve1DClass, linearly interpolates the keys
  450. **
  451. ***********************************************************************************************/
  452. void LinearCurve1DClass::Evaluate(float time,float * set_val)
  453. {
  454. if (!IsLooping) {
  455. if (time < Keys[0].Time) {
  456. *set_val = Keys[0].Point;
  457. return;
  458. }
  459. if (time >= Keys[Keys.Count() - 1].Time) {
  460. *set_val = Keys[Keys.Count() - 1].Point;
  461. return;
  462. }
  463. }
  464. int i0,i1;
  465. float t;
  466. Find_Interval(time,&i0,&i1,&t);
  467. *set_val = Keys[i0].Point + t * (Keys[i1].Point - Keys[i0].Point);
  468. }
  469. const PersistFactoryClass & LinearCurve1DClass::Get_Factory(void) const
  470. {
  471. return _LinearCurve1DFactory;
  472. }
  473. bool LinearCurve1DClass::Save(ChunkSaveClass & csave)
  474. {
  475. csave.Begin_Chunk(LINEARCURVE1D_CHUNK_CURVE1D);
  476. Curve1DClass::Save(csave);
  477. csave.End_Chunk();
  478. return true;
  479. }
  480. bool LinearCurve1DClass::Load(ChunkLoadClass & cload)
  481. {
  482. while (cload.Open_Chunk()) {
  483. switch(cload.Cur_Chunk_ID())
  484. {
  485. case LINEARCURVE1D_CHUNK_CURVE1D:
  486. Curve1DClass::Load(cload);
  487. break;
  488. default:
  489. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  490. break;
  491. }
  492. cload.Close_Chunk();
  493. }
  494. return true;
  495. }