From e32376a8b74fbb818b1db326d2936902dc892e8c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 27 Dec 2019 02:06:41 +1100 Subject: [PATCH] updated NodeInputs to use a standard value as well as keyframed values Previously the non-keyframed value was stored as a static keyframe but this introduced issues when an input was in a state of keyframes being enabled but 0 keyframes existing. Having a standard value makes much more sense. --- app/node/block/block.cpp | 12 +-- app/node/input.cpp | 102 +++++++++++------- app/node/input.h | 26 +++-- app/node/input/media/media.cpp | 6 +- app/node/output/track/track.cpp | 4 +- .../nodeparamview/nodeparamviewitem.cpp | 26 ++--- .../nodeparamview/nodeparamviewundo.cpp | 18 ++++ app/widget/nodeparamview/nodeparamviewundo.h | 14 +++ .../nodeparamviewwidgetbridge.cpp | 7 +- 9 files changed, 142 insertions(+), 73 deletions(-) diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 56e256a1b..e3af91521 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -75,7 +75,7 @@ void Block::set_out(const rational &out) rational Block::length() const { - return length_input_->get_value_at_time(0).value(); + return length_input_->get_standard_value().value(); } void Block::set_length(const rational &length) @@ -86,7 +86,7 @@ void Block::set_length(const rational &length) return; } - length_input_->set_override_value(QVariant::fromValue(length)); + length_input_->set_standard_value(QVariant::fromValue(length)); } void Block::set_length_and_media_out(const rational &length) @@ -153,22 +153,22 @@ void Block::set_next(Block *next) rational Block::media_in() const { - return media_in_input_->get_value_at_time(0).value(); + return media_in_input_->get_standard_value().value(); } void Block::set_media_in(const rational &media_in) { - media_in_input_->set_override_value(QVariant::fromValue(media_in)); + media_in_input_->set_standard_value(QVariant::fromValue(media_in)); } rational Block::media_out() const { - return media_out_input_->get_value_at_time(0).value(); + return media_out_input_->get_standard_value().value(); } void Block::set_media_out(const rational &media_out) { - media_out_input_->set_override_value(QVariant::fromValue(media_out)); + media_out_input_->set_standard_value(QVariant::fromValue(media_out)); } rational Block::media_length() const diff --git a/app/node/input.cpp b/app/node/input.cpp index 034f409f2..e9a060905 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -29,13 +29,12 @@ NodeInput::NodeInput(const QString& id, const DataType &type, const QVariant &de NodeParam(id), data_type_(type), keyframable_(true), + standard_value_(default_value), keyframing_(false), dependent_(true), has_minimum_(false), has_maximum_(false) { - // Have at least one keyframe/value active at any time - insert_keyframe(std::make_shared(0, default_value, NodeKeyframe::kLinear)); } bool NodeInput::IsArray() @@ -84,50 +83,52 @@ Node *NodeInput::get_connected_node() const QVariant NodeInput::get_value_at_time(const rational &time) const { - if (is_keyframing()) { - if (keyframes_.first()->time() >= time) { - // This time precedes any keyframe, so we just return the first value - return keyframes_.first()->value(); - } + if (!is_keyframing() || keyframes_.isEmpty()) { + return standard_value_; + } - if (keyframes_.last()->time() <= time) { - // This time is after any keyframes so we return the last value - return keyframes_.last()->value(); - } + if (keyframes_.first()->time() >= time) { + // This time precedes any keyframe, so we just return the first value + return keyframes_.first()->value(); + } - // If we're here, the time must be somewhere in between the keyframes - for (int i=0;itime() <= time) { + // This time is after any keyframes so we return the last value + return keyframes_.last()->value(); + } - if (before->time() == time - || data_type() != kFloat // FIXME: Expand this to other types that can be interpolated - || (before->time() < time && before->type() == NodeKeyframe::kHold)) { + // If we're here, the time must be somewhere in between the keyframes + for (int i=0;ivalue(); + if (before->time() == time + || data_type() != kFloat // FIXME: Expand this to other types that can be interpolated + || (before->time() < time && before->type() == NodeKeyframe::kHold)) { - } else if (before->time() < time && after->time() > time) { - // We must interpolate between these keyframes + // Time == keyframe time, so value is precise + return before->value(); - if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { - // FIXME: Perform a cubic bezier interpolation - } else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) { - // FIXME: Perform a quadratic bezier interpolation with anchors from the AFTER keyframe - } else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) { - // FIXME: Perform a quadratic bezier interpolation with anchors from the BEFORE keyframe - } 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); + } else if (before->time() < time && after->time() > time) { + // We must interpolate between these keyframes - return interpolated_value; - } + if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { + // FIXME: Perform a cubic bezier interpolation + } else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) { + // FIXME: Perform a quadratic bezier interpolation with anchors from the AFTER keyframe + } else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) { + // FIXME: Perform a quadratic bezier interpolation with anchors from the BEFORE keyframe + } 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); + + return interpolated_value; } } } - return keyframes_.first()->value(); + return standard_value_; } NodeKeyframePtr NodeInput::get_keyframe_at_time(const rational &time) const @@ -147,7 +148,7 @@ NodeKeyframePtr NodeInput::get_keyframe_at_time(const rational &time) const NodeKeyframePtr NodeInput::get_closest_keyframe_to_time(const rational &time) const { - if (!is_keyframing()) { + if (!is_keyframing() || keyframes_.isEmpty()) { return nullptr; } @@ -179,6 +180,17 @@ NodeKeyframePtr NodeInput::get_closest_keyframe_to_time(const rational &time) co return nullptr; } +NodeKeyframe::Type NodeInput::get_best_keyframe_type_for_time(const rational &time) const +{ + NodeKeyframePtr closest_key = get_closest_keyframe_to_time(time); + + if (closest_key) { + return closest_key->type(); + } + + return NodeKeyframe::kDefaultType; +} + void NodeInput::insert_keyframe(NodeKeyframePtr key) { Q_ASSERT(is_keyframable() || keyframes_.isEmpty()); @@ -293,11 +305,18 @@ bool NodeInput::is_keyframable() const return keyframable_; } -void NodeInput::set_override_value(const QVariant &value) +const QVariant &NodeInput::get_standard_value() const { - Q_ASSERT(!is_keyframable()); + return standard_value_; +} - keyframes_.first()->set_value(value); +void NodeInput::set_standard_value(const QVariant &value) +{ + standard_value_ = value; + + if (!is_keyframing()) { + emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX); + } } const QList &NodeInput::keyframes() const @@ -346,7 +365,10 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn { Q_ASSERT(source->id() == dest->id()); - // Copy values + // Copy standard value + dest->standard_value_ = source->standard_value_; + + // Copy keyframes dest->keyframes_.clear(); foreach (NodeKeyframePtr key, source->keyframes_) { NodeKeyframePtr copy = std::make_shared(key->time(), key->value(), key->type()); diff --git a/app/node/input.h b/app/node/input.h index 1b8b9fb6d..9093787c3 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -94,10 +94,15 @@ public: /** * @brief Gets the closest keyframe to a time * - * If is_keyframing() is false, this will return nullptr. Otherwise, this is guaranteed to return a keyframe. + * If is_keyframing() is false or keyframes_ is empty, this will return nullptr. */ NodeKeyframePtr get_closest_keyframe_to_time(const rational& time) const; + /** + * @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes) + */ + NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time) const; + /** * @brief Inserts a keyframe at the given time and returns a reference to it */ @@ -131,11 +136,14 @@ public: bool is_keyframable() const; /** - * @brief Replaces all values with this value - * - * This can only be used on inputs where keyframing is disabled. + * @brief Get non-keyframed value */ - void set_override_value(const QVariant& value); + const QVariant& get_standard_value() const; + + /** + * @brief Set non-keyframed value + */ + void set_standard_value(const QVariant& value); /** * @brief Return list of keyframes in this parameter @@ -193,11 +201,15 @@ private: */ bool keyframable_; + /** + * @brief Non-keyframed value + */ + QVariant standard_value_; + /** * @brief Internal keyframe array * - * All internal/user-defined data is stored in this array. Even if keyframing is not enabled, this array will contain - * one entry which will be used, and its time value will be ignored. + * If keyframing is enabled, this data is used instead of standard_value. */ QList keyframes_; diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 624068e13..786d9c718 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -32,12 +32,12 @@ MediaInput::MediaInput() : StreamPtr MediaInput::footage() { - return footage_input_->get_value_at_time(0).value(); + return footage_input_->get_standard_value().value(); } void MediaInput::SetFootage(StreamPtr f) { - footage_input_->set_override_value(QVariant::fromValue(f)); + footage_input_->set_standard_value(QVariant::fromValue(f)); } void MediaInput::Retranslate() @@ -47,7 +47,7 @@ void MediaInput::Retranslate() void MediaInput::FootageChanged() { - StreamPtr new_footage = footage_input_->get_value_at_time(0).value(); + StreamPtr new_footage = footage_input_->get_standard_value().value(); if (new_footage == connected_footage_) { return; diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index a11380347..b1c33362c 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -350,7 +350,7 @@ QString TrackOutput::GetDefaultTrackName(TrackType type, int index) bool TrackOutput::IsMuted() const { - return muted_input_->get_value_at_time(0).toBool(); + return muted_input_->get_standard_value().toBool(); } bool TrackOutput::IsLocked() const @@ -365,7 +365,7 @@ void TrackOutput::SetTrackName(const QString &name) void TrackOutput::SetMuted(bool e) { - muted_input_->set_override_value(e); + muted_input_->set_standard_value(e); InvalidateCache(0, track_length()); } diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index e2fbff8ed..b0cd81dd8 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -108,6 +108,9 @@ void NodeParamViewItem::InputAddedKeyframeInternal(NodeInput *input, NodeKeyfram // Find global position lbl_center = lbl->mapToGlobal(lbl_center); + // Update keyframe control widget + UpdateKeyframeControl(KeyframeControlFromInput(input)); + emit KeyframeAdded(keyframe, lbl_center.y()); } @@ -186,8 +189,8 @@ void NodeParamViewItem::UpdateKeyframeControl(NodeParamViewKeyframeControl *key_ NodeInput* input = key_control->GetConnectedInput(); // Update UI based on time - key_control->SetPreviousButtonEnabled(time_ > input->keyframes().first()->time()); - key_control->SetNextButtonEnabled(time_ < input->keyframes().last()->time()); + key_control->SetPreviousButtonEnabled(!input->keyframes().isEmpty() && time_ > input->keyframes().first()->time()); + key_control->SetNextButtonEnabled(!input->keyframes().isEmpty() && time_ < input->keyframes().last()->time()); key_control->SetToggleButtonChecked(input->has_keyframe_at_time(time_)); } @@ -232,7 +235,8 @@ void NodeParamViewItem::UserChangedKeyframeEnable(bool e) new NodeParamSetKeyframingCommand(input, true, command); // NodeInputs already have one keyframe by default, we move it to the current time here - new NodeParamSetKeyframeTimeCommand(input->keyframes().first(), time_, command); + NodeKeyframePtr key = std::make_shared(time_, input->get_standard_value(), NodeKeyframe::kDefaultType); + new NodeParamInsertKeyframeCommand(input, key, command); } else { // Confirm the user wants to clear all keyframes if (QMessageBox::warning(this, @@ -243,13 +247,13 @@ void NodeParamViewItem::UserChangedKeyframeEnable(bool e) // Store value at this time, we'll set this as the persistent value later QVariant stored_val = input->get_value_at_time(time_); - // Delete all keyframes EXCEPT ONE - for (int i=input->keyframes().size()-1;i>0;i--) { + // Delete all keyframes + for (int i=input->keyframes().size()-1;i>=0;i--) { new NodeParamRemoveKeyframeCommand(input, input->keyframes().at(i), command); } - // Update value with this one - new NodeParamSetKeyframeValueCommand(input->keyframes().first(), stored_val, command); + // Update standard value + new NodeParamSetStandardValueCommand(input, stored_val, command); // Disable keyframing new NodeParamSetKeyframingCommand(input, false, command); @@ -273,9 +277,9 @@ void NodeParamViewItem::UserToggledKeyframe(bool e) if (e && !key) { // Add a keyframe here - NodeKeyframePtr closest_key = input->get_closest_keyframe_to_time(time_); - - key = std::make_shared(time_, input->get_value_at_time(time_), closest_key->type()); + key = std::make_shared(time_, + input->get_value_at_time(time_), + input->get_best_keyframe_type_for_time(time_)); new NodeParamInsertKeyframeCommand(input, key, command); } else if (!e && key) { @@ -307,8 +311,6 @@ void NodeParamViewItem::InputAddedKeyframe(NodeKeyframePtr key) NodeInput* input = static_cast(sender()); InputAddedKeyframeInternal(input, key); - - UpdateKeyframeControl(KeyframeControlFromInput(input)); } void NodeParamViewItem::GoToPreviousKey() diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 8f92a5d5c..3f4095413 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -87,3 +87,21 @@ void NodeParamSetKeyframeTimeCommand::undo() { key_->set_time(old_time_); } + +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, const QVariant &value, QUndoCommand *parent) : + QUndoCommand(parent), + input_(input), + old_value_(input_->get_standard_value()), + new_value_(value) +{ +} + +void NodeParamSetStandardValueCommand::redo() +{ + input_->set_standard_value(new_value_); +} + +void NodeParamSetStandardValueCommand::undo() +{ + input_->set_standard_value(old_value_); +} diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index a4f342cf5..44ffc881b 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -75,4 +75,18 @@ private: }; +class NodeParamSetStandardValueCommand : public QUndoCommand { +public: + NodeParamSetStandardValueCommand(NodeInput* input, const QVariant& value, QUndoCommand* parent = nullptr); + + virtual void redo() override; + virtual void undo() override; + +private: + NodeInput* input_; + + QVariant old_value_; + QVariant new_value_; +}; + #endif // NODEPARAMVIEWUNDO_H diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index acf5656ee..f1154727c 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -254,13 +254,14 @@ void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value) new NodeParamSetKeyframeValueCommand(existing_key, value, command); } else { // No existing key, create a new one - NodeKeyframePtr closest_key = input_->get_closest_keyframe_to_time(time_); - NodeKeyframePtr new_key = std::make_shared(time_, value, closest_key->type()); + NodeKeyframePtr new_key = std::make_shared(time_, + value, + input_->get_best_keyframe_type_for_time(time_)); new NodeParamInsertKeyframeCommand(input_, new_key, command); } } else { - new NodeParamSetKeyframeValueCommand(input_->keyframes().first(), value, command); + new NodeParamSetStandardValueCommand(input_, value, command); } Core::instance()->undo_stack()->pushIfHasChildren(command);