hermitespline.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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/hermitespline.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 6/13/01 2:18p $*
  29. * *
  30. * $Revision:: 11 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "hermitespline.h"
  36. #include "wwmathids.h"
  37. #include "persistfactory.h"
  38. #include "wwhack.h"
  39. /*
  40. ** Force-Link this module because the linker can't detect that we actually need it...
  41. */
  42. DECLARE_FORCE_LINK(hermitespline);
  43. /*
  44. ** Save-Load stuff
  45. */
  46. SimplePersistFactoryClass<HermiteSpline3DClass,WWMATH_CHUNKID_HERMITESPLINE3D> _HermiteSpline3DFactory;
  47. SimplePersistFactoryClass<HermiteSpline1DClass,WWMATH_CHUNKID_HERMITESPLINE1D> _HermiteSpline1DFactory;
  48. enum
  49. {
  50. // ID's used by HermiteSpline3D
  51. HERMITE3D_CHUNK_CURVE3D = 0x00020727,
  52. HERMITE3D_CHUNK_TANGENTS,
  53. // ID's used by HermiteSpline1D
  54. HERMITE1D_CHUNK_CURVE1D = 0x00020729,
  55. HERMITE1D_CHUNK_TANGENTS,
  56. };
  57. /*
  58. ** Hermite Spline
  59. */
  60. const HermiteSpline3DClass &HermiteSpline3DClass::operator= (const HermiteSpline3DClass &that)
  61. {
  62. //
  63. // This is included for completeness only, it basically
  64. // implements the default bitwise copy operator.
  65. //
  66. TangentsDirty = that.TangentsDirty;
  67. Tangents = that.Tangents;
  68. Curve3DClass::operator= (that);
  69. return (*this);
  70. }
  71. void HermiteSpline3DClass::Set_Looping(bool onoff)
  72. {
  73. if (onoff != IsLooping) {
  74. Curve3DClass::Set_Looping(onoff);
  75. TangentsDirty = true;
  76. }
  77. }
  78. void HermiteSpline3DClass::Evaluate(float time,Vector3 * set_val)
  79. {
  80. // if we're outside the range, return the start or end...
  81. if (time < Keys[0].Time) {
  82. *set_val = Keys[0].Point;
  83. return;
  84. }
  85. if (time > Keys[Keys.Count() - 1].Time) {
  86. *set_val = Keys[Keys.Count() - 1].Point;
  87. return;
  88. }
  89. // if the tangents are marked dirty, give derived classes a chance to recompute them
  90. if (TangentsDirty) {
  91. Update_Tangents();
  92. }
  93. // ok find the segment
  94. int i0,i1;
  95. float t;
  96. Find_Interval(time,&i0,&i1,&t);
  97. float t2 = t*t;
  98. float t3 = t2*t;
  99. // hermite basis functions:
  100. float h0 = 2*t3 - 3*t2 + 1;
  101. float h1 = -2*t3 + 3*t2;
  102. float h2 = t3 - 2*t2 + t;
  103. float h3 = t3 - t2;
  104. set_val->X = h0*Keys[i0].Point.X + h1*Keys[i1].Point.X +
  105. h2*Tangents[i0].OutTangent.X + h3*Tangents[i1].InTangent.X;
  106. set_val->Y = h0*Keys[i0].Point.Y + h1*Keys[i1].Point.Y +
  107. h2*Tangents[i0].OutTangent.Y + h3*Tangents[i1].InTangent.Y;
  108. set_val->Z = h0*Keys[i0].Point.Z + h1*Keys[i1].Point.Z +
  109. h2*Tangents[i0].OutTangent.Z + h3*Tangents[i1].InTangent.Z;
  110. }
  111. void HermiteSpline3DClass::Evaluate_Derivative(float time,Vector3 * set_val)
  112. {
  113. // if we're outside the range, return the value for the start or end...
  114. float min_time = Keys[0].Time;
  115. float max_time = Keys[Keys.Count() - 1].Time;
  116. time = MAX(time, min_time);
  117. time = MIN(time, max_time);
  118. // if the tangents are marked dirty, give derived classes a chance to recompute them
  119. if (TangentsDirty) {
  120. Update_Tangents();
  121. }
  122. // ok find the segment
  123. int i0,i1;
  124. float t;
  125. Find_Interval(time,&i0,&i1,&t);
  126. float t2 = t*t;
  127. // derivatives of hermite basis functions:
  128. float dh0 = 6*t2 - 6*t;
  129. float dh1 = -6*t2 + 6*t;
  130. float dh2 = 3*t2 - 4*t + 1;
  131. float dh3 = 3*t2 - 2*t;
  132. set_val->X = dh0*Keys[i0].Point.X + dh1*Keys[i1].Point.X +
  133. dh2*Tangents[i0].OutTangent.X + dh3*Tangents[i1].InTangent.X;
  134. set_val->Y = dh0*Keys[i0].Point.Y + dh1*Keys[i1].Point.Y +
  135. dh2*Tangents[i0].OutTangent.Y + dh3*Tangents[i1].InTangent.Y;
  136. set_val->Z = dh0*Keys[i0].Point.Z + dh1*Keys[i1].Point.Z +
  137. dh2*Tangents[i0].OutTangent.Z + dh3*Tangents[i1].InTangent.Z;
  138. }
  139. void HermiteSpline3DClass::Set_Key(int i,const Vector3 & point)
  140. {
  141. Curve3DClass::Set_Key(i,point);
  142. TangentsDirty = true;
  143. }
  144. int HermiteSpline3DClass::Add_Key(const Vector3 & point,float t)
  145. {
  146. int index = Curve3DClass::Add_Key(point,t);
  147. TangentsDirty = true;
  148. TangentsClass tan;
  149. tan.InTangent.Set(0,0,0);
  150. tan.OutTangent.Set(0,0,0);
  151. Tangents.Insert(index,tan);
  152. return index;
  153. }
  154. void HermiteSpline3DClass::Remove_Key(int i)
  155. {
  156. Tangents.Delete(i);
  157. Curve3DClass::Remove_Key(i);
  158. TangentsDirty = true;
  159. }
  160. void HermiteSpline3DClass::Clear_Keys(void)
  161. {
  162. Tangents.Clear();
  163. Curve3DClass::Clear_Keys();
  164. TangentsDirty = true;
  165. }
  166. void HermiteSpline3DClass::Set_Tangents(int i,const Vector3 & in_tan,const Vector3 & out_tan)
  167. {
  168. assert(i>=0);
  169. assert(i<Keys.Count());
  170. Tangents[i].InTangent = in_tan;
  171. Tangents[i].OutTangent = out_tan;
  172. }
  173. void HermiteSpline3DClass::Get_Tangents(int i,Vector3 * set_in,Vector3 * set_out)
  174. {
  175. assert(i>=0);
  176. assert(i<Keys.Count());
  177. *set_in = Tangents[i].InTangent;
  178. *set_out = Tangents[i].OutTangent;
  179. }
  180. const PersistFactoryClass & HermiteSpline3DClass::Get_Factory(void) const
  181. {
  182. return _HermiteSpline3DFactory;
  183. }
  184. bool HermiteSpline3DClass::Save(ChunkSaveClass &csave)
  185. {
  186. csave.Begin_Chunk(HERMITE3D_CHUNK_CURVE3D);
  187. Curve3DClass::Save(csave);
  188. csave.End_Chunk();
  189. csave.Begin_Chunk(HERMITE3D_CHUNK_TANGENTS);
  190. for (int i=0; i<Tangents.Count(); i++) {
  191. csave.Write(&(Tangents[i].InTangent),sizeof(Tangents[i].InTangent));
  192. csave.Write(&(Tangents[i].OutTangent),sizeof(Tangents[i].OutTangent));
  193. }
  194. csave.End_Chunk();
  195. return true;
  196. }
  197. bool HermiteSpline3DClass::Load(ChunkLoadClass &cload)
  198. {
  199. int i;
  200. TangentsClass newtangent;
  201. // reset the array of tangents
  202. Tangents.Delete_All();
  203. // read in the chunks
  204. while (cload.Open_Chunk()) {
  205. switch(cload.Cur_Chunk_ID())
  206. {
  207. case HERMITE3D_CHUNK_CURVE3D:
  208. Curve3DClass::Load(cload);
  209. break;
  210. case HERMITE3D_CHUNK_TANGENTS:
  211. for (i=0; i<Keys.Count(); i++) {
  212. cload.Read(&(newtangent.InTangent),sizeof(newtangent.InTangent));
  213. cload.Read(&(newtangent.OutTangent),sizeof(newtangent.OutTangent));
  214. Tangents.Add(newtangent);
  215. }
  216. break;
  217. default:
  218. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  219. break;
  220. }
  221. cload.Close_Chunk();
  222. }
  223. WWASSERT(Keys.Count() == Tangents.Count());
  224. return true;
  225. }
  226. /*
  227. ** 1-Dimensional Hermite Spline
  228. */
  229. void HermiteSpline1DClass::Set_Looping(bool onoff)
  230. {
  231. if (onoff != IsLooping) {
  232. Curve1DClass::Set_Looping(onoff);
  233. TangentsDirty = true;
  234. }
  235. }
  236. void HermiteSpline1DClass::Evaluate(float time,float * set_val)
  237. {
  238. if (Keys.Count() == 1)
  239. {
  240. *set_val = Keys[0].Point;
  241. return;
  242. }
  243. if (!IsLooping)
  244. {
  245. // if we're outside the range, return the start or end...
  246. if (time < Keys[0].Time) {
  247. *set_val = Keys[0].Point;
  248. return;
  249. }
  250. if (time > Keys[Keys.Count() - 1].Time) {
  251. *set_val = Keys[Keys.Count() - 1].Point;
  252. return;
  253. }
  254. }
  255. // if the tangents are marked dirty, give derived classes a chance to recompute them
  256. if (TangentsDirty) {
  257. Update_Tangents();
  258. }
  259. // ok find the segment
  260. int i0,i1;
  261. float t;
  262. Find_Interval(time,&i0,&i1,&t);
  263. float t2 = t*t;
  264. float t3 = t2*t;
  265. // hermite basis functions:
  266. float h0 = 2*t3 - 3*t2 + 1;
  267. float h1 = -2*t3 + 3*t2;
  268. float h2 = t3 - 2*t2 + t;
  269. float h3 = t3 - t2;
  270. *set_val = h0*Keys[i0].Point + h1*Keys[i1].Point +
  271. h2*Tangents[i0].OutTangent + h3*Tangents[i1].InTangent;
  272. }
  273. void HermiteSpline1DClass::Set_Key(int i,float point,unsigned int extra)
  274. {
  275. Curve1DClass::Set_Key(i,point,extra);
  276. TangentsDirty = true;
  277. }
  278. int HermiteSpline1DClass::Add_Key(float point,float t,unsigned int extra)
  279. {
  280. int index = Curve1DClass::Add_Key(point,t,extra);
  281. TangentsDirty = true;
  282. TangentsClass tan;
  283. tan.InTangent = 0.0f;
  284. tan.OutTangent = 0.0f;
  285. Tangents.Insert(index,tan);
  286. return index;
  287. }
  288. void HermiteSpline1DClass::Remove_Key(int i)
  289. {
  290. Tangents.Delete(i);
  291. Curve1DClass::Remove_Key(i);
  292. TangentsDirty = true;
  293. }
  294. void HermiteSpline1DClass::Clear_Keys(void)
  295. {
  296. Tangents.Clear();
  297. Curve1DClass::Clear_Keys();
  298. TangentsDirty = true;
  299. }
  300. void HermiteSpline1DClass::Set_Tangents(int i,float in_tan,float out_tan)
  301. {
  302. assert(i>=0);
  303. assert(i<Keys.Count());
  304. Tangents[i].InTangent = in_tan;
  305. Tangents[i].OutTangent = out_tan;
  306. }
  307. void HermiteSpline1DClass::Get_Tangents(int i,float * set_in,float * set_out)
  308. {
  309. assert(i>=0);
  310. assert(i<Keys.Count());
  311. *set_in = Tangents[i].InTangent;
  312. *set_out = Tangents[i].OutTangent;
  313. }
  314. const PersistFactoryClass & HermiteSpline1DClass::Get_Factory(void) const
  315. {
  316. return _HermiteSpline1DFactory;
  317. }
  318. bool HermiteSpline1DClass::Save(ChunkSaveClass &csave)
  319. {
  320. if (TangentsDirty) {
  321. Update_Tangents();
  322. }
  323. csave.Begin_Chunk(HERMITE1D_CHUNK_CURVE1D);
  324. Curve1DClass::Save(csave);
  325. csave.End_Chunk();
  326. csave.Begin_Chunk(HERMITE1D_CHUNK_TANGENTS);
  327. for (int i=0; i<Tangents.Count(); i++) {
  328. csave.Write(&(Tangents[i].InTangent),sizeof(Tangents[i].InTangent));
  329. csave.Write(&(Tangents[i].OutTangent),sizeof(Tangents[i].OutTangent));
  330. }
  331. csave.End_Chunk();
  332. return true;
  333. }
  334. bool HermiteSpline1DClass::Load(ChunkLoadClass &cload)
  335. {
  336. int i;
  337. TangentsClass newtangent;
  338. // reset the tangents array
  339. Tangents.Delete_All();
  340. // read in the chunks
  341. while (cload.Open_Chunk()) {
  342. switch(cload.Cur_Chunk_ID())
  343. {
  344. case HERMITE1D_CHUNK_CURVE1D:
  345. Curve1DClass::Load(cload);
  346. break;
  347. case HERMITE1D_CHUNK_TANGENTS:
  348. for (i=0; i<Keys.Count(); i++) {
  349. cload.Read(&(newtangent.InTangent),sizeof(newtangent.InTangent));
  350. cload.Read(&(newtangent.OutTangent),sizeof(newtangent.OutTangent));
  351. Tangents.Add(newtangent);
  352. }
  353. TangentsDirty = false;
  354. break;
  355. default:
  356. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
  357. break;
  358. }
  359. cload.Close_Chunk();
  360. }
  361. WWASSERT(Keys.Count() == Tangents.Count());
  362. return true;
  363. }