ISplines.h 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #ifndef CRYINCLUDE_CRYCOMMON_ISPLINES_H
  9. #define CRYINCLUDE_CRYCOMMON_ISPLINES_H
  10. #pragma once
  11. #include <IXml.h>
  12. #include <AzCore/std/containers/vector.h>
  13. //////////////////////////////////////////////////////////////////////////
  14. namespace AZ
  15. {
  16. class ReflectContext;
  17. }
  18. // These flags are mostly applicable for hermit based splines.
  19. enum ESplineKeyTangentType
  20. {
  21. SPLINE_KEY_TANGENT_NONE = 0,
  22. SPLINE_KEY_TANGENT_CUSTOM = 1,
  23. SPLINE_KEY_TANGENT_ZERO = 2,
  24. SPLINE_KEY_TANGENT_STEP = 3,
  25. SPLINE_KEY_TANGENT_LINEAR = 4,
  26. SPLINE_KEY_TANGENT_BEZIER = 5
  27. };
  28. #define SPLINE_KEY_TANGENT_IN_SHIFT (0)
  29. #define SPLINE_KEY_TANGENT_IN_MASK (0x07) // 0000111
  30. #define SPLINE_KEY_TANGENT_OUT_SHIFT (3)
  31. #define SPLINE_KEY_TANGENT_OUT_MASK (0x07 << (SPLINE_KEY_TANGENT_OUT_SHIFT)) // 0111000
  32. #define SPLINE_KEY_TANGENT_UNIFY_SHIFT (6)
  33. #define SPLINE_KEY_TANGENT_UNIFY_MASK (0x01 << (SPLINE_KEY_TANGENT_UNIFY_SHIFT)) // 1000000
  34. #define SPLINE_KEY_TANGENT_ALL_MASK (SPLINE_KEY_TANGENT_IN_MASK | SPLINE_KEY_TANGENT_OUT_MASK | SPLINE_KEY_TANGENT_UNIFY_MASK)
  35. #define SPLINE_KEY_TANGENT_UNIFIED ((SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_IN_SHIFT) \
  36. | (SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_OUT_SHIFT) \
  37. | (0x01 << SPLINE_KEY_TANGENT_UNIFY_SHIFT))
  38. #define SPLINE_KEY_TANGENT_BROKEN ((SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_IN_SHIFT) \
  39. | (SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_OUT_SHIFT) \
  40. | (0x00 << SPLINE_KEY_TANGENT_UNIFY_SHIFT))
  41. enum ESplineKeyFlags
  42. {
  43. ESPLINE_KEY_UI_SELECTED_SHIFT = 16,
  44. ESPLINE_KEY_UI_SELECTED_MAX_DIMENSION_COUNT = 4, // should be power of 2 (see ESPLINE_KEY_UI_SELECTED_MASK)
  45. ESPLINE_KEY_UI_SELECTED_MASK = ((1 << ESPLINE_KEY_UI_SELECTED_MAX_DIMENSION_COUNT) - 1) << ESPLINE_KEY_UI_SELECTED_SHIFT
  46. };
  47. // Return value closest to 0 if same sign, or 0 if opposite.
  48. template<class T>
  49. inline T minmag(T const& a, T const& b)
  50. {
  51. if (a * b <= T(0.f))
  52. {
  53. return T(0.f);
  54. }
  55. else if (a < T(0.f))
  56. {
  57. return max(a, b);
  58. }
  59. else
  60. {
  61. return min(a, b);
  62. }
  63. }
  64. template<class T>
  65. inline Vec3_tpl<T> minmag(Vec3_tpl<T> const& a, Vec3_tpl<T> const& b)
  66. {
  67. return Vec3_tpl<T>(minmag(a.x, b.x), minmag(a.y, b.y), minmag(a.z, b.z));
  68. }
  69. template<class T>
  70. T abs(Vec3_tpl<T> v)
  71. {
  72. return v.GetLength();
  73. }
  74. //////////////////////////////////////////////////////////////////////////
  75. // Interface returned by backup methods of ISplineInterpolator.
  76. //////////////////////////////////////////////////////////////////////////
  77. struct ISplineBackup
  78. {
  79. // <interfuscator:shuffle>
  80. virtual ~ISplineBackup(){}
  81. virtual void AddRef() = 0;
  82. virtual void Release() = 0;
  83. // </interfuscator:shuffle>
  84. };
  85. //////////////////////////////////////////////////////////////////////////
  86. // General Interpolation interface.
  87. //////////////////////////////////////////////////////////////////////////
  88. struct ISplineInterpolator
  89. {
  90. typedef float ElemType;
  91. typedef ElemType ValueType[4];
  92. // <interfuscator:shuffle>
  93. virtual ~ISplineInterpolator(){}
  94. // Dimension of the spline from 0 to 3, number of parameters used in ValueType.
  95. virtual int GetNumDimensions() = 0;
  96. // Insert`s a new key, returns index of the key.
  97. virtual int InsertKey(float time, ValueType value) = 0;
  98. virtual void RemoveKey(int key) = 0;
  99. virtual void FindKeysInRange(float startTime, float endTime, int& firstFoundKey, int& numFoundKeys) = 0;
  100. virtual void RemoveKeysInRange(float startTime, float endTime) = 0;
  101. virtual int GetKeyCount() = 0;
  102. virtual void SetKeyTime(int key, float time) = 0;
  103. virtual float GetKeyTime(int key) = 0;
  104. virtual void SetKeyValue(int key, ValueType value) = 0;
  105. virtual bool GetKeyValue(int key, ValueType& value) = 0;
  106. virtual void SetKeyInTangent(int key, ValueType tin) = 0;
  107. virtual void SetKeyOutTangent(int key, ValueType tout) = 0;
  108. virtual void SetKeyTangents(int key, ValueType tin, ValueType tout) = 0;
  109. virtual bool GetKeyTangents(int key, ValueType& tin, ValueType& tout) = 0;
  110. // Changes key flags, @see ESplineKeyFlags
  111. virtual void SetKeyFlags(int key, int flags) = 0;
  112. // Retrieve key flags, @see ESplineKeyFlags
  113. virtual int GetKeyFlags(int key) = 0;
  114. virtual void Interpolate(float time, ValueType& value) = 0;
  115. virtual void EvalInTangent([[maybe_unused]] float time, [[maybe_unused]] ValueType& value) {};
  116. virtual void EvalOutTangent([[maybe_unused]] float time, [[maybe_unused]] ValueType& value) {};
  117. virtual void SerializeSpline(XmlNodeRef& node, bool bLoading) = 0;
  118. virtual ISplineBackup* Backup() = 0;
  119. virtual void Restore(ISplineBackup* pBackup) = 0;
  120. void ClearAllKeys()
  121. {
  122. while (GetKeyCount() > 0)
  123. {
  124. RemoveKey(0);
  125. }
  126. Update();
  127. }
  128. // </interfuscator:shuffle>
  129. //////////////////////////////////////////////////////////////////////////
  130. // Helper functions.
  131. //////////////////////////////////////////////////////////////////////////
  132. inline bool IsKeySelectedAtAnyDimension(const int key)
  133. {
  134. const int flags = GetKeyFlags(key);
  135. const int dimensionCount = GetNumDimensions();
  136. const int mask = ((1 << dimensionCount) - 1) << ESPLINE_KEY_UI_SELECTED_SHIFT;
  137. return (flags & mask) != 0;
  138. }
  139. inline bool IsKeySelectedAtDimension(const int key, const int dimension)
  140. {
  141. const int flags = GetKeyFlags(key);
  142. const int mask = 1 << (ESPLINE_KEY_UI_SELECTED_SHIFT + dimension);
  143. return (flags & mask) != 0;
  144. }
  145. void SelectKeyAllDimensions(int key, bool select)
  146. {
  147. const int flags = GetKeyFlags(key);
  148. if (select)
  149. {
  150. const int dimensionCount = GetNumDimensions();
  151. const int mask = ((1 << dimensionCount) - 1) << ESPLINE_KEY_UI_SELECTED_SHIFT;
  152. SetKeyFlags(key, (flags & (~ESPLINE_KEY_UI_SELECTED_MASK)) | mask);
  153. }
  154. else
  155. {
  156. SetKeyFlags(key, flags & (~ESPLINE_KEY_UI_SELECTED_MASK));
  157. }
  158. }
  159. void SelectKeyAtDimension(int key, int dimension, bool select)
  160. {
  161. const int flags = GetKeyFlags(key);
  162. const int mask = 1 << (ESPLINE_KEY_UI_SELECTED_SHIFT + dimension);
  163. SetKeyFlags(key, (select ? (flags | mask) : (flags & (~mask))));
  164. }
  165. inline int InsertKeyFloat(float time, float val) { ValueType v = {val, 0, 0, 0}; return InsertKey(time, v); }
  166. inline int InsertKeyFloat3(float time, float* vals) { ValueType v = {vals[0], vals[1], vals[2], 0}; return InsertKey(time, v); }
  167. inline bool GetKeyValueFloat(int key, float& value) { ValueType v = {value}; bool b = GetKeyValue(key, v); value = v[0]; return b; }
  168. inline void SetKeyValueFloat(int key, float value) { ValueType v = {value, 0, 0, 0}; SetKeyValue(key, v); }
  169. inline void SetKeyValueFloat3(int key, float* vals) { ValueType v = {vals[0], vals[1], vals[2], 0}; SetKeyValue(key, v); }
  170. inline void InterpolateFloat(float time, float& val) { ValueType v = {val}; Interpolate(time, v); val = v[0]; }
  171. inline void InterpolateFloat3(float time, float* vals) { ValueType v = {vals[0], vals[1], vals[2]}; Interpolate(time, v); vals[0] = v[0]; vals[1] = v[1]; vals[2] = v[2]; }
  172. inline void EvalInTangentFloat(float time, float& val) { ValueType v = { val }; EvalInTangent(time, v); val = v[0]; }
  173. inline void EvalOutTangentFloat(float time, float& val) { ValueType v = { val }; EvalOutTangent(time, v); val = v[0]; }
  174. // Return Key closest to the specified time.
  175. inline int FindKey(float fTime, float fEpsilon = 0.01f)
  176. {
  177. int nKey = -1;
  178. // Find key.
  179. for (int k = 0; k < GetKeyCount(); k++)
  180. {
  181. if (fabs(GetKeyTime(k) - fTime) < fEpsilon)
  182. {
  183. nKey = k;
  184. break;
  185. }
  186. }
  187. return nKey;
  188. }
  189. // Force update.
  190. void Update() { ValueType val; Interpolate(0.f, val); }
  191. static void ZeroValue(ValueType& value) { value[0] = 0; value[1] = 0; value[2] = 0; value[3] = 0; }
  192. };
  193. //////////////////////////////////////////////////////////////////////////
  194. //
  195. //////////////////////////////////////////////////////////////////////////
  196. namespace spline
  197. {
  198. template <int N>
  199. class BasisFunction
  200. {
  201. public:
  202. const float& operator[](int i) const { return m_f[i]; };
  203. protected:
  204. float m_f[N];
  205. };
  206. // Special functions that makes parameter zero.
  207. template <class T>
  208. inline void Zero(T& val)
  209. {
  210. memset(&val, 0, sizeof(val));
  211. }
  212. // Specialized Zero functions
  213. template <>
  214. inline void Zero(float& val) { val = 0.0f; }
  215. template <>
  216. inline void Zero(Vec2& val) { val = Vec2(0.0f, 0.0f); }
  217. template <>
  218. inline void Zero(Vec3& val) { val = Vec3(0.0f, 0.0f, 0.0f); }
  219. template <>
  220. inline void Zero(Quat& val) { val.SetIdentity(); }
  221. inline float Concatenate(float left, float right) { return left + right; }
  222. inline Vec3 Concatenate(const Vec3& left, const Vec3& right) { return left + right; }
  223. inline Quat Concatenate(const Quat& left, const Quat& right) { return left * right; }
  224. inline float Subtract (float left, float right) { return left - right; }
  225. inline Vec3 Subtract (const Vec3& left, const Vec3& right) { return left - right; }
  226. inline Quat Subtract(const Quat& left, const Quat& right) { return left / right; }
  227. ///////////////////////////////////////////////////////////////////////////////
  228. // HermitBasis.
  229. class HermitBasis
  230. : public BasisFunction<4>
  231. {
  232. public:
  233. HermitBasis(float t)
  234. {
  235. float t2, t3, t2_3, t3_2, t3_t2;
  236. t2 = t * t; // t2 = t^2;
  237. t3 = t2 * t; // t3 = t^3;
  238. t3_2 = t3 + t3;
  239. t2_3 = 3 * t2;
  240. t3_t2 = t3 - t2;
  241. m_f[0] = t3_2 - t2_3 + 1;
  242. m_f[1] = -t3_2 + t2_3;
  243. m_f[2] = t3_t2 - t2 + t;
  244. m_f[3] = t3_t2;
  245. }
  246. };
  247. ///////////////////////////////////////////////////////////////////////////////
  248. // BezierBasis.
  249. class BezierBasis
  250. : public BasisFunction<4>
  251. {
  252. public:
  253. BezierBasis(const float t)
  254. {
  255. const float t2 = t * t;
  256. const float t3 = t2 * t;
  257. m_f[0] = -t3 + 3 * t2 - 3 * t + 1;
  258. m_f[1] = 3 * t3 - 6 * t2 + 3 * t;
  259. m_f[2] = -3 * t3 + 3 * t2;
  260. m_f[3] = t3;
  261. }
  262. };
  263. template<class T>
  264. struct TCoeffBasis
  265. {
  266. // Coefficients for a cubic polynomial.
  267. T m_c[4];
  268. inline T eval(float t) const
  269. {
  270. return m_c[0] + t * (m_c[1] + t * (m_c[2] + t * m_c[3]));
  271. }
  272. // Compute coeffs based on 2 endpoints & slopes.
  273. void set(float t0, T v0, T s0, float t1, T v1, T s1)
  274. {
  275. /*
  276. Solve cubic equation:
  277. v(u) = d t^3 + c t^2 + b t + a
  278. for
  279. v(0) = v0, v'(0) = s0, v(t1) = v1, v'(t1) = s1
  280. Solution:
  281. a = v0
  282. b = s0
  283. c = -3v0 +3v1 -2s0 -s1
  284. d = +2v0 -2v1 +s0 +s1
  285. */
  286. /*
  287. Polynomial will be evaluated on adjusted parameter u == t-t0. u0 = 0, u1 = t1-t0.
  288. The range is normalised to start at 0 to avoid extra terms in the coefficient
  289. computation that can compromise precision. However, the range is not normalised
  290. to length 1, because that would require a division at runtime. Instead, we perform
  291. the division on the coefficients.
  292. */
  293. m_c[0] = v0;
  294. if (t1 <= t0)
  295. {
  296. m_c[1] = m_c[2] = m_c[3] = T(0.f);
  297. }
  298. else
  299. {
  300. float idt = 1.f / (t1 - t0);
  301. m_c[1] = T(s0 * idt);
  302. m_c[2] = T((-3.0f * v0 + 3.0f * v1 - 2.0f * s0 - s1) * idt * idt);
  303. m_c[3] = T((2.0f * v0 - 2.0f * v1 + s0 + s1) * idt * idt * idt);
  304. }
  305. }
  306. };
  307. inline float fast_fmod(float x, float y)
  308. {
  309. return fmod_tpl(x, y);
  310. //int ival = ftoi(x/y);
  311. //return x - ival*y;
  312. }
  313. /****************************************************************************
  314. ** Key classes **
  315. ****************************************************************************/
  316. template <class T>
  317. struct SplineKey
  318. {
  319. typedef T value_type;
  320. float time; //!< Key time.
  321. int flags; //!< Key flags.
  322. value_type value; //!< Key value.
  323. value_type ds; //!< Incoming tangent.
  324. value_type dd; //!< Outgoing tangent.
  325. SplineKey()
  326. {
  327. memset(this, 0, sizeof(SplineKey));
  328. }
  329. SplineKey& operator=(const SplineKey& src) { memcpy(this, &src, sizeof(*this)); return *this; }
  330. static void Reflect(AZ::ReflectContext* context) {}
  331. };
  332. template <class T>
  333. bool operator ==(const SplineKey<T>& k1, const SplineKey<T>& k2) { return k1.time == k2.time; };
  334. template <class T>
  335. bool operator !=(const SplineKey<T>& k1, const SplineKey<T>& k2) { return k1.time != k2.time; };
  336. template <class T>
  337. bool operator < (const SplineKey<T>& k1, const SplineKey<T>& k2) { return k1.time < k2.time; };
  338. template <class T>
  339. bool operator > (const SplineKey<T>& k1, const SplineKey<T>& k2) { return k1.time > k2.time; };
  340. //////////////////////////////////////////////////////////////////////////
  341. // TCBSplineKey class
  342. //////////////////////////////////////////////////////////////////////////
  343. template <class T>
  344. struct TCBSplineKey
  345. : public SplineKey<T>
  346. {
  347. // Key controls.
  348. float tens; //!< Key tension value.
  349. float cont; //!< Key continuity value.
  350. float bias; //!< Key bias value.
  351. float easeto; //!< Key ease to value.
  352. float easefrom; //!< Key ease from value.
  353. TCBSplineKey() { tens = 0, cont = 0, bias = 0, easeto = 0, easefrom = 0; };
  354. };
  355. //! TCB spline key used in quaternion spline with angle axis as input.
  356. struct TCBAngAxisKey
  357. : public TCBSplineKey<Quat>
  358. {
  359. float angle;
  360. Vec3 axis;
  361. TCBAngAxisKey()
  362. : axis(0, 0, 0)
  363. , angle(0) {};
  364. };
  365. //////////////////////////////////////////////////////////////////////////
  366. // General Spline class
  367. //////////////////////////////////////////////////////////////////////////
  368. template <class KeyType, class BasisType>
  369. class TSpline
  370. {
  371. public:
  372. typedef KeyType key_type;
  373. typedef typename KeyType::value_type value_type;
  374. typedef BasisType basis_type;
  375. // Out of range types.
  376. enum
  377. {
  378. ORT_CONSTANT = 0x0001, // Constant track.
  379. ORT_CYCLE = 0x0002, // Cycle track
  380. ORT_LOOP = 0x0003, // Loop track.
  381. ORT_OSCILLATE = 0x0004, // Oscillate track.
  382. ORT_LINEAR = 0x0005, // Linear track.
  383. ORT_RELATIVE_REPEAT = 0x0007 // Relative repeat track.
  384. };
  385. // Spline flags.
  386. enum
  387. {
  388. MODIFIED = 0x0001, // Track modified.
  389. MUST_SORT = 0x0002, // Track modified and must be sorted.
  390. };
  391. /////////////////////////////////////////////////////////////////////////////
  392. // Methods.
  393. inline TSpline()
  394. {
  395. m_flags = MODIFIED;
  396. m_ORT = 0;
  397. m_curr = 0;
  398. m_rangeStart = 0;
  399. m_rangeEnd = 0;
  400. m_refCount = 0;
  401. }
  402. virtual ~TSpline() {};
  403. ILINE void flag_set(int flag) { m_flags |= flag; };
  404. ILINE void flag_clr(int flag) { m_flags &= ~flag; };
  405. ILINE int flag(int flag) { return m_flags & flag; };
  406. ILINE void ORT(int ort) { m_ORT = static_cast<uint8>(ort); };
  407. ILINE int ORT() const { return m_ORT; };
  408. ILINE int isORT(int o) const { return (m_ORT == o); };
  409. ILINE void SetRange(float start, float end) { m_rangeStart = start; m_rangeEnd = end; };
  410. ILINE float GetRangeStart() const { return m_rangeStart; };
  411. ILINE float GetRangeEnd() const { return m_rangeEnd; };
  412. // Keys access methods.
  413. ILINE void reserve_keys(int n) { m_keys.reserve(n); }; // Reserve memory for more keys.
  414. ILINE void clear() { m_keys.clear(); };
  415. ILINE void resize(int num) { m_keys.resize(num); SetModified(true); }; // Set new key count.
  416. ILINE bool empty() const { return m_keys.empty(); }; // Check if curve empty (no keys).
  417. ILINE int num_keys() const { return (int)m_keys.size(); }; // Return number of keys in curve.
  418. ILINE key_type& key(int n) { return m_keys[n]; }; // Return n key.
  419. ILINE float& time(int n) { return m_keys[n].time; }; // Shortcut to key n time.
  420. ILINE value_type& value(int n) { return m_keys[n].value; }; // Shortcut to key n value.
  421. ILINE value_type& ds(int n) { return m_keys[n].ds; }; // Shortcut to key n incoming tangent.
  422. ILINE value_type& dd(int n) { return m_keys[n].dd; }; // Shortcut to key n outgoing tangent.
  423. ILINE int& flags(int n) { return m_keys[n].flags; }; // Shortcut to key n flags.
  424. ILINE key_type const& key(int n) const { return m_keys[n]; }; // Return n key.
  425. ILINE float time(int n) const { return m_keys[n].time; }; // Shortcut to key n time.
  426. ILINE value_type const& value(int n) const { return m_keys[n].value; }; // Shortcut to key n value.
  427. ILINE value_type const& ds(int n) const { return m_keys[n].ds; }; // Shortcut to key n incoming tangent.
  428. ILINE value_type const& dd(int n) const { return m_keys[n].dd; }; // Shortcut to key n outgoing tangent.
  429. ILINE int flags(int n) const { return m_keys[n].flags; }; // Shortcut to key n flags.
  430. ILINE int GetInTangentType(int nkey) const { return (flags(nkey) & SPLINE_KEY_TANGENT_IN_MASK) >> SPLINE_KEY_TANGENT_IN_SHIFT; }
  431. ILINE int GetOutTangentType(int nkey) const { return (flags(nkey) & SPLINE_KEY_TANGENT_OUT_MASK) >> SPLINE_KEY_TANGENT_OUT_SHIFT; }
  432. ILINE void erase(int key) { m_keys.erase(m_keys.begin() + key); SetModified(true); };
  433. ILINE bool closed() { return (ORT() == ORT_LOOP); } // return True if curve closed.
  434. ILINE void SetModified(bool bOn, bool bSort = false)
  435. {
  436. if (bOn)
  437. {
  438. m_flags |= MODIFIED;
  439. }
  440. else
  441. {
  442. m_flags &= ~(MODIFIED);
  443. }
  444. if (bSort)
  445. {
  446. m_flags |= MUST_SORT;
  447. }
  448. m_curr = 0;
  449. }
  450. ILINE void sort_keys()
  451. {
  452. std::stable_sort(m_keys.begin(), m_keys.end());
  453. m_flags &= ~MUST_SORT;
  454. }
  455. ILINE void push_back(const key_type& k)
  456. {
  457. m_keys.push_back(k);
  458. SetModified(true);
  459. };
  460. ILINE int insert_key(const key_type& k)
  461. {
  462. int num = num_keys();
  463. for (int i = 0; i < num; i++)
  464. {
  465. if (m_keys[i].time > k.time)
  466. {
  467. m_keys.insert(m_keys.begin() + i, k);
  468. SetModified(true);
  469. return i;
  470. }
  471. }
  472. m_keys.push_back(k);
  473. SetModified(true);
  474. return num_keys() - 1;
  475. };
  476. ILINE int insert_key(float t, value_type const& val)
  477. {
  478. key_type key;
  479. key.time = t;
  480. key.value = val;
  481. key.flags = 0;
  482. Zero(key.ds);
  483. Zero(key.dd);
  484. return insert_key(key);
  485. }
  486. inline void update()
  487. {
  488. if (m_flags & MODIFIED)
  489. {
  490. sort_keys();
  491. if (m_flags & MODIFIED)
  492. {
  493. comp_deriv();
  494. }
  495. }
  496. }
  497. inline bool is_updated() const
  498. {
  499. return (m_flags & MODIFIED) == 0;
  500. }
  501. // Interpolate the value along the spline.
  502. inline bool interpolate(float t, value_type& val)
  503. {
  504. update();
  505. if (empty())
  506. {
  507. return false;
  508. }
  509. if (t < time(0))
  510. {
  511. val = value(0);
  512. return true;
  513. }
  514. adjust_time(t);
  515. int curr = seek_key(t);
  516. if (curr < num_keys() - 1)
  517. {
  518. assert(t >= time(curr));
  519. float u = (t - time(curr)) / (time(curr + 1) - time(curr));
  520. interp_keys(curr, curr + 1, u, val);
  521. }
  522. else
  523. {
  524. val = value(num_keys() - 1);
  525. }
  526. return true;
  527. }
  528. size_t mem_size() const
  529. {
  530. return this->m_keys.capacity() * sizeof(this->m_keys[0]);
  531. }
  532. size_t sizeofThis() const
  533. {
  534. return sizeof(*this) + mem_size();
  535. }
  536. void swap(TSpline<KeyType, BasisType>& b)
  537. {
  538. using std::swap;
  539. m_keys.swap(b.m_keys);
  540. swap(m_flags, b.m_flags);
  541. swap(m_ORT, b.m_ORT);
  542. swap(m_curr, b.m_curr);
  543. swap(m_rangeStart, b.m_rangeStart);
  544. swap(m_rangeEnd, b.m_rangeEnd);
  545. }
  546. //////////////////////////////////////////////////////////////////////////
  547. // This two functions must be overridden in derived spline classes.
  548. //////////////////////////////////////////////////////////////////////////
  549. // Pre-compute spline tangents.
  550. virtual void comp_deriv() = 0;
  551. // Interpolate value between two keys.
  552. virtual void interp_keys(int key1, int key2, float u, value_type& val) = 0;
  553. //////////////////////////////////////////////////////////////////////////
  554. static void Reflect(AZ::ReflectContext* context) {}
  555. inline void add_ref()
  556. {
  557. ++m_refCount;
  558. };
  559. inline void release()
  560. {
  561. AZ_Assert(m_refCount > 0, "Reference count logic error, trying to decrement reference when refCount is 0");
  562. if (--m_refCount == 0)
  563. {
  564. delete this;
  565. }
  566. }
  567. private:
  568. int m_refCount;
  569. protected:
  570. AZStd::vector<key_type> m_keys; // List of keys.
  571. uint8 m_flags;
  572. uint8 m_ORT; // Out-Of-Range type.
  573. int16 m_curr; // Current key in track.
  574. float m_rangeStart;
  575. float m_rangeEnd;
  576. // Return key before or equal to this time.
  577. inline int seek_key(float t)
  578. {
  579. assert(num_keys() < (1 << 15));
  580. if ((m_curr >= num_keys()) || (time(m_curr) > t))
  581. {
  582. // Search from begining.
  583. m_curr = 0;
  584. }
  585. while ((m_curr < num_keys() - 1) && (time(m_curr + 1) <= t))
  586. {
  587. ++m_curr;
  588. }
  589. return m_curr;
  590. }
  591. inline void adjust_time(float& t)
  592. {
  593. if (isORT(ORT_CYCLE) || isORT(ORT_LOOP))
  594. {
  595. if (num_keys() > 0)
  596. {
  597. float endtime = time(num_keys() - 1);
  598. if (t > endtime)
  599. {
  600. // Warp time.
  601. t = fast_fmod(t, endtime);
  602. }
  603. }
  604. }
  605. }
  606. };
  607. //////////////////////////////////////////////////////////////////////////
  608. // TSplineSlopes is default implementation of slope computation
  609. //////////////////////////////////////////////////////////////////////////
  610. template < class T, class Key = SplineKey<T>, bool bCLAMP = false >
  611. class TSplineSlopes
  612. : public TSpline< Key, TCoeffBasis<T> >
  613. {
  614. public:
  615. typedef TSpline< Key, TCoeffBasis<T> > super_type;
  616. using_type(super_type, key_type);
  617. using_type(super_type, value_type);
  618. static const bool clamp_range = bCLAMP;
  619. void comp_deriv()
  620. {
  621. this->SetModified(false);
  622. int last = this->num_keys() - 1;
  623. if (last <= 0)
  624. {
  625. return;
  626. }
  627. if (bCLAMP)
  628. {
  629. // Change discontinuous slopes.
  630. for (int i = 0; i <= last; ++i)
  631. {
  632. // Out slopes.
  633. if (i < last && this->GetOutTangentType(i) == SPLINE_KEY_TANGENT_LINEAR)
  634. {
  635. // Set linear between points.
  636. this->dd(i) = this->value(i + 1) - this->value(i);
  637. if (this->GetInTangentType(i + 1) == SPLINE_KEY_TANGENT_NONE)
  638. {
  639. // Match continuous slope on right.
  640. this->dd(i) = 2.0f * this->dd(i) - this->ds(i + 1);
  641. }
  642. }
  643. else if (i < last && this->GetOutTangentType(i) == SPLINE_KEY_TANGENT_NONE)
  644. {
  645. this->dd(i) = value_type(0.f);
  646. }
  647. // In slopes.
  648. if (i > 0 && this->GetInTangentType(i) == SPLINE_KEY_TANGENT_LINEAR)
  649. {
  650. // Set linear between points.
  651. this->ds(i) = this->value(i) - this->value(i - 1);
  652. if (this->GetOutTangentType(i - 1) == SPLINE_KEY_TANGENT_NONE)
  653. {
  654. // Match continuous slope on left.
  655. this->ds(i) = 2.0f * this->ds(i) - this->dd(i - 1);
  656. }
  657. }
  658. else if (i < last && this->GetInTangentType(i) == SPLINE_KEY_TANGENT_NONE)
  659. {
  660. this->ds(i) = value_type(0.f);
  661. }
  662. }
  663. }
  664. else
  665. {
  666. key_type& k0 = this->key(0);
  667. key_type& k1 = this->key(last);
  668. Zero(k0.ds);
  669. k0.dd = (0.5f) * (this->value(1) - this->value(0));
  670. k1.ds = (0.5f) * (this->value(last) - this->value(last - 1));
  671. Zero(k1.dd);
  672. for (int i = 1; i < (this->num_keys() - 1); ++i)
  673. {
  674. key_type& key = this->key(i);
  675. key.ds = 0.5f * (this->value(i + 1) - this->value(i - 1));
  676. key.dd = key.ds;
  677. }
  678. }
  679. }
  680. virtual void interp_keys(int key1, int key2, float u, value_type& val)
  681. {
  682. // Compute coeffs dynamically.
  683. TCoeffBasis<T> coeff;
  684. coeff.set(0.f, this->value(key1), this->dd(key1), 1.f, this->value(key2), this->ds(key2));
  685. val = coeff.eval(u);
  686. }
  687. };
  688. //////////////////////////////////////////////////////////////////////////
  689. // CatmullRomSpline class implementation
  690. //////////////////////////////////////////////////////////////////////////
  691. template <class T, class Key = SplineKey<T>, bool bRangeLimit = false >
  692. class CatmullRomSpline
  693. : public TSplineSlopes< T, Key, bRangeLimit >
  694. {
  695. protected:
  696. typedef TSplineSlopes< T, Key, bRangeLimit > super_type;
  697. std::vector< TCoeffBasis<T> > m_coeffs;
  698. virtual void comp_deriv()
  699. {
  700. super_type::comp_deriv();
  701. // Store coeffs for each segment.
  702. m_coeffs.resize(this->num_keys());
  703. if (this->num_keys() > 0)
  704. {
  705. unsigned i;
  706. for (i = 0; i < m_coeffs.size() - 1; i++)
  707. {
  708. m_coeffs[i].set(this->time(i), this->value(i), this->dd(i), this->time(i + 1), this->value(i + 1), this->ds(i + 1));
  709. }
  710. // Last segment is just constant value.
  711. m_coeffs[i].set(this->time(i), this->value(i), T(0.f), this->time(i) + 1.f, this->value(i), T(0.f));
  712. }
  713. }
  714. virtual void interp_keys(int key1, int key2, float u, typename TSpline<Key, HermitBasis>::value_type& val)
  715. {
  716. u *= this->time(key2) - this->time(key1);
  717. val = m_coeffs[key1].eval(u);
  718. }
  719. public:
  720. size_t mem_size() const
  721. {
  722. return super_type::mem_size() + this->m_coeffs.capacity() * sizeof(this->m_coeffs[0]);
  723. }
  724. size_t sizeofThis() const
  725. {
  726. return sizeof(*this) + mem_size();
  727. }
  728. };
  729. //////////////////////////////////////////////////////////////////////////
  730. // Extended version of Hermit Spline.
  731. // Provides more control on key tangents.
  732. //////////////////////////////////////////////////////////////////////////
  733. template <class T, class Key = SplineKey<T> >
  734. class HermitSplineEx
  735. : public TSpline< Key, HermitBasis >
  736. {
  737. public:
  738. typedef TSpline< Key, HermitBasis > super_type;
  739. using_type(super_type, key_type);
  740. using_type(super_type, value_type);
  741. virtual void comp_deriv()
  742. {
  743. this->SetModified(false);
  744. if (this->num_keys() > 1)
  745. {
  746. int last = this->num_keys() - 1;
  747. key_type& k0 = this->key(0);
  748. key_type& k1 = this->key(last);
  749. Zero(k0.ds);
  750. Zero(k0.dd);
  751. Zero(k1.ds);
  752. Zero(k1.dd);
  753. Zero(k0.ds);
  754. k0.dd = (0.5f) * (this->value(1) - this->value(0));
  755. k1.ds = (0.5f) * (this->value(last) - this->value(last - 1));
  756. Zero(k1.dd);
  757. for (int i = 1; i < (this->num_keys() - 1); ++i)
  758. {
  759. key_type& key = this->key(i);
  760. key.ds = 0.5f * (this->value(i + 1) - this->value(i - 1));
  761. key.dd = key.ds;
  762. switch (this->GetInTangentType(i))
  763. {
  764. case SPLINE_KEY_TANGENT_STEP:
  765. key.ds = value_type();
  766. break;
  767. case SPLINE_KEY_TANGENT_ZERO:
  768. key.ds = value_type();
  769. break;
  770. case SPLINE_KEY_TANGENT_LINEAR:
  771. key.ds = this->value(i) - this->value(i - 1);
  772. break;
  773. }
  774. switch (this->GetOutTangentType(i))
  775. {
  776. case SPLINE_KEY_TANGENT_STEP:
  777. key.dd = value_type();
  778. break;
  779. case SPLINE_KEY_TANGENT_ZERO:
  780. key.dd = value_type();
  781. break;
  782. case SPLINE_KEY_TANGENT_LINEAR:
  783. key.dd = this->value(i + 1) - this->value(i);
  784. break;
  785. }
  786. }
  787. }
  788. }
  789. protected:
  790. virtual void interp_keys(int from, int to, float u, T& val)
  791. {
  792. if (this->GetInTangentType(to) == SPLINE_KEY_TANGENT_STEP || this->GetOutTangentType(from) == SPLINE_KEY_TANGENT_STEP)
  793. {
  794. val = this->value(from);
  795. return;
  796. }
  797. typename TSpline<Key, HermitBasis >::basis_type basis(u);
  798. val = (basis[0] * this->value(from)) + (basis[1] * this->value(to)) + (basis[2] * this->dd(from)) + (basis[3] * this->ds(to));
  799. }
  800. };
  801. //////////////////////////////////////////////////////////////////////////
  802. // Bezier Spline.
  803. //////////////////////////////////////////////////////////////////////////
  804. template <class T, class Key = SplineKey<T> >
  805. class BezierSpline
  806. : public TSpline< Key, BezierBasis >
  807. {
  808. public:
  809. typedef TSpline< Key, BezierBasis > super_type;
  810. using_type(super_type, key_type);
  811. using_type(super_type, value_type);
  812. virtual void comp_deriv()
  813. {
  814. this->SetModified(false);
  815. if (this->num_keys() > 1)
  816. {
  817. const float oneThird = 1 / 3.0f;
  818. const int last = this->num_keys() - 1;
  819. {
  820. if (this->GetInTangentType(0) != SPLINE_KEY_TANGENT_CUSTOM)
  821. {
  822. Zero(this->key(0).ds);
  823. }
  824. if (this->GetOutTangentType(0) != SPLINE_KEY_TANGENT_CUSTOM)
  825. {
  826. this->key(0).dd = oneThird * (this->value(1) - this->value(0));
  827. }
  828. if (this->GetInTangentType(last) != SPLINE_KEY_TANGENT_CUSTOM)
  829. {
  830. this->key(last).ds = oneThird * (this->value(last) - this->value(last - 1));
  831. }
  832. if (this->GetOutTangentType(last) != SPLINE_KEY_TANGENT_CUSTOM)
  833. {
  834. Zero(this->key(last).dd);
  835. }
  836. }
  837. for (int i = 1; i < last; ++i)
  838. {
  839. key_type& key = this->key(i);
  840. T ds0 = key.ds;
  841. T dd0 = key.dd;
  842. const float deltaTime = this->time(i + 1) - this->time(i - 1);
  843. if (deltaTime <= 0)
  844. {
  845. Zero(key.ds);
  846. Zero(key.dd);
  847. }
  848. else
  849. {
  850. const float k = (this->time(i) - this->time(i - 1)) / deltaTime;
  851. const value_type deltaValue = this->value(i + 1) - this->value(i - 1);
  852. key.ds = oneThird * deltaValue * k;
  853. key.dd = oneThird * deltaValue * (1 - k);
  854. }
  855. switch (this->GetInTangentType(i))
  856. {
  857. case SPLINE_KEY_TANGENT_STEP:
  858. Zero(key.ds);
  859. break;
  860. case SPLINE_KEY_TANGENT_ZERO:
  861. Zero(key.ds);
  862. break;
  863. case SPLINE_KEY_TANGENT_LINEAR:
  864. key.ds = oneThird * (this->value(i) - this->value(i - 1));
  865. break;
  866. case SPLINE_KEY_TANGENT_CUSTOM:
  867. key.ds = ds0;
  868. break;
  869. }
  870. switch (this->GetOutTangentType(i))
  871. {
  872. case SPLINE_KEY_TANGENT_STEP:
  873. Zero(key.dd);
  874. break;
  875. case SPLINE_KEY_TANGENT_ZERO:
  876. Zero(key.dd);
  877. break;
  878. case SPLINE_KEY_TANGENT_LINEAR:
  879. key.dd = oneThird * (this->value(i + 1) - this->value(i));
  880. break;
  881. case SPLINE_KEY_TANGENT_CUSTOM:
  882. key.dd = dd0;
  883. break;
  884. }
  885. }
  886. }
  887. }
  888. static void Reflect(AZ::ReflectContext* context) {}
  889. protected:
  890. virtual void interp_keys(int from, int to, float u, T& val)
  891. {
  892. if (this->GetOutTangentType(from) == SPLINE_KEY_TANGENT_STEP)
  893. {
  894. val = this->value(to);
  895. }
  896. else if (this->GetInTangentType(to) == SPLINE_KEY_TANGENT_STEP)
  897. {
  898. val = this->value(from);
  899. }
  900. else
  901. {
  902. typename TSpline<Key, BezierBasis >::basis_type basis(u);
  903. const T p0 = this->value(from);
  904. const T p3 = this->value(to);
  905. const T p1 = p0 + this->dd(from);
  906. const T p2 = p3 - this->ds(to);
  907. val = (basis[0] * p0) + (basis[1] * p1) + (basis[2] * p2) + (basis[3] * p3);
  908. }
  909. }
  910. };
  911. //////////////////////////////////////////////////////////////////////////
  912. // Base class for spline interpolators.
  913. //////////////////////////////////////////////////////////////////////////
  914. template <typename spline_type>
  915. struct SSplineBackup
  916. : public ISplineBackup
  917. , public spline_type
  918. {
  919. int refCount;
  920. SSplineBackup(spline_type const& s)
  921. : spline_type(s)
  922. , refCount(0) {}
  923. virtual void AddRef() {++refCount; }
  924. virtual void Release()
  925. {
  926. if (--refCount <= 0)
  927. {
  928. delete this;
  929. }
  930. }
  931. };
  932. template <class value_type, class spline_type>
  933. class CBaseSplineInterpolator
  934. : public ISplineInterpolator
  935. , public spline_type
  936. {
  937. public:
  938. static const int DIM = sizeof(value_type) / sizeof(ElemType);
  939. //////////////////////////////////////////////////////////////////////////
  940. inline void ToValueType(const value_type& t, ValueType& v) { *(value_type*)v = t; }
  941. inline void FromValueType(ValueType v, value_type& t) { t = *(value_type*)v; }
  942. //////////////////////////////////////////////////////////////////////////
  943. virtual void SetModified(bool b, bool bSort = false)
  944. {
  945. spline_type::SetModified(b, bSort);
  946. }
  947. virtual int GetNumDimensions()
  948. {
  949. assert(sizeof(value_type) % sizeof(ElemType) == 0);
  950. return DIM;
  951. }
  952. virtual int InsertKey(float t, ValueType val)
  953. {
  954. value_type value;
  955. FromValueType(val, value);
  956. return spline_type::insert_key(t, value);
  957. }
  958. virtual void RemoveKey(int key)
  959. {
  960. if (key >= 0 && key < this->num_keys())
  961. {
  962. this->erase(key);
  963. }
  964. }
  965. virtual void FindKeysInRange(float startTime, float endTime, int& firstFoundKey, int& numFoundKeys)
  966. {
  967. int count = this->num_keys();
  968. int start = 0;
  969. int end = count;
  970. for (int i = 0; i < count; ++i)
  971. {
  972. float keyTime = this->key(i).time;
  973. if (keyTime < startTime)
  974. {
  975. start = i + 1;
  976. }
  977. if (keyTime > endTime && end > i)
  978. {
  979. end = i;
  980. }
  981. }
  982. if (start < end)
  983. {
  984. firstFoundKey = start;
  985. numFoundKeys = end - start;
  986. }
  987. else
  988. {
  989. firstFoundKey = -1;
  990. numFoundKeys = 0;
  991. }
  992. }
  993. virtual void RemoveKeysInRange(float startTime, float endTime)
  994. {
  995. int firstFoundKey, numFoundKeys;
  996. FindKeysInRange(startTime, endTime, firstFoundKey, numFoundKeys);
  997. while (numFoundKeys-- > 0)
  998. {
  999. this->erase(firstFoundKey++);
  1000. }
  1001. }
  1002. virtual int GetKeyCount()
  1003. {
  1004. return this->num_keys();
  1005. };
  1006. virtual float GetKeyTime(int key)
  1007. {
  1008. if (key >= 0 && key < this->num_keys())
  1009. {
  1010. return this->key(key).time;
  1011. }
  1012. return 0;
  1013. }
  1014. virtual bool GetKeyValue(int key, ValueType& val)
  1015. {
  1016. if (key >= 0 && key < this->num_keys())
  1017. {
  1018. ToValueType(this->key(key).value, val);
  1019. return true;
  1020. }
  1021. return false;
  1022. }
  1023. virtual void SetKeyValue(int k, ValueType val)
  1024. {
  1025. if (k >= 0 && k < this->num_keys())
  1026. {
  1027. FromValueType(val, this->key(k).value);
  1028. this->SetModified(true);
  1029. }
  1030. }
  1031. virtual void SetKeyTime(int k, float fTime)
  1032. {
  1033. if (k >= 0 && k < this->num_keys())
  1034. {
  1035. this->key(k).time = fTime;
  1036. this->SetModified(true, true);
  1037. }
  1038. }
  1039. virtual void SetKeyInTangent(int k, ValueType tin)
  1040. {
  1041. if (k >= 0 && k < this->num_keys())
  1042. {
  1043. FromValueType(tin, this->key(k).ds);
  1044. this->SetModified(true);
  1045. }
  1046. }
  1047. virtual void SetKeyOutTangent(int k, ValueType tout)
  1048. {
  1049. if (k >= 0 && k < this->num_keys())
  1050. {
  1051. FromValueType(tout, this->key(k).dd);
  1052. this->SetModified(true);
  1053. }
  1054. }
  1055. virtual void SetKeyTangents(int k, ValueType tin, ValueType tout)
  1056. {
  1057. if (k >= 0 && k < this->num_keys())
  1058. {
  1059. FromValueType(tin, this->key(k).ds);
  1060. FromValueType(tout, this->key(k).dd);
  1061. this->SetModified(true);
  1062. }
  1063. }
  1064. virtual bool GetKeyTangents(int k, ValueType& tin, ValueType& tout)
  1065. {
  1066. if (k >= 0 && k < this->num_keys())
  1067. {
  1068. ToValueType(this->key(k).ds, tin);
  1069. ToValueType(this->key(k).dd, tout);
  1070. return true;
  1071. }
  1072. else
  1073. {
  1074. return false;
  1075. }
  1076. }
  1077. virtual void SetKeyFlags(int k, int flags)
  1078. {
  1079. if (k >= 0 && k < this->num_keys())
  1080. {
  1081. this->key(k).flags = flags;
  1082. this->SetModified(true);
  1083. }
  1084. }
  1085. virtual int GetKeyFlags(int k)
  1086. {
  1087. if (k >= 0 && k < this->num_keys())
  1088. {
  1089. return this->key(k).flags;
  1090. }
  1091. return 0;
  1092. }
  1093. virtual void Interpolate(float time, ValueType& value)
  1094. {
  1095. value_type v;
  1096. if (spline_type::interpolate(time, v))
  1097. {
  1098. ToValueType(v, value);
  1099. }
  1100. }
  1101. virtual ISplineBackup* Backup()
  1102. {
  1103. return new SSplineBackup<spline_type>(*this);
  1104. }
  1105. virtual void Restore(ISplineBackup* p)
  1106. {
  1107. SSplineBackup<spline_type>* pBackup = static_cast<SSplineBackup<spline_type>*>(p);
  1108. static_cast<spline_type&>(*this) = *pBackup;
  1109. }
  1110. };
  1111. }
  1112. namespace AZ
  1113. {
  1114. AZ_TYPE_INFO_SPECIALIZE(spline::SplineKey<Vec2>, "{24A4D7E5-C36D-427D-AB49-CD86573B7288}");
  1115. }
  1116. #endif // CRYINCLUDE_CRYCOMMON_ISPLINES_H