TrackViewSplineCtrl.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  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. #include "EditorDefs.h"
  9. #include "TrackViewSplineCtrl.h"
  10. // Qt
  11. #include <QRubberBand>
  12. // Editor
  13. #include "AnimationContext.h"
  14. // AzCore
  15. #include <AzCore/std/algorithm.h>
  16. #include <AzCore/std/containers/list.h>
  17. class CUndoTrackViewSplineCtrl
  18. : public ISplineCtrlUndo
  19. {
  20. public:
  21. CUndoTrackViewSplineCtrl(CTrackViewSplineCtrl* pCtrl, std::vector<ISplineInterpolator*>& splineContainer)
  22. {
  23. if (GetIEditor()->GetAnimation()->GetSequence())
  24. {
  25. m_sequenceEntityId = GetIEditor()->GetAnimation()->GetSequence()->GetSequenceComponentEntityId();
  26. }
  27. m_pCtrl = pCtrl;
  28. // Loop over all affected splines
  29. for (size_t splineIndex = 0; splineIndex < splineContainer.size(); ++splineIndex)
  30. {
  31. AddSpline(splineContainer[splineIndex]);
  32. }
  33. SerializeSplines(&CSplineEntry::m_undo, false);
  34. }
  35. protected:
  36. void AddSpline(ISplineInterpolator* pSpline)
  37. {
  38. // Find corresponding track and remember it
  39. for (size_t trackIndex = 0; trackIndex < m_pCtrl->m_tracks.size(); ++trackIndex)
  40. {
  41. CTrackViewTrack* pTrack = m_pCtrl->m_tracks[trackIndex];
  42. if (pTrack->GetSpline() == pSpline)
  43. {
  44. CSplineEntry entry;
  45. entry.m_trackId = pTrack->GetId();
  46. m_splineEntries.push_back(entry);
  47. }
  48. }
  49. }
  50. int GetSize() override { return sizeof(*this); }
  51. void Undo(bool bUndo) override
  52. {
  53. CTrackViewSplineCtrl* pCtrl = FindControl(m_pCtrl);
  54. if (pCtrl)
  55. {
  56. pCtrl->SendNotifyEvent(SPLN_BEFORE_CHANGE);
  57. }
  58. const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
  59. CTrackViewSequence* sequence = sequenceManager->GetSequenceByEntityId(m_sequenceEntityId);
  60. AZ_Assert(sequence, "Expected valid sequence.");
  61. if (!sequence)
  62. {
  63. return;
  64. }
  65. CTrackViewSequenceNotificationContext context(sequence);
  66. if (bUndo)
  67. {
  68. // Save key selection states for redo if necessary
  69. m_redoKeyStates = sequence->SaveKeyStates();
  70. SerializeSplines(&CSplineEntry::m_redo, false);
  71. }
  72. SerializeSplines(&CSplineEntry::m_undo, true);
  73. // Undo key selection state
  74. sequence->RestoreKeyStates(m_undoKeyStates);
  75. if (pCtrl && bUndo)
  76. {
  77. pCtrl->m_bKeyTimesDirty = true;
  78. pCtrl->SendNotifyEvent(SPLN_CHANGE);
  79. pCtrl->update();
  80. }
  81. if (bUndo)
  82. {
  83. sequence->OnKeySelectionChanged();
  84. }
  85. }
  86. void Redo() override
  87. {
  88. const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
  89. CTrackViewSequence* sequence = pSequenceManager->GetSequenceByEntityId(m_sequenceEntityId);
  90. AZ_Assert(sequence, "Expected valid sequence.");
  91. if (!sequence)
  92. {
  93. return;
  94. }
  95. CTrackViewSequenceNotificationContext context(sequence);
  96. CTrackViewSplineCtrl* pCtrl = FindControl(m_pCtrl);
  97. if (pCtrl)
  98. {
  99. pCtrl->SendNotifyEvent(SPLN_BEFORE_CHANGE);
  100. }
  101. SerializeSplines(&CSplineEntry::m_redo, true);
  102. // Redo key selection state
  103. sequence->RestoreKeyStates(m_redoKeyStates);
  104. if (pCtrl)
  105. {
  106. pCtrl->m_bKeyTimesDirty = true;
  107. pCtrl->SendNotifyEvent(SPLN_CHANGE);
  108. pCtrl->update();
  109. }
  110. sequence->OnKeySelectionChanged();
  111. }
  112. bool IsSelectionChanged() const override
  113. {
  114. const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
  115. CTrackViewSequence* sequence = sequenceManager->GetSequenceByEntityId(m_sequenceEntityId);
  116. AZ_Assert(sequence, "Expected valid sequence.");
  117. if (!sequence)
  118. {
  119. return false;
  120. }
  121. const AZStd::vector<bool> currentKeyState = sequence->SaveKeyStates();
  122. return m_undoKeyStates != currentKeyState;
  123. }
  124. public:
  125. using CTrackViewSplineCtrls = AZStd::list<CTrackViewSplineCtrl*>;
  126. static CTrackViewSplineCtrl* FindControl(CTrackViewSplineCtrl* pCtrl)
  127. {
  128. if (!pCtrl)
  129. {
  130. return nullptr;
  131. }
  132. auto iter = AZStd::find(s_activeCtrls.begin(), s_activeCtrls.end(), pCtrl);
  133. if (iter == s_activeCtrls.end())
  134. {
  135. return nullptr;
  136. }
  137. return *iter;
  138. }
  139. static void RegisterControl(CTrackViewSplineCtrl* pCtrl)
  140. {
  141. if (!FindControl(pCtrl))
  142. {
  143. s_activeCtrls.push_back(pCtrl);
  144. }
  145. }
  146. static void UnregisterControl(CTrackViewSplineCtrl* pCtrl)
  147. {
  148. if (FindControl(pCtrl))
  149. {
  150. s_activeCtrls.remove(pCtrl);
  151. }
  152. }
  153. static CTrackViewSplineCtrls s_activeCtrls;
  154. private:
  155. class CSplineEntry
  156. {
  157. public:
  158. _smart_ptr<ISplineBackup> m_undo;
  159. _smart_ptr<ISplineBackup> m_redo;
  160. unsigned int m_trackId;
  161. };
  162. void SerializeSplines(_smart_ptr<ISplineBackup> CSplineEntry::* backup, bool bLoading)
  163. {
  164. const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
  165. CTrackViewSequence* sequence = sequenceManager->GetSequenceByEntityId(m_sequenceEntityId);
  166. AZ_Assert(sequence, "Expected valid sequence");
  167. if (!sequence)
  168. {
  169. return;
  170. }
  171. for (auto it = m_splineEntries.begin(); it != m_splineEntries.end(); ++it)
  172. {
  173. CSplineEntry& entry = *it;
  174. CTrackViewTrack* track = sequence->FindTrackById(entry.m_trackId);
  175. if (track)
  176. {
  177. ISplineInterpolator* pSpline = track->GetSpline();
  178. if (pSpline && bLoading)
  179. {
  180. pSpline->Restore(entry.*backup);
  181. }
  182. else if (pSpline)
  183. {
  184. (entry.*backup) = pSpline->Backup();
  185. }
  186. }
  187. }
  188. }
  189. AZ::EntityId m_sequenceEntityId;
  190. CTrackViewSplineCtrl* m_pCtrl;
  191. AZStd::vector<CSplineEntry> m_splineEntries;
  192. AZStd::vector<float> m_keyTimes;
  193. AZStd::vector<bool> m_undoKeyStates;
  194. AZStd::vector<bool> m_redoKeyStates;
  195. };
  196. CUndoTrackViewSplineCtrl::CTrackViewSplineCtrls CUndoTrackViewSplineCtrl::s_activeCtrls;
  197. CTrackViewSplineCtrl::CTrackViewSplineCtrl(QWidget* parent)
  198. : SplineWidget(parent)
  199. , m_bKeysFreeze(false)
  200. , m_bTangentsFreeze(false)
  201. , m_stashedRecordModeWhenDraggingTime(false)
  202. {
  203. CUndoTrackViewSplineCtrl::RegisterControl(this);
  204. }
  205. CTrackViewSplineCtrl::~CTrackViewSplineCtrl()
  206. {
  207. CUndoTrackViewSplineCtrl::UnregisterControl(this);
  208. }
  209. bool CTrackViewSplineCtrl::GetTangentHandlePts(QPoint& inTangentPt, QPoint& pt, QPoint& outTangentPt,
  210. int nSpline, int nKey, int nDimension)
  211. {
  212. ISplineInterpolator* pSpline = m_splines[nSpline].pSpline;
  213. CTrackViewTrack* pTrack = m_tracks[nSpline];
  214. float time = pSpline->GetKeyTime(nKey);
  215. ISplineInterpolator::ValueType value, tin, tout;
  216. ISplineInterpolator::ZeroValue(value);
  217. pSpline->GetKeyValue(nKey, value);
  218. pSpline->GetKeyTangents(nKey, tin, tout);
  219. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(nKey);
  220. if (pTrack->GetCurveType() == eAnimCurveType_TCBFloat)
  221. {
  222. ITcbKey tcbKey;
  223. keyHandle.GetKey(&tcbKey);
  224. Vec2 va, vb, vc;
  225. va.x = time - 1.0f;
  226. va.y = value[nDimension] - tin[nDimension];
  227. vb.x = time;
  228. vb.y = value[nDimension];
  229. vc.x = time + 1.0f;
  230. vc.y = value[nDimension] + tout[nDimension];
  231. inTangentPt = WorldToClient(va);
  232. pt = WorldToClient(vb);
  233. outTangentPt = WorldToClient(vc);
  234. Vec2 tinv, toutv;
  235. float maxLength = float(outTangentPt.x() - pt.x());
  236. tinv.x = float(inTangentPt.x() - pt.x());
  237. tinv.y = float(inTangentPt.y() - pt.y());
  238. toutv.x = float(outTangentPt.x() - pt.x());
  239. toutv.y = float(outTangentPt.y() - pt.y());
  240. tinv.Normalize();
  241. toutv.Normalize();
  242. tinv *= maxLength / (2 - tcbKey.easeto);
  243. toutv *= maxLength / (2 - tcbKey.easefrom);
  244. inTangentPt = pt + QPoint(int(tinv.x), int(tinv.y));
  245. outTangentPt = pt + QPoint(int(toutv.x), int(toutv.y));
  246. }
  247. else
  248. {
  249. AZ_Assert(pTrack->GetCurveType() == eAnimCurveType_BezierFloat, "Unexpected curve type %i", pTrack->GetCurveType());
  250. AZ_Assert(nDimension == 0, "Dimension is not zero");
  251. I2DBezierKey bezierKey;
  252. keyHandle.GetKey(&bezierKey);
  253. Vec2 va, vb, vc;
  254. va.x = time - tin[0];
  255. va.y = value[0] - tin[1];
  256. vb.x = time;
  257. vb.y = value[0];
  258. vc.x = time + tout[0];
  259. vc.y = value[0] + tout[1];
  260. inTangentPt = WorldToClient(va);
  261. pt = WorldToClient(vb);
  262. outTangentPt = WorldToClient(vc);
  263. }
  264. return true;
  265. }
  266. void CTrackViewSplineCtrl::ComputeIncomingTangentAndEaseTo(float& ds, float& easeTo, QPoint inTangentPt,
  267. int nSpline, int nKey, int nDimension)
  268. {
  269. ISplineInterpolator* pSpline = m_splines[nSpline].pSpline;
  270. CTrackViewTrack* pTrack = m_tracks[nSpline];
  271. float time = pSpline->GetKeyTime(nKey);
  272. ISplineInterpolator::ValueType value, tin, tout;
  273. ISplineInterpolator::ZeroValue(value);
  274. pSpline->GetKeyValue(nKey, value);
  275. pSpline->GetKeyTangents(nKey, tin, tout);
  276. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(nKey);
  277. ITcbKey tcbKey;
  278. keyHandle.GetKey(&tcbKey);
  279. // Get the control point.
  280. Vec2 tinv, vb;
  281. vb.x = time;
  282. vb.y = value[nDimension];
  283. QPoint pt = WorldToClient(vb);
  284. // Get the max length to compute the 'ease' value.
  285. float maxLength = float(WorldToClient(Vec2(vb.x + 1, vb.y)).x() - pt.x());
  286. QPoint tmp = inTangentPt - pt;
  287. tinv.x = float(tmp.x());
  288. tinv.y = float(tmp.y());
  289. // Compute the 'easeTo'.
  290. easeTo = 2.0f - maxLength / tinv.GetLength();
  291. // Compute the 'ds'.
  292. Vec2 va = ClientToWorld(inTangentPt);
  293. if (time < va.x + 0.000001f)
  294. {
  295. if (value[nDimension] > va.y)
  296. {
  297. ds = 1000000.0f;
  298. }
  299. else
  300. {
  301. ds = -1000000.0f;
  302. }
  303. }
  304. else
  305. {
  306. ds = (value[nDimension] - va.y) / (time - va.x);
  307. }
  308. }
  309. void CTrackViewSplineCtrl::ComputeOutgoingTangentAndEaseFrom(float& dd, float& easeFrom, QPoint outTangentPt,
  310. int nSpline, int nKey, int nDimension)
  311. {
  312. ISplineInterpolator* pSpline = m_splines[nSpline].pSpline;
  313. CTrackViewTrack* pTrack = m_tracks[nSpline];
  314. float time = pSpline->GetKeyTime(nKey);
  315. ISplineInterpolator::ValueType value, tin, tout;
  316. ISplineInterpolator::ZeroValue(value);
  317. pSpline->GetKeyValue(nKey, value);
  318. pSpline->GetKeyTangents(nKey, tin, tout);
  319. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(nKey);
  320. ITcbKey tcbKey;
  321. keyHandle.GetKey(&tcbKey);
  322. // Get the control point.
  323. Vec2 toutv, vb;
  324. vb.x = time;
  325. vb.y = value[nDimension];
  326. QPoint pt = WorldToClient(vb);
  327. // Get the max length to comute the 'ease' value.
  328. float maxLength = float(WorldToClient(Vec2(vb.x + 1, vb.y)).x() - pt.x());
  329. QPoint tmp = outTangentPt - pt;
  330. toutv.x = float(tmp.x());
  331. toutv.y = float(tmp.y());
  332. // Compute the 'easeFrom'.
  333. easeFrom = 2.0f - maxLength / toutv.GetLength();
  334. // Compute the 'dd'.
  335. Vec2 vc = ClientToWorld(outTangentPt);
  336. if (vc.x < time + 0.000001f)
  337. {
  338. if (value[nDimension] < vc.y)
  339. {
  340. dd = 1000000.0f;
  341. }
  342. else
  343. {
  344. dd = -1000000.0f;
  345. }
  346. }
  347. else
  348. {
  349. dd = (vc.y - value[nDimension]) / (vc.x - time);
  350. }
  351. }
  352. void CTrackViewSplineCtrl::AddSpline(ISplineInterpolator* pSpline, CTrackViewTrack* pTrack, const QColor& color)
  353. {
  354. QColor colorArray[4];
  355. colorArray[0] = colorArray[1] = colorArray[2] = colorArray[3] = color;
  356. AddSpline(pSpline, pTrack, colorArray);
  357. }
  358. void CTrackViewSplineCtrl::AddSpline(ISplineInterpolator* pSpline, CTrackViewTrack* pTrack, QColor anColorArray[4])
  359. {
  360. for (int i = 0; i < (int)m_splines.size(); i++)
  361. {
  362. if (m_splines[i].pSpline == pSpline)
  363. {
  364. return;
  365. }
  366. }
  367. SSplineInfo si;
  368. for (int nCurrentDimension = 0; nCurrentDimension < pSpline->GetNumDimensions(); nCurrentDimension++)
  369. {
  370. si.anColorArray[nCurrentDimension] = anColorArray[nCurrentDimension];
  371. }
  372. si.pSpline = pSpline;
  373. si.pDetailSpline = nullptr;
  374. m_splines.push_back(si);
  375. m_tracks.push_back(pTrack);
  376. m_bKeyTimesDirty = true;
  377. update();
  378. }
  379. void CTrackViewSplineCtrl::RemoveAllSplines()
  380. {
  381. m_tracks.clear();
  382. SplineWidget::RemoveAllSplines();
  383. }
  384. void CTrackViewSplineCtrl::MoveSelectedTangentHandleTo(const QPoint& point)
  385. {
  386. AZ_Assert(m_pHitSpline, "m_pHitSpline is null");
  387. AZ_Assert(m_nHitKeyIndex >= 0, "m_nHitKeyIndex is negative");
  388. AZ_Assert(m_bHitIncomingHandle >= 0, "m_bHitIncomingHandle is negative")
  389. // Set the custom flag to the key.
  390. int nRemoveFlags, nAddFlags;
  391. if (m_bHitIncomingHandle)
  392. {
  393. nRemoveFlags = SPLINE_KEY_TANGENT_IN_MASK;
  394. nAddFlags = SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_IN_SHIFT;
  395. }
  396. else
  397. {
  398. nRemoveFlags = SPLINE_KEY_TANGENT_OUT_MASK;
  399. nAddFlags = SPLINE_KEY_TANGENT_CUSTOM << SPLINE_KEY_TANGENT_OUT_SHIFT;
  400. }
  401. int flags = m_pHitSpline->GetKeyFlags(m_nHitKeyIndex);
  402. flags &= ~nRemoveFlags;
  403. flags |= nAddFlags;
  404. m_pHitSpline->SetKeyFlags(m_nHitKeyIndex, flags);
  405. // Adjust the incoming or outgoing tangent.
  406. int splineIndex;
  407. for (splineIndex = 0; splineIndex < m_splines.size(); ++splineIndex)
  408. {
  409. if (m_pHitSpline == m_splines[splineIndex].pSpline)
  410. {
  411. break;
  412. }
  413. }
  414. AZ_Assert(splineIndex < m_splines.size(), "splineIndex %i is out of range", splineIndex);
  415. CTrackViewTrack* pTrack = m_tracks[splineIndex];
  416. CTrackViewKeyHandle keyHandle = pTrack->GetKey(m_nHitKeyIndex);
  417. if (pTrack->GetCurveType() == eAnimCurveType_TCBFloat)
  418. {
  419. if (m_bHitIncomingHandle)
  420. {
  421. float ds, easeTo;
  422. ComputeIncomingTangentAndEaseTo(ds, easeTo, point, splineIndex, m_nHitKeyIndex, m_nHitDimension);
  423. // ease-to
  424. ITcbKey key;
  425. keyHandle.GetKey(&key);
  426. key.easeto += easeTo;
  427. if (key.easeto > 1)
  428. {
  429. key.easeto = 1.0f;
  430. }
  431. else if (key.easeto < 0)
  432. {
  433. key.easeto = 0;
  434. }
  435. keyHandle.SetKey(&key);
  436. // tin
  437. ISplineInterpolator::ValueType tin, tout;
  438. m_pHitSpline->GetKeyTangents(m_nHitKeyIndex, tin, tout);
  439. tin[m_nHitDimension] = ds;
  440. m_pHitSpline->SetKeyInTangent(m_nHitKeyIndex, tin);
  441. }
  442. else
  443. {
  444. float dd, easeFrom;
  445. ComputeOutgoingTangentAndEaseFrom(dd, easeFrom, point, splineIndex, m_nHitKeyIndex, m_nHitDimension);
  446. // ease-from
  447. ITcbKey key;
  448. keyHandle.GetKey(&key);
  449. key.easefrom += easeFrom;
  450. if (key.easefrom > 1)
  451. {
  452. key.easefrom = 1.0f;
  453. }
  454. else if (key.easefrom < 0)
  455. {
  456. key.easefrom = 0;
  457. }
  458. keyHandle.SetKey(&key);
  459. // tout
  460. ISplineInterpolator::ValueType tin, tout;
  461. m_pHitSpline->GetKeyTangents(m_nHitKeyIndex, tin, tout);
  462. tout[m_nHitDimension] = dd;
  463. m_pHitSpline->SetKeyOutTangent(m_nHitKeyIndex, tout);
  464. }
  465. }
  466. else
  467. {
  468. AZ_Assert(pTrack->GetCurveType() == eAnimCurveType_BezierFloat, "Unexpected curve type %i", pTrack->GetCurveType());
  469. AZ_Assert(m_nHitDimension == 0, "Hit dimension is not zero");
  470. Vec2 tp = ClientToWorld(point);
  471. if (m_bHitIncomingHandle)
  472. {
  473. // tin
  474. ISplineInterpolator::ValueType value, tin, tout;
  475. float time = m_pHitSpline->GetKeyTime(m_nHitKeyIndex);
  476. m_pHitSpline->GetKeyValue(m_nHitKeyIndex, value);
  477. m_pHitSpline->GetKeyTangents(m_nHitKeyIndex, tin, tout);
  478. tin[0] = time - tp.x;
  479. // Constrain the time range so that the time curve is always monotonically increasing.
  480. if (tin[0] < 0)
  481. {
  482. tin[0] = 0;
  483. }
  484. else if (m_nHitKeyIndex > 0
  485. && tin[0] > (time - m_pHitSpline->GetKeyTime(m_nHitKeyIndex - 1)))
  486. {
  487. tin[0] = time - m_pHitSpline->GetKeyTime(m_nHitKeyIndex - 1);
  488. }
  489. tin[1] = value[0] - tp.y;
  490. m_pHitSpline->SetKeyInTangent(m_nHitKeyIndex, tin);
  491. }
  492. else
  493. {
  494. // tout
  495. ISplineInterpolator::ValueType value, tin, tout;
  496. float time = m_pHitSpline->GetKeyTime(m_nHitKeyIndex);
  497. m_pHitSpline->GetKeyValue(m_nHitKeyIndex, value);
  498. m_pHitSpline->GetKeyTangents(m_nHitKeyIndex, tin, tout);
  499. tout[0] = tp.x - time;
  500. // Constrain the time range so that the time curve is always monotonically increasing.
  501. if (tout[0] < 0)
  502. {
  503. tout[0] = 0;
  504. }
  505. else if (m_nHitKeyIndex < m_pHitSpline->GetKeyCount() - 1
  506. && tout[0] > (m_pHitSpline->GetKeyTime(m_nHitKeyIndex + 1) - time))
  507. {
  508. tout[0] = m_pHitSpline->GetKeyTime(m_nHitKeyIndex + 1) - time;
  509. }
  510. tout[1] = tp.y - value[0];
  511. m_pHitSpline->SetKeyOutTangent(m_nHitKeyIndex, tout);
  512. }
  513. }
  514. SendNotifyEvent(SPLN_CHANGE);
  515. update();
  516. }
  517. void CTrackViewSplineCtrl::mouseMoveEvent(QMouseEvent* event)
  518. {
  519. const QPoint point = event->pos();
  520. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  521. if (!pSequence)
  522. {
  523. return;
  524. }
  525. CTrackViewSequenceNotificationContext context(pSequence);
  526. m_cMousePos = point;
  527. if (m_editMode == NothingMode)
  528. {
  529. switch (HitTest(point))
  530. {
  531. case HIT_SPLINE:
  532. setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE));
  533. break;
  534. case HIT_KEY:
  535. case HIT_TANGENT_HANDLE:
  536. setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK));
  537. break;
  538. default:
  539. unsetCursor();
  540. break;
  541. }
  542. }
  543. if (m_editMode == SelectMode)
  544. {
  545. unsetCursor();
  546. QRect rc(m_cMouseDownPos, point);
  547. rc = rc.normalized().intersected(m_rcSpline);
  548. m_rcSelect = rc;
  549. m_rubberBand->setGeometry(m_rcSelect);
  550. m_rubberBand->setVisible(true);
  551. }
  552. if (m_editMode == TimeMarkerMode)
  553. {
  554. unsetCursor();
  555. SetTimeMarker(XOfsToTime(point.x()));
  556. SendNotifyEvent(SPLN_TIME_CHANGE);
  557. }
  558. if (m_boLeftMouseButtonDown)
  559. {
  560. if (m_editMode == TrackingMode && point != m_cMouseDownPos)
  561. {
  562. m_startedDragging = true;
  563. GetIEditor()->RestoreUndo();
  564. m_pCurrentUndo = nullptr;
  565. StoreUndo();
  566. bool bAltClick = CheckVirtualKey(Qt::Key_Menu);
  567. bool bShiftClick = CheckVirtualKey(Qt::Key_Shift);
  568. bool bSpaceClick = CheckVirtualKey(Qt::Key_Space);
  569. Vec2 v0 = ClientToWorld(m_cMouseDownPos);
  570. Vec2 v1 = ClientToWorld(point);
  571. if (m_hitCode == HIT_TANGENT_HANDLE)
  572. {
  573. if (!m_bTangentsFreeze)
  574. {
  575. MoveSelectedTangentHandleTo(point);
  576. }
  577. }
  578. else if (!m_bKeysFreeze)
  579. {
  580. if (bAltClick && bShiftClick)
  581. {
  582. ValueScaleKeys(v0.y, v1.y);
  583. }
  584. else if (bAltClick)
  585. {
  586. TimeScaleKeys(m_fTimeMarker, v0.x, v1.x);
  587. }
  588. else if (bShiftClick)
  589. {
  590. // Constrains the move to the vertical direction.
  591. MoveSelectedKeys(Vec2(0, v1.y - v0.y), false);
  592. }
  593. else if (bSpaceClick)
  594. {
  595. // Reset to the original position.
  596. MoveSelectedKeys(Vec2(0, 0), false);
  597. }
  598. else
  599. {
  600. MoveSelectedKeys(v1 - v0, m_copyKeys);
  601. }
  602. }
  603. }
  604. }
  605. if (m_editMode == TrackingMode && GetNumSelected() == 1)
  606. {
  607. float time = 0;
  608. ISplineInterpolator::ValueType afValue;
  609. QString tipText;
  610. bool boFoundTheSelectedKey(false);
  611. for (size_t splineIndex = 0, endSpline = m_splines.size(); splineIndex < endSpline; ++splineIndex)
  612. {
  613. ISplineInterpolator* pSpline = m_splines[splineIndex].pSpline;
  614. CTrackViewTrack* pTrack = m_tracks[splineIndex];
  615. for (int i = 0; i < pSpline->GetKeyCount(); i++)
  616. {
  617. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  618. for (int nCurrentDimension = 0; nCurrentDimension < pSpline->GetNumDimensions(); nCurrentDimension++)
  619. {
  620. if (pSpline->IsKeySelectedAtDimension(i, nCurrentDimension))
  621. {
  622. time = pSpline->GetKeyTime(i);
  623. pSpline->GetKeyValue(i, afValue);
  624. if (pTrack->GetCurveType() == eAnimCurveType_TCBFloat)
  625. {
  626. ITcbKey key;
  627. keyHandle.GetKey(&key);
  628. tipText = QStringLiteral("t=%1 v=%2 / T=%3 C=%4 B=%5")
  629. .arg(time * m_fTooltipScaleX, 3, 'f').arg(afValue[nCurrentDimension] * m_fTooltipScaleY, 3, 'f', 2)
  630. .arg(key.tens, 3, 'f').arg(key.cont, 3, 'f').arg(key.bias, 3, 'f');
  631. }
  632. else
  633. {
  634. AZ_Assert(pTrack->GetCurveType() == eAnimCurveType_BezierFloat, "Unexpected curve type %i", pTrack->GetCurveType());
  635. ISplineInterpolator::ValueType tin, tout;
  636. pSpline->GetKeyTangents(i, tin, tout);
  637. tipText = QStringLiteral("t=%1 v=%2 / tin=(%3,%4) tout=(%5,%6)")
  638. .arg(time * m_fTooltipScaleX, 3, 'f').arg(afValue[0] * m_fTooltipScaleY, 3, 'f', 2)
  639. .arg(tin[0], 3, 'f').arg(tin[1], 3, 'f', 2).arg(tout[0], 3, 'f').arg(tout[1], 3, 'f', 2);
  640. }
  641. boFoundTheSelectedKey = true;
  642. break;
  643. }
  644. }
  645. if (boFoundTheSelectedKey)
  646. {
  647. break;
  648. }
  649. }
  650. }
  651. if (point != m_lastToolTipPos)
  652. {
  653. m_lastToolTipPos = point;
  654. m_tooltipText = tipText;
  655. update();
  656. }
  657. }
  658. switch (m_editMode)
  659. {
  660. case ScrollMode:
  661. {
  662. // Set the new scrolled coordinates
  663. const float ofsx = m_grid.origin.GetX() - (point.x() - m_cMouseDownPos.x()) / m_grid.zoom.GetX();
  664. const float ofsy = m_grid.origin.GetY() + (point.y() - m_cMouseDownPos.y()) / m_grid.zoom.GetY();
  665. SetScrollOffset(Vec2(ofsx, ofsy));
  666. m_cMouseDownPos = point;
  667. }
  668. break;
  669. case ZoomMode:
  670. {
  671. float ofsx = (point.x() - m_cMouseDownPos.x()) * 0.01f;
  672. float ofsy = (point.y() - m_cMouseDownPos.y()) * 0.01f;
  673. AZ::Vector2 z = m_grid.zoom;
  674. if (ofsx != 0)
  675. {
  676. z.SetX(max(z.GetX() * (1.0f + ofsx), 0.001f));
  677. }
  678. if (ofsy != 0)
  679. {
  680. z.SetY(max(z.GetY() * (1.0f + ofsy), 0.001f));
  681. }
  682. SetZoom(Vec2(z.GetX(), z.GetY()), m_cMouseDownPos);
  683. m_cMouseDownPos = point;
  684. }
  685. break;
  686. }
  687. }
  688. void CTrackViewSplineCtrl::AdjustTCB(float d_tension, float d_continuity, float d_bias)
  689. {
  690. CUndo undo("Modify Spline Keys TCB");
  691. ConditionalStoreUndo();
  692. SendNotifyEvent(SPLN_BEFORE_CHANGE);
  693. for (size_t splineIndex = 0, splineCount = m_splines.size(); splineIndex < splineCount; ++splineIndex)
  694. {
  695. ISplineInterpolator* pSpline = m_splines[splineIndex].pSpline;
  696. CTrackViewTrack* pTrack = m_tracks[splineIndex];
  697. if (pTrack->GetCurveType() != eAnimCurveType_TCBFloat)
  698. {
  699. continue;
  700. }
  701. for (int i = 0; i < (int)pSpline->GetKeyCount(); i++)
  702. {
  703. CTrackViewKeyHandle keyHandle = pTrack->GetKey(i);
  704. // If the key is selected in any dimension...
  705. for (
  706. int nCurrentDimension = 0;
  707. nCurrentDimension < pSpline->GetNumDimensions();
  708. nCurrentDimension++
  709. )
  710. {
  711. if (IsKeySelected(pSpline, i, nCurrentDimension))
  712. {
  713. ITcbKey key;
  714. keyHandle.GetKey(&key);
  715. // tension
  716. key.tens += d_tension;
  717. if (key.tens > 1)
  718. {
  719. key.tens = 1.0f;
  720. }
  721. else if (key.tens < -1)
  722. {
  723. key.tens = -1.0f;
  724. }
  725. // continuity
  726. key.cont += d_continuity;
  727. if (key.cont > 1)
  728. {
  729. key.cont = 1.0f;
  730. }
  731. else if (key.cont < -1)
  732. {
  733. key.cont = -1.0f;
  734. }
  735. // bias
  736. key.bias += d_bias;
  737. if (key.bias > 1)
  738. {
  739. key.bias = 1.0f;
  740. }
  741. else if (key.bias < -1)
  742. {
  743. key.bias = -1.0f;
  744. }
  745. keyHandle.SetKey(&key);
  746. OnUserCommand(ID_TANGENT_AUTO);
  747. break;
  748. }
  749. }
  750. }
  751. }
  752. SendNotifyEvent(SPLN_CHANGE);
  753. update();
  754. }
  755. void CTrackViewSplineCtrl::OnUserCommand(UINT cmd)
  756. {
  757. if (cmd == ID_TANGENT_UNIFY)
  758. {
  759. if (IsUnifiedKeyCurrentlySelected() == false)
  760. {
  761. ModifySelectedKeysFlags(SPLINE_KEY_TANGENT_ALL_MASK, SPLINE_KEY_TANGENT_UNIFIED);
  762. }
  763. else
  764. {
  765. ModifySelectedKeysFlags(SPLINE_KEY_TANGENT_ALL_MASK, SPLINE_KEY_TANGENT_BROKEN);
  766. }
  767. }
  768. else if (cmd == ID_FREEZE_KEYS)
  769. {
  770. m_bKeysFreeze = !m_bKeysFreeze;
  771. }
  772. else if (cmd == ID_FREEZE_TANGENTS)
  773. {
  774. m_bTangentsFreeze = !m_bTangentsFreeze;
  775. }
  776. else
  777. {
  778. SplineWidget::OnUserCommand(cmd);
  779. }
  780. }
  781. bool CTrackViewSplineCtrl::IsUnifiedKeyCurrentlySelected() const
  782. {
  783. for (size_t splineIndex = 0, splineCount = m_splines.size(); splineIndex < splineCount; ++splineIndex)
  784. {
  785. ISplineInterpolator* pSpline = m_splines[splineIndex].pSpline;
  786. if (pSpline == nullptr)
  787. {
  788. continue;
  789. }
  790. for (int i = 0; i < (int)pSpline->GetKeyCount(); i++)
  791. {
  792. // If the key is selected in any dimension...
  793. for (
  794. int nCurrentDimension = 0;
  795. nCurrentDimension < pSpline->GetNumDimensions();
  796. nCurrentDimension++
  797. )
  798. {
  799. if (IsKeySelected(pSpline, i, nCurrentDimension))
  800. {
  801. int flags = pSpline->GetKeyFlags(i);
  802. if ((flags & SPLINE_KEY_TANGENT_ALL_MASK) != SPLINE_KEY_TANGENT_UNIFIED)
  803. {
  804. return false;
  805. }
  806. }
  807. }
  808. }
  809. }
  810. return true;
  811. }
  812. void CTrackViewSplineCtrl::ClearSelection()
  813. {
  814. // In this case, we should deselect all keys, even ones in other tracks.
  815. // So this overriding is necessary.
  816. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  817. if (pSequence)
  818. {
  819. pSequence->DeselectAllKeys();
  820. }
  821. }
  822. ISplineCtrlUndo* CTrackViewSplineCtrl::CreateSplineCtrlUndoObject(std::vector<ISplineInterpolator*>& splineContainer)
  823. {
  824. return new CUndoTrackViewSplineCtrl(this, splineContainer);
  825. }
  826. void CTrackViewSplineCtrl::mousePressEvent(QMouseEvent* event)
  827. {
  828. if (GetIEditor()->GetAnimation()->GetSequence())
  829. {
  830. SplineWidget::mousePressEvent(event);
  831. if (m_editMode == TimeMarkerMode)
  832. {
  833. // turn off recording when dragging time
  834. m_stashedRecordModeWhenDraggingTime = GetIEditor()->GetAnimation()->IsRecordMode();
  835. GetIEditor()->GetAnimation()->SetRecording(false);
  836. }
  837. }
  838. }
  839. void CTrackViewSplineCtrl::mouseReleaseEvent(QMouseEvent* event)
  840. {
  841. if (GetIEditor()->GetAnimation()->GetSequence())
  842. {
  843. bool restoreRecordModeToTrue = (m_editMode == TimeMarkerMode && m_stashedRecordModeWhenDraggingTime);
  844. SplineWidget::mouseReleaseEvent(event);
  845. if (restoreRecordModeToTrue)
  846. {
  847. // restore recording when dragging time
  848. GetIEditor()->GetAnimation()->SetRecording(true);
  849. m_stashedRecordModeWhenDraggingTime = false; // reset stashed value
  850. }
  851. }
  852. }
  853. void CTrackViewSplineCtrl::mouseDoubleClickEvent(QMouseEvent* event)
  854. {
  855. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  856. if (sequence)
  857. {
  858. SplineWidget::mouseDoubleClickEvent(event);
  859. if (m_hitCode == HIT_SPLINE)
  860. {
  861. // HIT_SPLINE results in an added key that gets selected.
  862. // Use the selected keys to notify the sequence, key by key, of newly added keys
  863. CTrackViewKeyBundle addedKeys = sequence->GetSelectedKeys();
  864. for (int keyIdx = addedKeys.GetKeyCount(); --keyIdx >= 0; )
  865. {
  866. CTrackViewKeyHandle addedKey = addedKeys.GetKey(keyIdx);
  867. sequence->OnKeyAdded(addedKey);
  868. }
  869. }
  870. }
  871. }
  872. void CTrackViewSplineCtrl::keyPressEvent(QKeyEvent* event)
  873. {
  874. auto sequence = GetIEditor()->GetAnimation()->GetSequence();
  875. if (sequence)
  876. {
  877. // HAVE TO INCLUDE CASES FOR THESE IN THE ShortcutOverride handler in ::event() below
  878. if (event->key() == Qt::Key_S && m_playCallback)
  879. {
  880. m_playCallback();
  881. }
  882. else if (event->matches(QKeySequence::Delete))
  883. {
  884. CUndo undo("Delete Keys");
  885. SendNotifyEvent(SPLN_BEFORE_CHANGE);
  886. sequence->DeleteSelectedKeys();
  887. SendNotifyEvent(SPLN_CHANGE);
  888. update();
  889. }
  890. else if (event->matches(QKeySequence::Undo))
  891. {
  892. GetIEditor()->Undo();
  893. }
  894. else if (event->matches(QKeySequence::Redo))
  895. {
  896. GetIEditor()->Redo();
  897. }
  898. else
  899. {
  900. SplineWidget::keyPressEvent(event);
  901. }
  902. }
  903. }
  904. bool CTrackViewSplineCtrl::event(QEvent* e)
  905. {
  906. if (e->type() == QEvent::ShortcutOverride)
  907. {
  908. // since we respond to the following things, let Qt know so that shortcuts don't override us
  909. bool respondsToEvent = false;
  910. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
  911. if (keyEvent->key() == Qt::Key_S)
  912. {
  913. respondsToEvent = true;
  914. }
  915. else
  916. {
  917. respondsToEvent = keyEvent->matches(QKeySequence::Delete) ||
  918. keyEvent->matches(QKeySequence::Undo) ||
  919. keyEvent->matches(QKeySequence::Redo);
  920. }
  921. if (respondsToEvent)
  922. {
  923. e->accept();
  924. return true;
  925. }
  926. }
  927. return SplineWidget::event(e);
  928. }
  929. void CTrackViewSplineCtrl::wheelEvent(QWheelEvent* event)
  930. {
  931. if (GetIEditor()->GetAnimation()->GetSequence())
  932. {
  933. SplineWidget::wheelEvent(event);
  934. }
  935. }
  936. void CTrackViewSplineCtrl::SelectKey(ISplineInterpolator* pSpline, int nKey, int nDimension, bool bSelect)
  937. {
  938. SplineWidget::SelectKey(pSpline, nKey, nDimension, bSelect);
  939. GetIEditor()->GetAnimation()->GetSequence()->OnKeySelectionChanged();
  940. }
  941. void CTrackViewSplineCtrl::SelectRectangle(const QRect& rc, bool bSelect)
  942. {
  943. SplineWidget::SelectRectangle(rc, bSelect);
  944. GetIEditor()->GetAnimation()->GetSequence()->OnKeySelectionChanged();
  945. }
  946. void CTrackViewSplineCtrl::SetPlayCallback(const std::function<void()>& callback)
  947. {
  948. m_playCallback = callback;
  949. }