Timeline And Keyframes
The Ra::Gui::Timeline
class provides display and management of Ra::Core::Animation::KeyFramedValue
s through the Ra::Core::Animation::KeyFramedValueController
class.
The Timeline UI
The Timeline UI is a widget allowing to manipulate time and display animation keyframes: The central part of the UI shows the time space, in which we have:
- the time scale on the bottom part,
- the time window drawn as a green to red sheet between movable widgets defining its start-time and end-time,
- the current time drawn as a blue vertical line,
- the time values, drawn as yellow vertical lines, where a keyframe is defined for the selected KeyFramedValue.
On the upper-left corner, one can define the time window for time flow, i.e.
- define the start-time and end-time for time flow,
- change the current time value,
- change the maximal time display.
On the upper-middle part, one can manipulate time flow, i.e.
- reset time to the start-time or the end-time,
- get to the previous or next keyframe time,
- make time flow,
- manage the time flow policy (loop-around or forward-backward).
On the upper-right part, one can manipulate animation keyframes, i.e.
- choose the one to display and manipulate for the selected object,
- add a new keyframe or update the one at the current time,
- see the current number of keyframes,
- remove the current keyframe,
- [coming soon] open the keyframe editor.
On the upper-far-right corner, one can open the help window, displaying shortcuts and hints on how to manipulate the time space and the selected KeyFramedValue:
Display animated data using the Timeline
Ra::Core::Animation::KeyFramedValueController
s must be registered into the Ra::Gui::Timeline
, which can be done using the Ra::Gui::Timeline::registerKeyFramedValue
methods, binding them to the Ra::Engine::Scene::Entity
, Ra::Engine::Scene::Component
or Ra::Engine::Rendering::RenderObject
they belong to.
Binding "Static" KeyFramedValue
Those are Ra::Core::Animation::KeyFramedValue
s that are an explicit part of a Ra::Engine::Scene::Entity
, Ra::Engine::Scene::Component
or Ra::Engine::Rendering::RenderObject
data, either filled upon construction or through the Ra::Gui::Timeline
. Static Ra::Core::Animation::KeyFramedValue
s must be registered in the Ra::Gui::Timeline
after the object's construction. They usually are not bound to an UpdateCallback function since the object they belong to usually calls Ra::Core::Animation::KeyFramedValue::at
to query the current value.
Example Usage:
{
:
Ra::Engine::Scene::Component( name, entity )
, m_keyframes( value, 0_ra )
, m_currentValue( value )
{}
void setTime( Scalar t ) {
m_currentValue = m_keyframes.at( t, Ra::Core::Animation::linearInterpolate<Scalar> );
update();
}
private:
void update() {
}
Scalar m_currentValue;
};
{
:
Ra::Engine::Scene::System()
, m_timeline( timeline )
{}
auto comp = new MyComponentWithKeyFrame( "MyComponentWithKeyFrame_", entity );
if ( m_timeline )
{
auto inserter = [comp](
const Scalar& t ) {
comp->m_keyframes.insertKeyFrame( t, comp->m_keyframes.at( t ) + 1_ra );
};
m_timeline->registerKeyFramedValue( comp, KeyFramedValueController( &comp->m_keyframes, "KeyFrame", inserter ) );
}
}
for ( auto compEntry : m_components )
{
auto comp = static_cast<MyComponentWithKeyFrame*>( compEntry.second );
auto func =
std::bind( &MyComponentWithKeyFrame::setTime, comp, frameInfo.m_animationTime );
auto task = std::make_unique<Ra::Core::FunctionTask>( func, "MyUpdateTask" );
}
}
};
This class allows tasks to be registered and then executed in parallel on separate threads.
TaskId registerTask(std::unique_ptr< Task > task)
A component is an element that can be updated by a system. It is also linked to some other components...
virtual void initialize()=0
Pure virtual method to be overridden by any component. When this method is called you are guaranteed ...
An entity is an scene element. It ties together components with a transform.
virtual void registerComponent(const Entity *entity, Component *component)
virtual void generateTasks(Core::TaskQueue *taskQueue, const Engine::FrameInfo &frameInfo)=0
Pure virtual method to be overridden by any system. Must register in taskQueue the operations that mu...
The Timeline class provides display and management of time, as well as keyframes.
hepler function to manage enum as underlying types in VariableSet
Structure passed to each system before they fill the task queue.
Binding "Dynamic" KeyFramedValues
Those are Ra::Core::Animation::KeyFramedValue
s that are not part of an Ra::Engine::Scene::Entity
, Ra::Engine::Scene::Component
or Ra::Engine::Rendering::RenderObject
data, but are used to keyframe some of its data. Dynamic Ra::Core::Animation::KeyFramedValue
s must be created for and owned by the UI, and registered in the Ra::Gui::Timeline
. They are usually bound to an UpdateCallback function since they have to update the object's data they are linked to.
Example Usage:
struct MyComponent : public Component {
:
Ra::Engine::Scene::Component( name, entity )
, m_currentValue( value )
{}
void initialize() override {}
Scalar m_currentValue;
};
struct MyWidget : public QWidget {
Q_OBJECT
MyWidget( QWigdet* parent = nulltpr ) : QWidget( parent ) {
m_keyFrameValueCheckbox = new QCheckBox( "Keyframe current Value", this );
}
QCheckBox* m_keyFrameValueCheckbox;
};
Q_OBJECT
Q_RADIUM_PLUGIN_METADATA
MyPlugin() = default;
~MyPlugin() {
for ( auto kf : m_keyframes )
{ delete kf.second; }
}
m_timeline = context.m_timeline;
m_selectionManager = context.m_selectionManager;
if ( m_selectionManager )
{
connect( m_selectionManager, &Ra::Gui::SelectionManager::currentChanged, this, &MyPlugin::onCurrentChanged );
}
}
return true;
}
m_widget = new MyWidget;
connect( m_widget->m_keyFrameValueCheckbox, &QCheckBox::toggled, this, &MyPlugin::keyFrameValue );
return m_widget;
}
QMenu*
getMenu()
override {
return nullptr; }
QAction*
getAction(
int id )
override {
return nullptr; }
void onCurrentChanged( const QModelIndex& current, const QModelIndex& prev ) {
if ( m_selectionManager->hasSelection() )
{
m_current =
dynamic_cast<MyComponent*
>( ent.
m_component );
}
else
{
m_current = nullptr;
}
m_widget->m_keyFrameValueCheckbox->setChecked( m_keyframes.find( m_current ) != m_keyframes.end() );
}
void keyFrameValue( bool on ) {
if ( m_current == nullptr ) { return; }
if ( on )
{
auto keyframes =
new KeyFramedValue( m_current->m_currentValue, 0, interpolate );
auto inserter = [keyframes](
const Scalar& t ) {
keyframes->insertKeyFrame( t, keyframes->at( t ) + 1_ra );
};
Scalar& value = &m_current->m_currentValue;
auto updater = [keyframes, &value]( const Scalar& t ) {
value = keyframes->at( t );
};
m_keyframes.push_back( keyframes );
m_timeline->registerKeyFramedValue( m_current, KeyFramedValueController( keyframes, "KeyFrame", inserter, updater ) );
}
else
{
m_timeline->unregisterKeyFramedValue( m_current, "KeyFrame" );
}
};
MyWidget* m_widget{nullptr};
MyComponent* m_current;
};
Data passed to the plugin constructor.
Interface class for Radiums plugins.
virtual void registerPlugin(const Context &context)=0
Pass arguments for plugin initialization. This method must create and register all the services that ...
virtual QAction * getAction(int id)=0
Returns the action to be added to the ui and then returns it.
virtual QMenu * getMenu()=0
Creates to menu to be added to the ui and then returns it.
virtual QWidget * getWidget()=0
Creates the widget to be added to the ui and then returns it. If connections are needed (between plug...
virtual bool doAddWidget(QString &name)=0
Tells wether the plugin wants to add a widget (inside the UI tab widget) or not. If it does,...
virtual bool doAddMenu()=0
Tells wether the plugin wants to add a menu or not. If it does, getMenu() will be called.
virtual bool doAddAction(int &nb)=0
Tells wether the plugin wants to add actions or not. If it does, getAction() will be called for each ...