fully implemented setting keyframes and standard values in the NodeParamView
Widget now supports creating keyframe and standard values, supports dragging from sliders (not creating an undo command for each drag), and everything is undoable.
This commit is contained in:
@@ -32,4 +32,9 @@ T lerp(T a, T b, double t) {
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T lerp(T a, T b, float t) {
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
}
|
||||
|
||||
#endif // LERP_H
|
||||
|
||||
+33
-2
@@ -20,6 +20,10 @@
|
||||
|
||||
#include "input.h"
|
||||
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "common/lerp.h"
|
||||
#include "node.h"
|
||||
#include "output.h"
|
||||
@@ -81,6 +85,15 @@ Node *NodeInput::get_connected_node() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NodeInput::type_can_be_interpolated(NodeParam::DataType type)
|
||||
{
|
||||
return type == kFloat
|
||||
|| type == kVec2
|
||||
|| type == kVec3
|
||||
|| type == kVec4
|
||||
|| type == kColor;
|
||||
}
|
||||
|
||||
QVariant NodeInput::get_value_at_time(const rational &time) const
|
||||
{
|
||||
if (is_using_standard_value()) {
|
||||
@@ -103,7 +116,7 @@ QVariant NodeInput::get_value_at_time(const rational &time) const
|
||||
NodeKeyframePtr after = keyframes_.at(i+1);
|
||||
|
||||
if (before->time() == time
|
||||
|| data_type() != kFloat // FIXME: Expand this to other types that can be interpolated
|
||||
|| !type_can_be_interpolated(data_type())
|
||||
|| (before->time() < time && before->type() == NodeKeyframe::kHold)) {
|
||||
|
||||
// Time == keyframe time, so value is precise
|
||||
@@ -121,7 +134,25 @@ QVariant NodeInput::get_value_at_time(const rational &time) const
|
||||
} else {
|
||||
// To have arrived here, the keyframes must both be linear
|
||||
qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble());
|
||||
qreal interpolated_value = lerp(before->value().toDouble(), after->value().toDouble(), period_progress);
|
||||
|
||||
QVariant interpolated_value;
|
||||
|
||||
switch (data_type()) {
|
||||
case kFloat:
|
||||
interpolated_value = lerp(before->value().toDouble(), after->value().toDouble(), period_progress);
|
||||
break;
|
||||
case kVec2:
|
||||
interpolated_value = lerp(before->value().value<QVector2D>(), after->value().value<QVector2D>(), static_cast<float>(period_progress));
|
||||
break;
|
||||
case kVec3:
|
||||
interpolated_value = lerp(before->value().value<QVector3D>(), after->value().value<QVector3D>(), static_cast<float>(period_progress));
|
||||
break;
|
||||
case kVec4:
|
||||
interpolated_value = lerp(before->value().value<QVector4D>(), after->value().value<QVector4D>(), static_cast<float>(period_progress));
|
||||
break;
|
||||
default:
|
||||
interpolated_value = before->value();
|
||||
}
|
||||
|
||||
return interpolated_value;
|
||||
}
|
||||
|
||||
@@ -179,6 +179,11 @@ signals:
|
||||
void KeyframeRemoved(NodeKeyframePtr key);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns whether a data type can be interpolated or not
|
||||
*/
|
||||
static bool type_can_be_interpolated(DataType type);
|
||||
|
||||
/**
|
||||
* @brief We use Qt signals/slots for keyframe communication but store them as shared ptrs. This function converts
|
||||
* a raw ptr to a list index
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "keyframeviewundo.h"
|
||||
|
||||
KeyframeViewUndo::KeyframeViewUndo()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef KEYFRAMEVIEWUNDO_H
|
||||
#define KEYFRAMEVIEWUNDO_H
|
||||
|
||||
|
||||
class KeyframeViewUndo
|
||||
{
|
||||
public:
|
||||
KeyframeViewUndo();
|
||||
};
|
||||
|
||||
#endif // KEYFRAMEVIEWUNDO_H
|
||||
@@ -26,6 +26,15 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
key_(key),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeValueCommand::redo()
|
||||
{
|
||||
key_->set_value(new_value_);
|
||||
@@ -39,18 +48,30 @@ void NodeParamSetKeyframeValueCommand::undo()
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand* parent) :
|
||||
QUndoCommand(parent),
|
||||
input_(input),
|
||||
keyframe_(keyframe)
|
||||
keyframe_(keyframe),
|
||||
done_(false)
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
input_(input),
|
||||
keyframe_(keyframe),
|
||||
done_(already_done)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::redo()
|
||||
{
|
||||
input_->insert_keyframe(keyframe_);
|
||||
if (!done_) {
|
||||
input_->insert_keyframe(keyframe_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::undo()
|
||||
{
|
||||
input_->remove_keyframe(keyframe_);
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand *parent) :
|
||||
@@ -96,6 +117,14 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
input_(input),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::redo()
|
||||
{
|
||||
input_->set_standard_value(new_value_);
|
||||
|
||||
@@ -20,6 +20,7 @@ private:
|
||||
class NodeParamInsertKeyframeCommand : public QUndoCommand {
|
||||
public:
|
||||
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr);
|
||||
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent = nullptr);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
@@ -29,6 +30,8 @@ private:
|
||||
|
||||
NodeKeyframePtr keyframe_;
|
||||
|
||||
bool done_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamRemoveKeyframeCommand : public QUndoCommand {
|
||||
@@ -63,6 +66,7 @@ private:
|
||||
class NodeParamSetKeyframeValueCommand : public QUndoCommand {
|
||||
public:
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
@@ -78,6 +82,7 @@ private:
|
||||
class NodeParamSetStandardValueCommand : public QUndoCommand {
|
||||
public:
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
|
||||
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, QObject *parent) :
|
||||
QObject(parent),
|
||||
input_(input)
|
||||
input_(input),
|
||||
dragging_(false),
|
||||
drag_created_keyframe_(false)
|
||||
{
|
||||
CreateWidgets();
|
||||
|
||||
connect(input_, &NodeInput::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged);
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::SetTime(const rational &time)
|
||||
@@ -267,6 +271,80 @@ void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value)
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant &value)
|
||||
{
|
||||
if (slider->IsDragging()) {
|
||||
|
||||
// While we're dragging, we block the input's normal signalling and create our own
|
||||
input_->blockSignals(true);
|
||||
|
||||
if (!dragging_) {
|
||||
// Set up new drag
|
||||
dragging_ = true;
|
||||
|
||||
// Cache current value
|
||||
drag_old_value_ = input_->get_value_at_time(time_);
|
||||
|
||||
// Determine whether we are creating a keyframe or not
|
||||
if (input_->is_keyframing()) {
|
||||
dragging_keyframe_ = input_->get_keyframe_at_time(time_);
|
||||
drag_created_keyframe_ = !dragging_keyframe_;
|
||||
|
||||
if (drag_created_keyframe_) {
|
||||
dragging_keyframe_ = std::make_shared<NodeKeyframe>(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
input_->insert_keyframe(dragging_keyframe_);
|
||||
|
||||
// We re-enable signals temporarily to emit the keyframe added signal
|
||||
input_->blockSignals(false);
|
||||
emit input_->KeyframeAdded(dragging_keyframe_);
|
||||
input_->blockSignals(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input_->is_keyframing()) {
|
||||
dragging_keyframe_->set_value(value);
|
||||
} else {
|
||||
input_->set_standard_value(value);
|
||||
}
|
||||
|
||||
input_->blockSignals(false);
|
||||
emit input_->ValueChanged(time_, time_);
|
||||
|
||||
} else {
|
||||
if (dragging_) {
|
||||
// We were dragging and just stopped
|
||||
dragging_ = false;
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
if (input_->is_keyframing()) {
|
||||
if (drag_created_keyframe_) {
|
||||
// We created a keyframe in this process
|
||||
new NodeParamInsertKeyframeCommand(input_, dragging_keyframe_, true, command);
|
||||
}
|
||||
|
||||
// We just set a keyframe's value
|
||||
// We do this even when inserting a keyframe because we don't actually perform an insert in this undo command
|
||||
// so this will ensure the ValueChanged() signal is sent correctly
|
||||
new NodeParamSetKeyframeValueCommand(dragging_keyframe_, value, drag_old_value_, command);
|
||||
} else {
|
||||
// We just set the standard value
|
||||
new NodeParamSetStandardValueCommand(input_, value, drag_old_value_, command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
|
||||
} else {
|
||||
// No drag was involved, we can just push the value
|
||||
SetInputValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
{
|
||||
switch (input_->data_type()) {
|
||||
@@ -287,38 +365,59 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
case NodeParam::kInt:
|
||||
{
|
||||
// Widget is a IntegerSlider
|
||||
SetInputValue(static_cast<IntegerSlider*>(sender())->GetValue());
|
||||
IntegerSlider* slider = static_cast<IntegerSlider*>(sender());
|
||||
|
||||
ProcessSlider(slider, slider->GetValue());
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFloat:
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
SetInputValue(static_cast<FloatSlider*>(sender())->GetValue());
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
|
||||
ProcessSlider(slider, slider->GetValue());
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec2:
|
||||
SetInputValue(QVector2D(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue())
|
||||
));
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
|
||||
QVector2D value(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue())
|
||||
);
|
||||
|
||||
ProcessSlider(slider, value);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec3:
|
||||
// Widgets are three FloatSliders
|
||||
SetInputValue(QVector3D(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(2))->GetValue())
|
||||
));
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
|
||||
QVector3D value(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(2))->GetValue())
|
||||
);
|
||||
|
||||
ProcessSlider(slider, value);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
{
|
||||
// Widgets are three FloatSliders
|
||||
SetInputValue(QVector4D(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(2))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(3))->GetValue())
|
||||
));
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
|
||||
QVector4D value(
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(0))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(1))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(2))->GetValue()),
|
||||
static_cast<float>(static_cast<FloatSlider*>(widgets_.at(3))->GetValue())
|
||||
);
|
||||
|
||||
ProcessSlider(slider, value);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFile:
|
||||
@@ -353,3 +452,11 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::InputValueChanged(const rational &start, const rational &end)
|
||||
{
|
||||
if (!dragging_ && start <= time_ && end >= time_) {
|
||||
// We'll need to update the widgets because the values have changed on our current time
|
||||
SetTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "node/input.h"
|
||||
#include "widget/slider/sliderbase.h"
|
||||
|
||||
class NodeParamViewWidgetBridge : public QObject
|
||||
{
|
||||
@@ -20,14 +21,24 @@ private:
|
||||
|
||||
void SetInputValue(const QVariant& value);
|
||||
|
||||
void ProcessSlider(SliderBase* slider, const QVariant& value);
|
||||
|
||||
NodeInput* input_;
|
||||
|
||||
QList<QWidget*> widgets_;
|
||||
|
||||
rational time_;
|
||||
|
||||
bool dragging_;
|
||||
bool drag_created_keyframe_;
|
||||
QVariant drag_old_value_;
|
||||
NodeKeyframePtr dragging_keyframe_;
|
||||
|
||||
private slots:
|
||||
void WidgetCallback();
|
||||
|
||||
void InputValueChanged(const rational& start, const rational& end);
|
||||
|
||||
};
|
||||
|
||||
#endif // NODEPARAMVIEWWIDGETBRIDGE_H
|
||||
|
||||
Reference in New Issue
Block a user