Radium Engine  1.5.0
Timeline.cpp
1 #include "ui_Timeline.h"
2 #include <Gui/Timeline/Timeline.hpp>
3 
4 #include <QEvent>
5 #include <QMessageBox>
6 #include <QPainter>
7 #include <QTimer>
8 #include <QWheelEvent>
9 
10 #include <Core/Animation/KeyFramedValueInterpolators.hpp>
11 #include <Core/Utils/Log.hpp>
12 
13 #include <Engine/RadiumEngine.hpp>
14 #include <Engine/Rendering/RenderObject.hpp>
15 #include <Engine/Rendering/RenderObjectManager.hpp>
16 #include <Engine/Scene/Component.hpp>
17 #include <Engine/Scene/Entity.hpp>
18 #include <Engine/Scene/SignalManager.hpp>
19 
20 #include <Gui/Timeline/HelpDialog.hpp>
21 
22 using namespace Ra::Core::Utils;
23 
24 namespace Ra::Gui {
25 
26 Timeline::Timeline( QWidget* parent ) : QDialog( parent ), ui( new Ui::Timeline ) {
27  ui->setupUi( this );
28 
29  // first draw of ruler with current width (default in Timeline.ui) of dialog
30  ui->m_scrollArea->onDrawRuler( width() - 2 ); // left/right border width = 2 *1 pixel
31 
32  // --- SET INTERNAL REFERENCES ---
33  ui->frame_timescale->setScrollArea( ui->m_scrollArea );
34  ui->frame_selector->setTimelineUi( ui );
35 
36  // --- CREATE INTERNAL CONNECTIONS ---
37  connect( ui->frame_selector,
38  &TimelineFrameSelector::cursorChanged,
39  this,
40  &Timeline::updateKeyFrames );
41  connect( ui->frame_selector,
42  &TimelineFrameSelector::keyFrameAdded,
43  this,
44  &Timeline::onAddingKeyFrame );
45  connect( ui->frame_selector,
46  &TimelineFrameSelector::keyFrameDeleted,
47  this,
48  &Timeline::onRemovingKeyFrame );
49  connect( ui->frame_selector,
50  &TimelineFrameSelector::keyFrameChanged,
51  this,
52  &Timeline::onChangingKeyFrame );
53  connect( ui->frame_selector,
54  &TimelineFrameSelector::keyFrameMoved,
55  this,
56  &Timeline::onMovingKeyFrame );
57  connect( ui->frame_selector,
58  &TimelineFrameSelector::keyFramesMoved,
59  this,
60  &Timeline::onMovingKeyFrames );
61 
62  // --- CONNECT INTERNAL SIGNALS TO EXTERNAL SIGNALS ---
63  connect( ui->toolButton_playPause, &QToolButton::toggled, this, &Timeline::playClicked );
64  connect(
65  ui->frame_selector, &TimelineFrameSelector::cursorChanged, this, &Timeline::cursorChanged );
66  connect(
67  ui->frame_selector, &TimelineFrameSelector::startChanged, this, &Timeline::startChanged );
68  connect( ui->frame_selector, &TimelineFrameSelector::endChanged, this, &Timeline::endChanged );
69  connect( ui->frame_selector,
70  &TimelineFrameSelector::durationChanged,
71  this,
72  &Timeline::durationChanged );
73 
74  // --- DEAL WITH OBJET REMOVAL ---
75  auto signalManager = Ra::Engine::RadiumEngine::getInstance()->getSignalManager();
76 
77  auto& entityAddedObs = signalManager->getEntityCreatedNotifier();
78  m_entityAddObserverId =
79  entityAddedObs.attach( [this]( const Ra::Engine::Scene::ItemEntry& entry ) {
80  auto it = std::find_if(
81  m_entityKeyFrames.begin(), m_entityKeyFrames.end(), [entry]( const auto& frames ) {
82  return entry.m_entity == frames.first;
83  } );
84  if ( it != m_entityKeyFrames.end() ) { m_entityKeyFrames.erase( it ); }
85  } );
86 
87  auto& entityRemovedObs = signalManager->getEntityDestroyedNotifier();
88  m_entityRemoveObserverId =
89  entityRemovedObs.attach( [this]( const Ra::Engine::Scene::ItemEntry& entry ) {
90  auto it = std::find_if(
91  m_componentKeyFrames.begin(),
92  m_componentKeyFrames.end(),
93  [entry]( const auto& frames ) { return entry.m_component == frames.first; } );
94  if ( it != m_componentKeyFrames.end() ) { m_componentKeyFrames.erase( it ); }
95  } );
96 
97  auto& roAddedObs = signalManager->getRenderObjectCreatedNotifier();
98  m_roAddObserverId = roAddedObs.attach( [this]( const Ra::Engine::Scene::ItemEntry& entry ) {
99  auto it = std::find_if(
100  m_renderObjectKeyFrames.begin(),
101  m_renderObjectKeyFrames.end(),
102  [entry]( const auto& frames ) { return entry.m_roIndex == frames.first; } );
103  if ( it != m_renderObjectKeyFrames.end() ) { m_renderObjectKeyFrames.erase( it ); }
104  } );
105 }
106 
107 void Timeline::detachFromEngine() {
108  // Lifetime of Timeline (as a gui object) is hard to predict. Check all pointer and properties.
109  if ( auto engine = Ra::Engine::RadiumEngine::getInstance() ) {
110  if ( auto signalManager = engine->getSignalManager() ) {
111  if ( m_entityAddObserverId != -1 ) {
112  auto& obs = signalManager->getEntityCreatedNotifier();
113  obs.detach( m_entityAddObserverId );
114  m_entityAddObserverId = -1;
115  }
116  if ( m_entityRemoveObserverId != -1 ) {
117  auto& obs = signalManager->getEntityDestroyedNotifier();
118  obs.detach( m_entityRemoveObserverId );
119  m_entityRemoveObserverId = -1;
120  }
121  if ( m_roAddObserverId ) {
122  auto& obs = signalManager->getRenderObjectCreatedNotifier();
123  obs.detach( m_roAddObserverId );
124  m_roAddObserverId = -1;
125  }
126  }
127  }
128 }
129 
130 Timeline::~Timeline() {
131  detachFromEngine();
132  delete ui;
133 }
134 
135 void Timeline::selectionChanged( const Ra::Engine::Scene::ItemEntry& ent ) {
136  // enables ui if any keyframe
137  auto enableUI = [this]( bool enable ) {
138  ui->comboBox_attribute->setEnabled( enable );
139  ui->toolButton_keyFrame->setEnabled( enable );
140  ui->m_nbKeyFramesSpin->setEnabled( enable );
141  ui->m_removeKeyFrameButton->setEnabled( enable );
142  ui->pushButton_editAttribute->setEnabled( enable );
143  };
144  // reset ui
145  ui->comboBox_attribute->blockSignals( true );
146  ui->comboBox_attribute->clear();
147  ui->comboBox_attribute->blockSignals( false );
148  enableUI( false );
149  onClearKeyFrames();
151  // collects keyframedvalue name; if first one then display times and register as current
152  auto registerFrames = [this]( const Ra::Core::Animation::KeyFramedValueController& frame,
153  const std::string& prefix ) {
154  ui->comboBox_attribute->addItem( QString( ( prefix + frame.m_name ).c_str() ) );
155  if ( ui->comboBox_attribute->count() == 1 ) {
156  const auto times = frame.m_value->getTimes();
157  for ( const auto& t : times ) {
158  ui->frame_selector->onAddingKeyFrame( t, false );
159  }
160  m_current = frame;
161  }
162  };
163  // checks if the given keyframedvalue map contains the given key.
164  // if so, registers all keyframedvalues with the given prefix.
165 #define REGISTER_KEYFRAMED_VALUES( map, key, prefix ) \
166  { \
167  auto it = map.find( key ); \
168  if ( it != map.end() ) { \
169  for ( const auto& keyFramedValue : it->second ) { \
170  registerFrames( keyFramedValue, prefix ); \
171  } \
172  } \
173  }
174  // register keyframes for the Entity
175  if ( ent.m_entity == nullptr ) { return; }
176  const std::string& entityName = ent.m_entity->getName();
177  REGISTER_KEYFRAMED_VALUES( m_entityKeyFrames, ent.m_entity, entityName + "::" );
178  // register keyframes for the Component
179  if ( ent.m_component == nullptr ) {
180  enableUI( ui->comboBox_attribute->count() > 0 );
181  return;
182  }
183  const std::string& compName = ent.m_component->getName();
184  REGISTER_KEYFRAMED_VALUES(
185  m_componentKeyFrames, ent.m_component, entityName + "::" + compName + "::" );
186  // register keyframes for the RenderObject
187  if ( ent.m_roIndex == Ra::Core::Utils::Index::Invalid() ) {
188  enableUI( ui->comboBox_attribute->count() > 0 );
189  return;
190  }
191  const auto& roMngr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager();
192  const std::string roName =
193  roMngr->getRenderObject( ent.m_roIndex )->getName() + "_" + std::to_string( ent.m_roIndex );
194  REGISTER_KEYFRAMED_VALUES( m_renderObjectKeyFrames,
195  ent.m_roIndex,
196  entityName + "::" + compName + "::" + roName + "::" );
197 #undef REGISTER_KEYFRAMED_VALUES
198  if ( ui->comboBox_attribute->count() > 0 ) {
199  enableUI( true );
200  ui->comboBox_attribute->setCurrentIndex( 0 );
201  }
202 }
203 
204 void Timeline::registerKeyFramedValue(
206  const Ra::Core::Animation::KeyFramedValueController& keyFramedValueController ) {
207  auto& values = m_entityKeyFrames[ent];
208  auto& name = keyFramedValueController.m_name;
209  auto it = std::find_if( values.begin(), values.end(), [&name]( const auto& frame ) {
210  return frame.m_name == name;
211  } );
212  if ( it != values.end() ) {
213  LOG( logWARNING ) << "[Timeline] Already existing KeyFramedValue: " << name
214  << "\" on Entity \"" << ent->getName() << "\". Will not register."
215  << std::endl;
216  return;
217  }
218  values.push_back( keyFramedValueController );
219  selectionChanged( Engine::Scene::ItemEntry( ent ) );
220 }
221 
222 void Timeline::unregisterKeyFramedValue( Ra::Engine::Scene::Entity* ent, const std::string& name ) {
223  auto& values = m_entityKeyFrames[ent];
224  auto it = std::find_if( values.begin(), values.end(), [&name]( const auto& frame ) {
225  return frame.m_name == name;
226  } );
227  if ( it != values.end() ) { values.erase( it ); }
228 }
229 
230 void Timeline::registerKeyFramedValue(
232  const Ra::Core::Animation::KeyFramedValueController& keyFramedValueController ) {
233  auto& values = m_componentKeyFrames[comp];
234  auto name = keyFramedValueController.m_name;
235  auto it = std::find_if( values.begin(), values.end(), [name]( const auto& frame ) {
236  return frame.m_name == name;
237  } );
238  if ( it != values.end() ) {
239  LOG( logWARNING ) << "[Timeline] Already existing KeyFramedValue: \"" << name
240  << "\" on Component \"" << comp->getName() << "\". Will not register."
241  << std::endl;
242  return;
243  }
244  values.push_back( keyFramedValueController );
245  selectionChanged( Engine::Scene::ItemEntry( comp->getEntity(), comp ) );
246 }
247 
248 void Timeline::unregisterKeyFramedValue( Ra::Engine::Scene::Component* comp,
249  const std::string& name ) {
250  auto& values = m_componentKeyFrames[comp];
251  auto it = std::find_if( values.begin(), values.end(), [&name]( const auto& frame ) {
252  return frame.m_name == name;
253  } );
254  if ( it != values.end() ) { values.erase( it ); }
255 }
256 
257 void Timeline::registerKeyFramedValue(
258  Ra::Core::Utils::Index roIdx,
259  const Ra::Core::Animation::KeyFramedValueController& keyFramedValueController ) {
260  auto& values = m_renderObjectKeyFrames[roIdx];
261  auto name = keyFramedValueController.m_name;
262  auto it = std::find_if( values.begin(), values.end(), [&name]( const auto& frame ) {
263  return frame.m_name == name;
264  } );
265  if ( it != values.end() ) {
266  LOG( logWARNING ) << "[Timeline] Already existing KeyFramedValue: " << name
267  << "\" on RenderObject \"" << roIdx << "\". Will not register."
268  << std::endl;
269  return;
270  }
271  values.push_back( keyFramedValueController );
272  auto roMgr = Engine::RadiumEngine::getInstance()->getRenderObjectManager();
273  auto RO = roMgr->getRenderObject( roIdx );
274  auto comp = RO->getComponent();
275  selectionChanged( Engine::Scene::ItemEntry( comp->getEntity(), comp, roIdx ) );
276 }
277 
278 void Timeline::unregisterKeyFramedValue( Ra::Core::Utils::Index roIdx, const std::string& name ) {
279  auto& values = m_renderObjectKeyFrames[roIdx];
280  auto it = std::find_if( values.begin(), values.end(), [&name]( const auto& frame ) {
281  return frame.m_name == name;
282  } );
283  if ( it != values.end() ) { values.erase( it ); }
284 }
285 
286 Scalar Timeline::getTime() const {
287  return Scalar( ui->m_cursorSpin->value() );
288 }
289 
290 void Timeline::onChangeStart( Scalar time ) {
291  ui->frame_selector->onChangeStart( time, false );
292 }
293 
294 void Timeline::onChangeEnd( Scalar time ) {
295  ui->frame_selector->onChangeEnd( time, false );
296 }
297 
298 void Timeline::onChangeDuration( Scalar time ) {
299  ui->frame_selector->onChangeDuration( time, false );
300 }
301 
302 void Timeline::onChangeCursor( Scalar time ) {
303  if ( Ra::Core::Math::areApproxEqual( time, Scalar( ui->m_cursorSpin->value() ) ) ) { return; }
304  ui->frame_selector->onChangeCursor( time, false );
305  updateKeyFrames( time );
306 }
307 
308 void Timeline::onSetPlay( bool play ) {
309  ui->toolButton_playPause->setChecked( play );
310 }
311 
312 // todo : ctrlDown = shiftDown = false, on focus event (initialize state)
313 void Timeline::resizeEvent( QResizeEvent* event ) {
314  ui->m_scrollArea->onDrawRuler( event->size().width() - 2 );
315 }
316 
317 void Timeline::on_pingPong_toggled( bool checked ) {
318  emit setPingPong( checked );
319 }
320 
321 void Timeline::updateKeyFrames( Scalar time ) {
322  auto update = [time]( auto& keyFrames ) {
323  for ( auto& KF : keyFrames ) {
324  for ( auto& kf : KF.second ) {
325  kf.updateKeyFrame( time );
326  }
327  }
328  };
329  update( m_entityKeyFrames );
330  update( m_componentKeyFrames );
331  update( m_renderObjectKeyFrames );
332 }
333 
334 void Timeline::onClearKeyFrames() {
335  ui->frame_selector->onClearKeyFrames();
336 }
337 
338 void Timeline::onAddingKeyFrame( Scalar time ) {
339  if ( m_current.m_value ) {
340  m_current.insertKeyFrame( time );
341  emit keyFrameAdded( time );
342  }
343 }
344 
345 void Timeline::onRemovingKeyFrame( size_t i ) {
346  if ( m_current.m_value ) {
347  const Scalar time = m_current.m_value->getTimes()[i];
348  if ( m_current.m_value->removeKeyFrame( i ) ) {
349  m_current.updateKeyFrame( time );
350  emit keyFrameDeleted( i );
351  }
352  else {
353  LOG( logWARNING ) << "[Timeline] Error: Cannot remove keyFrame at "
354  << Scalar( ui->m_cursorSpin->value() );
355  }
356  }
357 }
358 
359 void Timeline::onChangingKeyFrame( size_t i ) {
360  if ( m_current.m_value ) {
361  Scalar time = m_current.m_value->getTimes()[i];
362  m_current.insertKeyFrame( time );
363  emit keyFrameChanged( i );
364  }
365 }
366 
367 void Timeline::onMovingKeyFrame( size_t i, Scalar time1 ) {
368  if ( m_current.m_value ) {
369  if ( !Ra::Core::Math::areApproxEqual( m_current.m_value->getTimes()[i], time1 ) ) {
370  m_current.m_value->moveKeyFrame( i, time1 );
371  m_current.updateKeyFrame( time1 );
372  emit keyFrameMoved( i, time1 );
373  }
374  }
375 }
376 
377 void Timeline::onMovingKeyFrames( size_t first, Scalar offset ) {
378  if ( m_current.m_value ) {
379  auto times = m_current.m_value->getTimes();
380  const Scalar time = times[first];
381  if ( offset < 0 ) {
382  for ( int i = 0; i < int( times.size() ); ++i ) {
383  const Scalar t = times[i];
384  if ( t >= time ) { m_current.m_value->moveKeyFrame( i, t + offset ); }
385  }
386  }
387  else // go from the end to ensure we do not mess up keyframes
388  {
389  for ( int i = times.size() - 1; i >= 0; --i ) {
390  const Scalar t = times[i];
391  if ( t >= time ) { m_current.m_value->moveKeyFrame( i, t + offset ); }
392  }
393  }
394  m_current.updateKeyFrame( time );
395  emit keyFramesMoved( first, offset );
396  }
397 }
398 
399 void Timeline::on_comboBox_attribute_currentTextChanged( const QString& arg1 ) {
400  onClearKeyFrames();
401  std::vector<Ra::Core::Animation::KeyFramedValueController> list;
402  const QStringList names = arg1.split( "::" );
403 
404  // prints a warning message for the attribute and returns
405 #define LOG_AND_RETURN \
406  LOG( logWARNING ) << "[Timeline] Error: attribute \"" << arg1.toStdString() \
407  << "\"'s name is not conform."; \
408  return
409 
410  // Checks if the given vector of KeyFramedValues contains a KeyFramedValue
411  // with the given name.
412  // If so, set the current frame to this KeyFramedValue;
413  // If not, prints a warning message for the attribute and returns.
414 #define GET_KEYFRAMEDVALUE( list, name ) \
415  { \
416  auto it = std::find_if( list.begin(), list.end(), [&name]( const auto& frame ) { \
417  return frame.m_name == name; \
418  } ); \
419  if ( it == list.end() ) { LOG_AND_RETURN; } \
420  m_current = *it; \
421  }
422 
423  // Checks if the given map has an element fulfilling lambda.
424  // If so, set list to the associated vector of KeyFramedValues.
425  // If not, prints a warning message for the attribute and returns.
426 #define GET_KEYFRAMEDVALUE_LIST( map, lambda ) \
427  { \
428  auto keyFramedValues = std::find_if( map.begin(), map.end(), lambda ); \
429  if ( keyFramedValues == map.end() ) { LOG_AND_RETURN; } \
430  list = keyFramedValues->second; \
431  }
432 
433  switch ( names.size() ) {
434  case 2: {
435  const std::string entityName = names.at( 0 ).toStdString();
436  const std::string frameName = names.at( 1 ).toStdString();
437  auto lambda = [entityName]( const auto& frame ) {
438  return frame.first->getName() == entityName;
439  };
440  GET_KEYFRAMEDVALUE_LIST( m_entityKeyFrames, lambda );
441  GET_KEYFRAMEDVALUE( list, frameName );
442  } break;
443  case 3: {
444  const std::string compName = names.at( 1 ).toStdString();
445  const std::string frameName = names.at( 2 ).toStdString();
446  auto lambda = [compName]( const auto& frame ) {
447  return frame.first->getName() == compName;
448  };
449  GET_KEYFRAMEDVALUE_LIST( m_componentKeyFrames, lambda );
450  GET_KEYFRAMEDVALUE( list, frameName );
451  } break;
452  case 4: {
453  const QStringList fullRoName = names.at( 2 ).split( '_' );
454  bool ok;
455  const auto roIdx = fullRoName.last().toInt( &ok );
456  if ( !ok ) { LOG_AND_RETURN; }
457  auto lambda = [&roIdx]( const auto& frame ) { return frame.first == roIdx; };
458  const std::string frameName = names.at( 3 ).toStdString();
459  GET_KEYFRAMEDVALUE_LIST( m_renderObjectKeyFrames, lambda );
460  GET_KEYFRAMEDVALUE( list, frameName );
461  } break;
462  default:
463  LOG_AND_RETURN;
464  }
465 #undef GET_KEYFRAMEDVALUE_LIST
466 #undef GET_KEYFRAMEDVALUE
467 #undef LOG_AND_RETURN
468 
469  // update ui
470  const auto times = m_current.m_value->getTimes();
471  for ( const auto& t : times ) {
472  ui->frame_selector->onAddingKeyFrame( t, false );
473  }
474 }
475 
476 void Timeline::on_pushButton_editAttribute_clicked() {
477  // TODO
478 }
479 
480 void Timeline::on_toolButton_help_clicked() {
481  static bool is_showing = false;
482  if ( !is_showing ) {
483  HelpDialog* dialog = new HelpDialog( this );
484  dialog->show();
485  is_showing = true;
486  connect( dialog, &HelpDialog::closed, [=]() { is_showing = false; } );
487  }
488 }
489 
490 } // namespace Ra::Gui
virtual std::vector< Scalar > getTimes() const =0
A component is an element that can be updated by a system. It is also linked to some other components...
Definition: Component.hpp:31
virtual const std::string & getName() const
Return the component's name.
Definition: Component.hpp:56
virtual Entity * getEntity() const
Return the entity the component belongs to.
Definition: Component.hpp:53
An entity is an scene element. It ties together components with a transform.
Definition: Entity.hpp:23
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.
Definition: Math.hpp:42
Ra::Core::Utils::Index m_roIndex
RO index of the represented object.
Definition: ItemEntry.hpp:67
Entity * m_entity
The entity represented by the item, or owning the object represented.
Definition: ItemEntry.hpp:60