From 81c1922911e51938b8844a74af89fe6443f56a6c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Feb 2021 15:40:43 +1100 Subject: [PATCH] use strings to connect nodes This makes the node system somewhat more high-level with the intent of making working with them far more flexible and stable. By making the architecture more abstracted, it becomes far less rigid which should allow us to do even more with it and make it much less crash prone. --- app/codec/ffmpeg/ffmpegdecoder.cpp | 2 + app/common/xmlutils.cpp | 8 +- app/common/xmlutils.h | 15 +- app/core.cpp | 2 + app/dialog/export/codec/codecsection.h | 2 +- .../sequence/sequencedialogpresettab.cpp | 1 - app/node/CMakeLists.txt | 26 +- app/node/audio/pan/pan.cpp | 31 +- app/node/audio/pan/pan.h | 5 +- app/node/audio/volume/volume.cpp | 30 +- app/node/audio/volume/volume.h | 12 +- app/node/block/block.cpp | 76 +- app/node/block/block.h | 27 +- app/node/block/clip/clip.cpp | 36 +- app/node/block/clip/clip.h | 13 +- app/node/block/gap/gap.h | 2 - .../crossdissolve/crossdissolvetransition.cpp | 2 + .../diptocolor/diptocolortransition.cpp | 6 +- .../diptocolor/diptocolortransition.h | 5 +- app/node/block/transition/transition.cpp | 145 +- app/node/block/transition/transition.h | 28 +- app/node/connectable.cpp | 77 - app/node/connectable.h | 87 - app/node/distort/crop/cropdistortnode.cpp | 108 +- app/node/distort/crop/cropdistortnode.h | 18 +- .../transform/transformdistortnode.cpp | 93 +- .../distort/transform/transformdistortnode.h | 17 +- app/node/factory.cpp | 17 +- app/node/filter/blur/blur.cpp | 65 +- app/node/filter/blur/blur.h | 20 +- app/node/filter/mosaic/mosaicfilternode.cpp | 42 +- app/node/filter/mosaic/mosaicfilternode.h | 11 +- app/node/filter/stroke/stroke.cpp | 59 +- app/node/filter/stroke/stroke.h | 17 +- app/node/generator/matrix/matrix.cpp | 79 +- app/node/generator/matrix/matrix.h | 47 +- app/node/generator/polygon/polygon.cpp | 46 +- app/node/generator/polygon/polygon.h | 9 +- app/node/generator/solid/solid.cpp | 15 +- app/node/generator/solid/solid.h | 5 +- app/node/generator/text/text.cpp | 68 +- app/node/generator/text/text.h | 17 +- app/node/graph.h | 6 +- app/node/input.cpp | 905 --------- app/node/input.h | 629 ------- app/node/input/media/media.cpp | 45 +- app/node/input/media/media.h | 8 +- app/node/input/time/timeinput.cpp | 4 +- app/node/input/time/timeinput.h | 2 +- app/node/inputdragger.cpp | 53 +- app/node/inputdragger.h | 12 +- app/node/inputimmediate.cpp | 1 - app/node/inputimmediate.h | 7 +- app/node/keyframe.cpp | 11 +- app/node/keyframe.h | 19 +- app/node/math/math/math.cpp | 47 +- app/node/math/math/math.h | 26 +- app/node/math/math/mathbase.cpp | 26 +- app/node/math/math/mathbase.h | 6 +- app/node/math/merge/merge.cpp | 46 +- app/node/math/merge/merge.h | 6 +- app/node/math/trigonometry/trigonometry.cpp | 21 +- app/node/math/trigonometry/trigonometry.h | 9 +- app/node/node.cpp | 1665 +++++++++++++++-- app/node/node.h | 727 ++++++- app/node/nodecopypaste.cpp | 32 +- app/node/output/track/track.cpp | 338 ++-- app/node/output/track/track.h | 27 +- app/node/output/track/tracklist.cpp | 35 +- app/node/output/track/tracklist.h | 36 +- app/node/output/viewer/viewer.cpp | 74 +- app/node/output/viewer/viewer.h | 34 +- app/node/param.cpp | 147 ++ app/node/param.h | 247 +++ app/node/traverser.cpp | 37 +- app/node/traverser.h | 9 +- app/node/value.cpp | 22 +- app/node/value.h | 56 +- app/node/valuedatabase.cpp | 35 + app/node/valuedatabase.h | 74 + app/panel/node/node.h | 2 + app/project/item/footage/footage.cpp | 2 + app/project/item/footage/stream.cpp | 2 +- app/project/item/sequence/sequence.cpp | 13 +- app/project/project.cpp | 6 - app/render/job/CMakeLists.txt | 1 + app/render/job/acceleratedjob.cpp | 32 + app/render/job/acceleratedjob.h | 19 +- app/render/job/samplejob.h | 2 +- app/render/job/shaderjob.h | 8 +- app/render/previewautocacher.cpp | 56 +- app/render/previewautocacher.h | 16 +- app/render/renderprocessor.cpp | 20 +- app/task/precache/precachetask.cpp | 2 +- app/widget/curvewidget/curveview.cpp | 43 +- app/widget/curvewidget/curveview.h | 14 +- app/widget/curvewidget/curvewidget.cpp | 76 +- app/widget/curvewidget/curvewidget.h | 11 +- app/widget/keyframeview/keyframeview.cpp | 6 +- app/widget/keyframeview/keyframeview.h | 4 +- app/widget/keyframeview/keyframeviewbase.cpp | 76 +- app/widget/keyframeview/keyframeviewbase.h | 12 +- app/widget/keyframeview/keyframeviewitem.cpp | 3 +- app/widget/keyframeview/keyframeviewundo.cpp | 5 +- app/widget/nodeparamview/nodeparamview.cpp | 25 +- .../nodeparamviewarraywidget.cpp | 18 +- .../nodeparamview/nodeparamviewarraywidget.h | 10 +- .../nodeparamviewconnectedlabel.cpp | 59 +- .../nodeparamviewconnectedlabel.h | 14 +- .../nodeparamview/nodeparamviewitem.cpp | 104 +- app/widget/nodeparamview/nodeparamviewitem.h | 18 +- .../nodeparamviewkeyframecontrol.cpp | 99 +- .../nodeparamviewkeyframecontrol.h | 18 +- .../nodeparamview/nodeparamviewundo.cpp | 50 +- app/widget/nodeparamview/nodeparamviewundo.h | 31 +- .../nodeparamviewwidgetbridge.cpp | 166 +- .../nodeparamview/nodeparamviewwidgetbridge.h | 19 +- app/widget/nodetableview/nodetableview.cpp | 11 +- app/widget/nodetableview/nodetablewidget.h | 2 +- app/widget/nodetreeview/nodetreeview.cpp | 84 +- app/widget/nodetreeview/nodetreeview.h | 27 +- app/widget/nodeview/nodeview.cpp | 49 +- app/widget/nodeview/nodeview.h | 4 +- app/widget/nodeview/nodeviewedge.cpp | 7 +- app/widget/nodeview/nodeviewedge.h | 10 +- app/widget/nodeview/nodeviewitem.cpp | 17 +- app/widget/nodeview/nodeviewitem.h | 11 +- app/widget/nodeview/nodeviewscene.cpp | 16 +- app/widget/nodeview/nodeviewscene.h | 8 +- app/widget/nodeview/nodeviewundo.cpp | 36 +- app/widget/nodeview/nodeviewundo.h | 14 +- app/widget/timelinewidget/timelineundo.h | 116 +- app/widget/timelinewidget/tool/add.cpp | 4 +- app/widget/timelinewidget/tool/import.cpp | 8 +- app/widget/timelinewidget/tool/import.h | 6 + app/widget/timelinewidget/tool/transition.cpp | 15 +- app/widget/viewer/footageviewer.cpp | 8 +- app/widget/viewer/viewer.cpp | 10 +- 138 files changed, 4487 insertions(+), 3913 deletions(-) delete mode 100644 app/node/connectable.cpp delete mode 100644 app/node/connectable.h delete mode 100644 app/node/input.cpp delete mode 100644 app/node/input.h create mode 100644 app/node/param.cpp create mode 100644 app/node/param.h create mode 100644 app/node/valuedatabase.cpp create mode 100644 app/node/valuedatabase.h create mode 100644 app/render/job/acceleratedjob.cpp diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index acb7625c7..ba27072e0 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -220,6 +220,8 @@ QString FFmpegDecoder::id() Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const { + Q_UNUSED(cancelled) + // Variable for receiving errors from FFmpeg int error_code; diff --git a/app/common/xmlutils.cpp b/app/common/xmlutils.cpp index e243ffcf7..cfc5042ed 100644 --- a/app/common/xmlutils.cpp +++ b/app/common/xmlutils.cpp @@ -29,13 +29,13 @@ namespace olive { void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command) { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { - Node* out = xml_node_data.node_ptrs.value(con.output); + NodeOutput out(xml_node_data.node_ptrs.value(con.output_node), con.output); - if (out) { + if (out.IsValid()) { if (command) { - command->add_child(new NodeEdgeAddCommand(out, con.input, con.element)); + command->add_child(new NodeEdgeAddCommand(out, con.input)); } else { - Node::ConnectEdge(out, con.input, con.element); + Node::ConnectEdge(out, con.input); } } } diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index ea8f43475..20c1585cc 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -23,6 +23,7 @@ #include +#include "node/param.h" #include "project/item/footage/stream.h" #include "undo/undocommand.h" @@ -38,15 +39,9 @@ class NodeInput; struct XMLNodeData { struct SerializedConnection { - NodeInput* input; - int element; - quintptr output; - }; - - struct FootageConnection { - NodeInput* input; - int element; - quintptr footage; + NodeInput input; + quintptr output_node; + QString output; }; struct BlockLink { @@ -56,8 +51,6 @@ struct XMLNodeData { QHash node_ptrs; QList desired_connections; - QHash footage_ptrs; - QList footage_connections; QList block_links; QHash item_ptrs; diff --git a/app/core.cpp b/app/core.cpp index 293aaaf26..b6bd2009e 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -892,6 +892,8 @@ QString Core::GetProjectFilter(bool include_any_filter) if (include_any_filter) { filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;")); } +#else + Q_UNUSED(include_any_filter) #endif // Append standard filter diff --git a/app/dialog/export/codec/codecsection.h b/app/dialog/export/codec/codecsection.h index 82f70b69e..807eab9af 100644 --- a/app/dialog/export/codec/codecsection.h +++ b/app/dialog/export/codec/codecsection.h @@ -33,7 +33,7 @@ class CodecSection : public QWidget public: CodecSection(QWidget* parent = nullptr); - virtual void AddOpts(EncodingParams* params){} + virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)} }; diff --git a/app/dialog/sequence/sequencedialogpresettab.cpp b/app/dialog/sequence/sequencedialogpresettab.cpp index a57852b64..a75e35479 100644 --- a/app/dialog/sequence/sequencedialogpresettab.cpp +++ b/app/dialog/sequence/sequencedialogpresettab.cpp @@ -31,7 +31,6 @@ #include "common/filefunctions.h" #include "config/config.h" -#include "node/input.h" #include "render/videoparams.h" #include "ui/icons/icons.h" #include "widget/menu/menu.h" diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 70b19a506..640b4466c 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -25,28 +25,28 @@ add_subdirectory(output) set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/connectable.cpp - node/connectable.h - node/factory.h node/factory.cpp - node/graph.h + node/factory.h node/graph.cpp - node/input.h - node/input.cpp - node/inputdragger.h + node/graph.h node/inputdragger.cpp - node/inputimmediate.h + node/inputdragger.h node/inputimmediate.cpp - node/keyframe.h + node/inputimmediate.h node/keyframe.cpp - node/node.h + node/keyframe.h node/node.cpp - node/nodecopypaste.h + node/node.h node/nodecopypaste.cpp + node/nodecopypaste.h + node/param.cpp + node/param.h node/splitvalue.h - node/traverser.h node/traverser.cpp - node/value.h + node/traverser.h node/value.cpp + node/value.h + node/valuedatabase.cpp + node/valuedatabase.h PARENT_SCOPE ) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 19ada088b..ab8256df2 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -24,14 +24,17 @@ namespace olive { +const QString PanNode::kSamplesInput = QStringLiteral("samples_in"); +const QString PanNode::kPanningInput = QStringLiteral("panning_in"); + PanNode::PanNode() { - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); - panning_input_ = new NodeInput(this, QStringLiteral("panning_in"), NodeValue::kFloat, 0.0); - panning_input_->setProperty("min", -1.0); - panning_input_->setProperty("max", 1.0); - panning_input_->setProperty("view", FloatSlider::kPercentage); + AddInput(kPanningInput, NodeValue::kFloat, 0.0); + SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0); + SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0); + SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage); } Node *PanNode::copy() const @@ -59,18 +62,20 @@ QString PanNode::Description() const return tr("Adjust the stereo panning of an audio source."); } -NodeValueTable PanNode::Value(NodeValueDatabase &value) const +NodeValueTable PanNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Create a sample job - SampleJob job(samples_input_, value); - job.InsertValue(panning_input_, value); + SampleJob job(kSamplesInput, value); + job.InsertValue(this, kPanningInput, value); // Push it to our table NodeValueTable table = value.Merge(); if (job.HasSamples()) { - float pan_volume = job.GetValue(panning_input_).data().toFloat(); - if (panning_input_->IsStatic()) { + float pan_volume = job.GetValue(kPanningInput).data().toFloat(); + if (IsInputStatic(kPanningInput)) { if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) { if (pan_volume > 0) { job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume); @@ -95,7 +100,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in return; } - float pan_val = values[panning_input_].Get(NodeValue::kFloat).toFloat(); + float pan_val = values[kPanningInput].Get(NodeValue::kFloat).toFloat(); for (int i=0;iaudio_params().channel_count();i++) { output->data()[i][index] = input->data()[i][index]; @@ -110,8 +115,8 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in void PanNode::Retranslate() { - samples_input_->set_name(tr("Samples")); - panning_input_->set_name(tr("Pan")); + SetInputName(kSamplesInput, tr("Samples")); + SetInputName(kPanningInput, tr("Pan")); } } diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index e6298fc84..f8262c1e8 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -38,12 +38,15 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; virtual void Retranslate() override; + static const QString kSamplesInput; + static const QString kPanningInput; + private: NodeInput* samples_input_; NodeInput* panning_input_; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 8172fdfaa..c84e03dc9 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -24,14 +24,16 @@ namespace olive { +const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in"); +const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in"); + VolumeNode::VolumeNode() { - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); - samples_input_->SetKeyframable(false); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); - volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0); - volume_input_->setProperty("min", 0.0); - volume_input_->setProperty("view", FloatSlider::kDecibel); + AddInput(kVolumeInput, NodeValue::kFloat, 1.0); + SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0); + SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel); } Node *VolumeNode::copy() const @@ -59,26 +61,28 @@ QString VolumeNode::Description() const return tr("Adjusts the volume of an audio source."); } -NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const +NodeValueTable VolumeNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + return ValueInternal(value, kOpMultiply, kPairSampleNumber, - samples_input_, - value[samples_input_].TakeWithMeta(NodeValue::kSamples), - volume_input_, - value[volume_input_].TakeWithMeta(NodeValue::kFloat)); + kSamplesInput, + value[kSamplesInput].TakeWithMeta(NodeValue::kSamples), + kVolumeInput, + value[kVolumeInput].TakeWithMeta(NodeValue::kFloat)); } void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const { - return ProcessSamplesInternal(values, kOpMultiply, samples_input_, volume_input_, input, output, index); + return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index); } void VolumeNode::Retranslate() { - samples_input_->set_name(tr("Samples")); - volume_input_->set_name(tr("Volume")); + SetInputName(kSamplesInput, tr("Samples")); + SetInputName(kVolumeInput, tr("Volume")); } } diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index a45aeb340..368a914b0 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -38,20 +38,14 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; virtual void Retranslate() override; - NodeInput* samples_input() const - { - return samples_input_; - } - -private: - NodeInput* samples_input_; - NodeInput* volume_input_; + static const QString kSamplesInput; + static const QString kVolumeInput; }; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 2787d0974..c07e5ebba 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -28,6 +28,11 @@ namespace olive { +const QString Block::kLengthInput = QStringLiteral("length_in"); +const QString Block::kMediaInInput = QStringLiteral("media_in_in"); +const QString Block::kEnabledInput = QStringLiteral("enabled_in"); +const QString Block::kSpeedInput = QStringLiteral("speed_in"); + Block::Block() : previous_(nullptr), next_(nullptr), @@ -36,24 +41,18 @@ Block::Block() : in_transition_(nullptr), out_transition_(nullptr) { - length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational); - length_input_->SetConnectable(false); - length_input_->SetKeyframable(false); - IgnoreInvalidationsFrom(length_input_); - connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthChanged); + AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + IgnoreInvalidationsFrom(kLengthInput); + IgnoreHashingFrom(kLengthInput); - media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational); - media_in_input_->SetConnectable(false); - media_in_input_->SetKeyframable(false); + AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + IgnoreHashingFrom(kMediaInInput); - enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean); - enabled_input_->SetConnectable(false); - enabled_input_->SetKeyframable(false); - enabled_input_->SetStandardValue(true); + AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat); - speed_input_->SetStandardValue(1.0); - speed_input_->setProperty("view", FloatSlider::kPercentage); + AddInput(kSpeedInput, NodeValue::kFloat, 1.0); + SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage); + IgnoreHashingFrom(kSpeedInput); // A block's length must be greater than 0 set_length_and_media_out(1); @@ -66,7 +65,7 @@ QVector Block::Category() const rational Block::length() const { - return length_input_->GetStandardValue().value(); + return GetStandardValue(kLengthInput).value(); } void Block::set_length_and_media_out(const rational &length) @@ -97,22 +96,22 @@ void Block::set_length_and_media_in(const rational &length) rational Block::media_in() const { - return media_in_input_->GetStandardValue().value(); + return GetStandardValue(kMediaInInput).value(); } void Block::set_media_in(const rational &media_in) { - media_in_input_->SetStandardValue(QVariant::fromValue(media_in)); + SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); } bool Block::is_enabled() const { - return enabled_input_->GetStandardValue().toBool(); + return GetStandardValue(kEnabledInput).toBool(); } void Block::set_enabled(bool e) { - enabled_input_->SetStandardValue(e); + SetStandardValue(kEnabledInput, e); emit EnabledChanged(); } @@ -127,10 +126,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const rational local_time = sequence_time; // FIXME: Doesn't handle reversing - if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { - // FIXME: We'll need to calculate the speed hoo boy - } else { - double speed_value = speed_input_->GetStandardValue().toDouble(); + if (IsInputStatic(kSpeedInput)) { + double speed_value = GetStandardValue(kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point @@ -139,6 +136,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const // Multiply time local_time = rational::fromDouble(local_time.toDouble() * speed_value); } + } else { + // FIXME: We'll need to calculate the speed hoo boy } return local_time + media_in(); @@ -154,10 +153,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const rational sequence_time = media_time - media_in(); // FIXME: Doesn't handle reversing - if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { + if (IsInputKeyframing(kSpeedInput) || IsInputConnected(kSpeedInput)) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = speed_input_->GetStandardValue().toDouble(); + double speed_value = GetStandardValue(kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point, also prevents divide by zero @@ -171,16 +170,15 @@ rational Block::MediaToSequenceTime(const rational &media_time) const return sequence_time; } -QVector Block::GetInputsToHash() const +void Block::InputValueChangedEvent(const QString &input, int element) { - QVector inputs = Node::GetInputsToHash(); + Q_UNUSED(element) - // Ignore these inputs - inputs.removeOne(media_in_input_); - inputs.removeOne(speed_input_); - inputs.removeOne(length_input_); - - return inputs; + if (input == kLengthInput) { + emit LengthChanged(); + } else if (input == kEnabledInput) { + emit EnabledChanged(); + } } void Block::LinkChangeEvent() @@ -198,17 +196,17 @@ void Block::LinkChangeEvent() void Block::set_length_internal(const rational &length) { - length_input_->SetStandardValue(QVariant::fromValue(length)); + SetStandardValue(kLengthInput, QVariant::fromValue(length)); } void Block::Retranslate() { Node::Retranslate(); - length_input_->set_name(tr("Length")); - media_in_input_->set_name(tr("Media In")); - enabled_input_->set_name(tr("Enabled")); - speed_input_->set_name(tr("Speed")); + SetInputName(kLengthInput, tr("Length")); + SetInputName(kMediaInInput, tr("Media In")); + SetInputName(kEnabledInput, tr("Enabled")); + SetInputName(kSpeedInput, tr("Speed")); } void Block::Hash(QCryptographicHash &, const rational &) const diff --git a/app/node/block/block.h b/app/node/block/block.h index f6866dfd6..8c5e883b9 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -114,21 +114,6 @@ public: virtual void Retranslate() override; - NodeInput* length_input() const - { - return length_input_; - } - - NodeInput* media_in_input() const - { - return media_in_input_; - } - - NodeInput* speed_input() const - { - return speed_input_; - } - TransitionBlock* in_transition() { return in_transition_; @@ -166,6 +151,11 @@ public: virtual void Hash(QCryptographicHash &hash, const rational &time) const override; + static const QString kLengthInput; + static const QString kMediaInInput; + static const QString kEnabledInput; + static const QString kSpeedInput; + public slots: signals: @@ -178,7 +168,7 @@ protected: rational MediaToSequenceTime(const rational& media_time) const; - virtual QVector GetInputsToHash() const override; + virtual void InputValueChangedEvent(const QString& input, int element) override; virtual void LinkChangeEvent() override; @@ -188,11 +178,6 @@ protected: private: void set_length_internal(const rational &length); - NodeInput* length_input_; - NodeInput* media_in_input_; - NodeInput* speed_input_; - NodeInput* enabled_input_; - rational in_point_; rational out_point_; Track* track_; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 41d0a81de..efb598988 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -22,10 +22,11 @@ namespace olive { +const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in"); + ClipBlock::ClipBlock() { - texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone); - texture_input_->SetKeyframable(false); + AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); } Node *ClipBlock::copy() const @@ -53,15 +54,12 @@ QString ClipBlock::Description() const return tr("A time-based node that represents a media source."); } -NodeInput *ClipBlock::texture_input() const +void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element) { - return texture_input_; -} + Q_UNUSED(element) -void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& from) -{ // If signal is from texture input, transform all times from media time to sequence time - if (from.input == texture_input_) { + if (from == kBufferIn) { // Adjust range from media time to sequence time rational start = MediaToSequenceTime(range.in()); rational end = MediaToSequenceTime(range.out()); @@ -73,32 +71,34 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& f } } -TimeRange ClipBlock::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) - if (input == texture_input_) { + if (input == kBufferIn) { return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out())); } return Block::InputTimeAdjustment(input, element, input_time); } -TimeRange ClipBlock::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) - if (input == texture_input_) { + if (input == kBufferIn) { return TimeRange(MediaToSequenceTime(input_time.in()), MediaToSequenceTime(input_time.out())); } return Block::OutputTimeAdjustment(input, element, input_time); } -NodeValueTable ClipBlock::Value(NodeValueDatabase &value) const +NodeValueTable ClipBlock::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // We discard most values here except for the buffer we received - NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer); + NodeValue data = value[kBufferIn].GetWithMeta(NodeValue::kBuffer); NodeValueTable table; if (data.type() != NodeValue::kNone) { @@ -111,15 +111,15 @@ void ClipBlock::Retranslate() { Block::Retranslate(); - texture_input_->set_name(tr("Buffer")); + SetInputName(kBufferIn, tr("Buffer")); } void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const { - if (texture_input_->IsConnected()) { - rational t = InputTimeAdjustment(texture_input_, -1, TimeRange(time, time)).in(); + if (IsInputConnected(kBufferIn)) { + rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in(); - texture_input_->GetConnectedNode()->Hash(hash, t); + GetConnectedNode(kBufferIn)->Hash(hash, t); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 5ed962e92..16b022c64 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -42,22 +42,19 @@ public: virtual QString id() const override; virtual QString Description() const override; - NodeInput* texture_input() const; + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override; - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; - - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void Retranslate() override; virtual void Hash(QCryptographicHash &hash, const rational &time) const override; -private: - NodeInput* texture_input_; + static const QString kBufferIn; }; diff --git a/app/node/block/gap/gap.h b/app/node/block/gap/gap.h index 17856c039..0560dcb37 100644 --- a/app/node/block/gap/gap.h +++ b/app/node/block/gap/gap.h @@ -42,8 +42,6 @@ public: virtual QString id() const override; virtual QString Description() const override; -private: - }; } diff --git a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp index c37ac9715..55f823e5b 100644 --- a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp +++ b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp @@ -61,6 +61,8 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) cons void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const { + Q_UNUSED(value) + job.SetAlphaChannelRequired(true); } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index cbad89c2a..36ab23611 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -22,9 +22,11 @@ namespace olive { +const QString DipToColorTransition::kColorInput = QStringLiteral("color_in"); + DipToColorTransition::DipToColorTransition() { - color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); } Node *DipToColorTransition::copy() const @@ -61,7 +63,7 @@ ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) const void DipToColorTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const { - job.InsertValue(color_input_, value); + job.InsertValue(this, kColorInput, value); } } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.h b/app/node/block/transition/diptocolor/diptocolortransition.h index 0f54b272b..cac7a528a 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.h +++ b/app/node/block/transition/diptocolor/diptocolortransition.h @@ -40,12 +40,11 @@ public: virtual ShaderCode GetShaderCode(const QString& shader_id) const override; + static const QString kColorInput; + protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const override; -private: - NodeInput* color_input_; - }; } diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 4f80f987d..822b84192 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -24,23 +24,19 @@ namespace olive { +const QString TransitionBlock::kOutBlockInput = QStringLiteral("out_block_in"); +const QString TransitionBlock::kInBlockInput = QStringLiteral("in_block_in"); +const QString TransitionBlock::kCurveInput = QStringLiteral("curve_in"); + TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), connected_in_block_(nullptr) { - out_block_input_ = new NodeInput(this, QStringLiteral("out_block_in"), NodeValue::kNone); - out_block_input_->SetKeyframable(false); - connect(out_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::InBlockConnected); - connect(out_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::InBlockDisconnected); + AddInput(kOutBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - in_block_input_ = new NodeInput(this, QStringLiteral("in_block_in"), NodeValue::kNone); - in_block_input_->SetKeyframable(false); - connect(in_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::OutBlockConnected); - connect(in_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::OutBlockDisconnected); + AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo); - curve_input_->SetKeyframable(false); - curve_input_->SetConnectable(false); + AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); } Block::Type TransitionBlock::type() const @@ -48,26 +44,16 @@ Block::Type TransitionBlock::type() const return kTransition; } -NodeInput *TransitionBlock::out_block_input() const -{ - return out_block_input_; -} - -NodeInput *TransitionBlock::in_block_input() const -{ - return in_block_input_; -} - void TransitionBlock::Retranslate() { Block::Retranslate(); - out_block_input_->set_name(tr("From")); - in_block_input_->set_name(tr("To")); - curve_input_->set_name(tr("Curve")); + SetInputName(kOutBlockInput, tr("From")); + SetInputName(kInBlockInput, tr("To")); + SetInputName(kCurveInput, tr("Curve")); // These must correspond to the CurveType enum - curve_input_->set_combobox_strings({ tr("Linear"), tr("Exponential"), tr("Logarithmic") }); + SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") }); } rational TransitionBlock::in_offset() const @@ -169,56 +155,16 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t NodeValue(NodeValue::kFloat, GetInProgress(time), this)); } -void TransitionBlock::OutBlockConnected(Node *node) +NodeValueTable TransitionBlock::Value(const QString &output, NodeValueDatabase &value) const { - // If node is not a block, this will just be null - if ((connected_out_block_ = dynamic_cast(node))) { + Q_UNUSED(output) - Q_ASSERT(connected_out_block_->type() != Block::kTransition - && !connected_out_block_->out_transition() - && connected_out_block_ == this->previous()); - - connected_out_block_->set_out_transition(this); - } -} - -void TransitionBlock::OutBlockDisconnected() -{ - if (connected_out_block_) { - connected_out_block_->set_in_transition(nullptr); - connected_out_block_ = nullptr; - } -} - -void TransitionBlock::InBlockConnected(Node *node) -{ - // If node is not a block, this will just be null - if ((connected_in_block_ = dynamic_cast(node))) { - - Q_ASSERT(connected_in_block_->type() != Block::kTransition - && !connected_in_block_->in_transition() - && connected_in_block_ == this->next()); - - connected_in_block_->set_in_transition(this); - } -} - -void TransitionBlock::InBlockDisconnected() -{ - if (connected_in_block_) { - connected_in_block_->set_in_transition(nullptr); - connected_in_block_ = nullptr; - } -} - -NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const -{ NodeValue::Type data_type; - if (out_block_input()->IsConnected()) { - data_type = value[out_block_input()].GetWithMeta(NodeValue::kBuffer).type(); - } else if (in_block_input()->IsConnected()) { - data_type = value[in_block_input()].GetWithMeta(NodeValue::kBuffer).type(); + if (IsInputConnected(kOutBlockInput)) { + data_type = value[kOutBlockInput].GetWithMeta(NodeValue::kBuffer).type(); + } else if (IsInputConnected(kInBlockInput)) { + data_type = value[kInBlockInput].GetWithMeta(NodeValue::kBuffer).type(); } else { data_type = NodeValue::kNone; } @@ -230,9 +176,9 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const // This must be a visual transition ShaderJob job; - job.InsertValue(out_block_input(), value); - job.InsertValue(in_block_input(), value); - job.InsertValue(curve_input_, value); + job.InsertValue(this, kOutBlockInput, value); + job.InsertValue(this, kInBlockInput, value); + job.InsertValue(this, kCurveInput, value); double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); InsertTransitionTimes(&job, time); @@ -243,8 +189,8 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const push_job = QVariant::fromValue(job); } else if (data_type == NodeValue::kSamples) { // This must be an audio transition - SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value(); - SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value(); + SampleBufferPtr from_samples = value[kOutBlockInput].Take(NodeValue::kSamples).value(); + SampleBufferPtr to_samples = value[kInBlockInput].Take(NodeValue::kSamples).value(); if (from_samples || to_samples) { double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); @@ -287,7 +233,7 @@ void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferP double TransitionBlock::TransformCurve(double linear) const { - switch (static_cast(curve_input_->GetStandardValue().toInt())) { + switch (static_cast(GetStandardValue(kCurveInput).toInt())) { case kLinear: break; case kExponential: @@ -301,4 +247,49 @@ double TransitionBlock::TransformCurve(double linear) const return linear; } +void TransitionBlock::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(element) + + if (input == kOutBlockInput) { + // If node is not a block, this will just be null + if ((connected_out_block_ = dynamic_cast(output.node()))) { + + Q_ASSERT(connected_out_block_->type() != Block::kTransition + && !connected_out_block_->out_transition() + && connected_out_block_ == this->previous()); + + connected_out_block_->set_out_transition(this); + } + } else if (input == kInBlockInput) { + // If node is not a block, this will just be null + if ((connected_in_block_ = dynamic_cast(output.node()))) { + + Q_ASSERT(connected_in_block_->type() != Block::kTransition + && !connected_in_block_->in_transition() + && connected_in_block_ == this->next()); + + connected_in_block_->set_in_transition(this); + } + } +} + +void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(element) + Q_UNUSED(output) + + if (input == kOutBlockInput) { + if (connected_out_block_) { + connected_out_block_->set_in_transition(nullptr); + connected_out_block_ = nullptr; + } + } else if (input == kInBlockInput) { + if (connected_in_block_) { + connected_in_block_->set_in_transition(nullptr); + connected_in_block_ = nullptr; + } + } +} + } diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index af1b230e3..a02b3d7c1 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -33,9 +33,6 @@ public: virtual Type type() const override; - NodeInput* out_block_input() const; - NodeInput* in_block_input() const; - virtual void Retranslate() override; rational in_offset() const; @@ -50,7 +47,11 @@ public: virtual void Hash(QCryptographicHash& hash, const rational &time) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; + + static const QString kOutBlockInput; + static const QString kInBlockInput; + static const QString kCurveInput; protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const; @@ -59,6 +60,10 @@ protected: double TransformCurve(double linear) const; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override; + private: enum CurveType { kLinear, @@ -70,25 +75,10 @@ private: void InsertTransitionTimes(AcceleratedJob* job, const double& time) const; - NodeInput* out_block_input_; - - NodeInput* in_block_input_; - - NodeInput* curve_input_; - Block* connected_out_block_; Block* connected_in_block_; -private slots: - void OutBlockConnected(Node* node); - - void OutBlockDisconnected(); - - void InBlockConnected(Node* node); - - void InBlockDisconnected(); - }; } diff --git a/app/node/connectable.cpp b/app/node/connectable.cpp deleted file mode 100644 index 7fed464b1..000000000 --- a/app/node/connectable.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "connectable.h" - -#include "input.h" -#include "node.h" - -namespace olive { - -void NodeConnectable::ConnectEdge(Node *output, NodeInput *input, int element) -{ - InputConnection conn_to_in = {input, element}; - - // Connection exists - if (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) != output->output_connections_.end()) { - qDebug() << "Ignored connect that already exists:" << output << input->parent() << input->id() << element; - return; - } - - // Ensure a connection isn't getting overwritten - Q_ASSERT(input->input_connections_.find(element) == input->input_connections_.end()); - - // Insert connections in both sides - output->output_connections_.push_back(conn_to_in); - input->input_connections_[element] = output; - - // Emit signals - emit input->InputConnected(output, element); - emit output->OutputConnected(input, element); -} - -void NodeConnectable::DisconnectEdge(Node *output, NodeInput *input, int element) -{ - InputConnection conn_to_in = {input, element}; - - // Connection exists - if (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) == output->output_connections_.end()) { - qDebug() << "Ignored disconnect that doesn't exist:" << output << input->parent() << input->id() << element; - return; - } - - // Assertions to ensure connection exists - Q_ASSERT(input->input_connections_.at(element) == output); - - // Remove connections from both sides - output->output_connections_.erase(std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in)); - input->input_connections_.erase(input->input_connections_.find(element)); - - // Emit signals - emit input->InputDisconnected(output, element); - emit output->OutputDisconnected(input, element); -} - -uint qHash(const NodeConnectable::InputConnection &r, uint seed) -{ - return ::qHash(r.input, seed) ^ ::qHash(r.element, seed); -} - -} diff --git a/app/node/connectable.h b/app/node/connectable.h deleted file mode 100644 index cada64d43..000000000 --- a/app/node/connectable.h +++ /dev/null @@ -1,87 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef CONNECTABLE_H -#define CONNECTABLE_H - -#include -#include -#include - -namespace olive { - -class Node; -class NodeInput; - -class NodeConnectable : public QObject -{ - Q_OBJECT -public: - NodeConnectable() = default; - - struct InputConnection { - InputConnection(NodeInput* i = nullptr, int e = -1) - { - input = i; - element = e; - } - - bool operator==(const InputConnection& rhs) const - { - return input == rhs.input && element == rhs.element; - } - - NodeInput* input; - int element; - }; - - static void ConnectEdge(Node* output, NodeInput* input, int element = -1); - - static void DisconnectEdge(Node* output, NodeInput* input, int element = -1); - - struct Edge { - Node* output; - NodeInput* input; - int element; - }; - -protected: - const std::vector& output_connections() const - { - return output_connections_; - } - - const std::map& input_connections() const - { - return input_connections_; - } - -private: - std::vector output_connections_; - - std::map input_connections_; - -}; - -uint qHash(const NodeConnectable::InputConnection& r, uint seed = 0); - -} - -#endif // CONNECTABLE_H diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index afc323560..42ccb2fab 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -25,67 +25,61 @@ namespace olive { +const QString CropDistortNode::kTextureInput = QStringLiteral("tex_in"); +const QString CropDistortNode::kLeftInput = QStringLiteral("left_in"); +const QString CropDistortNode::kTopInput = QStringLiteral("top_in"); +const QString CropDistortNode::kRightInput = QStringLiteral("right_in"); +const QString CropDistortNode::kBottomInput = QStringLiteral("bottom_in"); +const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in"); + CropDistortNode::CropDistortNode() { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - left_input_ = new NodeInput(this, QStringLiteral("left_in"), NodeValue::kFloat, 0.0); - left_input_->setProperty("min", 0.0); - left_input_->setProperty("max", 1.0); - left_input_->setProperty("view", FloatSlider::kPercentage); + CreateCropSideInput(kLeftInput); + CreateCropSideInput(kTopInput); + CreateCropSideInput(kRightInput); + CreateCropSideInput(kBottomInput); - top_input_ = new NodeInput(this, QStringLiteral("top_in"), NodeValue::kFloat, 0.0); - top_input_->setProperty("min", 0.0); - top_input_->setProperty("max", 1.0); - top_input_->setProperty("view", FloatSlider::kPercentage); - - right_input_ = new NodeInput(this, QStringLiteral("right_in"), NodeValue::kFloat, 0.0); - right_input_->setProperty("min", 0.0); - right_input_->setProperty("max", 1.0); - right_input_->setProperty("view", FloatSlider::kPercentage); - - bottom_input_ = new NodeInput(this, QStringLiteral("bottom_in"), NodeValue::kFloat, 0.0); - bottom_input_->setProperty("min", 0.0); - bottom_input_->setProperty("max", 1.0); - bottom_input_->setProperty("view", FloatSlider::kPercentage); - - feather_input_ = new NodeInput(this, QStringLiteral("feather_in"), NodeValue::kFloat, 0.0); - feather_input_->setProperty("min", 0.0); + AddInput(kFeatherInput, NodeValue::kFloat, 0.0); + SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0); } void CropDistortNode::Retranslate() { - texture_input_->set_name(tr("Texture")); - left_input_->set_name(tr("Left")); - top_input_->set_name(tr("Top")); - right_input_->set_name(tr("Right")); - bottom_input_->set_name(tr("Bottom")); - feather_input_->set_name(tr("Feather")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kLeftInput, tr("Left")); + SetInputName(kTopInput, tr("Top")); + SetInputName(kRightInput, tr("Right")); + SetInputName(kBottomInput, tr("Bottom")); + SetInputName(kFeatherInput, tr("Feather")); } -NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const +NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(texture_input_, value); - job.InsertValue(left_input_, value); - job.InsertValue(top_input_, value); - job.InsertValue(right_input_, value); - job.InsertValue(bottom_input_, value); - job.InsertValue(feather_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kLeftInput, value); + job.InsertValue(this, kTopInput, value); + job.InsertValue(this, kRightInput, value); + job.InsertValue(this, kBottomInput, value); + job.InsertValue(this, kFeatherInput, value); job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); job.SetAlphaChannelRequired(true); NodeValueTable table = value.Merge(); - if (!job.GetValue(texture_input_).data().isNull()) { - if (!qIsNull(job.GetValue(left_input_).data().toDouble()) - || !qIsNull(job.GetValue(right_input_).data().toDouble()) - || !qIsNull(job.GetValue(top_input_).data().toDouble()) - || !qIsNull(job.GetValue(bottom_input_).data().toDouble())) { + if (!job.GetValue(kTextureInput).data().isNull()) { + if (!qIsNull(job.GetValue(kLeftInput).data().toDouble()) + || !qIsNull(job.GetValue(kRightInput).data().toDouble()) + || !qIsNull(job.GetValue(kTopInput).data().toDouble()) + || !qIsNull(job.GetValue(kBottomInput).data().toDouble())) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(NodeValue::kTexture, job.GetValue(texture_input_).data(), this); + table.Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this); } } @@ -106,10 +100,10 @@ void CropDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) p->setPen(QPen(Qt::white, 0)); - double left_pt = resolution.x() * db[left_input_].Get(NodeValue::kFloat).toDouble(); - double top_pt = resolution.y() * db[top_input_].Get(NodeValue::kFloat).toDouble(); - double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeValue::kFloat).toDouble()); - double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeValue::kFloat).toDouble()); + double left_pt = resolution.x() * db[kLeftInput].Get(NodeValue::kFloat).toDouble(); + double top_pt = resolution.y() * db[kTopInput].Get(NodeValue::kFloat).toDouble(); + double right_pt = resolution.x() * (1.0 - db[kRightInput].Get(NodeValue::kFloat).toDouble()); + double bottom_pt = resolution.y() * (1.0 - db[kBottomInput].Get(NodeValue::kFloat).toDouble()); double center_x_pt = lerp(left_pt, right_pt, 0.5); double center_y_pt = lerp(top_pt, bottom_pt, 0.5); @@ -156,7 +150,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoLeft; - gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kLeftInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopLeft] @@ -165,7 +159,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoTop; - gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kTopInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopRight] @@ -174,7 +168,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoRight; - gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kRightInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleBottomLeft] @@ -183,7 +177,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoBottom; - gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kBottomInput].Get(NodeValue::kFloat)); } if (gizmo_drag_ > kGizmoNone) { @@ -204,22 +198,22 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time) int counter = 0; if (gizmo_drag_ & kGizmoLeft) { - gizmo_dragger_[counter].Start(left_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kLeftInput), time); counter++; } if (gizmo_drag_ & kGizmoTop) { - gizmo_dragger_[counter].Start(top_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kTopInput), time); counter++; } if (gizmo_drag_ & kGizmoRight) { - gizmo_dragger_[counter].Start(right_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kRightInput), time); counter++; } if (gizmo_drag_ & kGizmoBottom) { - gizmo_dragger_[counter].Start(bottom_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kBottomInput), time); counter++; } } @@ -260,4 +254,12 @@ void CropDistortNode::GizmoRelease() gizmo_start_.clear(); } +void CropDistortNode::CreateCropSideInput(const QString &id) +{ + AddInput(id, NodeValue::kFloat, 0.0); + SetInputProperty(id, QStringLiteral("min"), 0.0); + SetInputProperty(id, QStringLiteral("max"), 1.0); + SetInputProperty(id, QStringLiteral("view"), FloatSlider::kPercentage); +} + } diff --git a/app/node/distort/crop/cropdistortnode.h b/app/node/distort/crop/cropdistortnode.h index 92ece059f..692855981 100644 --- a/app/node/distort/crop/cropdistortnode.h +++ b/app/node/distort/crop/cropdistortnode.h @@ -60,7 +60,7 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; @@ -75,15 +75,15 @@ public: virtual void GizmoMove(const QPointF &p, const rational &time) override; virtual void GizmoRelease() override; + static const QString kTextureInput; + static const QString kLeftInput; + static const QString kTopInput; + static const QString kRightInput; + static const QString kBottomInput; + static const QString kFeatherInput; + private: - NodeInput* texture_input_; - - NodeInput* left_input_; - NodeInput* top_input_; - NodeInput* right_input_; - NodeInput* bottom_input_; - - NodeInput* feather_input_; + void CreateCropSideInput(const QString& id); // Gizmo variables QRectF gizmo_resize_handle_[kGizmoScaleCount]; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 9c705899d..b40e850e7 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -26,35 +26,40 @@ namespace olive { +const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in"); +const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in"); +const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in"); + TransformDistortNode::TransformDistortNode() { - autoscale_input_ = new NodeInput(this, QStringLiteral("autoscale_in"), NodeValue::kCombo, 0); + AddInput(kAutoscaleInput, NodeValue::kCombo, 0); - interpolation_input_ = new NodeInput(this, QStringLiteral("interpolation_in"), NodeValue::kCombo, 2); + AddInput(kInterpolationInput, NodeValue::kCombo, 2); - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - texture_input_->SetKeyframable(false); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); } void TransformDistortNode::Retranslate() { MatrixGenerator::Retranslate(); - autoscale_input_->set_name(tr("Auto-Scale")); - texture_input_->set_name(tr("Texture")); - interpolation_input_->set_name(tr("Interpolation")); + SetInputName(kAutoscaleInput, tr("Auto-Scale")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kInterpolationInput, tr("Interpolation")); - autoscale_input_->set_combobox_strings({tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")}); - interpolation_input_->set_combobox_strings({tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); + SetComboBoxStrings(kAutoscaleInput, {tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")}); + SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); } -NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const +NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Generate matrix QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false); // Pop texture - TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value(); + TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value(); // Merge table NodeValueTable table = value.Merge(); @@ -64,7 +69,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const // Adjust our matrix by the resolutions involved QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height()); - AutoScaleType autoscale = static_cast(value[autoscale_input_].Get(NodeValue::kCombo).toInt()); + AutoScaleType autoscale = static_cast(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix, sequence_res, @@ -79,7 +84,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const ShaderJob job; job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this)); job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this)); - job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[interpolation_input_].Get(NodeValue::kCombo).toInt())); + job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[kInterpolationInput].Get(NodeValue::kCombo).toInt())); // FIXME: This should be optimized, we can use matrix math to determine if this operation will // end up with gaps in the screen that will require an alpha channel. @@ -121,10 +126,10 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) if (scaling) { // Dragging scale handle - gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)}; - gizmo_drag_ = scale_input(); + gizmo_start_ = {db[kScaleInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kScaleInput; - gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeValue::kBoolean).toBool(); + gizmo_scale_uniform_ = db[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(); if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) { @@ -136,8 +141,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } // Store texture size - QVector2D texture_sz = db[texture_input()].Get(NodeValue::kTexture).value(); - gizmo_scale_anchor_ = db[anchor_input()].Get(NodeValue::kVec2).value() + texture_sz/2; + QVector2D texture_sz = db[kTextureInput].Get(NodeValue::kTexture).value(); + gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value() + texture_sz/2; if (gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomRight] @@ -161,9 +166,9 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } else if (gizmo_anchor_pt_.contains(p)) { // Dragging the anchor point specifically - gizmo_start_ = {db[anchor_input()].Get(NodeValue::kVec2), - db[position_input()].Get(NodeValue::kVec2)}; - gizmo_drag_ = anchor_input(); + gizmo_start_ = {db[kAnchorInput].Get(NodeValue::kVec2), + db[kPositionInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kAnchorInput; // Store current matrix gizmo_matrix_ = GenerateMatrix(db, false, true, true, false); @@ -173,16 +178,16 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } else if (gizmo_rect_.containsPoint(p, Qt::OddEvenFill)) { // Dragging the main rectangle - gizmo_start_ = {db[position_input()].Get(NodeValue::kVec2)}; - gizmo_drag_ = position_input(); + gizmo_start_ = {db[kPositionInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kPositionInput; return true; } else { // Dragging rotation - gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)}; - gizmo_drag_ = rotation_input(); + gizmo_start_ = {db[kRotationInput].Get(NodeValue::kFloat)}; + gizmo_drag_ = kRotationInput; gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(), gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x()); @@ -198,15 +203,15 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) QPointF movement = (p - gizmo_drag_pos_); QVector2D vec_movement(movement); - if (gizmo_drag_ == anchor_input()) { + if (gizmo_drag_ == kAnchorInput) { // Dragging the anchor point around if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(4); - gizmo_dragger_[0].Start(anchor_input(), time, 0); - gizmo_dragger_[1].Start(anchor_input(), time, 1); - gizmo_dragger_[2].Start(position_input(), time, 0); - gizmo_dragger_[3].Start(position_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1), time); + gizmo_dragger_[2].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time); + gizmo_dragger_[3].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time); } QVector2D inverted_movement(gizmo_matrix_.toTransform().inverted().map(movement)); @@ -218,13 +223,13 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) gizmo_dragger_[2].Drag(position_cont.x()); gizmo_dragger_[3].Drag(position_cont.y()); - } else if (gizmo_drag_ == position_input()) { + } else if (gizmo_drag_ == kPositionInput) { // Dragging the main rectangle around if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(2); - gizmo_dragger_[0].Start(position_input(), time, 0); - gizmo_dragger_[1].Start(position_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time); } QVector2D position_cont = gizmo_start_[0].value() + vec_movement; @@ -232,20 +237,20 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) gizmo_dragger_[0].Drag(position_cont.x()); gizmo_dragger_[1].Drag(position_cont.y()); - } else if (gizmo_drag_ == scale_input()) { + } else if (gizmo_drag_ == kScaleInput) { // Dragging a resize handle if (gizmo_dragger_.isEmpty()) { if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(scale_input(), time, 0); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time); } else if (gizmo_scale_axes_ == kGizmoScaleYOnly) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(scale_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time); } else { gizmo_dragger_.resize(2); - gizmo_dragger_[0].Start(scale_input(), time, 0); - gizmo_dragger_[1].Start(scale_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time); } } @@ -274,12 +279,12 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) break; } - } else if (gizmo_drag_ == rotation_input()) { + } else if (gizmo_drag_ == kRotationInput) { // Dragging outside the rectangle to rotate if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(rotation_input(), time, 0); + gizmo_dragger_[0].Start(NodeInput(this, kRotationInput), time); } double current_angle = qAtan2(p.y() - gizmo_anchor_pt_.center().y(), @@ -367,10 +372,10 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) QPointF sequence_half_res_pt = sequence_half_res.toPointF(); // GizmoTraverser just returns the sizes of the textures and no other data - QVector2D tex_sz = db[texture_input_].Get(NodeValue::kTexture).value(); + QVector2D tex_sz = db[kTextureInput].Get(NodeValue::kTexture).value(); // Retrieve autoscale value - AutoScaleType autoscale = static_cast(db[autoscale_input_].Get(NodeValue::kCombo).toInt()); + AutoScaleType autoscale = static_cast(db[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); // Fold values into a matrix for the rectangle QMatrix4x4 rectangle_matrix; @@ -435,8 +440,8 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) // Use offsets to make the appearance of values that start in the top left, even though we // really anchor around the center - position_input()->setProperty("offset", sequence_res * 0.5); - anchor_input()->setProperty("offset", tex_sz * 0.5); + SetInputProperty(kPositionInput, QStringLiteral("offset"), sequence_res * 0.5); + SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5); } } diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index 5002db06b..ca7606a94 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -63,7 +63,7 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual ShaderCode GetShaderCode(const QString& shader_id) const override; @@ -78,11 +78,6 @@ public: virtual void GizmoMove(const QPointF &p, const rational &time) override; virtual void GizmoRelease() override; - NodeInput* texture_input() const - { - return texture_input_; - } - enum AutoScaleType { kAutoScaleNone, kAutoScaleFit, @@ -95,15 +90,15 @@ public: const QVector2D& texture_res, AutoScaleType autoscale_type = kAutoScaleNone); + static const QString kTextureInput; + static const QString kAutoscaleInput; + static const QString kInterpolationInput; + private: static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat); - NodeInput* texture_input_; - NodeInput* autoscale_input_; - NodeInput* interpolation_input_; - // Gizmo variables - NodeInput* gizmo_drag_; + QString gizmo_drag_; QVector gizmo_start_; QVector gizmo_dragger_; QPointF gizmo_drag_pos_; diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 21365c2d4..f6c1f8443 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -52,13 +52,18 @@ void NodeFactory::Initialize() // Add internal types for (int i=0;i(i))); - } + Node* created_node = CreateFromFactoryIndex(static_cast(i)); - /* - library_.append(new ExternalTransition(":/shaders/crossdissolve.xml")); - library_.append(new ExternalTransition(":/shaders/diptoblack.xml")); - */ + library_.append(created_node); + + if (created_node->inputs().isEmpty()) { + qWarning() << "Node" << created_node->id() << "has no inputs"; + } + + if (created_node->outputs().isEmpty()) { + qWarning() << "Node" << created_node->id() << "has no outputs"; + } + } } void NodeFactory::Destroy() diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index b3f8afd30..33975b68e 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -22,20 +22,27 @@ namespace olive { +const QString BlurFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString BlurFilterNode::kMethodInput = QStringLiteral("method_in"); +const QString BlurFilterNode::kRadiusInput = QStringLiteral("radius_in"); +const QString BlurFilterNode::kHorizInput = QStringLiteral("horiz_in"); +const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in"); +const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in"); + BlurFilterNode::BlurFilterNode() { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - method_input_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo, 0); + AddInput(kMethodInput, NodeValue::kCombo, 0); - radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); - radius_input_->setProperty("min", 0.0f); + AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kBoolean, true); + AddInput(kHorizInput, NodeValue::kBoolean, true); - vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kBoolean, true); + AddInput(kVertInput, NodeValue::kBoolean, true); - repeat_edge_pixels_input_ = new NodeInput(this, QStringLiteral("repeat_edge_pixels_in"), NodeValue::kBoolean, false); + AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, false); } Node *BlurFilterNode::copy() const @@ -65,13 +72,13 @@ QString BlurFilterNode::Description() const void BlurFilterNode::Retranslate() { - texture_input_->set_name(tr("Input")); - method_input_->set_name(tr("Method")); - method_input_->set_combobox_strings({ tr("Box"), tr("Gaussian") }); - radius_input_->set_name(tr("Radius")); - horiz_input_->set_name(tr("Horizontal")); - vert_input_->set_name(tr("Vertical")); - repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels")); + SetInputName(kTextureInput, tr("Input")); + SetInputName(kMethodInput, tr("Method")); + SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") }); + SetInputName(kRadiusInput, tr("Radius")); + SetInputName(kHorizInput, tr("Horizontal")); + SetInputName(kVertInput, tr("Vertical")); + SetInputName(kRepeatEdgePixelsInput, tr("Repeat Edge Pixels")); } ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const @@ -80,35 +87,37 @@ ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag")); } -NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(texture_input_, value); - job.InsertValue(method_input_, value); - job.InsertValue(radius_input_, value); - job.InsertValue(horiz_input_, value); - job.InsertValue(vert_input_, value); - job.InsertValue(repeat_edge_pixels_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kMethodInput, value); + job.InsertValue(this, kRadiusInput, value); + job.InsertValue(this, kHorizInput, value); + job.InsertValue(this, kVertInput, value); + job.InsertValue(this, kRepeatEdgePixelsInput, value); job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); NodeValueTable table = value.Merge(); // If there's no texture, no need to run an operation - if (!job.GetValue(texture_input_).data().isNull()) { + if (!job.GetValue(kTextureInput).data().isNull()) { // Check if radius > 0, and both "horiz" and/or "vert" are enabled - if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool()) - && job.GetValue(radius_input_).data().toDouble() > 0.0) { + if ((job.GetValue(kHorizInput).data().toBool() || job.GetValue(kVertInput).data().toBool()) + && job.GetValue(kRadiusInput).data().toDouble() > 0.0) { // Set iteration count to 2 if we're blurring both horizontally and vertically - if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) { - job.SetIterations(2, texture_input_); + if (job.GetValue(kHorizInput).data().toBool() && job.GetValue(kVertInput).data().toBool()) { + job.SetIterations(2, kTextureInput); } // If we're not repeating pixels, expect an alpha channel to appear - if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) { + if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) { job.SetAlphaChannelRequired(true); } @@ -116,7 +125,7 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const } else { // If we're not performing the blur job, just push the texture - table.Push(job.GetValue(texture_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/blur/blur.h b/app/node/filter/blur/blur.h index 7a121a34b..6a0ddb366 100644 --- a/app/node/filter/blur/blur.h +++ b/app/node/filter/blur/blur.h @@ -41,20 +41,14 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; -private: - NodeInput* texture_input_; - - NodeInput* method_input_; - - NodeInput* radius_input_; - - NodeInput* horiz_input_; - - NodeInput* vert_input_; - - NodeInput* repeat_edge_pixels_input_; + static const QString kTextureInput; + static const QString kMethodInput; + static const QString kRadiusInput; + static const QString kHorizInput; + static const QString kVertInput; + static const QString kRepeatEdgePixelsInput; }; diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index b03532cdc..6b0afe207 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -22,46 +22,52 @@ namespace olive { +const QString MosaicFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString MosaicFilterNode::kHorizInput = QStringLiteral("horiz_in"); +const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in"); + MosaicFilterNode::MosaicFilterNode() { - tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kFloat, 32.0); - horiz_input_->setProperty("min", 1.0); + AddInput(kHorizInput, NodeValue::kFloat, 32.0); + SetInputProperty(kHorizInput, QStringLiteral("min"), 1.0); - vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0); - vert_input_->setProperty("min", 1.0); + AddInput(kVertInput, NodeValue::kFloat, 18.0); + SetInputProperty(kVertInput, QStringLiteral("min"), 1.0); } void MosaicFilterNode::Retranslate() { - tex_input_->set_name(tr("Texture")); - horiz_input_->set_name(tr("Horizontal")); - vert_input_->set_name(tr("Vertical")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kHorizInput, tr("Horizontal")); + SetInputName(kVertInput, tr("Vertical")); } -NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable MosaicFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(tex_input_, value); - job.InsertValue(horiz_input_, value); - job.InsertValue(vert_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kHorizInput, value); + job.InsertValue(this, kVertInput, value); // Mipmapping makes this look weird, so we just use bilinear for finding the color of each block - job.SetInterpolation(tex_input_, Texture::kLinear); + job.SetInterpolation(kTextureInput, Texture::kLinear); NodeValueTable table = value.Merge(); - if (!job.GetValue(tex_input_).data().isNull()) { - TexturePtr texture = job.GetValue(tex_input_).data().value(); + if (!job.GetValue(kTextureInput).data().isNull()) { + TexturePtr texture = job.GetValue(kTextureInput).data().value(); if (texture - && job.GetValue(horiz_input_).data().toInt() != texture->width() - && job.GetValue(vert_input_).data().toInt() != texture->height()) { + && job.GetValue(kHorizInput).data().toInt() != texture->width() + && job.GetValue(kVertInput).data().toInt() != texture->height()) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/mosaic/mosaicfilternode.h b/app/node/filter/mosaic/mosaicfilternode.h index 104e05b31..2ecff1e29 100644 --- a/app/node/filter/mosaic/mosaicfilternode.h +++ b/app/node/filter/mosaic/mosaicfilternode.h @@ -58,15 +58,12 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* tex_input_; - - NodeInput* horiz_input_; - - NodeInput* vert_input_; + static const QString kTextureInput; + static const QString kHorizInput; + static const QString kVertInput; }; diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index 70cabe396..d611fb606 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -25,24 +25,27 @@ namespace olive { +const QString StrokeFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString StrokeFilterNode::kColorInput = QStringLiteral("color_in"); +const QString StrokeFilterNode::kRadiusInput = QStringLiteral("radius_in"); +const QString StrokeFilterNode::kOpacityInput = QStringLiteral("opacity_in"); +const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in"); + StrokeFilterNode::StrokeFilterNode() { - tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); - radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); - radius_input_->setProperty("min", 0.0f); + AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f); - opacity_input_->setProperty("view", FloatSlider::kPercentage); - opacity_input_->setProperty("min", 0.0f); - opacity_input_->setProperty("max", 1.0f); + AddInput(kOpacityInput, NodeValue::kFloat, 1.0f); + SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage); + SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.0f); + SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f); - inner_input_ = new NodeInput(this, QStringLiteral("inner_in"), NodeValue::kBoolean, false); + AddInput(kInnerInput, NodeValue::kBoolean, false); } Node *StrokeFilterNode::copy() const @@ -72,33 +75,35 @@ QString StrokeFilterNode::Description() const void StrokeFilterNode::Retranslate() { - tex_input_->set_name(tr("Input")); - color_input_->set_name(tr("Color")); - radius_input_->set_name(tr("Radius")); - opacity_input_->set_name(tr("Opacity")); - inner_input_->set_name(tr("Inner")); + SetInputName(kTextureInput, tr("Input")); + SetInputName(kColorInput, tr("Color")); + SetInputName(kRadiusInput, tr("Radius")); + SetInputName(kOpacityInput, tr("Opacity")); + SetInputName(kInnerInput, tr("Inner")); } -NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable StrokeFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(tex_input_, value); - job.InsertValue(color_input_, value); - job.InsertValue(radius_input_, value); - job.InsertValue(opacity_input_, value); - job.InsertValue(inner_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kColorInput, value); + job.InsertValue(this, kRadiusInput, value); + job.InsertValue(this, kOpacityInput, value); + job.InsertValue(this, kInnerInput, value); job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); NodeValueTable table = value.Merge(); - if (!job.GetValue(tex_input_).data().isNull()) { - if (job.GetValue(radius_input_).data().toDouble() > 0.0 - && job.GetValue(opacity_input_).data().toDouble() > 0.0) { + if (!job.GetValue(kTextureInput).data().isNull()) { + if (job.GetValue(kRadiusInput).data().toDouble() > 0.0 + && job.GetValue(kOpacityInput).data().toDouble() > 0.0) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/stroke/stroke.h b/app/node/filter/stroke/stroke.h index fb29aea33..338e266ea 100644 --- a/app/node/filter/stroke/stroke.h +++ b/app/node/filter/stroke/stroke.h @@ -40,19 +40,14 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* tex_input_; - - NodeInput* color_input_; - - NodeInput* radius_input_; - - NodeInput* opacity_input_; - - NodeInput* inner_input_; + static const QString kTextureInput; + static const QString kColorInput; + static const QString kRadiusInput; + static const QString kOpacityInput; + static const QString kInnerInput; }; diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index ac04b173f..d5088ecfc 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -27,23 +27,26 @@ namespace olive { +const QString MatrixGenerator::kPositionInput = QStringLiteral("pos_in"); +const QString MatrixGenerator::kRotationInput = QStringLiteral("rot_in"); +const QString MatrixGenerator::kScaleInput = QStringLiteral("scale_in"); +const QString MatrixGenerator::kUniformScaleInput = QStringLiteral("uniform_scale_in"); +const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in"); + MatrixGenerator::MatrixGenerator() { - position_input_ = new NodeInput(this, QStringLiteral("pos_in"), NodeValue::kVec2, QVector2D()); + AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); - rotation_input_ = new NodeInput(this, QStringLiteral("rot_in"), NodeValue::kFloat, 0.0f); + AddInput(kRotationInput, NodeValue::kFloat, 0.0); - scale_input_ = new NodeInput(this, QStringLiteral("scale_in"), NodeValue::kVec2, QVector2D(1.0f, 1.0f)); - scale_input_->setProperty("min", QVector2D(0, 0)); - scale_input_->setProperty("view", FloatSlider::kPercentage); - scale_input_->setProperty("disabley", true); + AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f)); + SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0)); + SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage); + SetInputProperty(kScaleInput, QStringLiteral("disabley"), true); - uniform_scale_input_ = new NodeInput(this, QStringLiteral("uniform_scale_in"), NodeValue::kBoolean, true); - uniform_scale_input_->SetKeyframable(false); - uniform_scale_input_->SetConnectable(false); - connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); + AddInput(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D()); + AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); } Node *MatrixGenerator::copy() const @@ -78,20 +81,22 @@ QString MatrixGenerator::Description() const void MatrixGenerator::Retranslate() { - position_input_->set_name(tr("Position")); - rotation_input_->set_name(tr("Rotation")); - scale_input_->set_name(tr("Scale")); - uniform_scale_input_->set_name(tr("Uniform Scale")); - anchor_input_->set_name(tr("Anchor Point")); + SetInputName(kPositionInput, tr("Position")); + SetInputName(kRotationInput, tr("Rotation")); + SetInputName(kScaleInput, tr("Scale")); + SetInputName(kUniformScaleInput, tr("Uniform Scale")); + SetInputName(kAnchorInput, tr("Anchor Point")); } -NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const +NodeValueTable MatrixGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Push matrix output QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false); - NodeValueTable output = value.Merge(); - output.Push(NodeValue::kMatrix, mat, this); - return output; + NodeValueTable out = value.Merge(); + out.Push(NodeValue::kMatrix, mat, this); + return out; } QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const @@ -103,47 +108,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, if (!ignore_anchor) { if (take) { // Take and store - anchor = value[anchor_input_].Take(NodeValue::kVec2).value(); + anchor = value[kAnchorInput].Take(NodeValue::kVec2).value(); } else { // Get and store - anchor = value[anchor_input_].Get(NodeValue::kVec2).value(); + anchor = value[kAnchorInput].Get(NodeValue::kVec2).value(); } } else if (take) { // Just take - value[anchor_input_].Take(NodeValue::kVec2).value(); + value[kAnchorInput].Take(NodeValue::kVec2).value(); } if (!ignore_scale) { if (take) { - scale = value[scale_input_].Take(NodeValue::kVec2).value(); + scale = value[kScaleInput].Take(NodeValue::kVec2).value(); } else { - scale = value[scale_input_].Get(NodeValue::kVec2).value(); + scale = value[kScaleInput].Get(NodeValue::kVec2).value(); } } else if (take) { - value[scale_input_].Take(NodeValue::kVec2).value(); + value[kScaleInput].Take(NodeValue::kVec2).value(); } if (!ignore_position) { if (take) { - position = value[position_input_].Take(NodeValue::kVec2).value(); + position = value[kPositionInput].Take(NodeValue::kVec2).value(); } else { - position = value[position_input_].Get(NodeValue::kVec2).value(); + position = value[kPositionInput].Get(NodeValue::kVec2).value(); } } else if (take) { - value[position_input_].Take(NodeValue::kVec2).value(); + value[kPositionInput].Take(NodeValue::kVec2).value(); } if (take) { return GenerateMatrix(position, - value[rotation_input_].Take(NodeValue::kFloat).toFloat(), + value[kRotationInput].Take(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(), + value[kUniformScaleInput].Take(NodeValue::kBoolean).toBool(), anchor); } else { return GenerateMatrix(position, - value[rotation_input_].Get(NodeValue::kFloat).toFloat(), + value[kRotationInput].Get(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(), + value[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(), anchor); } @@ -178,9 +183,13 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos, return mat; } -void MatrixGenerator::UniformScaleChanged() +void MatrixGenerator::InputValueChangedEvent(const QString &input, int element) { - scale_input_->setProperty("disabley", uniform_scale_input_->GetStandardValue().toBool()); + Q_UNUSED(element) + + if (input == kUniformScaleInput) { + SetInputProperty(kScaleInput, QStringLiteral("disabley"), GetStandardValue(kUniformScaleInput).toBool()); + } } } diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 6c22e2e5a..5401a02b6 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -44,7 +44,13 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; + + static const QString kPositionInput; + static const QString kRotationInput; + static const QString kScaleInput; + static const QString kUniformScaleInput; + static const QString kAnchorInput; protected: QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const; @@ -54,44 +60,7 @@ protected: bool uniform_scale, const QVector2D &anchor); - NodeInput* position_input() const - { - return position_input_; - } - - NodeInput* rotation_input() const - { - return rotation_input_; - } - - NodeInput* scale_input() const - { - return scale_input_; - } - - NodeInput* uniform_scale_input() const - { - return uniform_scale_input_; - } - - NodeInput* anchor_input() const - { - return anchor_input_; - } - -private: - NodeInput* position_input_; - - NodeInput* rotation_input_; - - NodeInput* scale_input_; - - NodeInput* uniform_scale_input_; - - NodeInput* anchor_input_; - -private slots: - void UniformScaleChanged(); + virtual void InputValueChangedEvent(const QString& input, int element) override; }; diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 1a9d053e4..6456694d9 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -25,25 +25,27 @@ namespace olive { +const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in"); +const QString PolygonGenerator::kColorInput = QStringLiteral("color_in"); + PolygonGenerator::PolygonGenerator() { - points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0)); - points_input_->SetIsArray(true); + AddInput(kPointsInput, NodeValue::kVec2, QVector2D(0, 0), InputFlags(kInputFlagArray)); - color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); // The Default Pentagon(tm) - points_input_->ArrayResize(5); - points_input_->SetStandardValueOnTrack(960, 0, 0); - points_input_->SetStandardValueOnTrack(240, 1, 0); - points_input_->SetStandardValueOnTrack(640, 0, 1); - points_input_->SetStandardValueOnTrack(480, 1, 1); - points_input_->SetStandardValueOnTrack(760, 0, 2); - points_input_->SetStandardValueOnTrack(800, 1, 2); - points_input_->SetStandardValueOnTrack(1100, 0, 3); - points_input_->SetStandardValueOnTrack(800, 1, 3); - points_input_->SetStandardValueOnTrack(1280, 0, 4); - points_input_->SetStandardValueOnTrack(480, 1, 4); + InputArrayResize(kPointsInput, 5); + SetSplitStandardValueOnTrack(kPointsInput, 0, 960, 0); + SetSplitStandardValueOnTrack(kPointsInput, 1, 240, 0); + SetSplitStandardValueOnTrack(kPointsInput, 0, 640, 1); + SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 1); + SetSplitStandardValueOnTrack(kPointsInput, 0, 760, 2); + SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 2); + SetSplitStandardValueOnTrack(kPointsInput, 0, 1100, 3); + SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 3); + SetSplitStandardValueOnTrack(kPointsInput, 0, 1200, 4); + SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 4); } Node *PolygonGenerator::copy() const @@ -73,8 +75,8 @@ QString PolygonGenerator::Description() const void PolygonGenerator::Retranslate() { - points_input_->set_name(tr("Points")); - color_input_->set_name(tr("Color")); + SetInputName(kPointsInput, tr("Points")); + SetInputName(kColorInput, tr("Color")); } ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const @@ -84,12 +86,14 @@ ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/polygon.frag"))); } -NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const +NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(points_input_, value); - job.InsertValue(color_input_, value); + job.InsertValue(this, kPointsInput, value); + job.InsertValue(this, kColorInput, value); job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution"))); job.SetAlphaChannelRequired(true); @@ -168,10 +172,10 @@ void PolygonGenerator::GizmoRelease() QVector PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const { // FIXME: Should Get() use a `kArray` type instead of a `kVec2` type? - QVector array_tbl = db[points_input_].Get(NodeValue::kVec2).value< QVector >(); + QVector array_tbl = db[kPointsInput].Get(NodeValue::kVec2).value< QVector >(); QVector points(array_tbl.size()); - for (int i=0;iArraySize();i++) { + for (int i=0;i(); v *= scale; diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index 6ab1fbc7e..cb0e08d25 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -42,7 +42,7 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString& shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual bool HasGizmos() const override; //virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p) const override; @@ -51,15 +51,14 @@ public: //virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override; //virtual void GizmoRelease() override; + static const QString kPointsInput; + static const QString kColorInput; + private: QVector GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const; QVector GetGizmoRects(const QVector& points) const; - NodeInput* points_input_; - - NodeInput* color_input_; - NodeInput* gizmo_drag_; QPointF gizmo_drag_start_; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 11afc2ce2..d6b98a46d 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -24,13 +24,12 @@ namespace olive { +const QString SolidGenerator::kColorInput = QStringLiteral("color_in"); + SolidGenerator::SolidGenerator() { // Default to a color that isn't black - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); } Node *SolidGenerator::copy() const @@ -60,13 +59,15 @@ QString SolidGenerator::Description() const void SolidGenerator::Retranslate() { - color_input_->set_name(tr("Color")); + SetInputName(kColorInput, tr("Color")); } -NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const +NodeValueTable SolidGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(color_input_, value); + job.InsertValue(this, kColorInput, value); NodeValueTable table = value.Merge(); table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index dbf21cf93..bf4aa6544 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -40,11 +40,10 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* color_input_; + static const QString kColorInput; }; diff --git a/app/node/generator/text/text.cpp b/app/node/generator/text/text.cpp index 55746b5cb..18cb9532d 100644 --- a/app/node/generator/text/text.cpp +++ b/app/node/generator/text/text.cpp @@ -30,31 +30,23 @@ enum TextVerticalAlign { kVerticalAlignBottom, }; +const QString TextGenerator::kTextInput = QStringLiteral("text_in"); +const QString TextGenerator::kColorInput = QStringLiteral("color_in"); +const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in"); +const QString TextGenerator::kFontInput = QStringLiteral("font_in"); +const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in"); + TextGenerator::TextGenerator() { - text_input_ = new NodeInput(this, - QStringLiteral("text_in"), - NodeValue::kText, - tr("Sample Text")); + AddInput(kTextInput, NodeValue::kText, tr("Sample Text")); - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); - valign_input_ = new NodeInput(this, - QStringLiteral("valign_in"), - NodeValue::kCombo, - 1); + AddInput(kVAlignInput, NodeValue::kCombo, 1); - font_input_ = new NodeInput(this, - QStringLiteral("font_in"), - NodeValue::kFont); + AddInput(kFontInput, NodeValue::kFont); - font_size_input_ = new NodeInput(this, - QStringLiteral("font_size_in"), - NodeValue::kFloat, - 72.0f); + AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f); } Node *TextGenerator::copy() const @@ -84,27 +76,29 @@ QString TextGenerator::Description() const void TextGenerator::Retranslate() { - text_input_->set_name(tr("Text")); - font_input_->set_name(tr("Font")); - font_size_input_->set_name(tr("Font Size")); - color_input_->set_name(tr("Color")); - valign_input_->set_name(tr("Vertical Align")); - valign_input_->set_combobox_strings({tr("Top"), tr("Center"), tr("Bottom")}); + SetInputName(kTextInput, tr("Text")); + SetInputName(kFontInput, tr("Font")); + SetInputName(kFontSizeInput, tr("Font Size")); + SetInputName(kColorInput, tr("Color")); + SetInputName(kVAlignInput, tr("Vertical Align")); + SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const +NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + GenerateJob job; - job.InsertValue(text_input_, value); - job.InsertValue(color_input_, value); - job.InsertValue(valign_input_, value); - job.InsertValue(font_input_, value); - job.InsertValue(font_size_input_, value); + job.InsertValue(this, kTextInput, value); + job.InsertValue(this, kColorInput, value); + job.InsertValue(this, kVAlignInput, value); + job.InsertValue(this, kFontInput, value); + job.InsertValue(this, kFontSizeInput, value); job.SetAlphaChannelRequired(true); NodeValueTable table = value.Merge(); - if (!job.GetValue(text_input_).data().toString().isEmpty()) { + if (!job.GetValue(kTextInput).data().toString().isEmpty()) { table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this); } @@ -124,14 +118,14 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const // Set default font QFont default_font; - default_font.setFamily(job.GetValue(font_input_).data().toString()); - default_font.setPointSizeF(job.GetValue(font_size_input_).data().toFloat()); + default_font.setFamily(job.GetValue(kFontInput).data().toString()); + default_font.setPointSizeF(job.GetValue(kFontSizeInput).data().toFloat()); text_doc.setDefaultFont(default_font); // Center by default text_doc.setDefaultTextOption(QTextOption(Qt::AlignCenter)); - text_doc.setHtml(job.GetValue(text_input_).data().toString()); + text_doc.setHtml(job.GetValue(kTextInput).data().toString()); // Align to 80% width because that's considered the "title safe" area int tenth_of_width = frame->video_params().width() / 10; @@ -144,7 +138,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const // Push 10% inwards to compensate for title safe area p.translate(tenth_of_width, 0); - TextVerticalAlign valign = static_cast(job.GetValue(valign_input_).data().toInt()); + TextVerticalAlign valign = static_cast(job.GetValue(kVAlignInput).data().toInt()); int doc_height = text_doc.size().height(); switch (valign) { @@ -165,7 +159,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const text_doc.drawContents(&p); // Transplant alpha channel to frame - Color rgb = job.GetValue(color_input_).data().value(); + Color rgb = job.GetValue(kColorInput).data().value(); for (int x=0; xwidth(); x++) { for (int y=0; yheight(); y++) { uchar src_alpha = img.bits()[img.bytesPerLine() * y + x]; diff --git a/app/node/generator/text/text.h b/app/node/generator/text/text.h index f672459a5..1ea6a69cf 100644 --- a/app/node/generator/text/text.h +++ b/app/node/generator/text/text.h @@ -40,20 +40,15 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; -private: - NodeInput* text_input_; - - NodeInput* color_input_; - - NodeInput* valign_input_; - - NodeInput* font_input_; - - NodeInput* font_size_input_; + static const QString kTextInput; + static const QString kColorInput; + static const QString kVAlignInput; + static const QString kFontInput; + static const QString kFontSizeInput; }; diff --git a/app/node/graph.h b/app/node/graph.h index c52eecdfa..016461b0d 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -65,11 +65,11 @@ signals: */ void NodeRemoved(Node* node); - void InputConnected(Node* output, NodeInput* input, int element); + void InputConnected(const NodeOutput& output, const NodeInput& input); - void InputDisconnected(Node* output, NodeInput* input, int element); + void InputDisconnected(const NodeOutput& output, const NodeInput& input); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input); protected: virtual void childEvent(QChildEvent* event) override; diff --git a/app/node/input.cpp b/app/node/input.cpp deleted file mode 100644 index d694ac70d..000000000 --- a/app/node/input.cpp +++ /dev/null @@ -1,905 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "input.h" - -#include "common/bezier.h" -#include "common/lerp.h" -#include "common/xmlutils.h" -#include "core.h" -#include "node.h" -#include "project/item/footage/stream.h" -#include "widget/nodeview/nodeviewundo.h" - -#define super NodeConnectable - -namespace olive { - -NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const SplitValue &default_value) -{ - Init(parent, id, type, default_value); -} - -NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, const QVariant &default_value) -{ - Init(parent, id, type, NodeValue::split_normal_value_into_track_values(type, default_value)); -} - -NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type) -{ - Init(parent, id, type, SplitValue()); -} - -NodeInput::~NodeInput() -{ - // Disconnect everything - DisconnectAll(); - - // Delete instances - delete primary_; - qDeleteAll(subinputs_); -} - -QString NodeInput::name() const -{ - if (name_.isEmpty()) { - return tr("Input"); - } - - return name_; -} - -void NodeInput::DisconnectAll() -{ - std::map copied_edges = edges(); - - for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - DisconnectEdge(it->second, this, it->first); - } -} - -void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) -{ - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("primary")) { - // Load primary immediate - LoadImmediate(reader, -1, xml_node_data, cancelled); - } else if (reader->name() == QStringLiteral("subelements")) { - // Load subelements - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("count")) { - ArrayResize(attr.value().toInt()); - } - } - - int element_counter = 0; - - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("element")) { - LoadImmediate(reader, element_counter, xml_node_data, cancelled); - - element_counter++; - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("connections")) { - // Load connections - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("connection")) { - int ele = -1; - - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("element")) { - ele = attr.value().toInt(); - } - } - - xml_node_data.desired_connections.append({this, ele, reader->readElementText().toULongLong()}); - } else { - reader->skipCurrentElement(); - } - } - } else { - reader->skipCurrentElement(); - } - } -} - -void NodeInput::Save(QXmlStreamWriter *writer) const -{ - writer->writeAttribute(QStringLiteral("id"), id()); - - writer->writeStartElement(QStringLiteral("primary")); - - SaveImmediate(writer, -1); - - writer->writeEndElement(); // primary - - writer->writeStartElement(QStringLiteral("subelements")); - - writer->writeAttribute(QStringLiteral("count"), QString::number(array_size_)); - - for (int i=0; iwriteStartElement(QStringLiteral("element")); - - SaveImmediate(writer, i); - - writer->writeEndElement(); // element - } - - writer->writeEndElement(); // subelements - - writer->writeStartElement(QStringLiteral("connections")); - - for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { - writer->writeStartElement(QStringLiteral("connection")); - - writer->writeAttribute(QStringLiteral("element"), QString::number(it->first)); - - writer->writeCharacters(QString::number(reinterpret_cast(it->second))); - - writer->writeEndElement(); // connection - } - - writer->writeEndElement(); // connections -} - -Node *NodeInput::parent() const -{ - return static_cast(QObject::parent()); -} - -bool NodeInput::event(QEvent *e) -{ - if (e->type() == QEvent::DynamicPropertyChange) { - QByteArray key = static_cast(e)->propertyName(); - emit PropertyChanged(key, property(key)); - return true; - } - - return QObject::event(e); -} - -void NodeInput::childEvent(QChildEvent *event) -{ - super::childEvent(event); - - NodeKeyframe* key = dynamic_cast(event->child()); - - if (key) { - if (event->type() == QEvent::ChildAdded) { - GetImmediate(key->element())->insert_keyframe(key); - - connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::InvalidateFromKeyframeTimeChange); - connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); - connect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange); - connect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged); - connect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange); - connect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange); - - emit KeyframeAdded(key); - emit ValueChanged(get_range_affected_by_keyframe(key), key->element()); - } else if (event->type() == QEvent::ChildRemoved) { - TimeRange time_affected = get_range_affected_by_keyframe(key); - - disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::InvalidateFromKeyframeTimeChange); - disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); - disconnect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange); - disconnect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged); - disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange); - disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange); - - GetImmediate(key->element())->remove_keyframe(key); - - emit KeyframeRemoved(key); - emit ValueChanged(time_affected, key->element()); - } - } -} - -void NodeInput::ClearElement(int index) -{ - GetImmediate(index)->delete_all_keyframes(); - - if (IsKeyframable()) { - SetIsKeyframing(false, index); - } - - SetSplitStandardValue(default_value_, index); -} - -void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_val) -{ - setParent(parent); - - id_ = id; - keyframable_ = true; - connectable_ = true; - default_value_ = default_val; - array_size_ = 0; - data_type_ = type; - is_array_ = false; - - primary_ = CreateImmediate(); -} - -void NodeInput::LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) -{ - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("standard")) { - // Load standard value - int val_index = 0; - - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("track")) { - QString value_text = reader->readElementText(); - QVariant value_on_track; - - if (!value_text.isEmpty()) { - value_on_track = StringToValue(value_text, xml_node_data.footage_connections, element); - } - - SetStandardValueOnTrack(value_on_track, val_index, element); - - val_index++; - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("keyframing") && IsKeyframable()) { - SetIsKeyframing(reader->readElementText().toInt(), element); - } else if (reader->name() == QStringLiteral("keyframes")) { - int track = 0; - - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("track")) { - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("key")) { - rational key_time; - NodeKeyframe::Type key_type; - QVariant key_value; - QPointF key_in_handle; - QPointF key_out_handle; - - XMLAttributeLoop(reader, attr) { - if (cancelled && *cancelled) { - return; - } - - if (attr.name() == QStringLiteral("time")) { - key_time = rational::fromString(attr.value().toString()); - } else if (attr.name() == QStringLiteral("type")) { - key_type = static_cast(attr.value().toInt()); - } else if (attr.name() == QStringLiteral("inhandlex")) { - key_in_handle.setX(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("inhandley")) { - key_in_handle.setY(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("outhandlex")) { - key_out_handle.setX(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("outhandley")) { - key_out_handle.setY(attr.value().toDouble()); - } - } - - key_value = StringToValue(reader->readElementText(), xml_node_data.footage_connections, element); - - NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, this); - key->set_bezier_control_in(key_in_handle); - key->set_bezier_control_out(key_out_handle); - } else { - reader->skipCurrentElement(); - } - } - - track++; - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("csinput")) { - setProperty("col_input", reader->readElementText()); - } else if (reader->name() == QStringLiteral("csdisplay")) { - setProperty("col_display", reader->readElementText()); - } else if (reader->name() == QStringLiteral("csview")) { - setProperty("col_view", reader->readElementText()); - } else if (reader->name() == QStringLiteral("cslook")) { - setProperty("col_look", reader->readElementText()); - } else { - reader->skipCurrentElement(); - } - } -} - -void NodeInput::SaveImmediate(QXmlStreamWriter* writer, int element) const -{ - if (IsKeyframable()) { - writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsKeyframing(element))); - } - - // Write standard value - writer->writeStartElement("standard"); - - foreach (const QVariant& v, GetSplitStandardValue(element)) { - writer->writeTextElement("track", ValueToString(v)); - } - - writer->writeEndElement(); // standard - - // Write keyframes - writer->writeStartElement("keyframes"); - - foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(element)) { - writer->writeStartElement("track"); - - foreach (NodeKeyframe* key, track) { - writer->writeStartElement("key"); - - writer->writeAttribute("time", key->time().toString()); - writer->writeAttribute("type", QString::number(key->type())); - writer->writeAttribute("inhandlex", QString::number(key->bezier_control_in().x())); - writer->writeAttribute("inhandley", QString::number(key->bezier_control_in().y())); - writer->writeAttribute("outhandlex", QString::number(key->bezier_control_out().x())); - writer->writeAttribute("outhandley", QString::number(key->bezier_control_out().y())); - - writer->writeCharacters(ValueToString(key->value())); - - writer->writeEndElement(); // key - } - - writer->writeEndElement(); // track - } - - writer->writeEndElement(); // keyframes - - if (data_type_ == NodeValue::kColor) { - // Save color management information - writer->writeTextElement(QStringLiteral("csinput"), property("col_input").toString()); - writer->writeTextElement(QStringLiteral("csdisplay"), property("col_display").toString()); - writer->writeTextElement(QStringLiteral("csview"), property("col_view").toString()); - writer->writeTextElement(QStringLiteral("cslook"), property("col_look").toString()); - } -} - -void NodeInput::ArrayResizeInternal(int size) -{ - if (array_size_ != size) { - // Update array size - if (array_size_ < size) { - // Size is larger, create any immediates that don't exist - for (int i=subinputs_.size(); i &list, bool traverse, bool exclusive_only) const -{ - for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { - Node* connected = it->second; - - if (connected->edges().size() == 1 || !exclusive_only) { - if (!list.contains(connected)) { - list.append(connected); - - if (traverse) { - foreach (NodeInput* i, connected->parameters()) { - i->GetDependencies(list, traverse, exclusive_only); - } - } - } - } - } -} - -QVariant NodeInput::GetDefaultValueForTrack(int track) const -{ - if (default_value_.isEmpty()) { - return QVariant(); - } - - return default_value_.at(track); -} - -QVector NodeInput::GetDependencies(bool traverse, bool exclusive_only) const -{ - QVector list; - - GetDependencies(list, traverse, exclusive_only); - - return list; -} - -QVector NodeInput::GetExclusiveDependencies() const -{ - return GetDependencies(true, true); -} - -QVector NodeInput::GetImmediateDependencies() const -{ - return GetDependencies(false, false); -} - -void NodeInput::ArrayAppend(bool undoable) -{ - ArrayResize(ArraySize() + 1, undoable); -} - -void NodeInput::ArrayInsert(int index, bool undoable) -{ - if (undoable) { - Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, index)); - } else { - // Add new input - ArrayResizeInternal(ArraySize() + 1); - - // Move connections down - std::map copied_edges = edges(); - for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element down - DisconnectEdge(it->second, this, it->first); - ConnectEdge(it->second, this, it->first + 1); - } - } - - // Shift values and keyframes up one element - for (int i=ArraySize()-1; i>index; i--) { - CopyValuesOfElement(this, this, i-1, i); - } - - // Reset value of element we just "inserted" - ClearElement(index); - } -} - -void NodeInput::ArrayRemove(int index, bool undoable) -{ - if (undoable) { - Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, index)); - } else { - // Remove input - ArrayResizeInternal(ArraySize() - 1); - - // Move connections up - std::map copied_edges = edges(); - for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element up if it's not the element being removed - DisconnectEdge(it->second, this, it->first); - - if (it->first > index) { - ConnectEdge(it->second, this, it->first - 1); - } - } - } - - // Shift values and keyframes down one element - for (int i=index; iundo_stack()->push(c); - } else { - c->redo(); - delete c; - } -} - -void NodeInput::ArrayRemoveLast(bool undoable) -{ - ArrayResize(ArraySize() - 1, undoable); -} - -QVariant NodeInput::GetValueAtTime(const rational &time, int element) const -{ - return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitValuesAtTime(time, element)); -} - -SplitValue NodeInput::GetSplitValuesAtTime(const rational &time, int element) const -{ - SplitValue vals; - - int nb_tracks = GetNumberOfKeyframeTracks(); - - for (int i=0;itime() >= time) { - // This time precedes any keyframe, so we just return the first value - return key_track.first()->value(); - } - - if (key_track.last()->time() <= time) { - // This time is after any keyframes so we return the last value - return key_track.last()->value(); - } - - // If we're here, the time must be somewhere in between the keyframes - for (int i=0;itime() == time - || !NodeValue::type_can_be_interpolated(data_type_) - || (before->time() < time && before->type() == NodeKeyframe::kHold)) { - - // Time == keyframe time, so value is precise - return before->value(); - - } else if (after->time() == time) { - - // Time == keyframe time, so value is precise - return after->value(); - - } else if (before->time() < time && after->time() > time) { - // We must interpolate between these keyframes - - if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { - // Perform a cubic bezier with two control points - - double t = Bezier::CubicXtoT(time.toDouble(), - before->time().toDouble(), - before->time().toDouble() + before->valid_bezier_control_out().x(), - after->time().toDouble() + after->valid_bezier_control_in().x(), - after->time().toDouble()); - - double y = Bezier::CubicTtoY(before->value().toDouble(), - before->value().toDouble() + before->valid_bezier_control_out().y(), - after->value().toDouble() + after->valid_bezier_control_in().y(), - after->value().toDouble(), - t); - - return y; - - } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { - // Perform a quadratic bezier with only one control point - - QPointF control_point; - double control_point_time; - double control_point_value; - - if (before->type() == NodeKeyframe::kBezier) { - control_point = before->valid_bezier_control_out(); - control_point_time = before->time().toDouble() + control_point.x(); - control_point_value = before->value().toDouble() + control_point.y(); - } else { - control_point = after->valid_bezier_control_in(); - control_point_time = after->time().toDouble() + control_point.x(); - control_point_value = after->value().toDouble() + control_point.y(); - } - - // Generate T from time values - used to determine bezier progress - double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble()); - - // Generate value using T - double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t); - - return y; - - } 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()); - - return lerp(before->value().toDouble(), after->value().toDouble(), period_progress); - } - } - } - } - - return GetStandardValueOnTrack(track, element); -} - -bool NodeInput::IsKeyframable() const -{ - return keyframable_; -} - -void NodeInput::SetKeyframable(bool k) -{ - keyframable_ = k; -} - -void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool traverse_arrays) -{ - Q_ASSERT(source->id() == dest->id()); - - CopyValuesOfElement(source, dest, -1); - - // Copy array size - dest->ArrayResize(source->ArraySize()); - - // If enabled, copy arrays too - if (traverse_arrays) { - for (int i=0; iArraySize(); i++) { - CopyValuesOfElement(source, dest, i); - } - } - - // Copy connections - if (include_connections) { - if (traverse_arrays) { - // Copy all connections - for (auto it=source->input_connections().cbegin(); it!=source->input_connections().cend(); it++) { - ConnectEdge(it->second, dest, it->first); - } - } else { - // Just copy the primary connection (at -1) - if (source->IsConnected()) { - ConnectEdge(source->GetConnectedNode(), dest); - } - } - } -} - -void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int src_element, int dst_element) -{ - if (dst_element >= dst->subinputs_.size()) { - qDebug() << "Ignored destination element that was out of array bounds"; - return; - } - - // Copy standard value - dst->SetSplitStandardValue(src->GetSplitStandardValue(src_element), dst_element); - - // Copy keyframes - dst->GetImmediate(dst_element)->delete_all_keyframes(); - foreach (const NodeKeyframeTrack& track, src->GetImmediate(src_element)->keyframe_tracks()) { - foreach (NodeKeyframe* key, track) { - key->copy(dst_element, dst); - } - } - - // Copy keyframing state - if (src->IsKeyframable()) { - dst->SetIsKeyframing(src->IsKeyframing(src_element), dst_element); - } - - // If this is the root of an array, copy the array size - if (src_element == -1 && dst_element == -1) { - dst->ArrayResize(src->ArraySize()); - } -} - -QStringList NodeInput::get_combobox_strings() const -{ - return property("combo_str").toStringList(); -} - -void NodeInput::set_combobox_strings(const QStringList &strings) -{ - setProperty("combo_str", strings); -} - -void NodeInput::InvalidateFromKeyframeBezierInChange() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - int keyframe_index = track.indexOf(key); - - rational start = RATIONAL_MIN; - rational end = key->time(); - - if (keyframe_index > 0) { - start = track.at(keyframe_index - 1)->time(); - } - - emit ValueChanged(TimeRange(start, end), key->element()); -} - -void NodeInput::InvalidateFromKeyframeBezierOutChange() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - int keyframe_index = track.indexOf(key); - - rational start = key->time(); - rational end = RATIONAL_MAX; - - if (keyframe_index < track.size() - 1) { - end = track.at(keyframe_index + 1)->time(); - } - - emit ValueChanged(TimeRange(start, end), key->element()); -} - -void NodeInput::InvalidateFromKeyframeTimeChange() -{ - NodeKeyframe* key = static_cast(sender()); - NodeInputImmediate* immediate = GetImmediate(key->element()); - TimeRange original_range = get_range_affected_by_keyframe(key); - - TimeRangeList invalidate_range; - invalidate_range.insert(original_range); - - if (!(original_range.in() < key->time() && original_range.out() > key->time())) { - // This keyframe needs resorting, store it and remove it from the list - immediate->remove_keyframe(key); - - // Automatically insertion sort - immediate->insert_keyframe(key); - - // Invalidate new area that the keyframe has been moved to - invalidate_range.insert(get_range_affected_by_keyframe(key)); - } - - // Invalidate entire area surrounding the keyframe (either where it currently is, or where it used to be before it - // was resorted in the if block above) - foreach (const TimeRange& r, invalidate_range) { - emit ValueChanged(r, key->element()); - } -} - -void NodeInput::InvalidateFromKeyframeValueChange() -{ - NodeKeyframe* key = static_cast(sender()); - emit ValueChanged(get_range_affected_by_keyframe(key), key->element()); -} - -void NodeInput::InvalidateFromKeyframeTypeChanged() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - - if (track.size() == 1) { - // If there are no other frames, the interpolation won't do anything - return; - } - - // Invalidate entire range - emit ValueChanged(get_range_around_index(track.indexOf(key), key->track(), key->element()), key->element()); -} - -TimeRange NodeInput::get_range_affected_by_keyframe(NodeKeyframe *key) const -{ - const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key); - int keyframe_index = key_track.indexOf(key); - - TimeRange range = get_range_around_index(keyframe_index, key->track(), key->element()); - - // If a previous key exists and it's a hold, we don't need to invalidate those frames - if (key_track.size() > 1 - && keyframe_index > 0 - && key_track.at(keyframe_index - 1)->type() == NodeKeyframe::kHold) { - range.set_in(key->time()); - } - - return range; -} - -TimeRange NodeInput::get_range_around_index(int index, int track, int element) const -{ - rational range_begin = RATIONAL_MIN; - rational range_end = RATIONAL_MAX; - - const NodeKeyframeTrack& key_track = GetImmediate(element)->keyframe_tracks().at(track); - - if (key_track.size() > 1) { - if (index > 0) { - // If this is not the first key, we'll need to limit it to the key just before - range_begin = key_track.at(index - 1)->time(); - } - if (index < key_track.size() - 1) { - // If this is not the last key, we'll need to limit it to the key just after - range_end = key_track.at(index + 1)->time(); - } - } - - return TimeRange(range_begin, range_end); -} - -QString NodeInput::ValueToString(const QVariant &value) const -{ - return NodeValue::ValueToString(data_type_, value, true); -} - -QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections, int element) -{ - if (data_type_ == NodeValue::kFootage) { - footage_connections.append({this, element, string.toULongLong()}); - } - - return NodeValue::StringToValue(data_type_, string, true); -} - -uint qHash(const NodeInput::KeyframeTrackReference &ref, uint seed) -{ - return qHash(ref.input, seed) ^ qHash(ref.element, seed) ^ qHash(ref.track, seed); -} - -Project *NodeInput::ArrayRemoveCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -Project *NodeInput::ArrayResizeCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -Project *NodeInput::ArrayInsertCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -} diff --git a/app/node/input.h b/app/node/input.h deleted file mode 100644 index 567bbe942..000000000 --- a/app/node/input.h +++ /dev/null @@ -1,629 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODEINPUT_H -#define NODEINPUT_H - -#include "common/timerange.h" -#include "keyframe.h" -#include "node/connectable.h" -#include "node/inputimmediate.h" -#include "node/value.h" -#include "splitvalue.h" -#include "undo/undocommand.h" - -namespace olive { - -class Node; - -/** - * @brief A node parameter designed to take either user input or data from another node - */ -class NodeInput : public NodeConnectable -{ - Q_OBJECT -public: - /** - * @brief NodeInput Constructor - * - * @param id - * - * Unique ID associated with this parameter for this Node. This ID only has to be unique within this Node. Used for - * saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an - * older version. This of course assumes that parameters don't change their ID. - */ - NodeInput(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_value); - NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVariant& default_value); - NodeInput(Node* parent, const QString &id, NodeValue::Type type); - - virtual ~NodeInput() override; - - const QString& id() const - { - return id_; - } - - QString name() const; - - void set_name(const QString& name) - { - name_ = name; - - emit NameChanged(name_); - } - - bool IsArray() const - { - return is_array_; - } - - void SetIsArray(bool e) - { - is_array_ = e; - } - - void DisconnectAll(); - - void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); - - void Save(QXmlStreamWriter* writer) const; - - /** - * @brief Deliberately shadow QObject parent, since we only expect NodeInput to have Node as a parent - */ - Node* parent() const; - - /** - * @brief The data type this parameter outputs - * - * This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be - * connected to it. - */ - NodeValue::Type GetDataType() const - { - return data_type_; - } - - void SetDataType(NodeValue::Type type) - { - data_type_ = type; - - emit DataTypeChanged(type); - } - - const std::map& edges() const - { - return input_connections(); - } - - bool IsConnected(int element = -1) const - { - return input_connections().find(element) != input_connections().end(); - } - - /** - * @brief Returns TRUE if the value is expected to always be the same (i.e. no keyframes and - * not connected to anything) - */ - bool IsStatic(int element = -1) const - { - return !IsConnected(element) && !GetImmediate(element)->is_keyframing(); - } - - Node* GetConnectedNode(int element = -1) const - { - return input_connections().at(element); - } - - bool IsConnectable() const - { - return connectable_; - } - - void SetConnectable(bool e) - { - connectable_ = e; - } - - const QVector &GetKeyframeTracks(int element = -1) const - { - return GetImmediate(element)->keyframe_tracks(); - } - - NodeKeyframe* GetKeyframeAtTimeOnTrack(const rational& time, int track, int element) const - { - return GetImmediate(element)->get_keyframe_at_time_on_track(time, track); - } - - NodeKeyframe::Type GetBestKeyframeTypeForTime(const rational& time, int track, int element) const - { - return GetImmediate(element)->get_best_keyframe_type_for_time(time, track); - } - - /** - * @brief Return whether this input can be keyframed or not - */ - bool IsKeyframable() const; - - bool IsKeyframing(int element = -1) const - { - return GetImmediate(element)->is_keyframing(); - } - - /** - * @brief Get non-keyframed value - */ - QVariant GetStandardValue(int element = -1) const - { - return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitStandardValue(element)); - } - - /** - * @brief Get non-keyframed value split into components (the way it's stored) - */ - const SplitValue& GetSplitStandardValue(int element = -1) const - { - return GetImmediate(element)->get_split_standard_value(); - } - - QVariant GetStandardValueOnTrack(int track, int element = -1) const - { - return GetImmediate(element)->get_split_standard_value().at(track); - } - - /** - * @brief Set non-keyframed value - * - * This is the value used if keyframing is not enabled. If keyframing is enabled, it is - * overwritten. - */ - void SetStandardValue(const QVariant& value, int element = -1) - { - SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element); - } - - void SetSplitStandardValue(const SplitValue& value, int element = -1) - { - GetImmediate(element)->set_split_standard_value(value); - - for (int i=0; iset_standard_value_on_track(value, track); - - if (IsUsingStandardValue(track, element)) { - // If this standard value is being used, we need to send a value changed signal - emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), element); - } - } - - /** - * @brief Set whether this input can be keyframed or not - */ - void SetKeyframable(bool k); - - /** - * @brief Copy all values including keyframe information and connections from another NodeInput - */ - static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool traverse_arrays = true); - - static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int src_element, int dst_element); - static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element) - { - CopyValuesOfElement(source, dst, element, element); - } - - QStringList get_combobox_strings() const; - - void set_combobox_strings(const QStringList& strings); - - void GetDependencies(QVector &list, bool traverse, bool exclusive_only) const; - - QVariant GetDefaultValue() const - { - return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_); - } - - const SplitValue& GetSplitDefaultValue() const - { - return default_value_; - } - - QVariant GetDefaultValueForTrack(int track) const; - - QVector GetDependencies(bool traverse = true, bool exclusive_only = false) const; - - QVector GetExclusiveDependencies() const; - - QVector GetImmediateDependencies() const; - - NodeKeyframe* GetEarliestKeyframe(int element = -1) - { - return GetImmediate(element)->get_earliest_keyframe(); - } - - NodeKeyframe* GetLatestKeyframe(int element = -1) - { - return GetImmediate(element)->get_latest_keyframe(); - } - - bool HasKeyframeAtTime(const rational& time, int element = -1) - { - return GetImmediate(element)->has_keyframe_at_time(time); - } - - QVector GetKeyframesAtTime(const rational& time, int element = -1) - { - return GetImmediate(element)->get_keyframe_at_time(time); - } - - void SetIsKeyframing(bool keyframing, int element) - { - if (!IsKeyframable()) { - qDebug() << "Ignored set keyframing because this input is not keyframable"; - return; - } - - GetImmediate(element)->set_is_keyframing(keyframing); - - emit KeyframeEnableChanged(keyframing, element); - } - - /// Alias for ArrayResize(ArraySize() + 1) - void ArrayAppend(bool undoable = false); - - void ArrayInsert(int index, bool undoable = false); - - void ArrayRemove(int index, bool undoable = false); - - /// Alias for ArrayInsert(0) - void ArrayPrepend(bool undoable = false); - - void ArrayResize(int size, bool undoable = false); - - /// Alias for ArrayResize(ArraySize() - 1) - void ArrayRemoveLast(bool undoable = false); - - int ArraySize() const - { - return array_size_; - } - - int GetNumberOfKeyframeTracks() const - { - return NodeValue::get_number_of_keyframe_tracks(data_type_); - } - - /** - * @brief Calculate what the stored value should be at a certain time - * - * If this is a multi-track data type (e.g. kVec2), this will automatically combine the result into a QVector2D. - */ - QVariant GetValueAtTime(const rational& time, int element = -1) const; - - SplitValue GetSplitValuesAtTime(const rational& time, int element = -1) const; - - /** - * @brief Calculate the stored value for a specific track - * - * For most data types, there is only one track (e.g. `track == 0`), but multi-track data types like kVec2 will - * produce the X value on track 0 and the Y value on track 1. - */ - QVariant GetValueAtTimeForTrack(const rational& time, int track, int element = -1) const; - - NodeKeyframe* GetClosestKeyframeBeforeTime(const rational& time, int element = -1) const - { - return GetImmediate(element)->get_closest_keyframe_before_time(time); - } - - NodeKeyframe* GetClosestKeyframeAfterTime(const rational& time, int element = -1) const - { - return GetImmediate(element)->get_closest_keyframe_after_time(time); - } - - struct KeyframeTrackReference { - NodeInput* input; - int element; - int track; - - bool operator==(const KeyframeTrackReference& rhs) const - { - return input == rhs.input && element == rhs.element && track == rhs.track; - } - }; - -signals: - void NameChanged(const QString& name); - - void ValueChanged(const olive::TimeRange& range, int element); - - void KeyframeAdded(NodeKeyframe* key); - - void KeyframeRemoved(NodeKeyframe* key); - - void KeyframeTimeChanged(); - - void PropertyChanged(const QString& s, const QVariant& v); - - void ArraySizeChanged(int size); - - void KeyframeEnableChanged(bool enabled, int element); - - void DataTypeChanged(NodeValue::Type type); - - void InputConnected(Node* source, int element); - - void InputDisconnected(Node* source, int element); - -protected: - virtual bool event(QEvent* e) override; - - virtual void childEvent(QChildEvent* e) override; - -private: - class ArrayInsertCommand : public UndoCommand - { - public: - ArrayInsertCommand(NodeInput* input, int index) : - input_(input), - index_(index) - { - } - - virtual Project* GetRelevantProject() const override; - - virtual void redo() override - { - input_->ArrayInsert(index_, false); - } - - virtual void undo() override - { - input_->ArrayRemove(index_, false); - } - - private: - NodeInput* input_; - int index_; - - }; - - class ArrayRemoveCommand : public UndoCommand - { - public: - ArrayRemoveCommand(NodeInput* input, int index) : - input_(input), - index_(index) - { - } - - virtual Project* GetRelevantProject() const override; - - protected: - virtual void redo() override - { - // Save immediate data - if (input_->IsKeyframable()) { - is_keyframing_ = input_->IsKeyframing(index_); - } - standard_value_ = input_->GetSplitStandardValue(index_); - keyframes_ = input_->GetImmediate(index_)->keyframe_tracks(); - input_->GetImmediate(index_)->delete_all_keyframes(&memory_manager_); - - input_->ArrayRemove(index_, false); - } - - virtual void undo() override - { - input_->ArrayInsert(index_, false); - - // Restore keyframes - foreach (const NodeKeyframeTrack& track, keyframes_) { - foreach (NodeKeyframe* key, track) { - key->setParent(input_); - } - } - input_->SetSplitStandardValue(standard_value_, index_); - - if (input_->IsKeyframable()) { - input_->SetIsKeyframing(is_keyframing_, index_); - } - } - - private: - NodeInput* input_; - int index_; - - SplitValue standard_value_; - bool is_keyframing_; - QVector keyframes_; - QObject memory_manager_; - - }; - - class ArrayResizeCommand : public UndoCommand - { - public: - ArrayResizeCommand(NodeInput* input, int size) : - input_(input), - size_(size) - {} - - virtual void redo() override - { - old_size_ = input_->array_size_; - - if (old_size_ > size_) { - // Decreasing in size, disconnect any extraneous edges - for (int i=size_; iedges().at(i); - - removed_connections_.insert(i, output); - - DisconnectEdge(output, input_, i); - } catch (std::out_of_range&) {} - } - } - - input_->ArrayResizeInternal(size_); - } - - virtual void undo() override - { - for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { - ConnectEdge(it.value(), input_, it.key()); - } - removed_connections_.clear(); - - input_->ArrayResizeInternal(old_size_); - } - - virtual Project* GetRelevantProject() const override; - - private: - NodeInput* input_; - int size_; - int old_size_; - - QHash removed_connections_; - - }; - - void ClearElement(int index); - - void Init(Node *parent, const QString& id, NodeValue::Type type, const SplitValue &default_val); - - void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); - - void SaveImmediate(QXmlStreamWriter *writer, int element) const; - - void ArrayResizeInternal(int size); - - NodeInputImmediate* CreateImmediate(); - - const NodeInputImmediate* GetImmediate(int element = -1) const - { - return element > -1 ? subinputs_.at(element) : primary_; - } - - NodeInputImmediate* GetImmediate(int element = -1) - { - return element > -1 ? subinputs_[element] : primary_; - } - - const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const - { - return GetImmediate(key->element())->keyframe_tracks().at(key->track()); - } - - bool IsUsingStandardValue(int track = 0, int element = -1) const - { - return GetImmediate(element)->is_using_standard_value(track); - } - - QString ValueToString(const QVariant& value) const; - - QVariant StringToValue(const QString &string, QList &footage_connections, int element); - - /** - * @brief Intelligently determine how what time range is affected by a keyframe - */ - TimeRange get_range_affected_by_keyframe(NodeKeyframe *key) const; - - /** - * @brief Gets a time range between the previous and next keyframes of index - */ - TimeRange get_range_around_index(int index, int track, int element) const; - - NodeInputImmediate* primary_; - - QVector subinputs_; - - SplitValue default_value_; - - /** - * @brief Unique identifier of this input within this node - */ - QString id_; - - /** - * @brief User displayable name of input - */ - QString name_; - - /** - * @brief Internal keyframable value - */ - bool keyframable_; - - bool connectable_; - - bool is_array_; - - int array_size_; - - /** - * @brief Default data type - */ - NodeValue::Type data_type_; - -private slots: - /** - * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time - */ - void InvalidateFromKeyframeTimeChange(); - - /** - * @brief Slot when a keyframe's value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeValueChange(); - - /** - * @brief Slot when a keyframe's type changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeTypeChanged(); - - /** - * @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeBezierInChange(); - - /** - * @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeBezierOutChange(); - -}; - -uint qHash(const NodeInput::KeyframeTrackReference& ref, uint seed = 0); - -} - -#endif // NODEINPUT_H diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 09211a58e..4c2805c28 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -25,13 +25,12 @@ namespace olive { +const QString MediaInput::kFootageInput = QStringLiteral("footage_in"); + MediaInput::MediaInput() : connected_footage_(nullptr) { - footage_input_ = new NodeInput(this, QStringLiteral("footage_in"), NodeValue::kFootage); - footage_input_->SetConnectable(false); - footage_input_->SetKeyframable(false); - connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged); + AddInput(kFootageInput, NodeValue::kFootage, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); } QVector MediaInput::Category() const @@ -41,21 +40,23 @@ QVector MediaInput::Category() const Stream *MediaInput::stream() const { - return Node::ValueToPtr(footage_input_->GetStandardValue()); + return Node::ValueToPtr(GetStandardValue(kFootageInput)); } void MediaInput::SetStream(Stream* s) { - footage_input_->SetStandardValue(Node::PtrToValue(s)); + SetStandardValue(kFootageInput, Node::PtrToValue(s)); } void MediaInput::Retranslate() { - footage_input_->set_name(tr("Media")); + SetInputName(kFootageInput, tr("Media")); } -NodeValueTable MediaInput::Value(NodeValueDatabase &value) const +NodeValueTable MediaInput::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + NodeValueTable table = value.Merge(); if (connected_footage_) { @@ -68,28 +69,32 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const return table; } -void MediaInput::FootageChanged() +void MediaInput::InputValueChangedEvent(const QString &input, int element) { - Stream* new_footage = footage_input_->GetStandardValue().value(); + Q_UNUSED(element) - if (new_footage == connected_footage_) { - return; - } + if (input == kFootageInput) { + Stream* new_footage = stream(); - if (connected_footage_) { - disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); - } + if (new_footage == connected_footage_) { + return; + } - connected_footage_ = new_footage; + if (connected_footage_) { + disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + } - if (connected_footage_) { - connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + connected_footage_ = new_footage; + + if (connected_footage_) { + connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + } } } void MediaInput::FootageParametersChanged() { - InvalidateCache(TimeRange(0, RATIONAL_MAX), InputConnection(footage_input_)); + InvalidateCache(TimeRange(0, RATIONAL_MAX), kFootageInput); } } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 6dc2b8270..c3c50bf33 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -63,16 +63,16 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; + + static const QString kFootageInput; protected: - NodeInput* footage_input_; + virtual void InputValueChangedEvent(const QString& input, int element); Stream* connected_footage_; private slots: - void FootageChanged(); - void FootageParametersChanged(); }; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 705f6fc16..5eadf8b83 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -51,8 +51,10 @@ QString TimeInput::Description() const return tr("Generates the time (in seconds) at this frame"); } -NodeValueTable TimeInput::Value(NodeValueDatabase &value) const +NodeValueTable TimeInput::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + NodeValueTable table = value.Merge(); table.Push(NodeValue::kFloat, diff --git a/app/node/input/time/timeinput.h b/app/node/input/time/timeinput.h index eb2dac8f2..85f5b3d39 100644 --- a/app/node/input/time/timeinput.h +++ b/app/node/input/time/timeinput.h @@ -38,7 +38,7 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void Hash(QCryptographicHash& hash, const rational& time) const override; diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index 2060b37d2..0d835fc77 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -26,61 +26,64 @@ namespace olive { -NodeInputDragger::NodeInputDragger() : - input_(nullptr) +NodeInputDragger::NodeInputDragger() { } bool NodeInputDragger::IsStarted() const { - return input_; + return input_.IsValid(); } -void NodeInputDragger::Start(NodeInput *input, const rational &time, int track, int element) +void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rational &time) { - Q_ASSERT(!input_); + Q_ASSERT(!IsStarted()); // Set up new drag input_ = input; time_ = time; - track_ = track; - element_ = element; + + Node* node = input_.input().node(); // Cache current value - start_value_ = input_->GetValueAtTimeForTrack(time, track, element_); + start_value_ = node->GetSplitValueAtTimeOnTrack(input_, time); // Determine whether we are creating a keyframe or not - if (input_->IsKeyframing(element_)) { - dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_); + if (input_.input().IsKeyframing()) { + dragging_key_ = node->GetKeyframeAtTimeOnTrack(input_, time); drag_created_key_ = !dragging_key_; if (drag_created_key_) { dragging_key_ = new NodeKeyframe(time, start_value_, - input_->GetBestKeyframeTypeForTime(time, track, element_), - track, - element_, - input_); + node->GetBestKeyframeTypeForTimeOnTrack(input_, time), + input_.track(), + input_.input().element(), + input_.input().input(), + node); } } } void NodeInputDragger::Drag(QVariant value) { - Q_ASSERT(input_); + Q_ASSERT(IsStarted()); - if (input_->property("min").isValid()) { + Node* node = input_.input().node(); + const QString& input = input_.input().input(); + + if (node->HasInputProperty(input, QStringLiteral("min"))) { // Assumes the value is a double of some kind - double min = input_->property("min").toDouble(); + double min = node->GetInputProperty(input, QStringLiteral("min")).toDouble(); double v = value.toDouble(); if (v < min) { value = min; } } - if (input_->property("max").isValid()) { - double max = input_->property("max").toDouble(); + if (node->HasInputProperty(input, QStringLiteral("max"))) { + double max = node->GetInputProperty(input, QStringLiteral("max")).toDouble(); double v = value.toDouble(); if (v > max) { value = max; @@ -91,10 +94,10 @@ void NodeInputDragger::Drag(QVariant value) //input_->blockSignals(true); - if (input_->IsKeyframing(element_)) { + if (input_.input().IsKeyframing()) { dragging_key_->set_value(value); } else { - input_->SetStandardValueOnTrack(value, track_, element_); + node->SetSplitStandardValueOnTrack(input_, value); } //input_->blockSignals(false); @@ -108,10 +111,10 @@ void NodeInputDragger::End() MultiUndoCommand* command = new MultiUndoCommand(); - if (input_->IsKeyframing(element_)) { + if (input_.input().node()->IsInputKeyframing(input_.input())) { if (drag_created_key_) { // We created a keyframe in this process - command->add_child(new NodeParamInsertKeyframeCommand(input_, dragging_key_)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.input().node(), dragging_key_)); } // We just set a keyframe's value @@ -120,12 +123,12 @@ void NodeInputDragger::End() command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_)); } else { // We just set the standard value - command->add_child(new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_)); + command->add_child(new NodeParamSetStandardValueCommand(input_, end_value_, start_value_)); } Core::instance()->undo_stack()->push(command); - input_ = nullptr; + input_.Reset(); } } diff --git a/app/node/inputdragger.h b/app/node/inputdragger.h index fd8efb6e7..8743e7aff 100644 --- a/app/node/inputdragger.h +++ b/app/node/inputdragger.h @@ -21,7 +21,9 @@ #ifndef NODEINPUTDRAGGER_H #define NODEINPUTDRAGGER_H -#include "node/input.h" +#include "common/rational.h" +#include "node/keyframe.h" +#include "node/param.h" namespace olive { @@ -32,21 +34,17 @@ public: bool IsStarted() const; - void Start(NodeInput* input, const rational& time, int track, int element = -1); + void Start(const NodeKeyframeTrackReference& input, const rational& time); void Drag(QVariant value); void End(); private: - NodeInput* input_; - - int track_; + NodeKeyframeTrackReference input_; rational time_; - int element_; - QVariant start_value_; QVariant end_value_; diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index dd1f083bc..d3fae09ae 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -23,7 +23,6 @@ #include "common/bezier.h" #include "common/lerp.h" #include "common/tohex.h" -#include "input.h" namespace olive { diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index 86694214e..ff6c2794c 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -53,7 +53,12 @@ public: return standard_value_; } - void set_standard_value_on_track(const QVariant &value, int track = 0); + const QVariant& get_split_standard_value_on_track(int track) const + { + return standard_value_.at(track); + } + + void set_standard_value_on_track(const QVariant &value, int track); void set_split_standard_value(const SplitValue& value); diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index 0a49c9868..6062f6ad2 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -20,18 +20,19 @@ #include "keyframe.h" -#include "input.h" +#include "node.h" namespace olive { const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear; -NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track, int element, QObject *parent) : +NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, Type type, int track, int element, const QString &input, QObject *parent) : time_(time), value_(value), type_(type), bezier_control_in_(QPointF(0.0, 0.0)), bezier_control_out_(QPointF(0.0, 0.0)), + input_(input), track_(track), element_(element), previous_(nullptr), @@ -47,7 +48,7 @@ NodeKeyframe::~NodeKeyframe() NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const { - NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, parent); + NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, input_, parent); copy->bezier_control_in_ = bezier_control_in_; copy->bezier_control_out_ = bezier_control_out_; return copy; @@ -58,9 +59,9 @@ NodeKeyframe *NodeKeyframe::copy(QObject* parent) const return copy(element_, parent); } -NodeInput *NodeKeyframe::parent() const +Node *NodeKeyframe::parent() const { - return static_cast(QObject::parent()); + return static_cast(QObject::parent()); } const rational &NodeKeyframe::time() const diff --git a/app/node/keyframe.h b/app/node/keyframe.h index 1659b4e48..2eb80d9b9 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -26,11 +26,11 @@ #include #include "common/rational.h" +#include "node/param.h" namespace olive { -class NodeInput; -class NodeInputImmediate; +class Node; /** * @brief A point of data to be used at a certain time and interpolated with other data @@ -61,14 +61,23 @@ public: /** * @brief NodeKeyframe Constructor */ - NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track, int element, QObject* parent = nullptr); + NodeKeyframe(const rational& time, const QVariant& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr); virtual ~NodeKeyframe() override; NodeKeyframe* copy(int element, QObject* parent = nullptr) const; NodeKeyframe* copy(QObject* parent = nullptr) const; - NodeInput* parent() const; + Node* parent() const; + const QString& input() const + { + return input_; + } + + NodeKeyframeTrackReference key_track_ref() const + { + return NodeKeyframeTrackReference(NodeInput(parent(), input(), element()), track()); + } /** * @brief The time this keyframe is set at @@ -194,6 +203,8 @@ private: QPointF bezier_control_out_; + QString input_; + int track_; int element_; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 58c9c97fd..1fa074028 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -22,19 +22,22 @@ namespace olive { +const QString MathNode::kMethodIn = QStringLiteral("method_in"); +const QString MathNode::kParamAIn = QStringLiteral("param_a_in"); +const QString MathNode::kParamBIn = QStringLiteral("param_b_in"); +const QString MathNode::kParamCIn = QStringLiteral("param_c_in"); + MathNode::MathNode() { - method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); - method_in_->SetConnectable(false); - method_in_->SetKeyframable(false); + AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - param_a_in_ = new NodeInput(this, QStringLiteral("param_a_in"), NodeValue::kFloat, 0.0); - param_a_in_->setProperty("decimalplaces", 8); - param_a_in_->setProperty("autotrim", true); + AddInput(kParamAIn, NodeValue::kFloat, 0.0); + SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8); + SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true); - param_b_in_ = new NodeInput(this, QStringLiteral("param_b_in"), NodeValue::kFloat, 0.0); - param_b_in_->setProperty("decimalplaces", 8); - param_b_in_->setProperty("autotrim", true); + AddInput(kParamBIn, NodeValue::kFloat, 0.0); + SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8); + SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true); } Node *MathNode::copy() const @@ -66,9 +69,9 @@ void MathNode::Retranslate() { Node::Retranslate(); - method_in_->set_name(tr("Method")); - param_a_in_->set_name(tr("Value")); - param_b_in_->set_name(tr("Value")); + SetInputName(kMethodIn, tr("Method")); + SetInputName(kParamAIn, tr("Value")); + SetInputName(kParamBIn, tr("Value")); QStringList operations = {tr("Add"), tr("Subtract"), @@ -77,19 +80,21 @@ void MathNode::Retranslate() QString(), tr("Power")}; - method_in_->set_combobox_strings(operations); + SetComboBoxStrings(kMethodIn, operations); } ShaderCode MathNode::GetShaderCode(const QString &shader_id) const { - return GetShaderCodeInternal(shader_id, param_a_in_, param_b_in_); + return GetShaderCodeInternal(shader_id, kParamAIn, kParamBIn); } -NodeValueTable MathNode::Value(NodeValueDatabase &value) const +NodeValueTable MathNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Auto-detect what values to operate with // FIXME: Add manual override for this - PairingCalculator calc(value[param_a_in_], value[param_b_in_]); + PairingCalculator calc(value[kParamAIn], value[kParamBIn]); // Do nothing if no pairing was found if (!calc.FoundMostLikelyPairing()) { @@ -97,23 +102,23 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const } NodeValue val_a = calc.GetMostLikelyValueA(); - value[param_a_in_].Remove(val_a); + value[kParamAIn].Remove(val_a); NodeValue val_b = calc.GetMostLikelyValueB(); - value[param_b_in_].Remove(val_b); + value[kParamBIn].Remove(val_b); return ValueInternal(value, GetOperation(), calc.GetMostLikelyPairing(), - param_a_in_, + kParamAIn, val_a, - param_b_in_, + kParamBIn, val_b); } void MathNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const { - return ProcessSamplesInternal(values, GetOperation(), param_a_in_, param_b_in_, input, output, index); + return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index); } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index bf78b59c7..b988f3380 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -44,34 +44,22 @@ public: Operation GetOperation() const { - return static_cast(method_in_->GetStandardValue().toInt()); + return static_cast(GetStandardValue(kMethodIn).toInt()); } void SetOperation(Operation o) { - method_in_->SetStandardValue(o); + SetStandardValue(kMethodIn, o); } - NodeInput* param_a_in() const - { - return param_a_in_; - } - - NodeInput* param_b_in() const - { - return param_b_in_; - } - - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; -private: - NodeInput* method_in_; - - NodeInput* param_a_in_; - - NodeInput* param_b_in_; + static const QString kMethodIn; + static const QString kParamAIn; + static const QString kParamBIn; + static const QString kParamCIn; }; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index d81e3bcb9..172b17db7 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -29,7 +29,7 @@ namespace olive { -ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInput *param_a_in, olive::NodeInput *param_b_in) const +ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const QString& param_a_in, const QString& param_b_in) const { QStringList code_id = shader_id.split('.'); @@ -43,11 +43,11 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp if (pairing == kPairTextureMatrix && op == kOpMultiply) { // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - NodeInput* tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in; - NodeInput* mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in; + const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in; + const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in; // No-op frag shader (can we return QString() instead?) - operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id()); + operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); vert = QStringLiteral("uniform mat4 %1;\n" "\n" @@ -59,7 +59,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp "void main() {\n" " gl_Position = %1 * a_position;\n" " ove_texcoord = a_texcoord;\n" - "}\n").arg(mat_in->id()); + "}\n").arg(mat_in); } else { switch (op) { @@ -89,8 +89,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp break; } - operation = operation.arg(GetShaderVariableCall(param_a_in->id(), type_a), - GetShaderVariableCall(param_b_in->id(), type_b)); + operation = operation.arg(GetShaderVariableCall(param_a_in, type_a), + GetShaderVariableCall(param_b_in, type_b)); } frag = QStringLiteral("uniform %1 %3;\n" @@ -104,8 +104,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp " fragColor = %5;\n" "}\n").arg(GetShaderUniformType(type_a), GetShaderUniformType(type_b), - param_a_in->id(), - param_b_in->id(), + param_a_in, + param_b_in, operation); return ShaderCode(frag, vert); @@ -165,7 +165,7 @@ void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type typ } } -NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput *param_a_in, const NodeValue& val_a, NodeInput *param_b_in, const NodeValue& val_b) const +NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b) const { NodeValueTable output = value.Merge(); @@ -342,7 +342,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o { // Queue a sample job const NodeValue& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a; - NodeInput* number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; + const QString& number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; float number = RetrieveNumber(number_val); @@ -350,7 +350,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this)); if (job.HasSamples()) { - if (number_param->IsStatic()) { + if (IsInputStatic(number_param)) { if (!NumberIsNoOp(operation, number)) { for (int i=0;iaudio_params().channel_count();i++) { for (int j=0;jsample_count();j++) { @@ -375,7 +375,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o return output; } -void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, NodeInput *param_a_in, NodeInput *param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const +void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const { // This function is only used for sample+number pairing NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber); diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index 08be7cae6..857204de5 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -109,13 +109,13 @@ protected: static bool NumberIsNoOp(const Operation& op, const float& number); - ShaderCode GetShaderCodeInternal(const QString &shader_id, NodeInput* param_a_in, NodeInput* param_b_in) const; + ShaderCode GetShaderCodeInternal(const QString &shader_id, const QString ¶m_a_in, const QString ¶m_b_in) const; void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const; - NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput* param_a_in, const NodeValue &val_a, NodeInput* param_b_in, const NodeValue& val_b) const; + NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b) const; - void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, NodeInput* param_a_in, NodeInput* param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const; + void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const; }; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index f4f2c23e2..b6263fe53 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -22,11 +22,14 @@ namespace olive { +const QString MergeNode::kBaseIn = QStringLiteral("base_in"); +const QString MergeNode::kBlendIn = QStringLiteral("blend_in"); + MergeNode::MergeNode() { - base_in_ = new NodeInput(this, QStringLiteral("base_in"), NodeValue::kTexture); + AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - blend_in_ = new NodeInput(this, QStringLiteral("blend_in"), NodeValue::kTexture); + AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); } Node *MergeNode::copy() const @@ -56,8 +59,9 @@ QString MergeNode::Description() const void MergeNode::Retranslate() { - base_in_->set_name(tr("Base")); - blend_in_->set_name(tr("Blend")); + SetInputName(kBaseIn, tr("Base")); + + SetInputName(kBlendIn, tr("Blend")); } ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const @@ -67,24 +71,26 @@ ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } -NodeValueTable MergeNode::Value(NodeValueDatabase &value) const +NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(base_in_, value); - job.InsertValue(blend_in_, value); + job.InsertValue(this, kBaseIn, value); + job.InsertValue(this, kBlendIn, value); NodeValueTable table = value.Merge(); - TexturePtr base_tex = job.GetValue(base_in_).data().value(); - TexturePtr blend_tex = job.GetValue(blend_in_).data().value(); + TexturePtr base_tex = job.GetValue(kBaseIn).data().value(); + TexturePtr blend_tex = job.GetValue(kBlendIn).data().value(); if (base_tex || blend_tex) { if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) { // We only have a blend texture or the blend texture is RGB only, no need to alpha over - table.Push(job.GetValue(blend_in_)); + table.Push(job.GetValue(kBlendIn)); } else if (!blend_tex) { // We only have a base texture, no need to alpha over - table.Push(job.GetValue(base_in_)); + table.Push(job.GetValue(kBaseIn)); } else { // We have both textures, push the job table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); @@ -94,16 +100,6 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const return table; } -NodeInput *MergeNode::base_in() const -{ - return base_in_; -} - -NodeInput *MergeNode::blend_in() const -{ - return blend_in_; -} - void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const { // We do some hash optimization here. If only one of the inputs is connected, this node @@ -116,16 +112,16 @@ void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const bool base_changed_hash = false; bool blend_changed_hash = false; - if (base_in_->IsConnected()) { - base_in_->GetConnectedNode()->Hash(hash, time); + if (IsInputConnected(kBaseIn)) { + GetConnectedNode(kBaseIn)->Hash(hash, time); QByteArray post_base_hash = hash.result(); base_changed_hash = (post_base_hash != current_result); current_result = post_base_hash; } - if (blend_in_->IsConnected()) { - blend_in_->GetConnectedNode()->Hash(hash, time); + if(IsInputConnected(kBlendIn)) { + GetConnectedNode(kBlendIn)->Hash(hash, time); blend_changed_hash = (hash.result() != current_result); } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 6fd205734..d2a8933d4 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -41,10 +41,10 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; - NodeInput* base_in() const; - NodeInput* blend_in() const; + static const QString kBaseIn; + static const QString kBlendIn; virtual void Hash(QCryptographicHash &hash, const rational &time) const override; diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index c464d0f0e..b58edbfd3 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -22,13 +22,14 @@ namespace olive { +const QString TrigonometryNode::kMethodIn = QStringLiteral("method_in"); +const QString TrigonometryNode::kXIn = QStringLiteral("x_in"); + TrigonometryNode::TrigonometryNode() { - method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); - method_in_->SetConnectable(false); - method_in_->SetKeyframable(false); + AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0); + AddInput(kXIn, NodeValue::kFloat, 0.0); } olive::Node *olive::TrigonometryNode::copy() const @@ -70,18 +71,20 @@ void TrigonometryNode::Retranslate() tr("Hyperbolic Cosine"), tr("Hyperbolic Tangent")}; - method_in_->set_combobox_strings(strings); + SetComboBoxStrings(kMethodIn, strings); - method_in_->set_name(tr("Method")); + SetInputName(kMethodIn, tr("Method")); } -NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const +NodeValueTable TrigonometryNode::Value(const QString &output, NodeValueDatabase &value) const { - float x = value[x_in_].Take(NodeValue::kFloat).toFloat(); + Q_UNUSED(output) + + float x = value[kXIn].Take(NodeValue::kFloat).toFloat(); NodeValueTable table = value.Merge(); - switch (static_cast(method_in_->GetStandardValue().toInt())) { + switch (static_cast(GetStandardValue(kMethodIn).toInt())) { case kOpSine: x = qSin(x); break; diff --git a/app/node/math/trigonometry/trigonometry.h b/app/node/math/trigonometry/trigonometry.h index 86c74d235..d59f52161 100644 --- a/app/node/math/trigonometry/trigonometry.h +++ b/app/node/math/trigonometry/trigonometry.h @@ -40,7 +40,10 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; + + static const QString kMethodIn; + static const QString kXIn; private: enum Operation { @@ -55,10 +58,6 @@ private: kOpHypTangent }; - NodeInput* method_in_; - - NodeInput* x_in_; - }; } diff --git a/app/node/node.cpp b/app/node/node.cpp index 4848f00c7..935357678 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -25,8 +25,11 @@ #include #include +#include "common/bezier.h" +#include "common/lerp.h" #include "common/timecodefunctions.h" #include "common/xmlutils.h" +#include "core.h" #include "config/config.h" #include "project/project.h" #include "project/item/footage/footage.h" @@ -36,19 +39,36 @@ namespace olive { -#define super NodeConnectable +#define super QObject -Node::Node() : +const QString Node::kDefaultOutput = QStringLiteral("output"); + +Node::Node(bool create_default_output) : can_be_deleted_(true), override_color_(-1) { + if (create_default_output) { + AddOutput(); + } } Node::~Node() { + // Disconnect all edges DisconnectAll(); + // Remove self from anything while we're still a node rather than a base QObject setParent(nullptr); + + // Remove all immediates + foreach (NodeInputImmediate* i, standard_immediates_) { + delete i; + } + for (auto it=array_immediates_.cbegin(); it!=array_immediates_.cend(); it++) { + foreach (NodeInputImmediate* i, it.value()) { + delete i; + } + } } NodeGraph *Node::parent() const @@ -64,30 +84,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto } if (reader->name() == QStringLiteral("input")) { - QString param_id; - - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - param_id = attr.value().toString(); - - break; - } - } - - if (param_id.isEmpty()) { - qDebug() << "Found parameter with no ID"; - continue; - } - - NodeInput* param = GetInputWithID(param_id); - - if (!param) { - qDebug() << "No parameter in" << id() << "with parameter" << param_id; - reader->skipCurrentElement(); - continue; - } - - param->Load(reader, xml_node_data, cancelled); + LoadInput(reader, xml_node_data, cancelled); } else if (reader->name() == QStringLiteral("ptr")) { xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this); } else if (reader->name() == QStringLiteral("pos")) { @@ -118,6 +115,39 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto } } else if (reader->name() == QStringLiteral("custom")) { LoadInternal(reader, xml_node_data); + } else if (reader->name() == QStringLiteral("connections")) { + // Load connections + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("connection")) { + QString param_id; + int ele = -1; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("element")) { + ele = attr.value().toInt(); + } else if (attr.name() == QStringLiteral("input")) { + param_id = attr.value().toString(); + } + } + + QString output_node_id; + QString output_param_id; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + output_node_id = reader->readElementText(); + } else if (reader->name() == QStringLiteral("output")) { + output_param_id = reader->readElementText(); + } else { + reader->skipCurrentElement(); + } + } + + xml_node_data.desired_connections.append({NodeInput(this, param_id, ele), output_node_id.toULongLong(), output_param_id}); + } else { + reader->skipCurrentElement(); + } + } } else { reader->skipCurrentElement(); } @@ -136,10 +166,10 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("label"), GetLabel()); writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_)); - foreach (NodeInput* input, inputs_) { + foreach (const QString& input, input_ids_) { writer->writeStartElement(QStringLiteral("input")); - input->Save(writer); + SaveInput(writer, input); writer->writeEndElement(); // input } @@ -150,6 +180,20 @@ void Node::Save(QXmlStreamWriter *writer) const } writer->writeEndElement(); // links + writer->writeStartElement(QStringLiteral("connections")); + for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { + writer->writeStartElement(QStringLiteral("connection")); + + writer->writeAttribute(QStringLiteral("input"), it->first.input()); + writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element())); + + writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast(it->second.node()))); + writer->writeTextElement(QStringLiteral("output"), it->second.output()); + + writer->writeEndElement(); // connection + } + writer->writeEndElement(); // connections + writer->writeStartElement(QStringLiteral("custom")); SaveInternal(writer); writer->writeEndElement(); // custom @@ -207,14 +251,753 @@ QBrush Node::brush(qreal top, qreal bottom) const } } -NodeValueTable Node::Value(NodeValueDatabase &value) const +void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input) { + // Ensure parameters exist on the nodes requested + Q_ASSERT(input.node()->HasInputWithID(input.input())); + Q_ASSERT(output.node()->HasOutputWithID(output.output())); + + // Ensure a connection isn't getting overwritten + Q_ASSERT(input.node()->input_connections().find(input) == input.node()->input_connections().end()); + + // Insert connection on both sides + input.node()->input_connections_[input] = output; + output.node()->output_connections_.push_back(std::pair({output, input})); + + // Call internal event + input.node()->InputConnectedEvent(input.input(), input.element(), output); + + // Emit signals + emit input.node()->InputConnected(output, input); + emit output.node()->OutputConnected(output, input); + + // Invalidate all if this node isn't ignoring this input + if (!input.node()->ignore_connections_.contains(input.input())) { + input.node()->InvalidateAll(input.input(), input.element()); + } +} + +void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input) +{ + // Ensure parameters exist on the nodes requested + Q_ASSERT(input.node()->HasInputWithID(input.input())); + Q_ASSERT(output.node()->HasOutputWithID(output.output())); + + // Ensure connection exists + Q_ASSERT(input.node()->input_connections().at(input) == output); + + // Remove connection from both sides + InputConnections& inputs = input.node()->input_connections_; + inputs.erase(inputs.find(input)); + + OutputConnections& outputs = output.node()->output_connections_; + outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair({output, input}))); + + // Call internal event + input.node()->InputDisconnectedEvent(input.input(), input.element(), output); + + emit input.node()->InputDisconnected(output, input); + emit output.node()->OutputDisconnected(output, input); + + if (!input.node()->ignore_connections_.contains(input.input())) { + input.node()->InvalidateAll(input.input(), input.element()); + } +} + +QString Node::GetInputName(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->human_name; + } else { + ReportInvalidInput("get name of", id); + return QString(); + } +} + +void Node::LoadInput(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) +{ + QString param_id; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + param_id = attr.value().toString(); + + break; + } + } + + if (param_id.isEmpty()) { + qWarning() << "Failed to load parameter with missing ID"; + return; + } + + if (!HasInputWithID(param_id)) { + qWarning() << "Failed to load parameter that didn't exist"; + return; + } + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("primary")) { + // Load primary immediate + LoadImmediate(reader, param_id, -1, xml_node_data, cancelled); + } else if (reader->name() == QStringLiteral("subelements")) { + // Load subelements + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("count")) { + InputArrayResize(param_id, attr.value().toInt()); + } + } + + int element_counter = 0; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("element")) { + LoadImmediate(reader, param_id, element_counter, xml_node_data, cancelled); + + element_counter++; + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } +} + +void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const +{ + writer->writeAttribute(QStringLiteral("id"), id); + + writer->writeStartElement(QStringLiteral("primary")); + + SaveImmediate(writer, id, -1); + + writer->writeEndElement(); // primary + + writer->writeStartElement(QStringLiteral("subelements")); + + int arr_sz = InputArraySize(id); + + writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); + + for (int i=0; iwriteStartElement(QStringLiteral("element")); + + SaveImmediate(writer, id, i); + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // subelements +} + +bool Node::IsInputConnectable(const QString &input) const +{ + return !(GetInputFlags(input) & kInputFlagNotConnectable); +} + +bool Node::IsInputKeyframable(const QString &input) const +{ + return !(GetInputFlags(input) & kInputFlagNotKeyframable); +} + +bool Node::IsInputKeyframing(const QString &input, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->is_keyframing(); + } else { + ReportInvalidInput("get keyframing state of", input); + return false; + } +} + +void Node::SetInputIsKeyframing(const QString &input, bool e, int element) +{ + if (!IsInputKeyframable(input)) { + qDebug() << "Ignored set keyframing of" << input << "because this input is not keyframable"; + return; + } + + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + imm->set_is_keyframing(e); + + emit KeyframeEnableChanged(NodeInput(this, input, element), e); + } else { + ReportInvalidInput("set keyframing state of", input); + } +} + +bool Node::IsInputConnected(const QString &input, int element) const +{ + return GetConnectedOutput(input, element).IsValid(); +} + +NodeOutput Node::GetConnectedOutput(const QString &input, int element) const +{ + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + if (it->first.input() == input && it->first.element() == element) { + return it->second; + } + } + + return NodeOutput(); +} + +bool Node::IsUsingStandardValue(const QString &input, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->is_using_standard_value(track); + } else { + ReportInvalidInput("determine whether using standard value in", input); + return true; + } +} + +NodeValue::Type Node::GetInputDataType(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->type; + } else { + ReportInvalidInput("get data type of", id); + return NodeValue::kNone; + } +} + +void Node::SetInputDataType(const QString &id, const NodeValue::Type &type) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->type = type; + + emit InputDataTypeChanged(id, type); + } else { + ReportInvalidInput("set data type of", id); + } +} + +bool Node::HasInputProperty(const QString &id, const QString &name) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties.contains(name); + } else { + ReportInvalidInput("get property of", id); + return false; + } +} + +QHash Node::GetInputProperties(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties; + } else { + ReportInvalidInput("get property table of", id); + return QHash(); + } +} + +QVariant Node::GetInputProperty(const QString &id, const QString &name) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties.value(name); + } else { + ReportInvalidInput("get property of", id); + return QVariant(); + } +} + +void Node::SetInputProperty(const QString &id, const QString &name, const QVariant &value) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->properties.insert(name, value); + + emit InputPropertyChanged(id, name, value); + } else { + ReportInvalidInput("set property of", id); + } +} + +SplitValue Node::GetSplitValueAtTime(const QString &input, const rational &time, int element) const +{ + SplitValue vals; + + int nb_tracks = GetNumberOfKeyframeTracks(input); + + for (int i=0;itime() >= time) { + // This time precedes any keyframe, so we just return the first value + return key_track.first()->value(); + } + + if (key_track.last()->time() <= time) { + // This time is after any keyframes so we return the last value + return key_track.last()->value(); + } + + // If we're here, the time must be somewhere in between the keyframes + for (int i=0;itime() == time + || !NodeValue::type_can_be_interpolated(GetInputDataType(input)) + || (before->time() < time && before->type() == NodeKeyframe::kHold)) { + + // Time == keyframe time, so value is precise + return before->value(); + + } else if (after->time() == time) { + + // Time == keyframe time, so value is precise + return after->value(); + + } else if (before->time() < time && after->time() > time) { + // We must interpolate between these keyframes + + if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { + // Perform a cubic bezier with two control points + + double t = Bezier::CubicXtoT(time.toDouble(), + before->time().toDouble(), + before->time().toDouble() + before->valid_bezier_control_out().x(), + after->time().toDouble() + after->valid_bezier_control_in().x(), + after->time().toDouble()); + + double y = Bezier::CubicTtoY(before->value().toDouble(), + before->value().toDouble() + before->valid_bezier_control_out().y(), + after->value().toDouble() + after->valid_bezier_control_in().y(), + after->value().toDouble(), + t); + + return y; + + } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { + // Perform a quadratic bezier with only one control point + + QPointF control_point; + double control_point_time; + double control_point_value; + + if (before->type() == NodeKeyframe::kBezier) { + control_point = before->valid_bezier_control_out(); + control_point_time = before->time().toDouble() + control_point.x(); + control_point_value = before->value().toDouble() + control_point.y(); + } else { + control_point = after->valid_bezier_control_in(); + control_point_time = after->time().toDouble() + control_point.x(); + control_point_value = after->value().toDouble() + control_point.y(); + } + + // Generate T from time values - used to determine bezier progress + double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble()); + + // Generate value using T + double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t); + + return y; + + } 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()); + + return lerp(before->value().toDouble(), after->value().toDouble(), period_progress); + } + } + } + } + + return GetSplitStandardValueOnTrack(input, track, element); +} + +QVariant Node::GetDefaultValue(const QString &input) const +{ + NodeValue::Type type = GetInputDataType(input); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitDefaultValue(input)); +} + +SplitValue Node::GetSplitDefaultValue(const QString &input) const +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return i->default_value; + } else { + ReportInvalidInput("retrieve default value of", input); + return SplitValue(); + } +} + +QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) const +{ + return GetSplitDefaultValue(input).at(track); +} + +const QVector &Node::GetKeyframeTracks(const QString &input, int element) const +{ + return GetImmediate(input, element)->keyframe_tracks(); +} + +QVector Node::GetKeyframesAtTime(const QString &input, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_keyframe_at_time(time); + } else { + ReportInvalidInput("get keyframes at time from", input); + return QVector(); + } +} + +NodeKeyframe *Node::GetKeyframeAtTimeOnTrack(const QString &input, const rational &time, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_keyframe_at_time_on_track(time, track); + } else { + ReportInvalidInput("get keyframe at time on track from", input); + return nullptr; + } +} + +NodeKeyframe::Type Node::GetBestKeyframeTypeForTimeOnTrack(const QString &input, const rational &time, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_best_keyframe_type_for_time(time, track); + } else { + ReportInvalidInput("get closest keyframe before a time from", input); + return NodeKeyframe::kDefaultType; + } +} + +int Node::GetNumberOfKeyframeTracks(const QString &id) const +{ + return NodeValue::get_number_of_keyframe_tracks(GetInputDataType(id)); +} + +NodeKeyframe *Node::GetEarliestKeyframe(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_earliest_keyframe(); + } else { + ReportInvalidInput("get earliest keyframe from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetLatestKeyframe(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_latest_keyframe(); + } else { + ReportInvalidInput("get latest keyframe from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetClosestKeyframeBeforeTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_closest_keyframe_before_time(time); + } else { + ReportInvalidInput("get closest keyframe before a time from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetClosestKeyframeAfterTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_closest_keyframe_after_time(time); + } else { + ReportInvalidInput("get closest keyframe after a time from", id); + return nullptr; + } +} + +bool Node::HasKeyframeAtTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->has_keyframe_at_time(time); + } else { + ReportInvalidInput("determine if it has a keyframe at a time from", id); + return false; + } +} + +QStringList Node::GetComboBoxStrings(const QString &id) const +{ + return GetInputProperty(id, QStringLiteral("combo_str")).toStringList(); +} + +QVariant Node::GetStandardValue(const QString &id, int element) const +{ + NodeValue::Type type = GetInputDataType(id); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitStandardValue(id, element)); +} + +SplitValue Node::GetSplitStandardValue(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_split_standard_value(); + } else { + ReportInvalidInput("get standard value of", id); + return SplitValue(); + } +} + +QVariant Node::GetSplitStandardValueOnTrack(const QString &input, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_split_standard_value_on_track(track); + } else { + ReportInvalidInput("get standard value of", input); + return QVariant(); + } +} + +void Node::SetStandardValue(const QString &id, const QVariant &value, int element) +{ + NodeValue::Type type = GetInputDataType(id); + + SetSplitStandardValue(id, NodeValue::split_normal_value_into_track_values(type, value), element); +} + +void Node::SetSplitStandardValue(const QString &id, const SplitValue &value, int element) +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + imm->set_split_standard_value(value); + + for (int i=0; iset_standard_value_on_track(value, track); + + if (IsUsingStandardValue(id, track, element)) { + // If this standard value is being used, we need to send a value changed signal + emit ValueChanged(NodeInput(this, id, element), TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } + } else { + ReportInvalidInput("set standard value of", id); + } +} + +bool Node::InputIsArray(const QString &id) const +{ + return GetInputFlags(id) & kInputFlagArray; +} + +void Node::InputArrayInsert(const QString &id, int index, bool undoable) +{ + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, id, index)); + } else { + // Add new input + ArrayResizeInternal(id, InputArraySize(id) + 1); + + // Move connections down + InputConnections copied_edges = input_connections(); + for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { + if (it->first.element() >= index) { + // Disconnect this and reconnect it one element down + NodeInput new_edge = it->first; + new_edge.set_element(new_edge.element() + 1); + + DisconnectEdge(it->second, it->first); + ConnectEdge(it->second, new_edge); + } + } + + // Shift values and keyframes up one element + for (int i=InputArraySize(id)-1; i>index; i--) { + CopyValuesOfElement(this, this, id, i-1, i); + } + + // Reset value of element we just "inserted" + ClearElement(id, index); + } +} + +void Node::InputArrayResize(const QString &id, int size, bool undoable) +{ + if (InputArraySize(id) == size) { + return; + } + + ArrayResizeCommand* c = new ArrayResizeCommand(this, id, size); + + if (undoable) { + Core::instance()->undo_stack()->push(c); + } else { + c->redo(); + delete c; + } +} + +void Node::InputArrayRemove(const QString &id, int index, bool undoable) +{ + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, id, index)); + } else { + // Remove input + ArrayResizeInternal(id, InputArraySize(id) - 1); + + // Move connections up + InputConnections copied_edges = input_connections(); + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + if (it->first.element() >= index) { + // Disconnect this and reconnect it one element up if it's not the element being removed + DisconnectEdge(it->second, it->first); + + if (it->first.element() > index) { + NodeInput new_edge = it->first; + new_edge.set_element(new_edge.element() - 1); + + ConnectEdge(it->second, new_edge); + } + } + } + + // Shift values and keyframes down one element + int arr_sz = InputArraySize(id); + for (int i=index; iarray_size; + } else { + ReportInvalidInput("retrieve array size of", id); + return 0; + } +} + +const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const +{ + return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track()); +} + +NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const +{ + if (element == -1) { + return standard_immediates_.value(input, nullptr); + } else if (array_immediates_.contains(input)) { + const QVector& imm_arr = array_immediates_.value(input); + + if (element >= 0 && element < imm_arr.size()) { + return imm_arr.at(element); + } + } + + return nullptr; +} + +Node::InputFlags Node::GetInputFlags(const QString &input) const +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return i->flags; + } else { + ReportInvalidInput("retrieve flags of", input); + return InputFlags(kInputFlagNormal); + } +} + +NodeValueTable Node::Value(const QString& output, NodeValueDatabase &value) const +{ + Q_UNUSED(output) + return value.Merge(); } -void Node::InvalidateCache(const TimeRange &range, const InputConnection &from) +void Node::InvalidateCache(const TimeRange &range, const QString &from, int element) { Q_UNUSED(from) + Q_UNUSED(element) SendInvalidateCache(range); } @@ -222,26 +1005,26 @@ void Node::InvalidateCache(const TimeRange &range, const InputConnection &from) void Node::BeginOperation() { // Ripple through graph - foreach (const InputConnection& conn, output_connections()) { - conn.input->parent()->BeginOperation(); + for (const std::pair& output : output_connections_) { + output.second.node()->BeginOperation(); } } void Node::EndOperation() { // Ripple through graph - foreach (const InputConnection& conn, output_connections()) { - conn.input->parent()->EndOperation(); + for (const std::pair& output : output_connections_) { + output.second.node()->EndOperation(); } } -TimeRange Node::InputTimeAdjustment(NodeInput *, int, const TimeRange &input_time) const +TimeRange Node::InputTimeAdjustment(const QString &, int, const TimeRange &input_time) const { // Default behavior is no time adjustment at all return input_time; } -TimeRange Node::OutputTimeAdjustment(NodeInput *, int, const TimeRange &input_time) const +TimeRange Node::OutputTimeAdjustment(const QString &, int, const TimeRange &input_time) const { // Default behavior is no time adjustment at all return input_time; @@ -280,20 +1063,24 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, MultiUnd void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, MultiUndoCommand *command) { for (int i=0; iinputs()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - int connection_index = src.indexOf(it->second); + Node* src_node = src.at(i); + Node* dst_node = dst.at(i); - if (connection_index > -1) { - // Found a connection - Node* dst_output = dst.at(connection_index); - NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id()); + for (auto it=src_node->input_connections_.cbegin(); it!=src_node->input_connections_.cend(); it++) { + // Determine if the connected node is in our src list + int connection_index = src.indexOf(it->second.node()); - if (command) { - command->add_child(new NodeEdgeAddCommand(dst_output, dst_input, it->first)); - } else { - ConnectEdge(dst_output, dst_input, it->first); - } + if (connection_index > -1) { + // Find the equivalent node in the dst list + Node* dst_connection = dst.at(connection_index); + + NodeOutput copied_output = NodeOutput(dst_connection, it->second.output()); + NodeInput copied_input = NodeInput(dst_node, it->first.input(), it->first.element()); + + if (command) { + command->add_child(new NodeEdgeAddCommand(copied_output, copied_input)); + } else { + ConnectEdge(copied_output, copied_input); } } } @@ -302,15 +1089,17 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector void Node::SendInvalidateCache(const TimeRange &range) { - foreach (const InputConnection& conn, output_connections()) { + for (const OutputConnection& conn : output_connections_) { // Send clear cache signal to the Node - conn.input->parent()->InvalidateCache(range, conn); + const NodeInput& in = conn.second; + + in.node()->InvalidateCache(range, in.input(), in.element()); } } -void Node::InvalidateAll(NodeInput* input, int element) +void Node::InvalidateAll(const QString &input, int element) { - InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), {input, element}); + InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), input, element); } bool Node::Link(Node *a, Node *b) @@ -358,9 +1147,149 @@ bool Node::AreLinked(Node *a, Node *b) return a->links_.contains(b); } -void Node::IgnoreInvalidationsFrom(NodeInput *input) +void Node::AddInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags) { - ignore_connections_.append(input); + if (id.isEmpty()) { + qWarning() << "Rejected adding input with an empty ID on node" << this->id(); + return; + } + + if (HasParamWithID(id)) { + qWarning() << "Failed to add input to node" << this->id() << "- param with ID" << id << "already exists"; + return; + } + + Node::Input i; + + i.type = type; + i.default_value = NodeValue::split_normal_value_into_track_values(type, default_value); + i.flags = flags; + i.array_size = 0; + + input_ids_.append(id); + input_data_.append(i); + + if (!standard_immediates_.value(id, nullptr)) { + standard_immediates_.insert(id, CreateImmediate(id)); + } + + emit InputAdded(id); +} + +void Node::RemoveInput(const QString &id) +{ + int index = input_ids_.indexOf(id); + + if (index == -1) { + ReportInvalidInput("remove", id); + return; + } + + input_ids_.removeAt(index); + input_data_.removeAt(index); + + emit InputRemoved(id); +} + +void Node::AddOutput(const QString &id) +{ + if (id.isEmpty()) { + qWarning() << "Rejected adding output with an empty ID on node" << this->id(); + return; + } + + if (HasParamWithID(id)) { + qWarning() << "Failed to add output to node" << this->id() << "- param with ID" << id << "already exists"; + return; + } + + outputs_.append(id); + + emit OutputAdded(id); +} + +void Node::RemoveOutput(const QString &id) +{ + if (outputs_.removeOne(id)) { + emit OutputRemoved(id); + } else { + ReportInvalidInput("remove", id); + } +} + +void Node::ReportInvalidInput(const char *attempted_action, const QString& id) const +{ + qWarning() << "Failed to" << attempted_action << "parameter" << id + << "in node" << this->id() << "- input doesn't exist"; +} + +NodeInputImmediate *Node::CreateImmediate(const QString &input) +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return new NodeInputImmediate(i->type, i->default_value); + } else { + ReportInvalidInput("create immediate", input); + return nullptr; + } +} + +void Node::ArrayResizeInternal(const QString &id, int size) +{ + Input* imm = GetInternalInputData(id); + + if (!imm) { + ReportInvalidInput("set array size", id); + return; + } + + if (imm->array_size != size) { + // Update array size + if (imm->array_size < size) { + // Size is larger, create any immediates that don't exist + QVector& subinputs = array_immediates_[id]; + for (int i=subinputs.size(); iarray_size = size; + emit InputArraySizeChanged(id, size); + emit ValueChanged(NodeInput(this, id, -1), TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } +} + +int Node::GetInternalInputArraySize(const QString &input) +{ + return array_immediates_.value(input).size(); +} + +void Node::SetInputName(const QString &id, const QString &name) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->human_name = name; + + emit InputNameChanged(id, name); + } else { + ReportInvalidInput("set name of", id); + } +} + +void Node::IgnoreInvalidationsFrom(const QString& input_id) +{ + ignore_connections_.append(input_id); +} + +void Node::IgnoreHashingFrom(const QString &input_id) +{ + ignore_when_hashing_.append(input_id); } void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &) @@ -372,11 +1301,6 @@ void Node::SaveInternal(QXmlStreamWriter *) const { } -QVector Node::GetInputsToHash() const -{ - return inputs_; -} - bool Node::HasGizmos() const { return false; @@ -418,12 +1342,14 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const // Add this Node's ID hash.addData(id().toUtf8()); - QVector inputs = GetInputsToHash(); - - foreach (NodeInput* input, inputs) { + foreach (const QString& input, input_ids_) { // For each input, try to hash its value - HashInputElement(hash, input, -1, time); - for (int i=0; iArraySize(); i++) { + if (ignore_when_hashing_.contains(input)) { + continue; + } + + int arr_sz = InputArraySize(input); + for (int i=-1; iid() == destination->id()); - const QVector& src_param = source->inputs_; - const QVector& dst_param = destination->inputs_; - - for (int i=0;i(src_param.at(i)); - NodeInput* dst = static_cast(dst_param.at(i)); - - NodeInput::CopyValues(src, dst, include_connections); + foreach (const QString& input, source->inputs()) { + CopyInput(source, destination, input, include_connections, true); } destination->SetPosition(source->GetPosition()); destination->SetLabel(source->GetLabel()); } +void Node::CopyInput(Node *src, Node *dst, const QString &input, bool include_connections, bool traverse_arrays) +{ + Q_ASSERT(src->id() == dst->id()); + + CopyValuesOfElement(src, dst, input, -1); + + // Copy array size + if (src->InputIsArray(input) && traverse_arrays) { + int src_array_sz = src->InputArraySize(input); + + for (int i=0; iinput_connections().cbegin(); it!=src->input_connections().cend(); it++) { + ConnectEdge(it->second, NodeInput(dst, input, it->first.element())); + } + } else { + // Just copy the primary connection (at -1) + if (src->IsInputConnected(input)) { + ConnectEdge(src->GetConnectedOutput(input), NodeInput(dst, input)); + } + } + } +} + +void Node::CopyValuesOfElement(Node *src, Node *dst, const QString &input, int src_element, int dst_element) +{ + if (dst_element >= dst->GetInternalInputArraySize(input)) { + qDebug() << "Ignored destination element that was out of array bounds"; + return; + } + + // Copy standard value + dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element); + + // Copy keyframes + dst->GetImmediate(input, dst_element)->delete_all_keyframes(); + foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) { + foreach (NodeKeyframe* key, track) { + key->copy(dst_element, dst); + } + } + + // Copy keyframing state + if (src->IsInputKeyframable(input)) { + dst->SetInputIsKeyframing(input, src->IsInputKeyframing(input, src_element), dst_element); + } + + // If this is the root of an array, copy the array size + if (src_element == -1 && dst_element == -1) { + dst->InputArrayResize(input, dst->InputArraySize(input)); + } +} + bool Node::CanBeDeleted() const { return can_be_deleted_; @@ -457,6 +1437,23 @@ void Node::SetCanBeDeleted(bool s) can_be_deleted_ = s; } +void GetDependenciesRecursively(QVector& list, const Node* node, bool traverse, bool exclusive_only) +{ + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + Node* connected_node = it->second.node(); + + if (connected_node->outputs().size() == 1 || !exclusive_only) { + if (!list.contains(connected_node)) { + list.append(connected_node); + + if (traverse) { + GetDependenciesRecursively(list, connected_node, traverse, exclusive_only); + } + } + } + } +} + /** * @brief Recursively collects dependencies of Node `n` and appends them to QList `list` * @@ -469,31 +1466,29 @@ QVector Node::GetDependenciesInternal(bool traverse, bool exclusive_only { QVector list; - foreach (NodeInput* i, inputs_) { - i->GetDependencies(list, traverse, exclusive_only); - } + GetDependenciesRecursively(list, this, traverse, exclusive_only); return list; } -void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int element, const rational &time) const +void Node::HashInputElement(QCryptographicHash &hash, const QString& input, int element, const rational &time) const { // Get time adjustment // For a single frame, we only care about one of the times rational input_time = InputTimeAdjustment(input, element, TimeRange(time, time)).in(); - if (input->IsConnected(element)) { + if (IsInputConnected(input, element)) { // Traverse down this edge - input->GetConnectedNode(element)->Hash(hash, input_time); + GetConnectedNode(input, element)->Hash(hash, input_time); } else { // Grab the value at this time - QVariant value = input->GetValueAtTime(input_time, element); - hash.addData(NodeValue::ValueToBytes(input->GetDataType(), value)); + QVariant value = GetValueAtTime(input, input_time, element); + hash.addData(NodeValue::ValueToBytes(GetInputDataType(input), value)); } // We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer - if (input->GetDataType() == NodeValue::kFootage) { - Stream* stream = Node::ValueToPtr(input->GetStandardValue(element)); + if (GetInputDataType(input) == NodeValue::kFootage) { + Stream* stream = Node::ValueToPtr(GetStandardValue(input, element)); if (stream) { // Add footage details to hash @@ -537,32 +1532,6 @@ void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int elem } } -void Node::ParameterConnected(Node *source, int element) -{ - NodeInput* input = static_cast(sender()); - - emit InputConnected(source, input, element); - - if (ignore_connections_.contains(input)) { - return; - } - - InvalidateAll(input, element); -} - -void Node::ParameterDisconnected(Node *source, int element) -{ - NodeInput* input = static_cast(sender()); - - emit InputDisconnected(source, input, element); - - if (ignore_connections_.contains(input)) { - return; - } - - InvalidateAll(input, element); -} - QVector Node::GetDependencies() const { return GetDependenciesInternal(true, false); @@ -595,21 +1564,10 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const Q_UNUSED(job) } -NodeInput *Node::GetInputWithID(const QString &id) const -{ - foreach (NodeInput* i, inputs_) { - if (i->id() == id) { - return i; - } - } - - return nullptr; -} - bool Node::OutputsTo(Node *n, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - Node* connected = conn.input->parent(); + for (const OutputConnection& conn : output_connections_) { + Node* connected = conn.second.node(); if (connected == n) { return true; @@ -623,8 +1581,8 @@ bool Node::OutputsTo(Node *n, bool recursively) const bool Node::OutputsTo(const QString &id, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - Node* connected = conn.input->parent(); + for (const OutputConnection& conn : output_connections_) { + Node* connected = conn.second.node(); if (connected->id() == id) { return true; @@ -636,14 +1594,14 @@ bool Node::OutputsTo(const QString &id, bool recursively) const return false; } -bool Node::OutputsTo(NodeInput *input, bool recursively) const +bool Node::OutputsTo(const NodeInput &input, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - NodeInput* connected = conn.input; + for (const OutputConnection& conn : output_connections_) { + const NodeInput& connected = conn.second; if (connected == input) { return true; - } else if (recursively && connected->parent()->OutputsTo(input, recursively)) { + } else if (recursively && connected.node()->OutputsTo(input, recursively)) { return true; } } @@ -653,15 +1611,13 @@ bool Node::OutputsTo(NodeInput *input, bool recursively) const bool Node::InputsFrom(Node *n, bool recursively) const { - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + const NodeOutput& connected = it->second; - if (connected == n) { - return true; - } else if (recursively && connected->InputsFrom(n, recursively)) { - return true; - } + if (connected.node() == n) { + return true; + } else if (recursively && connected.node()->InputsFrom(n, recursively)) { + return true; } } @@ -670,15 +1626,13 @@ bool Node::InputsFrom(Node *n, bool recursively) const bool Node::InputsFrom(const QString &id, bool recursively) const { - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + const NodeOutput& connected = it->second; - if (connected->id() == id) { - return true; - } else if (recursively && connected->InputsFrom(id, recursively)) { - return true; - } + if (connected.node()->id() == id) { + return true; + } else if (recursively && connected.node()->InputsFrom(id, recursively)) { + return true; } } @@ -690,8 +1644,8 @@ int Node::GetRoutesTo(Node *n) const bool outputs_directly = false; int routes = 0; - foreach (const InputConnection& conn, edges()) { - Node* connected_node = conn.input->parent(); + foreach (const OutputConnection& conn, output_connections_) { + Node* connected_node = conn.second.node(); if (connected_node == n) { outputs_directly = true; @@ -709,9 +1663,15 @@ int Node::GetRoutesTo(Node *n) const void Node::DisconnectAll() { - // Disconnect outputs (inputs will be disconnected in their respective destructors) - while (!edges().empty()) { - DisconnectEdge(this, edges().front().input, edges().front().element); + // Disconnect inputs (copy map since internal map will change as we disconnect) + InputConnections copy = input_connections_; + for (auto it=copy.cbegin(); it!=copy.cend(); it++) { + DisconnectEdge(it->second, it->first); + } + + while (!output_connections_.empty()) { + OutputConnection conn = output_connections_.back(); + DisconnectEdge(conn.first, conn.second); } } @@ -754,33 +1714,31 @@ QVector Node::TransformTimeTo(const TimeRange &time, Node *target, bo if (input_dir) { // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - TimeRange input_adjustment = InputTimeAdjustment(input, it->first, time); - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + TimeRange input_adjustment = InputTimeAdjustment(it->first.input(), it->first.element(), time); + Node* connected = it->second.node(); - if (connected == target) { - // We found the target, no need to keep traversing - if (!paths_found.contains(input_adjustment)) { - paths_found.append(input_adjustment); - } - } else { - // We did NOT find the target, traverse this - paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir)); + if (connected == target) { + // We found the target, no need to keep traversing + if (!paths_found.contains(input_adjustment)) { + paths_found.append(input_adjustment); } + } else { + // We did NOT find the target, traverse this + paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir)); } } } else { // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (const InputConnection& conn, edges()) { - Node* input_node = conn.input->parent(); + foreach (const OutputConnection& conn, output_connections_) { + Node* connected_node = conn.second.node(); - TimeRange output_adjustment = input_node->OutputTimeAdjustment(conn.input, conn.element, time); + TimeRange output_adjustment = connected_node->OutputTimeAdjustment(conn.second.input(), conn.second.element(), time); - if (input_node == target) { + if (connected_node == target) { paths_found.append(output_adjustment); } else { - paths_found.append(input_node->TransformTimeTo(output_adjustment, target, input_dir)); + paths_found.append(connected_node->TransformTimeTo(output_adjustment, target, input_dir)); } } } @@ -793,17 +1751,6 @@ QVariant Node::PtrToValue(void *ptr) return reinterpret_cast(ptr); } -bool Node::HasParamWithID(const QString &id) const -{ - foreach (NodeInput* i, inputs_) { - if (i->id() == id) { - return true; - } - } - - return false; -} - const QPointF &Node::GetPosition() const { return position_; @@ -816,17 +1763,223 @@ void Node::SetPosition(const QPointF &pos) emit PositionChanged(position_); } -void Node::ParameterValueChanged(const TimeRange& range, int element) +void Node::ParameterValueChanged(const QString& input, int element, const TimeRange& range) { - NodeInput* input = static_cast(sender()); + InputValueChangedEvent(input, element); - emit ValueChanged(input, element); + emit ValueChanged(NodeInput(this, input, element), range); if (ignore_connections_.contains(input)) { return; } - InvalidateCache(range, InputConnection(static_cast(sender()), element)); + InvalidateCache(range, input, element); +} + +void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) +{ + NodeValue::Type data_type = GetInputDataType(input); + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("standard")) { + // Load standard value + int val_index = 0; + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + QString value_text = reader->readElementText(); + QVariant value_on_track; + + if (!value_text.isEmpty()) { + value_on_track = NodeValue::StringToValue(data_type, value_text, element); + } + + SetSplitStandardValueOnTrack(input, val_index, value_on_track, element); + + val_index++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("keyframing") && IsInputKeyframable(input)) { + SetInputIsKeyframing(input, reader->readElementText().toInt(), element); + } else if (reader->name() == QStringLiteral("keyframes")) { + int track = 0; + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("key")) { + QString key_input; + rational key_time; + NodeKeyframe::Type key_type; + QVariant key_value; + QPointF key_in_handle; + QPointF key_out_handle; + + XMLAttributeLoop(reader, attr) { + if (cancelled && *cancelled) { + return; + } + + if (attr.name() == QStringLiteral("input")) { + key_input = attr.value().toString(); + } else if (attr.name() == QStringLiteral("time")) { + key_time = rational::fromString(attr.value().toString()); + } else if (attr.name() == QStringLiteral("type")) { + key_type = static_cast(attr.value().toInt()); + } else if (attr.name() == QStringLiteral("inhandlex")) { + key_in_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("inhandley")) { + key_in_handle.setY(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandlex")) { + key_out_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandley")) { + key_out_handle.setY(attr.value().toDouble()); + } + } + + key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true); + + NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, this); + key->set_bezier_control_in(key_in_handle); + key->set_bezier_control_out(key_out_handle); + } else { + reader->skipCurrentElement(); + } + } + + track++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("csinput")) { + SetInputProperty(input, QStringLiteral("col_input"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csdisplay")) { + SetInputProperty(input, QStringLiteral("col_display"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csview")) { + SetInputProperty(input, QStringLiteral("col_view"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("cslook")) { + SetInputProperty(input, QStringLiteral("col_look"), reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } +} + +void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int element) const +{ + if (IsInputKeyframable(input)) { + writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsInputKeyframing(input, element))); + } + + NodeValue::Type data_type = GetInputDataType(input); + + // Write standard value + writer->writeStartElement(QStringLiteral("standard")); + + foreach (const QVariant& v, GetSplitStandardValue(input, element)) { + writer->writeTextElement(QStringLiteral("track"), NodeValue::ValueToString(data_type, v, true)); + } + + writer->writeEndElement(); // standard + + // Write keyframes + writer->writeStartElement(QStringLiteral("keyframes")); + + foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(input, element)) { + writer->writeStartElement(QStringLiteral("track")); + + foreach (NodeKeyframe* key, track) { + writer->writeStartElement(QStringLiteral("key")); + + writer->writeAttribute(QStringLiteral("input"), key->input()); + writer->writeAttribute(QStringLiteral("time"), key->time().toString()); + writer->writeAttribute(QStringLiteral("type"), QString::number(key->type())); + writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x())); + writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y())); + writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x())); + writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y())); + + writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true)); + + writer->writeEndElement(); // key + } + + writer->writeEndElement(); // track + } + + writer->writeEndElement(); // keyframes + + if (data_type == NodeValue::kColor) { + // Save color management information + writer->writeTextElement(QStringLiteral("csinput"), GetInputProperty(input, QStringLiteral("col_input")).toString()); + writer->writeTextElement(QStringLiteral("csdisplay"), GetInputProperty(input, QStringLiteral("col_display")).toString()); + writer->writeTextElement(QStringLiteral("csview"), GetInputProperty(input, QStringLiteral("col_view")).toString()); + writer->writeTextElement(QStringLiteral("cslook"), GetInputProperty(input, QStringLiteral("col_look")).toString()); + } +} + +TimeRange Node::GetRangeAffectedByKeyframe(NodeKeyframe *key) const +{ + const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key); + int keyframe_index = key_track.indexOf(key); + + TimeRange range = GetRangeAroundIndex(key->input(), keyframe_index, key->track(), key->element()); + + // If a previous key exists and it's a hold, we don't need to invalidate those frames + if (key_track.size() > 1 + && keyframe_index > 0 + && key_track.at(keyframe_index - 1)->type() == NodeKeyframe::kHold) { + range.set_in(key->time()); + } + + return range; +} + +TimeRange Node::GetRangeAroundIndex(const QString &input, int index, int track, int element) const +{ + rational range_begin = RATIONAL_MIN; + rational range_end = RATIONAL_MAX; + + const NodeKeyframeTrack& key_track = GetImmediate(input, element)->keyframe_tracks().at(track); + + if (key_track.size() > 1) { + if (index > 0) { + // If this is not the first key, we'll need to limit it to the key just before + range_begin = key_track.at(index - 1)->time(); + } + if (index < key_track.size() - 1) { + // If this is not the last key, we'll need to limit it to the key just after + range_end = key_track.at(index + 1)->time(); + } + } + + return TimeRange(range_begin, range_end); +} + +void Node::ClearElement(const QString& input, int index) +{ + GetImmediate(input, index)->delete_all_keyframes(); + + if (IsInputKeyframable(input)) { + SetInputIsKeyframing(input, false, index); + } + + SetSplitStandardValue(input, GetSplitDefaultValue(input), index); } QRectF Node::CreateGizmoHandleRect(const QPointF &pt, int radius) @@ -859,31 +2012,157 @@ void Node::DrawAndExpandGizmoHandles(QPainter *p, int handle_radius, QRectF *rec } } +void Node::InputValueChangedEvent(const QString &input, int element) +{ + Q_UNUSED(input) + Q_UNUSED(element) +} + +void Node::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(input) + Q_UNUSED(element) + Q_UNUSED(output) +} + +void Node::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(input) + Q_UNUSED(element) + Q_UNUSED(output) +} + void Node::childEvent(QChildEvent *event) { super::childEvent(event); - NodeInput* input = dynamic_cast(event->child()); + NodeKeyframe* key = dynamic_cast(event->child()); + + if (key) { + NodeInput i(this, key->input(), key->element()); - if (input) { if (event->type() == QEvent::ChildAdded) { - // Ensure no other param with this ID has been added to this Node (since that defeats the purpose) - Q_ASSERT(!HasParamWithID(input->id())); + GetImmediate(key->input(), key->element())->insert_keyframe(key); - // Keep main output as the last parameter, assume if there are no parameters that this is the output parameter - inputs_.append(input); + connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange); + connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged); + connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange); + connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged); + connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange); + connect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange); - connect(input, &NodeInput::ValueChanged, this, &Node::ParameterValueChanged); - connect(input, &NodeInput::InputConnected, this, &Node::ParameterConnected); - connect(input, &NodeInput::InputDisconnected, this, &Node::ParameterDisconnected); + emit KeyframeAdded(key); + emit ValueChanged(i, GetRangeAffectedByKeyframe(key)); } else if (event->type() == QEvent::ChildRemoved) { - disconnect(input, &NodeInput::ValueChanged, this, &Node::ParameterValueChanged); - disconnect(input, &NodeInput::InputConnected, this, &Node::ParameterConnected); - disconnect(input, &NodeInput::InputDisconnected, this, &Node::ParameterDisconnected); + TimeRange time_affected = GetRangeAffectedByKeyframe(key); - inputs_.removeOne(input); + disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange); + disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged); + disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange); + disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged); + disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange); + disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange); + + GetImmediate(key->input(), key->element())->remove_keyframe(key); + + emit KeyframeRemoved(key); + emit ValueChanged(i, time_affected); } } } +void Node::InvalidateFromKeyframeBezierInChange() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); + + rational start = RATIONAL_MIN; + rational end = key->time(); + + if (keyframe_index > 0) { + start = track.at(keyframe_index - 1)->time(); + } + + emit ValueChanged(key->key_track_ref().input(), TimeRange(start, end)); +} + +void Node::InvalidateFromKeyframeBezierOutChange() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); + + rational start = key->time(); + rational end = RATIONAL_MAX; + + if (keyframe_index < track.size() - 1) { + end = track.at(keyframe_index + 1)->time(); + } + + emit ValueChanged(key->key_track_ref().input(), TimeRange(start, end)); +} + +void Node::InvalidateFromKeyframeTimeChange() +{ + NodeKeyframe* key = static_cast(sender()); + NodeInputImmediate* immediate = GetImmediate(key->input(), key->element()); + TimeRange original_range = GetRangeAffectedByKeyframe(key); + + TimeRangeList invalidate_range; + invalidate_range.insert(original_range); + + if (!(original_range.in() < key->time() && original_range.out() > key->time())) { + // This keyframe needs resorting, store it and remove it from the list + immediate->remove_keyframe(key); + + // Automatically insertion sort + immediate->insert_keyframe(key); + + // Invalidate new area that the keyframe has been moved to + invalidate_range.insert(GetRangeAffectedByKeyframe(key)); + } + + // Invalidate entire area surrounding the keyframe (either where it currently is, or where it used to be before it + // was resorted in the if block above) + foreach (const TimeRange& r, invalidate_range) { + emit ValueChanged(key->key_track_ref().input(), r); + } +} + +void Node::InvalidateFromKeyframeValueChange() +{ + NodeKeyframe* key = static_cast(sender()); + emit ValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key)); +} + +void Node::InvalidateFromKeyframeTypeChanged() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + + if (track.size() == 1) { + // If there are no other frames, the interpolation won't do anything + return; + } + + // Invalidate entire range + emit ValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element())); +} + +Project *Node::ArrayInsertCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + +Project *Node::ArrayRemoveCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + +Project *Node::ArrayResizeCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + } diff --git a/app/node/node.h b/app/node/node.h index 7ae591793..6ecf2b9cb 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -21,6 +21,7 @@ #ifndef NODE_H #define NODE_H +#include #include #include #include @@ -30,15 +31,17 @@ #include "codec/frame.h" #include "codec/samplebuffer.h" #include "common/rational.h" +#include "common/timerange.h" #include "common/xmlutils.h" -#include "node/connectable.h" -#include "node/input.h" -#include "node/value.h" +#include "node/keyframe.h" +#include "node/inputimmediate.h" +#include "node/param.h" #include "render/audioparams.h" #include "render/job/generatejob.h" #include "render/job/samplejob.h" #include "render/job/shaderjob.h" #include "render/shadercode.h" +#include "splitvalue.h" namespace olive { @@ -58,7 +61,7 @@ class NodeGraph; * This is a simple base class designed to contain all the functionality for this kind of processing connective unit. * It is an abstract class intended to be subclassed to create nodes with actual functionality. */ -class Node : public NodeConnectable +class Node : public QObject { Q_OBJECT public: @@ -80,7 +83,7 @@ public: kCategoryCount }; - Node(); + Node(bool create_default_output = true); virtual ~Node() override; @@ -154,6 +157,31 @@ public: */ virtual void Retranslate(); + const QVector& inputs() const + { + return input_ids_; + } + + const QVector& outputs() const + { + return outputs_; + } + + bool HasInputWithID(const QString& id) const + { + return input_ids_.contains(id); + } + + bool HasOutputWithID(const QString& id) const + { + return outputs_.contains(id); + } + + bool HasParamWithID(const QString& id) const + { + return HasInputWithID(id) || HasOutputWithID(id); + } + /** * @brief Retrieve the color of this node */ @@ -180,26 +208,266 @@ public: } } - /** - * @brief Return a list of NodeParams - */ - const QVector& parameters() const + static void ConnectEdge(const NodeOutput& output, const NodeInput& input); + + static void DisconnectEdge(const NodeOutput& output, const NodeInput& input); + + QString GetInputName(const QString& id) const; + + void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled); + void SaveInput(QXmlStreamWriter* writer, const QString& id) const; + + bool IsInputConnectable(const QString& input) const; + bool IsInputKeyframable(const QString& input) const; + + bool IsInputKeyframing(const QString& input, int element = -1) const; + bool IsInputKeyframing(const NodeInput& input) const { - return inputs_; + return IsInputKeyframing(input.input(), input.element()); } - const QVector& inputs() const + void SetInputIsKeyframing(const QString& input, bool e, int element = -1); + void SetInputIsKeyframing(const NodeInput& input, bool e) { - return inputs_; + SetInputIsKeyframing(input.input(), e, input.element()); } - /** - * @brief Return the index of a parameter - * @return Parameter index or -1 if this parameter is not part of this Node - */ - int IndexOfParameter(NodeInput* param) const + bool IsInputConnected(const QString& input, int element = -1) const; + bool IsInputConnected(const NodeInput& input) const { - return inputs_.indexOf(param); + return IsInputConnected(input.input(), input.element()); + } + + bool IsInputStatic(const QString& input, int element = -1) const + { + return !IsInputConnected(input, element) && !IsInputKeyframing(input, element); + } + + bool IsInputStatic(const NodeInput& input) const + { + return IsInputStatic(input.input(), input.element()); + } + + NodeOutput GetConnectedOutput(const QString& input, int element = -1) const; + + NodeOutput GetConnectedOutput(const NodeInput& input) const + { + return GetConnectedOutput(input.input(), input.element()); + } + + Node* GetConnectedNode(const QString& input, int element = -1) const + { + return GetConnectedOutput(input, element).node(); + } + + Node* GetConnectedNode(const NodeInput& input) const + { + return GetConnectedNode(input.input(), input.element()); + } + + bool IsUsingStandardValue(const QString& input, int track, int element = -1) const; + + NodeValue::Type GetInputDataType(const QString& id) const; + void SetInputDataType(const QString& id, const NodeValue::Type& type); + + bool HasInputProperty(const QString& id, const QString& name) const; + QHash GetInputProperties(const QString& id) const; + QVariant GetInputProperty(const QString& id, const QString& name) const; + void SetInputProperty(const QString& id, const QString& name, const QVariant& value); + + QVariant GetValueAtTime(const QString& input, const rational& time, int element = -1) const + { + NodeValue::Type type = GetInputDataType(input); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitValueAtTime(input, time, element)); + } + + QVariant GetValueAtTime(const NodeInput& input, const rational& time) + { + return GetValueAtTime(input.input(), time, input.element()); + } + + SplitValue GetSplitValueAtTime(const QString& input, const rational& time, int element = -1) const; + + SplitValue GetSplitValueAtTime(const NodeInput& input, const rational& time) + { + return GetSplitValueAtTime(input.input(), time, input.element()); + } + + QVariant GetSplitValueAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + QVariant GetSplitValueAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetSplitValueAtTimeOnTrack(input.input(), time, track, input.element()); + } + + QVariant GetSplitValueAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetSplitValueAtTimeOnTrack(input.input(), time, input.track()); + } + + QVariant GetDefaultValue(const QString& input) const; + SplitValue GetSplitDefaultValue(const QString& input) const; + QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const; + + const QVector& GetKeyframeTracks(const QString& input, int element) const; + const QVector& GetKeyframeTracks(const NodeInput& input) const + { + return GetKeyframeTracks(input.input(), input.element()); + } + + QVector GetKeyframesAtTime(const QString& input, const rational& time, int element = -1) const; + QVector GetKeyframesAtTime(const NodeInput& input, const rational& time) const + { + return GetKeyframesAtTime(input.input(), time, input.element()); + } + + NodeKeyframe* GetKeyframeAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetKeyframeAtTimeOnTrack(input.input(), time, track, input.element()); + } + + NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetKeyframeAtTimeOnTrack(input.input(), time, input.track()); + } + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, track, input.element()); + } + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, input.track()); + } + + int GetNumberOfKeyframeTracks(const QString& id) const; + int GetNumberOfKeyframeTracks(const NodeInput& id) const + { + return GetNumberOfKeyframeTracks(id.input()); + } + + NodeKeyframe* GetEarliestKeyframe(const QString& id, int element = -1) const; + NodeKeyframe* GetEarliestKeyframe(const NodeInput& id) const + { + return GetEarliestKeyframe(id.input(), id.element()); + } + + NodeKeyframe* GetLatestKeyframe(const QString& id, int element = -1) const; + NodeKeyframe* GetLatestKeyframe(const NodeInput& id) const + { + return GetLatestKeyframe(id.input(), id.element()); + } + + NodeKeyframe* GetClosestKeyframeBeforeTime(const QString& id, const rational& time, int element = -1) const; + NodeKeyframe* GetClosestKeyframeBeforeTime(const NodeInput& id, const rational& time) const + { + return GetClosestKeyframeBeforeTime(id.input(), time, id.element()); + } + + NodeKeyframe* GetClosestKeyframeAfterTime(const QString& id, const rational& time, int element = -1) const; + NodeKeyframe* GetClosestKeyframeAfterTime(const NodeInput& id, const rational& time) const + { + return GetClosestKeyframeAfterTime(id.input(), time, id.element()); + } + + bool HasKeyframeAtTime(const QString& id, const rational& time, int element = -1) const; + bool HasKeyframeAtTime(const NodeInput& id, const rational& time) const + { + return HasKeyframeAtTime(id.input(), time, id.element()); + } + + QStringList GetComboBoxStrings(const QString& id) const; + + QVariant GetStandardValue(const QString& id, int element = -1) const; + QVariant GetStandardValue(const NodeInput& id) const + { + return GetStandardValue(id.input(), id.element()); + } + + SplitValue GetSplitStandardValue(const QString& id, int element = -1) const; + SplitValue GetSplitStandardValue(const NodeInput& id) const + { + return GetSplitStandardValue(id.input(), id.element()); + } + + QVariant GetSplitStandardValueOnTrack(const QString& input, int track, int element = -1) const; + QVariant GetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id) const + { + return GetSplitStandardValueOnTrack(id.input().input(), id.track(), id.input().element()); + } + + void SetStandardValue(const QString& id, const QVariant& value, int element = -1); + void SetStandardValue(const NodeInput& id, const QVariant& value) + { + SetStandardValue(id.input(), value, id.element()); + } + + void SetSplitStandardValue(const QString& id, const SplitValue& value, int element = -1); + void SetSplitStandardValue(const NodeInput& id, const SplitValue& value) + { + SetSplitStandardValue(id.input(), value, id.element()); + } + + void SetSplitStandardValueOnTrack(const QString& id, int track, const QVariant& value, int element = -1); + void SetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id, const QVariant& value) + { + SetSplitStandardValueOnTrack(id.input().input(), id.track(), value, id.input().element()); + } + + bool InputIsArray(const QString& id) const; + + void InputArrayInsert(const QString& id, int index, bool undoable = false); + void InputArrayResize(const QString& id, int size, bool undoable = false); + void InputArrayRemove(const QString& id, int index, bool undoable = false); + + void InputArrayAppend(const QString& id, bool undoable = false) + { + InputArrayResize(id, InputArraySize(id) + 1, undoable); + } + + void InputArrayPrepend(const QString& id, bool undoable = false) + { + InputArrayInsert(id, 0, undoable); + } + + void InputArrayRemoveLast(const QString& id, bool undoable = false) + { + InputArrayResize(id, InputArraySize(id) - 1, undoable); + } + + int InputArraySize(const QString& id) const; + + const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const; + + using InputConnections = std::map; + + /** + * @brief Return map of input connections + * + * Inputs can only have one connection, so the key is the input connected and the value is the + * output that it's connected to. + */ + const InputConnections& input_connections() const + { + return input_connections_; + } + + using OutputConnection = std::pair; + using OutputConnections = std::vector; + + /** + * @brief Return list of output connections + * + * An output can connect an infinite amount of inputs, so in this map, the key is the output and + * the value is a vector of inputs. + */ + const OutputConnections& output_connections() const + { + return output_connections_; } /** @@ -239,11 +507,6 @@ public: */ virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const; - /** - * @brief Returns the input with the specified ID (or nullptr if it doesn't exist) - */ - NodeInput* GetInputWithID(const QString& id) const; - /** * @brief Returns whether this Node outputs to `n` * @@ -257,7 +520,6 @@ public: * (FALSE). */ bool OutputsTo(Node* n, bool recursively) const; - /** * @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance. */ @@ -266,7 +528,7 @@ public: /** * @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node. */ - bool OutputsTo(NodeInput* input, bool recursively) const; + bool OutputsTo(const NodeInput &input, bool recursively) const; /** * @brief Returns whether this node ever receives an input from a particular node instance @@ -330,7 +592,11 @@ public: * the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can * call this function with transformed time and relay the signal that way. */ - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()); + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1); + void InvalidateCache(const TimeRange& range, const NodeInput& from) + { + InvalidateCache(range, from.input(), from.element()); + } /** * @brief Limits cache invalidation temporarily @@ -351,12 +617,12 @@ public: * If this node modifies the `time` (i.e. a clip converting sequence time to media time), this function should be * overridden to do so. Also make sure to override OutputTimeAdjustment() to provide the inverse function. */ - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const; /** * @brief The inverse of InputTimeAdjustment() */ - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const; /** * @brief Copies inputs from from Node to another including connections @@ -365,6 +631,14 @@ public: */ static void CopyInputs(Node* source, Node* destination, bool include_connections = true); + static void CopyInput(Node* src, Node* dst, const QString& input, bool include_connections, bool traverse_arrays); + + static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int src_element, int dst_element); + static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int element) + { + return CopyValuesOfElement(src, dst, input, element, element); + } + /** * @brief Clones a set of nodes and connects the new ones the way the old ones were */ @@ -394,12 +668,7 @@ public: * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual NodeValueTable Value(NodeValueDatabase& value) const; - - /** - * @brief Return whether a parameter with ID `id` has already been added to this Node - */ - bool HasParamWithID(const QString& id) const; + virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const; const QPointF& GetPosition() const; @@ -418,12 +687,7 @@ public: virtual void Hash(QCryptographicHash& hash, const rational &time) const; - const std::vector& edges() const - { - return output_connections(); - } - - void InvalidateAll(NodeInput *input, int element); + void InvalidateAll(const QString& input, int element = -1); bool HasLinks() const { @@ -439,7 +703,58 @@ public: static bool Unlink(Node* a, Node* b); static bool AreLinked(Node* a, Node* b); + static const QString kDefaultOutput; + protected: + enum InputFlag { + /// By default, inputs are keyframable, connectable, and NOT arrays + kInputFlagNormal = 0x0, + kInputFlagArray = 0x1, + kInputFlagNotKeyframable = 0x2, + kInputFlagNotConnectable = 0x4 + }; + + class InputFlags { + public: + explicit InputFlags() + { + f_ = kInputFlagNormal; + } + + explicit InputFlags(uint64_t flags) + { + f_ = flags; + } + + bool operator&(const InputFlag& f) const + { + return f_ & f; + } + + private: + uint64_t f_; + + }; + + void AddInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal)); + void AddInput(const QString& id, NodeValue::Type type, InputFlags flags = InputFlags(kInputFlagNormal)) + { + AddInput(id, type, QVariant(), flags); + } + + void RemoveInput(const QString& id); + + void AddOutput(const QString& id = kDefaultOutput); + + void RemoveOutput(const QString& id); + + void SetInputName(const QString& id, const QString& name); + + void SetComboBoxStrings(const QString& id, const QStringList& strings) + { + SetInputProperty(id, QStringLiteral("combo_str"), strings); + } + void SendInvalidateCache(const TimeRange &range); /** @@ -449,14 +764,14 @@ protected: * parameters has changed throughout the duration of the clip (essential from 0 to infinity). * In some scenarios, it may be preferable to handle this signal separately in order to */ - void IgnoreInvalidationsFrom(NodeInput* input); + void IgnoreInvalidationsFrom(const QString &input_id); + + void IgnoreHashingFrom(const QString& input_id); virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data); virtual void SaveInternal(QXmlStreamWriter* writer) const; - virtual QVector GetInputsToHash() const; - enum GizmoScaleHandles { kGizmoScaleTopLeft, kGizmoScaleTopCenter, @@ -475,10 +790,16 @@ protected: static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count); - virtual void childEvent(QChildEvent* event) override; - virtual void LinkChangeEvent(){} + virtual void InputValueChangedEvent(const QString& input, int element); + + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output); + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output); + + virtual void childEvent(QChildEvent *event) override; + signals: /** * @brief Signal emitted whenever the position is set through SetPosition() @@ -492,19 +813,231 @@ signals: void ColorChanged(); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input, const TimeRange& range); - void InputConnected(Node* output, NodeInput* input, int element); + void InputConnected(const NodeOutput& output, const NodeInput& input); - void InputDisconnected(Node* output, NodeInput* input, int element); + void InputDisconnected(const NodeOutput& output, const NodeInput& input); - void OutputConnected(NodeInput* destination, int element); + void OutputConnected(const NodeOutput& output, const NodeInput& input); - void OutputDisconnected(NodeInput* destination, int element); + void OutputDisconnected(const NodeOutput& output, const NodeInput& input); + + void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value); void LinksChanged(); + void InputArraySizeChanged(const QString& input, int new_size); + + void KeyframeAdded(NodeKeyframe* key); + + void KeyframeRemoved(NodeKeyframe* key); + + void KeyframeTimeChanged(); + + void KeyframeEnableChanged(const NodeInput& input, bool enabled); + + void InputAdded(const QString& id); + + void InputRemoved(const QString& id); + + void OutputAdded(const QString& id); + + void OutputRemoved(const QString& id); + + void InputNameChanged(const QString& id, const QString& name); + + void InputDataTypeChanged(const QString& id, NodeValue::Type type); + private: + class ArrayInsertCommand : public UndoCommand + { + public: + ArrayInsertCommand(Node* node, const QString& input, int index) : + node_(node), + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + virtual void redo() override + { + node_->InputArrayInsert(input_, index_, false); + } + + virtual void undo() override + { + node_->InputArrayRemove(input_, index_, false); + } + + private: + Node* node_; + QString input_; + int index_; + + }; + + class ArrayRemoveCommand : public UndoCommand + { + public: + ArrayRemoveCommand(Node* node, const QString& input, int index) : + node_(node), + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override + { + // Save immediate data + if (node_->IsInputKeyframable(input_)) { + is_keyframing_ = node_->IsInputKeyframing(input_, index_); + } + standard_value_ = node_->GetSplitStandardValue(input_, index_); + keyframes_ = node_->GetKeyframeTracks(input_, index_); + node_->GetImmediate(input_, index_)->delete_all_keyframes(&memory_manager_); + + node_->InputArrayRemove(input_, index_, false); + } + + virtual void undo() override + { + node_->InputArrayInsert(input_, index_, false); + + // Restore keyframes + foreach (const NodeKeyframeTrack& track, keyframes_) { + foreach (NodeKeyframe* key, track) { + key->setParent(node_); + } + } + node_->SetSplitStandardValue(input_, standard_value_, index_); + + if (node_->IsInputKeyframable(input_)) { + node_->SetInputIsKeyframing(input_, is_keyframing_, index_); + } + } + + private: + Node* node_; + QString input_; + int index_; + + SplitValue standard_value_; + bool is_keyframing_; + QVector keyframes_; + QObject memory_manager_; + + }; + + class ArrayResizeCommand : public UndoCommand + { + public: + ArrayResizeCommand(Node* node, const QString& input, int size) : + node_(node), + input_(input), + size_(size) + {} + + virtual void redo() override + { + old_size_ = node_->GetInternalInputArraySize(input_); + + if (old_size_ > size_) { + // Decreasing in size, disconnect any extraneous edges + for (int i=size_; iinput_connections().at(input); + + removed_connections_[input] = output; + + DisconnectEdge(output, input); + } catch (std::out_of_range&) {} + } + } + + node_->ArrayResizeInternal(input_, size_); + } + + virtual void undo() override + { + for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { + ConnectEdge(it->second, it->first); + } + removed_connections_.clear(); + + node_->ArrayResizeInternal(input_, old_size_); + } + + virtual Project* GetRelevantProject() const override; + + private: + Node* node_; + QString input_; + int size_; + int old_size_; + + InputConnections removed_connections_; + + }; + + struct Input { + NodeValue::Type type; + InputFlags flags; + SplitValue default_value; + QHash properties; + QString human_name; + int array_size; + }; + + NodeInputImmediate* CreateImmediate(const QString& input); + + NodeInputImmediate* GetImmediate(const QString& input, int element) const; + + int GetInternalInputIndex(const QString& input) const + { + return input_ids_.indexOf(input); + } + + InputFlags GetInputFlags(const QString& input) const; + + Input* GetInternalInputData(const QString& input) + { + int i = GetInternalInputIndex(input); + + if (i == -1) { + return nullptr; + } else { + return &input_data_[i]; + } + } + + const Input* GetInternalInputData(const QString& input) const + { + int i = GetInternalInputIndex(input); + + if (i == -1) { + return nullptr; + } else { + return &input_data_.at(i); + } + } + + void ReportInvalidInput(const char* attempted_action, const QString &id) const; + + void ArrayResizeInternal(const QString& id, int size); + + /** + * @brief Immediates aren't deleted, so the actual array size may be larger than ArraySize() + */ + int GetInternalInputArraySize(const QString& input); + template static void FindInputNodeInternal(const Node* n, QVector& list); @@ -513,11 +1046,29 @@ private: QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; - void HashInputElement(QCryptographicHash& hash, NodeInput* input, int element, const rational& time) const; + void HashInputElement(QCryptographicHash& hash, const QString &input, int element, const rational& time) const; - QVector inputs_; + void ParameterValueChanged(const QString &input, int element, const olive::TimeRange &range); - QVector ignore_connections_; + void LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); + + void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const; + + /** + * @brief Intelligently determine how what time range is affected by a keyframe + */ + TimeRange GetRangeAffectedByKeyframe(NodeKeyframe *key) const; + + /** + * @brief Gets a time range between the previous and next keyframes of index + */ + TimeRange GetRangeAroundIndex(const QString& input, int index, int track, int element) const; + + void ClearElement(const QString &input, int index); + + QVector ignore_connections_; + + QVector ignore_when_hashing_; /** * @brief Internal variable for whether this Node can be deleted or not @@ -544,29 +1095,59 @@ private: */ QVector links_; + QVector input_ids_; + QVector input_data_; + + QVector outputs_; + + QMap standard_immediates_; + + QMap > array_immediates_; + + InputConnections input_connections_; + + OutputConnections output_connections_; + private slots: - void ParameterValueChanged(const olive::TimeRange &range, int element); + /** + * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time + */ + void InvalidateFromKeyframeTimeChange(); - void ParameterConnected(Node* source, int element); + /** + * @brief Slot when a keyframe's value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeValueChange(); - void ParameterDisconnected(Node* source, int element); + /** + * @brief Slot when a keyframe's type changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeTypeChanged(); + + /** + * @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeBezierInChange(); + + /** + * @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeBezierOutChange(); }; template void Node::FindInputNodeInternal(const Node* n, QVector &list) { - foreach (NodeInput* input, n->inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* edge = it->second; - T* cast_test = dynamic_cast(edge); + for (auto it=n->input_connections_.cbegin(); it!=n->input_connections_.cend(); it++) { + Node* edge = it->second.node(); + T* cast_test = dynamic_cast(edge); - if (cast_test) { - list.append(cast_test); - } - - FindInputNodeInternal(edge, list); + if (cast_test) { + list.append(cast_test); } + + FindInputNodeInternal(edge, list); } } @@ -589,15 +1170,17 @@ T* Node::ValueToPtr(const QVariant &ptr) template void Node::FindOutputNodeInternal(const Node* n, QVector& list) { - foreach (const InputConnection& edge, n->edges()) { - Node* connected = static_cast(edge.input->parent()); - T* cast_test = dynamic_cast(connected); + foreach (const std::vector& outputs, n->output_connections_) { + foreach (const NodeInput& output, outputs) { + Node* connected = output.node(); + T* cast_test = dynamic_cast(connected); - if (cast_test) { - list.append(cast_test); + if (cast_test) { + list.append(cast_test); + } + + FindOutputNodeInternal(connected); } - - FindOutputNodeInternal(connected); } } diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 0a64328a4..f9f000eb0 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -132,37 +132,6 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, // Link blocks XMLLinkBlocks(xml_node_data); - // Connect footage to existing footage if it exists - if (!xml_node_data.footage_connections.isEmpty()) { - // Get list of all footage from project - QVector footage = graph->project()->get_items_of_type(Item::kFootage); - - if (!footage.isEmpty()) { - foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) { - if (con.footage) { - // Assume this is a pointer to a Stream* - Stream* loaded_stream = reinterpret_cast(con.footage); - - bool found = false; - - foreach (Item* item, footage) { - foreach (Stream* s, static_cast(item)->streams()) { - if (s == loaded_stream) { - con.input->SetStandardValue(Node::PtrToValue(s), con.element); - found = true; - break; - } - } - - if (found) { - break; - } - } - } - } - } - } - return pasted_nodes; } @@ -172,6 +141,7 @@ void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, void* void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*) { + Q_UNUSED(xml_node_data) reader->skipCurrentElement(); } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index c84c67dd7..9726c69f8 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -33,24 +33,21 @@ const double Track::kTrackHeightDefault = 3.0; const double Track::kTrackHeightMinimum = 1.5; const double Track::kTrackHeightInterval = 0.5; +const QString Track::kBlockInput = QStringLiteral("block_in"); +const QString Track::kMutedInput = QStringLiteral("muted_in"); + Track::Track() : track_type_(Track::kNone), index_(-1), locked_(false) { - block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone); - block_input_->SetKeyframable(false); - block_input_->SetIsArray(true); - connect(block_input_, &NodeInput::InputConnected, this, &Track::BlockConnected); - connect(block_input_, &NodeInput::InputDisconnected, this, &Track::BlockDisconnected); + AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable)); // Since blocks are time based, we can handle the invalidate timing a little more intelligently // on our end - IgnoreInvalidationsFrom(block_input_); + IgnoreInvalidationsFrom(kBlockInput); - muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean); - muted_input_->SetKeyframable(false); - connect(muted_input_, &NodeInput::ValueChanged, this, &Track::MutedInputValueChanged); + AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); // Set default height track_height_ = kTrackHeightDefault; @@ -97,9 +94,9 @@ QString Track::Description() const "a Sequence."); } -TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { - if (input == block_input_ && element >= 0) { + if (input == kBlockInput && element >= 0) { int cache_index = GetCacheIndexFromArrayIndex(element); return TransformRangeForBlock(blocks_.at(cache_index), input_time); @@ -108,9 +105,9 @@ TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRa return Node::InputTimeAdjustment(input, element, input_time); } -TimeRange Track::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange Track::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { - if (input == block_input_ && element >= 0) { + if (input == kBlockInput && element >= 0) { int cache_index = GetCacheIndexFromArrayIndex(element); const rational& block_in = blocks_.at(cache_index)->in(); @@ -157,12 +154,153 @@ void Track::SaveInternal(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight())); } +void Track::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kBlockInput) { + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InvalidateAll(kBlockInput, element); + return; + } + + // Check if a block was connected, if not, ignore + Block* block = dynamic_cast(output.node()); + + if (!block) { + return; + } + + // Determine where in the cache this block will be + int cache_index = -1; + Block *previous = nullptr, *next = nullptr; + + int arr_sz = InputArraySize(kBlockInput); + for (int i=element+1; i= 0) { + next = blocks_.at(cache_index); + break; + } + } + + // If there was no next, this will be inserted at the end + if (cache_index == -1) { + cache_index = blocks_.size(); + } + + // Determine previous block, either by using next's previous or the last block if there was no + // next. If there are neither, they'll both remain null + if (next) { + previous = next->previous(); + } else if (!blocks_.isEmpty()) { + previous = blocks_.last(); + } + + // Insert at index + blocks_.insert(cache_index, block); + block_array_indexes_.insert(cache_index, element); + + // Update previous/next + if (previous) { + previous->set_next(block); + block->set_previous(previous); + } + + if (next) { + block->set_next(next); + next->set_previous(block); + } + + block->set_track(this); + + // Update ins/outs + UpdateInOutFrom(cache_index); + + // Connect to the block + connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged); + + // Invalidate cache now that block should have an in point + InvalidateCache(TimeRange(block->in(), track_length())); + + // Emit block added signal + emit BlockAdded(block); + } +} + +void Track::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kBlockInput) { + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InvalidateAll(kBlockInput, element); + return; + } + + Block* b = dynamic_cast(output.node()); + + if (!b) { + return; + } + + emit BlockRemoved(b); + + TimeRange invalidate_range(b->in(), track_length()); + + // Get cache index + int cache_index = GetCacheIndexFromArrayIndex(element); + + // Remove block here + blocks_.removeAt(cache_index); + block_array_indexes_.removeAt(cache_index); + + // Update previous/nexts + Block* previous = b->previous(); + Block* next = b->next(); + + if (previous) { + previous->set_next(next); + } + + if (next) { + next->set_previous(previous); + } + + b->set_previous(nullptr); + b->set_next(nullptr); + b->set_track(nullptr); + + // Update lengths + if (next) { + UpdateInOutFrom(blocks_.indexOf(next)); + } else if (blocks_.isEmpty()) { + SetLengthInternal(rational()); + } else { + SetLengthInternal(blocks_.last()->out()); + } + + disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged); + + InvalidateCache(invalidate_range); + } +} + +void Track::InputValueChangedEvent(const QString &input, int element) +{ + Q_UNUSED(element) + + if (input == kMutedInput) { + emit MutedChanged(IsMuted()); + } +} + void Track::Retranslate() { Node::Retranslate(); - block_input_->set_name(tr("Blocks")); - muted_input_->set_name(tr("Muted")); + SetInputName(kBlockInput, tr("Blocks")); + SetInputName(kMutedInput, tr("Muted")); } void Track::SetIndex(const int &index) @@ -276,15 +414,15 @@ QVector Track::BlocksAtTimeRange(const TimeRange &range) const return list; } -void Track::InvalidateCache(const TimeRange& range, const InputConnection& from) +void Track::InvalidateCache(const TimeRange& range, const QString& from, int element) { TimeRange limited; - Block* b; + const Block* b; - if (from.input == block_input_ - && from.element >= 0 - && (b = dynamic_cast(from.input->GetConnectedNode(from.element)))) { + if (from == kBlockInput + && element >= 0 + && (b = dynamic_cast(GetConnectedOutput(from, element).node()))) { // Limit the range signal to the corresponding block if (range.out() <= b->in() || range.in() >= b->out()) { return; @@ -329,8 +467,8 @@ void Track::PrependBlock(Block *block) { BeginOperation(); - block_input_->ArrayPrepend(); - Node::ConnectEdge(block, block_input_, 0); + InputArrayPrepend(kBlockInput); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, 0)); EndOperation(); @@ -343,8 +481,8 @@ void Track::InsertBlockAtIndex(Block *block, int index) BeginOperation(); int insert_index = GetArrayIndexFromCacheIndex(index); - block_input_->ArrayInsert(insert_index); - Node::ConnectEdge(block, block_input_, insert_index); + InputArrayInsert(kBlockInput, insert_index); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, insert_index)); EndOperation(); @@ -355,8 +493,8 @@ void Track::AppendBlock(Block *block) { BeginOperation(); - block_input_->ArrayAppend(); - Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1); + InputArrayAppend(kBlockInput); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, InputArraySize(kBlockInput) - 1)); EndOperation(); @@ -371,7 +509,7 @@ void Track::RippleRemoveBlock(Block *block) rational remove_in = block->in(); rational remove_out = block->out(); - block_input_->ArrayRemove(GetArrayIndexFromBlock(block)); + InputArrayRemove(kBlockInput, GetArrayIndexFromBlock(block)); EndOperation(); @@ -384,9 +522,9 @@ void Track::ReplaceBlock(Block *old, Block *replace) int index_of_old_block = GetArrayIndexFromBlock(old); - DisconnectEdge(old, block_input_, index_of_old_block); + DisconnectEdge(old, NodeInput(this, kBlockInput, index_of_old_block)); - ConnectEdge(replace, block_input_, index_of_old_block); + ConnectEdge(replace, NodeInput(this, kBlockInput, index_of_old_block)); EndOperation(); @@ -421,7 +559,7 @@ QString Track::GetDefaultTrackName(Track::Type type, int index) bool Track::IsMuted() const { - return muted_input_->GetStandardValue().toBool(); + return GetStandardValue(kMutedInput).toBool(); } bool Track::IsLocked() const @@ -429,11 +567,6 @@ bool Track::IsLocked() const return locked_; } -NodeInput *Track::block_input() const -{ - return block_input_; -} - void Track::Hash(QCryptographicHash &hash, const rational &time) const { Block* b = BlockAtTime(time); @@ -446,8 +579,7 @@ void Track::Hash(QCryptographicHash &hash, const rational &time) const void Track::SetMuted(bool e) { - muted_input_->SetStandardValue(e); - InvalidateCache(TimeRange(0, track_length())); + SetStandardValue(kMutedInput, e); } void Track::SetLocked(bool e) @@ -511,133 +643,6 @@ void Track::SetLengthInternal(const rational &r, bool invalidate) } } -void Track::BlockConnected(Node *node, int element) -{ - if (element == -1) { - // User has replaced the entire array, we will invalidate everything - InvalidateAll(block_input_, element); - return; - } - - // Check if a block was connected, if not, ignore - Block* block = dynamic_cast(node); - - if (!block) { - return; - } - - // Determine where in the cache this block will be - int cache_index = -1; - Block *previous = nullptr, *next = nullptr; - - for (int i=element+1; iArraySize(); i++) { - // Find next block because this will be the index that we want to insert at - cache_index = GetCacheIndexFromArrayIndex(i); - - if (cache_index >= 0) { - next = blocks_.at(cache_index); - break; - } - } - - // If there was no next, this will be inserted at the end - if (cache_index == -1) { - cache_index = blocks_.size(); - } - - // Determine previous block, either by using next's previous or the last block if there was no - // next. If there are neither, they'll both remain null - if (next) { - previous = next->previous(); - } else if (!blocks_.isEmpty()) { - previous = blocks_.last(); - } - - // Insert at index - blocks_.insert(cache_index, block); - block_array_indexes_.insert(cache_index, element); - - // Update previous/next - if (previous) { - previous->set_next(block); - block->set_previous(previous); - } - - if (next) { - block->set_next(next); - next->set_previous(block); - } - - block->set_track(this); - - // Update ins/outs - UpdateInOutFrom(cache_index); - - // Connect to the block - connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged); - - // Invalidate cache now that block should have an in point - InvalidateCache(TimeRange(block->in(), track_length())); - - // Emit block added signal - emit BlockAdded(block); -} - -void Track::BlockDisconnected(Node* node, int element) -{ - if (element == -1) { - // User has replaced the entire array, we will invalidate everything - InvalidateAll(block_input_, element); - return; - } - - Block* b = dynamic_cast(node); - - if (!b) { - return; - } - - emit BlockRemoved(b); - - TimeRange invalidate_range(b->in(), track_length()); - - // Get cache index - int cache_index = GetCacheIndexFromArrayIndex(element); - - // Remove block here - blocks_.removeAt(cache_index); - block_array_indexes_.removeAt(cache_index); - - // Update previous/nexts - Block* previous = b->previous(); - Block* next = b->next(); - - if (previous) { - previous->set_next(next); - } - - if (next) { - next->set_previous(previous); - } - - b->set_previous(nullptr); - b->set_next(nullptr); - b->set_track(nullptr); - - // Update lengths - if (next) { - UpdateInOutFrom(blocks_.indexOf(next)); - } else if (blocks_.isEmpty()) { - SetLengthInternal(rational()); - } else { - SetLengthInternal(blocks_.last()->out()); - } - - disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged); - - InvalidateCache(invalidate_range); -} - void Track::BlockLengthChanged() { // Assumes sender is a Block @@ -654,11 +659,6 @@ void Track::BlockLengthChanged() InvalidateCache(invalidate_region); } -void Track::MutedInputValueChanged() -{ - emit MutedChanged(IsMuted()); -} - uint qHash(const Track::Reference &r, uint seed) { // Not super efficient, but couldn't think of any better way to ensure a different hash each time diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 32b76f4ac..3beb78ba1 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -56,9 +56,9 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; static rational TransformTimeForBlock(Block* block, const rational& time); @@ -222,7 +222,7 @@ public: return blocks_; } - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()) override; + virtual void InvalidateCache(const TimeRange& range, const QString& from = QString(), int element = -1) override; /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips @@ -274,8 +274,6 @@ public: bool IsLocked() const; - NodeInput* block_input() const; - virtual void Hash(QCryptographicHash& hash, const rational &time) const override; AudioVisualWaveform& waveform() @@ -287,6 +285,9 @@ public: static const double kTrackHeightMinimum; static const double kTrackHeightInterval; + static const QString kBlockInput; + static const QString kMutedInput; + public slots: void SetMuted(bool e); @@ -338,6 +339,12 @@ protected: virtual void SaveInternal(QXmlStreamWriter* writer) const override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputValueChangedEvent(const QString& input, int element) override; + private: void UpdateInOutFrom(int index); @@ -352,10 +359,6 @@ private: QVector blocks_; QVector block_array_indexes_; - NodeInput* block_input_; - - NodeInput* muted_input_; - Track::Type track_type_; rational track_length_; @@ -371,14 +374,8 @@ private: AudioVisualWaveform waveform_; private slots: - void BlockConnected(Node* node, int element); - - void BlockDisconnected(Node* node, int element); - void BlockLengthChanged(); - void MutedInputValueChanged(); - }; uint qHash(const Track::Reference& r, uint seed = 0); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index dd082966e..06ce79869 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -27,13 +27,11 @@ namespace olive { -TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, NodeInput *track_input) : +TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, const QString &track_input) : QObject(parent), track_input_(track_input), type_(type) { - connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected); - connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected); } Track *TrackList::GetTrackAt(int index) const @@ -48,7 +46,7 @@ Track *TrackList::GetTrackAt(int index) const void TrackList::TrackConnected(Node *node, int element) { if (element == -1) { - parent()->InvalidateAll(track_input_, element); + parent()->InvalidateAll(track_input(), element); return; } @@ -60,7 +58,7 @@ void TrackList::TrackConnected(Node *node, int element) // Determine where in the cache this block will be int cache_index = -1; - for (int i=element+1; iArraySize(); i++) { + for (int i=element+1; iInvalidateAll(track_input_, element); + parent()->InvalidateAll(track_input(), element); return; } @@ -141,11 +139,36 @@ NodeGraph *TrackList::GetParentGraph() const return static_cast(parent()->parent()); } +const QString& TrackList::track_input() const +{ + return track_input_; +} + +NodeInput TrackList::track_input(int element) const +{ + return NodeInput(parent(), track_input(), element); +} + ViewerOutput *TrackList::parent() const { return static_cast(QObject::parent()); } +int TrackList::ArraySize() const +{ + return parent()->InputArraySize(track_input()); +} + +void TrackList::ArrayAppend(bool undoable) +{ + parent()->InputArrayAppend(track_input(), undoable); +} + +void TrackList::ArrayRemoveLast(bool undoable) +{ + parent()->InputArrayRemoveLast(track_input(), undoable); +} + void TrackList::UpdateTotalLength() { total_length_ = 0; diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 16cd647a9..042b93558 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -35,7 +35,7 @@ class TrackList : public QObject { Q_OBJECT public: - TrackList(ViewerOutput *parent, const Track::Type& type, NodeInput* track_input); + TrackList(ViewerOutput *parent, const Track::Type& type, const QString& track_input); const Track::Type& type() const { @@ -61,13 +61,27 @@ public: NodeGraph* GetParentGraph() const; - NodeInput* track_input() const - { - return track_input_; - } + const QString &track_input() const; + NodeInput track_input(int element) const; ViewerOutput* parent() const; + int ArraySize() const; + + void ArrayAppend(bool undoable = false); + void ArrayRemoveLast(bool undoable = false); + +public slots: + /** + * @brief Slot for when the track connection is added + */ + void TrackConnected(Node* node, int element); + + /** + * @brief Slot for when the track connection is removed + */ + void TrackDisconnected(Node* node, int element); + signals: void TrackListChanged(); @@ -96,23 +110,13 @@ private: return track_array_indexes_.indexOf(index); } - NodeInput* track_input_; + QString track_input_; rational total_length_; enum Track::Type type_; private slots: - /** - * @brief Slot for when the track connection is added - */ - void TrackConnected(Node* node, int element); - - /** - * @brief Slot for when the track connection is removed - */ - void TrackDisconnected(Node* node, int element); - /** * @brief Slot for when any of the track's length changes so we can update the length of the tracklist */ diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 0d51c1251..52b9c55df 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -24,31 +24,31 @@ namespace olive { +const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in"); +const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in"); +const QString ViewerOutput::kTrackInputFormat = QStringLiteral("track_in_%1"); + ViewerOutput::ViewerOutput() : video_frame_cache_(this), audio_playback_cache_(this), operation_stack_(0) { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - texture_input_->SetKeyframable(false); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); - samples_input_->SetKeyframable(false); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); // Create TrackList instances - track_inputs_.resize(Track::kCount); track_lists_.resize(Track::kCount); for (int i=0;iSetIsArray(true); - track_input->SetKeyframable(false); + QString track_input_id = kTrackInputFormat.arg(i); - IgnoreInvalidationsFrom(track_input); - track_inputs_.replace(i, track_input); + AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray)); - TrackList* list = new TrackList(this, static_cast(i), track_input); + IgnoreInvalidationsFrom(track_input_id); + + TrackList* list = new TrackList(this, static_cast(i), track_input_id); track_lists_.replace(i, list); connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache); connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength); @@ -110,15 +110,17 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to) ShiftAudioCache(from, to); } -void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from) +void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element) { + Q_UNUSED(element) + if (operation_stack_ == 0) { - if (from.input == texture_input_ || from.input == samples_input_) { + if (from == kTextureInput || from == kSamplesInput) { TimeRange invalidated_range(qMax(rational(), range.in()), qMin(GetLength(), range.out())); if (invalidated_range.in() != invalidated_range.out()) { - if (from.input == texture_input_) { + if (from == kTextureInput) { video_frame_cache_.Invalidate(invalidated_range); } else { audio_playback_cache_.Invalidate(invalidated_range); @@ -216,8 +218,8 @@ void ViewerOutput::VerifyLength() { video_length = track_lists_.at(Track::kVideo)->GetTotalLength(); - if (video_length.isNull() && texture_input_->IsConnected()) { - NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0); + if (video_length.isNull() && IsInputConnected(kTextureInput)) { + NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0)); video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value(); } @@ -227,8 +229,8 @@ void ViewerOutput::VerifyLength() { audio_length = track_lists_.at(Track::kAudio)->GetTotalLength(); - if (audio_length.isNull() && samples_input_->IsConnected()) { - NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0); + if (audio_length.isNull() && IsInputConnected(kSamplesInput)) { + NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0)); audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value(); } @@ -251,11 +253,11 @@ void ViewerOutput::Retranslate() { Node::Retranslate(); - texture_input_->set_name(tr("Texture")); + SetInputName(kTextureInput, tr("Texture")); - samples_input_->set_name(tr("Samples")); + SetInputName(kSamplesInput, tr("Samples")); - for (int i=0;i(i)) { @@ -274,7 +276,7 @@ void ViewerOutput::Retranslate() } if (!input_name.isEmpty()) { - track_inputs_.at(i)->set_name(input_name); + SetInputName(kTrackInputFormat.arg(i), input_name); } } } @@ -293,4 +295,32 @@ void ViewerOutput::EndOperation() Node::EndOperation(); } +void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kTextureInput) { + emit TextureInputChanged(); + } else { + foreach (TrackList* list, track_lists_) { + if (list->track_input() == input) { + list->TrackConnected(output.node(), element); + break; + } + } + } +} + +void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kTextureInput) { + emit TextureInputChanged(); + } else { + foreach (TrackList* list, track_lists_) { + if (list->track_input() == input) { + list->TrackDisconnected(output.node(), element); + break; + } + } + } +} + } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index ed88e671a..80a0a944b 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -59,17 +59,7 @@ public: void ShiftAudioCache(const rational& from, const rational& to); void ShiftCache(const rational& from, const rational& to); - NodeInput* texture_input() const - { - return texture_input_; - } - - NodeInput* samples_input() const - { - return samples_input_; - } - - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override; const VideoParams& video_params() const { @@ -106,11 +96,6 @@ public: */ QVector GetUnlockedTracks() const; - NodeInput* track_input(Track::Type type) const - { - return track_inputs_.at(type); - } - TrackList* track_list(Track::Type type) const { return track_lists_.at(type); @@ -132,6 +117,10 @@ public: virtual void EndOperation() override; + static const QString kTextureInput; + static const QString kSamplesInput; + static const QString kTrackInputFormat; + signals: void TimebaseChanged(const rational&); @@ -149,19 +138,20 @@ signals: void TrackAdded(Track* track); void TrackRemoved(Track* track); + void TextureInputChanged(); + +protected: + void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; + + void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; + private: QUuid uuid_; - NodeInput* texture_input_; - - NodeInput* samples_input_; - VideoParams video_params_; AudioParams audio_params_; - QVector track_inputs_; - QVector track_lists_; QVector track_cache_; diff --git a/app/node/param.cpp b/app/node/param.cpp new file mode 100644 index 000000000..f4e716787 --- /dev/null +++ b/app/node/param.cpp @@ -0,0 +1,147 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "param.h" + +#include "node.h" + +namespace olive { + +QString NodeInput::name() const +{ + if (IsValid()) { + return node_->GetInputName(input_); + } else { + return QString(); + } +} + +bool NodeInput::IsConnected() const +{ + if (IsValid()) { + return node_->IsInputConnected(*this); + } else { + return false; + } +} + +bool NodeInput::IsKeyframing() const +{ + if (IsValid()) { + return node_->IsInputKeyframing(*this); + } else { + return false; + } +} + +bool NodeInput::IsArray() const +{ + if (IsValid()) { + return node_->InputIsArray(input_); + } else { + return false; + } +} + +NodeOutput NodeInput::GetConnectedOutput() const +{ + if (IsValid()) { + return node_->GetConnectedOutput(*this); + } else { + return NodeOutput(); + } +} + +NodeValue::Type NodeInput::GetDataType() const +{ + if (IsValid()) { + return node_->GetInputDataType(input_); + } else { + return NodeValue::kNone; + } +} + +QStringList NodeInput::GetComboBoxStrings() const +{ + if (IsValid()) { + return node_->GetComboBoxStrings(input_); + } else { + return QStringList(); + } +} + +QVariant NodeInput::GetProperty(const QString &key) const +{ + if (IsValid()) { + return node_->GetInputProperty(input_, key); + } else { + return QVariant(); + } +} + +QVariant NodeInput::GetValueAtTime(const rational &time) const +{ + if (IsValid()) { + return node_->GetValueAtTime(*this, time); + } else { + return QVariant(); + } +} + +NodeKeyframe* NodeInput::GetKeyframeAtTimeOnTrack(const rational &time, int track) const +{ + if (IsValid()) { + return node_->GetKeyframeAtTimeOnTrack(*this, time, track); + } else { + return nullptr; + } +} + +QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const +{ + if (IsValid()) { + return node_->GetSplitDefaultValueOnTrack(input_, track); + } else { + return QVariant(); + } +} + +uint qHash(const NodeInput &i) +{ + return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element()); +} + +NodeOutput::NodeOutput(Node *n) +{ + node_ = n; + output_ = Node::kDefaultOutput; +} + +uint qHash(const NodeKeyframeTrackReference &i) +{ + return qHash(i.input()) & qHash(i.track()); +} + +uint qHash(const NodeInputPair &i) +{ + return qHash(i.node) & qHash(i.input); +} + +} diff --git a/app/node/param.h b/app/node/param.h new file mode 100644 index 000000000..5b7076449 --- /dev/null +++ b/app/node/param.h @@ -0,0 +1,247 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef NODEPARAM_H +#define NODEPARAM_H + +#include + +#include "common/rational.h" +#include "value.h" + +namespace olive { + +class Node; +class NodeKeyframe; + +struct NodeInputPair { + bool operator==(const NodeInputPair& rhs) const + { + return node == rhs.node && input == rhs.input; + } + + Node* node; + QString input; +}; + +/** + * @brief Defines a node output + */ +class NodeOutput +{ +public: + NodeOutput(Node* n, const QString& o) + { + node_ = n; + output_ = o; + } + + NodeOutput(Node* n); + + NodeOutput() + { + node_ = nullptr; + } + + bool operator==(const NodeOutput& rhs) const + { + return node_ == rhs.node_ && output_ == rhs.output_; + } + + Node* node() const + { + return node_; + } + + const QString& output() const + { + return output_; + } + + bool IsValid() const + { + return node_; + } + +private: + Node* node_; + QString output_; + +}; + +/** + * @brief Defines a Node input + */ +class NodeInput +{ +public: + NodeInput() + { + node_ = nullptr; + element_ = -1; + } + + NodeInput(Node* n, const QString& i, int e = -1) + { + node_ = n; + input_ = i; + element_ = e; + } + + bool operator==(const NodeInput& rhs) const + { + return node_ == rhs.node_ && input_ == rhs.input_ && element_ == rhs.element_; + } + + bool operator!=(const NodeInput& rhs) const + { + return !(*this == rhs); + } + + bool operator<(const NodeInput& rhs) const + { + if (node_ != rhs.node_) { + return node_ < rhs.node_; + } + + if (input_ != rhs.input_) { + return input_ < rhs.input_; + } + + return element_ < rhs.element_; + } + + Node* node() const + { + return node_; + } + + NodeInputPair input_pair() const + { + return {node_, input_}; + } + + const QString& input() const + { + return input_; + } + + int element() const + { + return element_; + } + + void set_element(int e) + { + element_ = e; + } + + QString name() const; + + bool IsValid() const + { + return node_ && !input_.isEmpty() && element_ >= -1; + } + + bool IsConnected() const; + + bool IsKeyframing() const; + + bool IsArray() const; + + NodeOutput GetConnectedOutput() const; + + NodeValue::Type GetDataType() const; + + QStringList GetComboBoxStrings() const; + + QVariant GetProperty(const QString& key) const; + + QVariant GetValueAtTime(const rational& time) const; + + NodeKeyframe *GetKeyframeAtTimeOnTrack(const rational& time, int track) const; + + QVariant GetSplitDefaultValueForTrack(int track) const; + + void Reset() + { + *this = NodeInput(); + } + +private: + Node* node_; + QString input_; + int element_; + +}; + +class NodeKeyframeTrackReference { +public: + NodeKeyframeTrackReference() + { + track_ = -1; + } + + NodeKeyframeTrackReference(const NodeInput& input, int track = 0) + { + input_ = input; + track_ = track; + } + + bool operator==(const NodeKeyframeTrackReference& rhs) const + { + return input_ == rhs.input_ && track_ == rhs.track_; + } + + const NodeInput& input() const + { + return input_; + } + + int track() const + { + return track_; + } + + bool IsValid() const + { + return input_.IsValid() && track_ >= 0; + } + + void Reset() + { + *this = NodeKeyframeTrackReference(); + } + +private: + NodeInput input_; + int track_; + +}; + +uint qHash(const NodeInputPair& i); +uint qHash(const NodeInput& i); +uint qHash(const NodeKeyframeTrackReference& i); + +} + +Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference) + +#endif // NODEPARAM_H diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index b7757c415..4724d55f9 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -29,12 +29,12 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueDatabase database; // We need to insert tables into the database for each input - foreach (NodeInput* input, node->parameters()) { + foreach (const QString& input, node->inputs()) { if (IsCancelled()) { return NodeValueDatabase(); } - database.Insert(input, ProcessInput(input, range)); + database.Insert(input, ProcessInput(node, input, range)); } AddGlobalsToDatabase(database, range); @@ -42,37 +42,35 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa return database; } -NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& range) +NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range) { // If input is connected, retrieve value directly - Node* node = input->parent(); - - if (input->IsConnected()) { + if (node->IsInputConnected(input)) { TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); // Value will equal something from the connected node, follow it - return GenerateTable(input->GetConnectedNode(), adjusted_range); + return GenerateTable(node->GetConnectedOutput(input), adjusted_range); } else { // Store node QVariant return_val; - if (input->IsArray()) { + if (node->InputIsArray(input)) { // Value is an array, we will return a list of NodeValueTables - QVector array_tbl(input->ArraySize()); + QVector array_tbl(node->InputArraySize(input)); for (int i=0; iInputTimeAdjustment(input, i, range); - if (input->IsConnected(i)) { - sub_tbl = GenerateTable(input->GetConnectedNode(i), adjusted_range); + if (node->IsInputConnected(input, i)) { + sub_tbl = GenerateTable(node->GetConnectedOutput(input, i), adjusted_range); } else { - QVariant input_value = input->GetValueAtTime(adjusted_range.in(), i); - sub_tbl.Push(input->GetDataType(), input_value, node); + QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), i); + sub_tbl.Push(node->GetInputDataType(input), input_value, node); } } @@ -83,18 +81,18 @@ NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& ra // Not connected or an array, just pull the immediate TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); - return_val = input->GetValueAtTime(adjusted_range.in()); + return_val = node->GetValueAtTime(input, adjusted_range.in()); } NodeValueTable return_table; - return_table.Push(input->GetDataType(), return_val, node, true); + return_table.Push(node->GetInputDataType(input), return_val, node, true); return return_table; } } -NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range) +NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output, const TimeRange& range) { const Track* track = dynamic_cast(n); if (track) { @@ -108,18 +106,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang NodeValueDatabase database = GenerateDatabase(n, range); // By this point, the node should have all the inputs it needs to render correctly - NodeValueTable table = n->Value(database); + NodeValueTable table = n->Value(output, database); PostProcessTable(n, range, table); return table; } -NodeValueTable NodeTraverser::GenerateTable(const Node *n, const rational &in, const rational &out) -{ - return GenerateTable(n, TimeRange(in, out)); -} - NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeRange &range) { // By default, just follow the in point diff --git a/app/node/traverser.h b/app/node/traverser.h index 3d6158252..eebe6f823 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -36,13 +36,16 @@ class NodeTraverser : public CancelableObject public: NodeTraverser() = default; - NodeValueTable GenerateTable(const Node *n, const TimeRange &range); - NodeValueTable GenerateTable(const Node *n, const rational &in, const rational& out); + NodeValueTable GenerateTable(const Node *n, const QString &output, const TimeRange &range); + NodeValueTable GenerateTable(const NodeOutput& output, const TimeRange &range) + { + return GenerateTable(output.node(), output.output(), range); + } NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); protected: - NodeValueTable ProcessInput(NodeInput *input, const TimeRange &range); + NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range); virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range); diff --git a/app/node/value.cpp b/app/node/value.cpp index 93a822e01..7f25b522b 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -20,13 +20,13 @@ #include "value.h" +#include #include #include #include #include #include "common/tohex.h" -#include "node/input.h" #include "render/color.h" namespace olive { @@ -311,26 +311,6 @@ QString NodeValue::GetPrettyDataTypeName(Type type) return QCoreApplication::translate("NodeValue", "Unknown"); } -NodeValueTable &NodeValueDatabase::operator[](const NodeInput *input) -{ - return tables_[input->id()]; -} - -void NodeValueDatabase::Insert(const NodeInput *key, const NodeValueTable &value) -{ - tables_.insert(key->id(), value); -} - -NodeValueTable NodeValueDatabase::Merge() const -{ - QHash copy = tables_; - - // Kinda hacky, but we don't need this table to slipstream - copy.remove(QStringLiteral("global")); - - return NodeValueTable::Merge(copy.values()); -} - NodeValue NodeValueTable::GetWithMeta(const QVector &type, const QString &tag) const { int value_index = GetInternal(type, tag); diff --git a/app/node/value.h b/app/node/value.h index 42a7fa8a9..4300aaee7 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -18,16 +18,16 @@ ***/ -#ifndef VALUE_H -#define VALUE_H +#ifndef NODEVALUE_H +#define NODEVALUE_H #include #include +#include namespace olive { class Node; -class NodeInput; class NodeValue { @@ -374,59 +374,13 @@ public: private: int GetInternal(const QVector &type, const QString& tag) const; - QList values_; + QVector values_; }; -class NodeValueDatabase -{ -public: - NodeValueDatabase() = default; - - NodeValueTable& operator[](const QString& input_id) - { - return tables_[input_id]; - } - - NodeValueTable& operator[](const NodeInput* input); - - void Insert(const QString& key, const NodeValueTable &value) - { - tables_.insert(key, value); - } - - void Insert(const NodeInput* key, const NodeValueTable& value); - - NodeValueTable Merge() const; - - using const_iterator = QHash::const_iterator; - - inline QHash::const_iterator begin() const - { - return tables_.cbegin(); - } - - inline QHash::const_iterator end() const - { - return tables_.cend(); - } - - inline bool contains(const QString& s) const - { - return tables_.contains(s); - } - -private: - QHash tables_; - -}; - -using NodeValueMap = QHash; - } Q_DECLARE_METATYPE(olive::NodeValue) Q_DECLARE_METATYPE(olive::NodeValueTable) -Q_DECLARE_METATYPE(olive::NodeValueDatabase) -#endif // VALUE_H +#endif // NODEVALUE_H diff --git a/app/node/valuedatabase.cpp b/app/node/valuedatabase.cpp new file mode 100644 index 000000000..3e207badd --- /dev/null +++ b/app/node/valuedatabase.cpp @@ -0,0 +1,35 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "valuedatabase.h" + +namespace olive { + +NodeValueTable NodeValueDatabase::Merge() const +{ + QHash copy = tables_; + + // Kinda hacky, but we don't need this table to slipstream + copy.remove(QStringLiteral("global")); + + return NodeValueTable::Merge(copy.values()); +} + +} diff --git a/app/node/valuedatabase.h b/app/node/valuedatabase.h new file mode 100644 index 000000000..269525a4b --- /dev/null +++ b/app/node/valuedatabase.h @@ -0,0 +1,74 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef NODEVALUEDATABASE_H +#define NODEVALUEDATABASE_H + +#include "param.h" +#include "value.h" + +namespace olive { + +class NodeValueDatabase +{ +public: + NodeValueDatabase() = default; + + NodeValueTable& operator[](const QString& input_id) + { + return tables_[input_id]; + } + + void Insert(const QString& key, const NodeValueTable &value) + { + tables_.insert(key, value); + } + + NodeValueTable Merge() const; + + using const_iterator = QHash::const_iterator; + + inline QHash::const_iterator begin() const + { + return tables_.cbegin(); + } + + inline QHash::const_iterator end() const + { + return tables_.cend(); + } + + inline bool contains(const QString& s) const + { + return tables_.contains(s); + } + +private: + QHash tables_; + +}; + +using NodeValueMap = QHash; + +} + +Q_DECLARE_METATYPE(olive::NodeValueDatabase) + +#endif // NODEVALUEDATABASE_H diff --git a/app/panel/node/node.h b/app/panel/node/node.h index e8a9e62b6..74cd2e62a 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -93,12 +93,14 @@ public slots: void SelectBlocks(const QVector& nodes) { + Q_UNUSED(nodes) qDebug() << "Stub"; //node_view_->SelectBlocks(nodes); } void DeselectBlocks(const QVector& nodes) { + Q_UNUSED(nodes) qDebug() << "Stub"; //node_view_->DeselectBlocks(nodes); } diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 137b94127..5f9501a58 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -44,6 +44,8 @@ Footage::~Footage() void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled) { + Q_UNUSED(version) + while (XMLReadNextStartElement(reader)) { if (cancelled && *cancelled) { return; diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index 75fb5db94..87843eb52 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -67,7 +67,7 @@ Stream *Stream::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("ptr")) { - xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream); + //xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream); } else if (reader->name() == QStringLiteral("index")) { stream->set_index(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("timebase")) { diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index d24e741ea..da5e657a1 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -207,17 +207,8 @@ void Sequence::Save(QXmlStreamWriter *writer) const void Sequence::add_default_nodes() { // Create tracks and connect them to the viewer - Track* video_track = new Track(); - video_track->setParent(this); - viewer_output_->track_input(Track::kVideo)->ArrayAppend(); - Node::ConnectEdge(video_track, viewer_output_->track_input(Track::kVideo), 0); - Node::ConnectEdge(video_track, viewer_output_->texture_input()); - - Track* audio_track = new Track(); - audio_track->setParent(this); - viewer_output_->track_input(Track::kAudio)->ArrayAppend(); - Node::ConnectEdge(audio_track, viewer_output_->track_input(Track::kAudio), 0); - Node::ConnectEdge(audio_track, viewer_output_->samples_input()); + TimelineAddTrackCommand(viewer_output_->track_list(Track::kVideo)).redo(); + TimelineAddTrackCommand(viewer_output_->track_list(Track::kAudio)).redo(); } Item::Type Sequence::type() const diff --git a/app/project/project.cpp b/app/project/project.cpp index 00ce92542..eca7d6604 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -86,12 +86,6 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint } } - - foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) { - if (con.footage) { - con.input->SetStandardValue(Node::PtrToValue(xml_node_data.footage_ptrs.value(con.footage)), con.element); - } - } } void Project::Save(QXmlStreamWriter *writer) const diff --git a/app/render/job/CMakeLists.txt b/app/render/job/CMakeLists.txt index 1a3d982ca..100d7f5e3 100644 --- a/app/render/job/CMakeLists.txt +++ b/app/render/job/CMakeLists.txt @@ -16,6 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + render/job/acceleratedjob.cpp render/job/acceleratedjob.h render/job/generatejob.h render/job/samplejob.h diff --git a/app/render/job/acceleratedjob.cpp b/app/render/job/acceleratedjob.cpp new file mode 100644 index 000000000..3a3ee3442 --- /dev/null +++ b/app/render/job/acceleratedjob.cpp @@ -0,0 +1,32 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "acceleratedjob.h" + +#include "node/node.h" + +namespace olive { + +void AcceleratedJob::InsertValue(const Node* node, const QString& input, NodeValueDatabase& value) +{ + InsertValue(input, value[input].TakeWithMeta(node->GetInputDataType(input))); +} + +} diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index ca52b7c5e..597c05be4 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -21,8 +21,8 @@ #ifndef ACCELERATEDJOB_H #define ACCELERATEDJOB_H -#include "node/input.h" -#include "node/value.h" +#include "node/param.h" +#include "node/valuedatabase.h" namespace olive { @@ -30,31 +30,18 @@ class AcceleratedJob { public: AcceleratedJob() = default; - NodeValue GetValue(NodeInput* input) const - { - return value_map_.value(input->id()); - } - NodeValue GetValue(const QString& input) const { return value_map_.value(input); } - void InsertValue(NodeInput* input, NodeValueDatabase& value) - { - InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType())); - } + void InsertValue(const Node* node, const QString& input, NodeValueDatabase& value); void InsertValue(const QString& input, const NodeValue& value) { value_map_.insert(input, value); } - void InsertValue(NodeInput* input, const NodeValue& value) - { - value_map_.insert(input->id(), value); - } - const NodeValueMap &GetValues() const { return value_map_; diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index c6d5d5eaf..7fec0607b 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -38,7 +38,7 @@ public: samples_ = value.data().value(); } - SampleJob(NodeInput* from, NodeValueDatabase& db) + SampleJob(const QString& from, NodeValueDatabase& db) { samples_ = db[from].Take(NodeValue::kSamples).value(); } diff --git a/app/render/job/shaderjob.h b/app/render/job/shaderjob.h index 64848c563..32005a32d 100644 --- a/app/render/job/shaderjob.h +++ b/app/render/job/shaderjob.h @@ -46,9 +46,9 @@ public: shader_id_ = id; } - void SetIterations(int iterations, NodeInput* iterative_input) + void SetIterations(int iterations, const NodeInput& iterative_input) { - SetIterations(iterations, iterative_input->id()); + SetIterations(iterations, iterative_input.input()); } void SetIterations(int iterations, const QString& iterative_input) @@ -72,9 +72,9 @@ public: return interpolation_.value(id, Texture::kDefaultInterpolation); } - void SetInterpolation(NodeInput* input, Texture::Interpolation interp) + void SetInterpolation(const NodeInput& input, Texture::Interpolation interp) { - interpolation_.insert(input->id(), interp); + interpolation_.insert(input.input(), interp); } void SetInterpolation(const QString& id, Texture::Interpolation interp) diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 88ad8a65c..d9548d0ef 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -65,7 +65,7 @@ void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cac foreach (const rational& time, times) { // See if hash already exists in disk cache - QByteArray hash = RenderManager::Hash(viewer->texture_input()->GetConnectedNode(), viewer->video_params(), time); + QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), viewer->video_params(), time); // Check memory list since disk checking is slow bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end()); @@ -256,13 +256,13 @@ void PreviewAutoCacher::ProcessUpdateQueue() RemoveNode(job.node); break; case QueuedJob::kEdgeAdded: - AddEdge(job.node, job.input, job.element); + AddEdge(job.output, job.input); break; case QueuedJob::kEdgeRemoved: - RemoveEdge(job.node, job.input, job.element); + RemoveEdge(job.output, job.input); break; case QueuedJob::kValueChanged: - CopyValue(job.input, job.element); + CopyValue(job.input); break; case QueuedJob::kVideoParamsChanged: UpdateVideoParams(); @@ -305,26 +305,26 @@ void PreviewAutoCacher::RemoveNode(Node *node) delete copy; } -void PreviewAutoCacher::AddEdge(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::AddEdge(const NodeOutput &output, const NodeInput &input) { - Node* our_output = copy_map_.value(output); - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + Node* our_output = copy_map_.value(output.node()); + Node* our_input = copy_map_.value(input.node()); - Node::ConnectEdge(our_output, our_input, element); + Node::ConnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } -void PreviewAutoCacher::RemoveEdge(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::RemoveEdge(const NodeOutput &output, const NodeInput &input) { - Node* our_output = copy_map_.value(output); - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + Node* our_output = copy_map_.value(output.node()); + Node* our_input = copy_map_.value(input.node()); - Node::DisconnectEdge(our_output, our_input, element); + Node::DisconnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } -void PreviewAutoCacher::CopyValue(NodeInput *input, int element) +void PreviewAutoCacher::CopyValue(const NodeInput &input) { - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); - NodeInput::CopyValuesOfElement(input, our_input, element); + Node* our_input = copy_map_.value(input.node()); + Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element()); } void PreviewAutoCacher::UpdateVideoParams() @@ -423,27 +423,27 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait) void PreviewAutoCacher::NodeAdded(Node *node) { - graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kNodeAdded, node, NodeInput(), NodeOutput()}); } void PreviewAutoCacher::NodeRemoved(Node *node) { - graph_update_queue_.append({QueuedJob::kNodeRemoved, node, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kNodeRemoved, node, NodeInput(), NodeOutput()}); } -void PreviewAutoCacher::EdgeAdded(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::EdgeAdded(const NodeOutput &output, const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kEdgeAdded, output, input, element}); + graph_update_queue_.append({QueuedJob::kEdgeAdded, nullptr, input, output}); } -void PreviewAutoCacher::EdgeRemoved(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::EdgeRemoved(const NodeOutput &output, const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, input, element}); + graph_update_queue_.append({QueuedJob::kEdgeRemoved, nullptr, input, output}); } -void PreviewAutoCacher::ValueChanged(NodeInput *input, int element) +void PreviewAutoCacher::ValueChanged(const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, element}); + graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, NodeOutput()}); } void PreviewAutoCacher::VideoParamsChanged() @@ -451,14 +451,14 @@ void PreviewAutoCacher::VideoParamsChanged() // In case the user is pressing the mouse at this exact moment IgnoreNextMouseButton(); - graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, NodeInput(), NodeOutput()}); ClearVideoQueue(); TryRender(); } void PreviewAutoCacher::AudioParamsChanged() { - graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, NodeInput(), NodeOutput()}); ClearAudioQueue(); TryRender(); } @@ -677,10 +677,8 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) // Add all connections foreach (Node* node, graph->nodes()) { - foreach (NodeInput* input, node->inputs()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - AddEdge(it->second, input, it->first); - } + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + AddEdge(it->second, it->first); } } diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index c80f7a371..1b28217d2 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -107,9 +107,9 @@ private: void AddNode(Node* node); void RemoveNode(Node* node); - void AddEdge(Node* output, NodeInput* input, int element); - void RemoveEdge(Node* output, NodeInput* input, int element); - void CopyValue(NodeInput* input, int element); + void AddEdge(const NodeOutput& output, const NodeInput& input); + void RemoveEdge(const NodeOutput& output, const NodeInput& input); + void CopyValue(const NodeInput& input); void UpdateVideoParams(); void UpdateAudioParams(); @@ -127,8 +127,8 @@ private: Type type; Node* node; - NodeInput* input; - int element; + NodeInput input; + NodeOutput output; }; ViewerOutput* viewer_node_; @@ -201,11 +201,11 @@ private slots: void NodeRemoved(Node* node); - void EdgeAdded(Node* output, NodeInput* input, int element); + void EdgeAdded(const NodeOutput& output, const NodeInput& input); - void EdgeRemoved(Node* output, NodeInput* input, int element); + void EdgeRemoved(const NodeOutput& output, const NodeInput& input); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input); void VideoParamsChanged(); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index a5bbb5c05..4079ee03a 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -58,7 +58,7 @@ void RenderProcessor::Run() const VideoParams& video_params = ticket_->property("vparam").value(); rational time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer->texture_input(), + NodeValueTable table = ProcessInput(viewer, ViewerOutput::kTextureInput, TimeRange(time, time + video_params.time_base())); TexturePtr texture = table.Get(NodeValue::kTexture).value(); @@ -133,7 +133,7 @@ void RenderProcessor::Run() ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); TimeRange time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer->samples_input(), time); + NodeValueTable table = ProcessInput(viewer, ViewerOutput::kSamplesInput, time); ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled()); break; @@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } // FIXME: Doesn't handle reversing - if (b->speed_input()->IsKeyframing() || b->speed_input()->IsConnected()) { + if (b->IsInputKeyframing(Block::kSpeedInput) || b->IsInputConnected(Block::kSpeedInput)) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = b->speed_input()->GetStandardValue().toDouble(); + double speed_value = b->GetStandardValue(Block::kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Just silence, don't think there's any other practical application of 0 speed audio @@ -450,16 +450,8 @@ QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &rang rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second); // Update all non-sample and non-footage inputs - NodeValueMap::const_iterator j; - for (j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) { - NodeValueTable value; - NodeInput* corresponding_input = node->GetInputWithID(j.key()); - - if (corresponding_input) { - value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time)); - } else { - value.Push(j.value()); - } + for (auto j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) { + NodeValueTable value = ProcessInput(node, j.key(), TimeRange(this_sample_time, this_sample_time)); value_db.Insert(j.key(), value); } diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 374df8cfa..2cc7aaed4 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -34,7 +34,7 @@ PreCacheTask::PreCacheTask(VideoStream *footage, Sequence* sequence) : video_node_ = new MediaInput(); video_node_->SetStream(footage); - Node::ConnectEdge(video_node_, viewer()->texture_input()); + Node::ConnectEdge(video_node_, NodeInput(viewer(), ViewerOutput::kTextureInput)); SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(), QString::number(footage->index()))); diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index ede34cb20..bce195ea2 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -63,59 +63,51 @@ void CurveView::Clear() lines_.clear(); } -void CurveView::ConnectInput(NodeInput *input, int element, int track) +void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref) { - NodeInput::KeyframeTrackReference ref = {input, element, track}; - if (connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } // Add keyframes from track - AddKeyframesOfTrack(input, element, track); + AddKeyframesOfTrack(ref); // Append to the list connected_inputs_.append(ref); } -void CurveView::DisconnectInput(NodeInput *input, int element, int track) +void CurveView::DisconnectInput(const NodeKeyframeTrackReference& ref) { - NodeInput::KeyframeTrackReference ref = {input, element, track}; - if (!connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } // Remove keyframes belonging to this element and track - RemoveKeyframesOfTrack(input, element, track); + RemoveKeyframesOfTrack(ref); // Remove from the list connected_inputs_.removeOne(ref); } -void CurveView::SelectKeyframesOfInput(NodeInput *input, int element, int track) +void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref) { DeselectAll(); for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == input - && it.key()->element() == element - && it.key()->track() == track) { + if (it.key()->key_track_ref() == ref) { it.value()->setSelected(true); } } } -void CurveView::ZoomToFitInput(NodeInput *input, int element, int track) +void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref) { QList keys; for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == input - && it.key()->element() == element - && it.key()->track() == track) { + if (it.key()->key_track_ref() == ref) { keys.append(it.key()); } } @@ -123,14 +115,14 @@ void CurveView::ZoomToFitInput(NodeInput *input, int element, int track) ZoomToFitInternal(keys); } -void CurveView::SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference &ref, const QColor &color) +void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert color into hashmap keyframe_colors_.insert(ref, color); // Update all keyframes for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == ref.input && it.key()->element() == ref.element && it.key()->track() == ref.track) { + if (it.key()->key_track_ref() == ref) { it.value()->SetOverrideBrush(color); } } @@ -187,13 +179,14 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) painter->drawLines(lines); // Draw keyframe lines - foreach (const NodeInput::KeyframeTrackReference& ref, connected_inputs_) { - NodeInput* input = ref.input; + foreach (const NodeKeyframeTrackReference& ref, connected_inputs_) { + Node* node = ref.input().node(); + const QString& input = ref.input().input(); - if (input->IsKeyframing(ref.element)) { - const QVector& tracks = input->GetKeyframeTracks(ref.element); + if (node->IsInputKeyframing(input, ref.input().element())) { + const QVector& tracks = node->GetKeyframeTracks(ref.input()); - const NodeKeyframeTrack& track = tracks.at(ref.track); + const NodeKeyframeTrack& track = tracks.at(ref.track()); if (!track.isEmpty()) { painter->setPen(QPen(keyframe_colors_.value(ref), @@ -348,7 +341,7 @@ void CurveView::ZoomToFitInternal(const QList &keys) double max_val = DBL_MIN; foreach (NodeKeyframe* key, keys) { - rational transformed_time = GetAdjustedTime(key->parent()->parent(), + rational transformed_time = GetAdjustedTime(key->parent(), GetTimeTarget(), key->time(), false); @@ -480,7 +473,7 @@ KeyframeViewItem* CurveView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = super::AddKeyframe(key); SetItemYFromKeyframeValue(key, item); - item->SetOverrideBrush(keyframe_colors_.value({key->parent(), key->element(), key->track()})); + item->SetOverrideBrush(keyframe_colors_.value(key->key_track_ref())); connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index 6ae76a0a2..da023e77c 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -38,15 +38,15 @@ public: virtual void Clear() override; - void ConnectInput(NodeInput* input, int element, int track); + void ConnectInput(const NodeKeyframeTrackReference &ref); - void DisconnectInput(NodeInput* input, int element, int track); + void DisconnectInput(const NodeKeyframeTrackReference &ref); - void SelectKeyframesOfInput(NodeInput* input, int element, int track); + void SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref); - void ZoomToFitInput(NodeInput* input, int element, int track); + void ZoomToFitInput(const NodeKeyframeTrackReference &ref); - void SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference& ref, const QColor& color); + void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); public slots: virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; @@ -84,7 +84,7 @@ private: void CreateBezierControlPoints(KeyframeViewItem *item); - QHash keyframe_colors_; + QHash keyframe_colors_; int text_padding_; @@ -94,7 +94,7 @@ private: QVector bezier_control_points_; - QVector connected_inputs_; + QVector connected_inputs_; private slots: void KeyframeValueChanged(); diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 11ff6ee69..f3f535d1e 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -219,24 +219,37 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp) void CurveWidget::ConnectNode(Node *node, bool connect) { - foreach (NodeInput* input, node->parameters()) { - ConnectInput(input, connect); + foreach (const QString& input, node->inputs()) { + ConnectInput(node, input, connect); + } + + // Connect add/remove signals + if (connect) { + QObject::connect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::connect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); + } else { + QObject::disconnect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::disconnect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); } } -void CurveWidget::ConnectInput(NodeInput *input, bool connect) +void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect) { - int track_count = NodeValue::get_number_of_keyframe_tracks(input->GetDataType()); + if (!node->IsInputKeyframable(input)) { + qWarning() << "Tried to connect input that isn't keyframable"; + return; + } + + int track_count = NodeValue::get_number_of_keyframe_tracks(node->GetInputDataType(input)); bool multiple_tracks = track_count > 1; - for (int i=-1; iArraySize(); i++) { - if (!input->IsKeyframable()) { - continue; - } - + int arr_sz = node->InputArraySize(input); + for (int i=-1; iGetKeyframeTracks(i).size(); j++) { - NodeInput::KeyframeTrackReference ref = {input, i, j}; + const QVector& tracks = node->GetKeyframeTracks(input, i); + + for (int j=0; jIsInputEnabled(input, i, multiple_tracks ? -1 : 0)) { + if (tree_view_->IsInputEnabled(NodeKeyframeTrackReference(NodeInput(node, input, i), multiple_tracks ? -1 : 0))) { if (multiple_tracks) { for (int j=0; jIsInputEnabled(input, i, j)) { + NodeKeyframeTrackReference ref(NodeInput(node, input, i), j); + if (tree_view_->IsInputEnabled(ref)) { if (connect) { - view_->ConnectInput(input, i, j); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(input, i, j); + view_->DisconnectInput(ref); } } } } else { + NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0); if (connect) { - view_->ConnectInput(input, i, 0); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(input, i, 0); + view_->DisconnectInput(ref); } } } } - - // Connect add/remove signals - if (connect) { - QObject::connect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::connect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } else { - QObject::disconnect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::disconnect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } } void CurveWidget::SelectionChanged() @@ -357,12 +363,12 @@ void CurveWidget::NodeEnabledChanged(Node* n, bool e) ConnectNode(n, e); } -void CurveWidget::InputEnabledChanged(NodeInput *i, int element, int track, bool e) +void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, bool e) { if (e) { - view_->ConnectInput(i, element, track); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(i, element, track); + view_->DisconnectInput(ref); } } @@ -376,18 +382,18 @@ void CurveWidget::RemoveKeyframe(NodeKeyframe *key) view_->RemoveKeyframe(key); } -void CurveWidget::InputSelectionChanged(NodeInput *input, int element, int track) +void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref) { - key_control_->SetInput(input, element); + key_control_->SetInput(ref.input()); - if (input) { - view_->SelectKeyframesOfInput(input, element, track); + if (ref.IsValid()) { + view_->SelectKeyframesOfInput(ref); } } -void CurveWidget::InputDoubleClicked(NodeInput *input, int element, int track) +void CurveWidget::InputDoubleClicked(const NodeKeyframeTrackReference& ref) { - view_->ZoomToFitInput(input, element, track); + view_->ZoomToFitInput(ref); } void CurveWidget::KeyframeViewDragged(int x, int y) diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 2fbf4720c..b0a281df7 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -27,7 +27,6 @@ #include #include "curveview.h" -#include "node/input.h" #include "widget/nodeparamview/nodeparamviewkeyframecontrol.h" #include "widget/nodeparamview/nodeparamviewwidgetbridge.h" #include "widget/nodetreeview/nodetreeview.h" @@ -81,9 +80,9 @@ private: void ConnectNode(Node* node, bool connect); - void ConnectInput(NodeInput* input, bool connect); + void ConnectInput(Node* node, const QString& input, bool connect); - QHash keyframe_colors_; + QHash keyframe_colors_; NodeTreeView* tree_view_; @@ -108,15 +107,15 @@ private slots: void NodeEnabledChanged(Node* n, bool e); - void InputEnabledChanged(NodeInput* i, int element, int track, bool e); + void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e); void AddKeyframe(NodeKeyframe* key); void RemoveKeyframe(NodeKeyframe* key); - void InputSelectionChanged(NodeInput* input, int element, int track); + void InputSelectionChanged(const NodeKeyframeTrackReference& ref); - void InputDoubleClicked(NodeInput* input, int element, int track); + void InputDoubleClicked(const NodeKeyframeTrackReference& ref); void KeyframeViewDragged(int x, int y); diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index 67dfca608..ae3993d63 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -31,14 +31,14 @@ KeyframeView::KeyframeView(QWidget *parent) : setAlignment(Qt::AlignLeft | Qt::AlignTop); } -void KeyframeView::SetElementY(const NodeConnectable::InputConnection &c, int y) +void KeyframeView::SetElementY(const NodeInput &c, int y) { qreal scene_y = mapToScene(mapFromGlobal(QPoint(0, y))).y(); element_y_.insert(c, scene_y); for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == c.input && it.key()->element() == c.element) { + if (it.key()->key_track_ref().input() == c) { it.value()->SetOverrideY(scene_y); } } @@ -60,7 +60,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect) KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = super::AddKeyframe(key); - item->SetOverrideY(element_y_.value({key->parent(), key->element()})); + item->SetOverrideY(element_y_.value(key->key_track_ref().input())); return item; } diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 37a378244..ec9121b12 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -36,7 +36,7 @@ public: max_scroll_ = i; } - void SetElementY(const Node::InputConnection& c, int y); + void SetElementY(const NodeInput& c, int y); protected: virtual void wheelEvent(QWheelEvent* event) override; @@ -47,7 +47,7 @@ public slots: virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; private: - QHash element_y_; + QHash element_y_; int max_scroll_; diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index d8e70429a..92e8304d2 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -74,35 +74,36 @@ void KeyframeViewBase::DeleteSelected() void KeyframeViewBase::AddKeyframesOfNode(Node *n) { - foreach (NodeInput* i, n->inputs()) { - AddKeyframesOfInput(i); + foreach (const QString& i, n->inputs()) { + AddKeyframesOfInput(n, i); } } -void KeyframeViewBase::AddKeyframesOfInput(NodeInput *input) +void KeyframeViewBase::AddKeyframesOfInput(Node* n, const QString& input) { - if (!input->IsKeyframable()) { + if (!n->IsInputKeyframable(input)) { return; } - for (int i=-1; iArraySize(); i++) { - AddKeyframesOfElement(input, i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& tracks = input->GetKeyframeTracks(element); + const QVector& tracks = input.node()->GetKeyframeTracks(input); for (int i=0; i& tracks = input->GetKeyframeTracks(element); - const NodeKeyframeTrack& t = tracks.at(track); + const QVector& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); + const NodeKeyframeTrack& t = tracks.at(ref.track()); foreach (NodeKeyframe* key, t) { AddKeyframe(key); @@ -111,35 +112,36 @@ void KeyframeViewBase::AddKeyframesOfTrack(NodeInput *input, int element, int tr void KeyframeViewBase::RemoveKeyframesOfNode(Node *n) { - foreach (NodeInput* i, n->inputs()) { - RemoveKeyframesOfInput(i); + foreach (const QString& i, n->inputs()) { + RemoveKeyframesOfInput(n, i); } } -void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *input) +void KeyframeViewBase::RemoveKeyframesOfInput(Node* n, const QString& input) { - if (!input->IsKeyframable()) { + if (!n->IsInputKeyframable(input)) { return; } - for (int i=-1; iArraySize(); i++) { - RemoveKeyframesOfElement(input, i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& tracks = input->GetKeyframeTracks(element); + const QVector& tracks = input.node()->GetKeyframeTracks(input); for (int i=0; i& tracks = input->GetKeyframeTracks(element); - const NodeKeyframeTrack& t = tracks.at(track); + const QVector& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); + const NodeKeyframeTrack& t = tracks.at(ref.track()); foreach (NodeKeyframe* key, t) { RemoveKeyframe(key); @@ -222,7 +224,7 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event) selected_keys_.replace(i, {key, key->x(), - GetAdjustedTime(key->key()->parent()->parent(), GetTimeTarget(), key->key()->time(), false), + GetAdjustedTime(key->key()->parent(), GetTimeTarget(), key->key()->time(), false), key->key()->value().toDouble()}); } } @@ -286,18 +288,17 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) if (IsYAxisEnabled()) { foreach (const KeyframeItemAndTime& keypair, selected_keys_) { - NodeInput* input = keypair.key->key()->parent(); - QList properties = input->dynamicPropertyNames(); - + Node* node = keypair.key->key()->parent(); + const QString& input = keypair.key->key()->input(); double new_val = keypair.value - mouse_diff_scaled.y(); double limited = new_val; - if (properties.contains("min")) { - limited = qMax(limited, input->property("min").toDouble()); + if (node->HasInputProperty(input, QStringLiteral("min"))) { + limited = qMax(limited, node->GetInputProperty(input, QStringLiteral("min")).toDouble()); } - if (properties.contains("max")) { - limited = qMin(limited, input->property("max").toDouble()); + if (node->HasInputProperty(input, QStringLiteral("max"))) { + limited = qMin(limited, node->GetInputProperty(input, QStringLiteral("max")).toDouble()); } if (limited != new_val) { @@ -305,15 +306,16 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) } } - NodeInput* initial_drag_input = initial_drag_item_->key()->parent(); - if (initial_drag_input->dynamicPropertyNames().contains("view")) { - display_type = static_cast(initial_drag_input->property("view").toInt()); + Node* initial_drag_input = initial_drag_item_->key()->parent(); + const QString& initial_drag_input_id = initial_drag_item_->key()->input(); + if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) { + display_type = static_cast(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt()); } } foreach (const KeyframeItemAndTime& keypair, selected_keys_) { rational node_time = GetAdjustedTime(GetTimeTarget(), - keypair.key->key()->parent()->parent(), + keypair.key->key()->parent(), CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()), true); @@ -590,7 +592,7 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors() rational key_time = key_item->key()->time(); - QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_time, key_item->key()->element()); + QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_item->key()->input(), key_time, key_item->key()->element()); foreach (NodeKeyframe* k, keys) { if (k == key_item->key()) { diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index b8f4c769a..2c2465589 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -42,19 +42,19 @@ public: void AddKeyframesOfNode(Node* n); - void AddKeyframesOfInput(NodeInput* input); + void AddKeyframesOfInput(Node *n, const QString &input); - void AddKeyframesOfElement(NodeInput* input, int element); + void AddKeyframesOfElement(const NodeInput &input); - void AddKeyframesOfTrack(NodeInput* input, int element, int track); + void AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref); void RemoveKeyframesOfNode(Node* n); - void RemoveKeyframesOfInput(NodeInput* input); + void RemoveKeyframesOfInput(Node *n, const QString &input); - void RemoveKeyframesOfElement(NodeInput* input, int element); + void RemoveKeyframesOfElement(const NodeInput &input); - void RemoveKeyframesOfTrack(NodeInput* input, int element, int track); + void RemoveKeyframesOfTrack(const NodeKeyframeTrackReference &ref); void SelectAll(); diff --git a/app/widget/keyframeview/keyframeviewitem.cpp b/app/widget/keyframeview/keyframeviewitem.cpp index a52b815b8..6d1ae9fe1 100644 --- a/app/widget/keyframeview/keyframeviewitem.cpp +++ b/app/widget/keyframeview/keyframeviewitem.cpp @@ -26,7 +26,6 @@ #include #include "common/qtutils.h" -#include "node/input.h" namespace olive { @@ -117,7 +116,7 @@ void KeyframeViewItem::TimeTargetChangedEvent(Node *) void KeyframeViewItem::UpdatePos() { - rational adjusted = GetAdjustedTime(key_->parent()->parent(), GetTimeTarget(), key_->time(), false); + rational adjusted = GetAdjustedTime(key_->parent(), GetTimeTarget(), key_->time(), false); setPos(adjusted.toDouble() * scale_, vert_center_); } diff --git a/app/widget/keyframeview/keyframeviewundo.cpp b/app/widget/keyframeview/keyframeviewundo.cpp index 279186f44..aa6d782d6 100644 --- a/app/widget/keyframeview/keyframeviewundo.cpp +++ b/app/widget/keyframeview/keyframeviewundo.cpp @@ -20,7 +20,6 @@ #include "keyframeviewundo.h" -#include "node/input.h" #include "node/node.h" #include "project/item/sequence/sequence.h" @@ -35,7 +34,7 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe:: Project *KeyframeSetTypeCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void KeyframeSetTypeCommand::redo() @@ -66,7 +65,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, Project *KeyframeSetBezierControlPoint::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void KeyframeSetBezierControlPoint::redo() diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index cb7f1c215..d718973e6 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -291,12 +291,8 @@ void NodeParamView::AddNode(Node *n) item->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); item->SetExpanded(node_expanded_state_.value(n, true)); - foreach (NodeInput* input, n->inputs()) { - if (input->IsKeyframable()) { - connect(input, &NodeInput::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - connect(input, &NodeInput::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - } + connect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); + connect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); @@ -326,12 +322,8 @@ void NodeParamView::RemoveNode(Node *n) { keyframe_view_->RemoveKeyframesOfNode(n); - foreach (NodeInput* input, n->inputs()) { - if (input->IsKeyframable()) { - disconnect(input, &NodeInput::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - disconnect(input, &NodeInput::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - } + disconnect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); + disconnect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); delete items_.take(n); @@ -414,9 +406,12 @@ void NodeParamView::KeyframeViewDragged(int x, int y) void NodeParamView::UpdateElementY() { for (auto it=items_.cbegin(); it!=items_.cend(); it++) { - foreach (NodeInput* input, it.key()->inputs()) { - for (int i=-1; iArraySize(); i++) { - Node::InputConnection ic = {input, i}; + foreach (const QString& input, it.key()->inputs()) { + int arr_sz = it.key()->InputArraySize(input); + + for (int i=-1; iGetElementY(ic); keyframe_view_->SetElementY(ic, y); } diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp index 412c54747..87a925405 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -20,22 +20,26 @@ #include "nodeparamviewarraywidget.h" +#include #include +#include "node/node.h" + namespace olive { -NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInput *array, QWidget* parent) : +NodeParamViewArrayWidget::NodeParamViewArrayWidget(Node *node, const QString &input, QWidget* parent) : QWidget(parent), - array_(array) + node_(node), + input_(input) { QHBoxLayout* layout = new QHBoxLayout(this); count_lbl_ = new QLabel(); layout->addWidget(count_lbl_); - connect(array_, &NodeInput::ArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); + connect(node_, &Node::InputArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); - UpdateCounter(); + UpdateCounter(input_, node_->InputArraySize(input_)); } void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) @@ -45,9 +49,11 @@ void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) emit DoubleClicked(); } -void NodeParamViewArrayWidget::UpdateCounter() +void NodeParamViewArrayWidget::UpdateCounter(const QString& input, int new_size) { - count_lbl_->setText(tr("%1 element(s)").arg(array_->ArraySize())); + if (input == input_) { + count_lbl_->setText(tr("%1 element(s)").arg(new_size)); + } } NodeParamViewArrayButton::NodeParamViewArrayButton(NodeParamViewArrayButton::Type type, QWidget *parent) : diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h index 19dae47bd..a61f02235 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.h +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -25,7 +25,7 @@ #include #include -#include "node/input.h" +#include "node/param.h" namespace olive { @@ -54,7 +54,7 @@ class NodeParamViewArrayWidget : public QWidget { Q_OBJECT public: - NodeParamViewArrayWidget(NodeInput* array, QWidget* parent = nullptr); + NodeParamViewArrayWidget(Node* node, const QString& input, QWidget* parent = nullptr); signals: void DoubleClicked(); @@ -63,12 +63,14 @@ protected: virtual void mouseDoubleClickEvent(QMouseEvent* event) override; private: - NodeInput* array_; + Node* node_; + + QString input_; QLabel* count_lbl_; private slots: - void UpdateCounter(); + void UpdateCounter(const QString &input, int new_size); }; diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index a6d680ed9..922f51b48 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -30,10 +30,10 @@ namespace olive { -NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int element, QWidget *parent) : +NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, QWidget *parent) : QWidget(parent), input_(input), - element_(element) + connected_node_(nullptr) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" "))); @@ -56,30 +56,38 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int e connected_to_lbl_->setForegroundRole(QPalette::Link); connected_to_lbl_->setFont(link_font); - UpdateConnected(nullptr, element_); + if (input_.IsConnected()) { + InputConnected(input_.GetConnectedOutput(), input_); + } else { + InputDisconnected(NodeOutput(), input_); + } - connect(input_, &NodeInput::InputConnected, this, &NodeParamViewConnectedLabel::UpdateConnected); - connect(input_, &NodeInput::InputDisconnected, this, &NodeParamViewConnectedLabel::UpdateConnected); + connect(input_.node(), &Node::InputConnected, this, &NodeParamViewConnectedLabel::InputConnected); + connect(input_.node(), &Node::InputDisconnected, this, &NodeParamViewConnectedLabel::InputDisconnected); } -void NodeParamViewConnectedLabel::UpdateConnected(Node *src, int element) +void NodeParamViewConnectedLabel::InputConnected(const NodeOutput& output, const NodeInput& input) { - Q_UNUSED(src) - - if (element_ != element) { - // Do nothing + if (input_ != input) { return; } - QString connection_str; + connected_node_ = output; - if (input_->IsConnected(element_)) { - connection_str = input_->GetConnectedNode(element_)->Name(); - } else { - connection_str = tr("Nothing"); + UpdateLabel(); +} + +void NodeParamViewConnectedLabel::InputDisconnected(const NodeOutput &output, const NodeInput &input) +{ + if (input_ != input) { + return; } - connected_to_lbl_->setText(connection_str); + Q_UNUSED(output) + + connected_node_ = NodeOutput(); + + UpdateLabel(); } void NodeParamViewConnectedLabel::ShowLabelContextMenu() @@ -88,7 +96,7 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() QAction* disconnect_action = m.addAction(tr("Disconnect")); connect(disconnect_action, &QAction::triggered, this, [this](){ - Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), input_, element_)); + Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(connected_node_, input_)); }); m.exec(QCursor::pos()); @@ -96,7 +104,22 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() void NodeParamViewConnectedLabel::ConnectionClicked() { - emit RequestSelectNode({input_->GetConnectedNode(element_)}); + if (connected_node_.IsValid()) { + emit RequestSelectNode({connected_node_.node()}); + } +} + +void NodeParamViewConnectedLabel::UpdateLabel() +{ + QString s; + + if (connected_node_.IsValid()) { + s = connected_node_.node()->Name(); + } else { + s = tr("Nothing"); + } + + connected_to_lbl_->setText(s); } } diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index 685fc4721..047c1f749 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -21,7 +21,7 @@ #ifndef NODEPARAMVIEWCONNECTEDLABEL_H #define NODEPARAMVIEWCONNECTEDLABEL_H -#include "node/input.h" +#include "node/param.h" #include "widget/clickablelabel/clickablelabel.h" namespace olive { @@ -29,24 +29,28 @@ namespace olive { class NodeParamViewConnectedLabel : public QWidget { Q_OBJECT public: - NodeParamViewConnectedLabel(NodeInput* input, int element, QWidget* parent = nullptr); + NodeParamViewConnectedLabel(const NodeInput& input, QWidget* parent = nullptr); signals: void RequestSelectNode(const QVector& node); private slots: - void UpdateConnected(Node* src, int element); + void InputConnected(const NodeOutput &output, const NodeInput &input); + + void InputDisconnected(const NodeOutput &output, const NodeInput &input); void ShowLabelContextMenu(); void ConnectionClicked(); private: + void UpdateLabel(); + ClickableLabel* connected_to_lbl_; - NodeInput* input_; + NodeInput input_; - int element_; + NodeOutput connected_node_; }; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 191768fe2..fb1dbd690 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -134,7 +134,7 @@ bool NodeParamViewItem::IsExpanded() const return body_->isVisible(); } -int NodeParamViewItem::GetElementY(const NodeConnectable::InputConnection &c) const +int NodeParamViewItem::GetElementY(const NodeInput &c) const { if (IsExpanded()) { return body_->GetElementY(c); @@ -209,14 +209,12 @@ NodeParamViewItemBody::NodeParamViewItemBody(Node* node, QWidget *parent) : int insert_row = 0; // Create widgets all root level components - for (int i=0; iinputs().size(); i++) { - NodeInput* input = node->inputs().at(i); - - CreateWidgets(root_layout, input, -1, insert_row); + foreach (const QString& input, node->inputs()) { + CreateWidgets(root_layout, node, input, -1, insert_row); insert_row++; - if (input->IsArray()) { + if (node->InputIsArray(input)) { // Insert here QWidget* array_widget = new QWidget(); @@ -225,28 +223,33 @@ NodeParamViewItemBody::NodeParamViewItemBody(Node* node, QWidget *parent) : root_layout->addWidget(array_widget, insert_row, 1, 1, 10); - for (int j=0; jArraySize(); j++) { - CreateWidgets(array_layout, input, j, j); + int arr_sz = node->InputArraySize(input); + for (int j=0; jaddWidget(append_btn, input->ArraySize(), kArrayInsertColumn); + array_layout->addWidget(append_btn, arr_sz, kArrayInsertColumn); array_widget->setVisible(false); - array_ui_.insert(input, {array_widget, input->ArraySize(), append_btn}); + array_ui_.insert({node, input}, {array_widget, arr_sz, append_btn}); insert_row++; - - connect(input, &NodeInput::ArraySizeChanged, this, &NodeParamViewItemBody::InputArraySizeChanged); } } + + connect(node, &Node::InputArraySizeChanged, this, &NodeParamViewItemBody::InputArraySizeChanged); + connect(node, &Node::InputConnected, this, &NodeParamViewItemBody::EdgeChanged); + connect(node, &Node::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged); } -void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, int element, int row) +void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const QString &input, int element, int row) { + NodeInput input_ref(node, input, element); + InputUI ui_objects; // Add descriptor label @@ -255,7 +258,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Label always goes into column 1 (array collapse button goes into 0 if applicable) layout->addWidget(ui_objects.main_label, row, 1); - if (input->IsArray()) { + if (node->InputIsArray(input)) { if (element == -1) { // Create a collapse toggle for expanding/collapsing the array @@ -270,7 +273,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Connect signal to show/hide array params when toggled connect(array_collapse_btn, &CollapseButton::toggled, this, &NodeParamViewItemBody::ArrayCollapseBtnPressed); - array_collapse_buttons_.insert(input, array_collapse_btn); + array_collapse_buttons_.insert({node, input}, array_collapse_btn); } else { @@ -290,7 +293,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, } // Create a widget/input bridge for this input - ui_objects.widget_bridge = new NodeParamViewWidgetBridge(input, element, this); + ui_objects.widget_bridge = new NodeParamViewWidgetBridge(NodeInput(node, input, element), this); connect(ui_objects.widget_bridge, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked, this, &NodeParamViewItemBody::ToggleArrayExpanded); // 0 is for the array collapse button, 1 is for the main label, widgets start at 2 @@ -303,28 +306,25 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, layout->addWidget(w, row, i+widget_start); } - if (input->IsConnectable()) { + if (node->IsInputConnectable(input)) { // Create clickable label used when an input is connected - ui_objects.connected_label = new NodeParamViewConnectedLabel(input, element); + ui_objects.connected_label = new NodeParamViewConnectedLabel(input_ref); connect(ui_objects.connected_label, &NodeParamViewConnectedLabel::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode); layout->addWidget(ui_objects.connected_label, row, widget_start); - - connect(input, &NodeInput::InputConnected, this, &NodeParamViewItemBody::EdgeChanged); - connect(input, &NodeInput::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged); } // Add keyframe control to this layout if parameter is keyframable - if (input->IsKeyframable()) { + if (node->IsInputKeyframable(input)) { ui_objects.key_control = new NodeParamViewKeyframeControl(); - ui_objects.key_control->SetInput(input, element); + ui_objects.key_control->SetInput(input_ref); layout->addWidget(ui_objects.key_control, row, kKeyControlColumn); connect(ui_objects.key_control, &NodeParamViewKeyframeControl::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); } - input_ui_map_.insert(Node::InputConnection(input, element), ui_objects); + input_ui_map_.insert(input_ref, ui_objects); - if (input->IsConnectable()) { - UpdateUIForEdgeConnection(input, element); + if (node->IsInputConnectable(input)) { + UpdateUIForEdgeConnection(input_ref); } } @@ -355,23 +355,23 @@ void NodeParamViewItemBody::SetTime(const rational &time) void NodeParamViewItemBody::Retranslate() { for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { - const Node::InputConnection& ic = i.key(); + const NodeInput& ic = i.key(); - if (ic.input->IsArray() && ic.element >= 0) { + if (ic.IsArray() && ic.element() >= 0) { // Make the label the array index - i.value().main_label->setText(tr("%n:", nullptr, ic.element)); + i.value().main_label->setText(tr("%n:", nullptr, ic.element())); } else { // Set to the input's name - i.value().main_label->setText(tr("%1:").arg(i.key().input->name())); + i.value().main_label->setText(tr("%1:").arg(ic.name())); } } } -int NodeParamViewItemBody::GetElementY(NodeConnectable::InputConnection c) const +int NodeParamViewItemBody::GetElementY(NodeInput c) const { - if (c.input->IsArray() && !array_ui_.value(c.input).widget->isVisible()) { + if (c.IsArray() && !array_ui_.value(c.input_pair()).widget->isVisible()) { // Array is collapsed, so we'll return the Y of its root - c.element = -1; + c.set_element(-1); } // Find its row in the parameters @@ -387,40 +387,40 @@ int NodeParamViewItemBody::GetElementY(NodeConnectable::InputConnection c) const return lbl_center.y(); } -void NodeParamViewItemBody::EdgeChanged(Node* src, int element) +void NodeParamViewItemBody::EdgeChanged(const NodeOutput& output, const NodeInput& input) { - Q_UNUSED(src) + Q_UNUSED(output) - UpdateUIForEdgeConnection(static_cast(sender()), element); + UpdateUIForEdgeConnection(input); } -void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input, int element) +void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input) { // Show/hide bridge widgets - const InputUI& ui_objects = input_ui_map_[{input, element}]; + const InputUI& ui_objects = input_ui_map_[input]; foreach (QWidget* w, ui_objects.widget_bridge->widgets()) { - w->setVisible(!input->IsConnected(element)); + w->setVisible(!input.IsConnected()); } // Show/hide connection label - ui_objects.connected_label->setVisible(input->IsConnected(element)); + ui_objects.connected_label->setVisible(input.IsConnected()); } void NodeParamViewItemBody::ArrayCollapseBtnPressed(bool checked) { - NodeInput* input = array_collapse_buttons_.key(static_cast(sender())); + const NodeInputPair& input = array_collapse_buttons_.key(static_cast(sender())); array_ui_.value(input).widget->setVisible(checked); emit ArrayExpandedChanged(checked); } -void NodeParamViewItemBody::InputArraySizeChanged(int size) +void NodeParamViewItemBody::InputArraySizeChanged(const QString& input, int size) { - NodeInput* input = static_cast(sender()); + Node* node = static_cast(sender()); - ArrayUI& array_ui = array_ui_[input]; + ArrayUI& array_ui = array_ui_[{node, input}]; if (size != array_ui.count) { QGridLayout* grid = static_cast(array_ui.widget->layout()); @@ -430,12 +430,12 @@ void NodeParamViewItemBody::InputArraySizeChanged(int size) grid->addWidget(array_ui.append_btn, size, kArrayInsertColumn); for (int i=array_ui.count; i=size; i--) { // Our UI count is larger than the size, delete - InputUI input_ui = input_ui_map_.take({input, i}); + InputUI input_ui = input_ui_map_.take({node, input, i}); delete input_ui.main_label; qDeleteAll(input_ui.widget_bridge->widgets()); delete input_ui.widget_bridge; @@ -458,7 +458,7 @@ void NodeParamViewItemBody::ArrayAppendClicked() { for (auto it=array_ui_.cbegin(); it!=array_ui_.cend(); it++) { if (it.value().append_btn == sender()) { - it.key()->ArrayAppend(true); + it.key().node->InputArrayAppend(it.key().input, true); break; } } @@ -469,8 +469,8 @@ void NodeParamViewItemBody::ArrayInsertClicked() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().array_insert_btn == sender()) { // Found our input and element - const Node::InputConnection& ic = it.key(); - ic.input->ArrayInsert(ic.element, true); + const NodeInput& ic = it.key(); + ic.node()->InputArrayInsert(ic.input(), ic.element(), true); break; } } @@ -481,8 +481,8 @@ void NodeParamViewItemBody::ArrayRemoveClicked() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().array_remove_btn == sender()) { // Found our input and element - const Node::InputConnection& ic = it.key(); - ic.input->ArrayRemove(ic.element, true); + const NodeInput& ic = it.key(); + ic.node()->InputArrayRemove(ic.input(), ic.element(), true); break; } } @@ -494,7 +494,7 @@ void NodeParamViewItemBody::ToggleArrayExpanded() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().widget_bridge == bridge) { - CollapseButton* b = array_collapse_buttons_.value(it.key().input); + CollapseButton* b = array_collapse_buttons_.value(it.key().input_pair()); b->setChecked(!b->isChecked()); return; } diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 4e07e2df4..a45fbc661 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -81,7 +81,7 @@ public: void Retranslate(); - int GetElementY(NodeConnectable::InputConnection c) const; + int GetElementY(NodeInput c) const; signals: void RequestSetTime(const rational& time); @@ -91,9 +91,9 @@ signals: void ArrayExpandedChanged(bool e); private: - void CreateWidgets(QGridLayout *layout, NodeInput* input, int element, int row_index); + void CreateWidgets(QGridLayout *layout, Node* node, const QString& input, int element, int row_index); - void UpdateUIForEdgeConnection(NodeInput* input, int element); + void UpdateUIForEdgeConnection(const NodeInput &input); struct InputUI { InputUI(); @@ -107,7 +107,7 @@ private: NodeParamViewArrayButton* array_remove_btn; }; - QHash input_ui_map_; + QHash input_ui_map_; struct ArrayUI { QWidget* widget; @@ -115,9 +115,9 @@ private: NodeParamViewArrayButton* append_btn; }; - QHash array_ui_; + QHash array_ui_; - QHash array_collapse_buttons_; + QHash array_collapse_buttons_; /** * @brief The column to place the keyframe controls in @@ -131,11 +131,11 @@ private: static const int kArrayRemoveColumn; private slots: - void EdgeChanged(Node *src, int element); + void EdgeChanged(const NodeOutput &output, const NodeInput &input); void ArrayCollapseBtnPressed(bool checked); - void InputArraySizeChanged(int size); + void InputArraySizeChanged(const QString &input, int size); void ArrayAppendClicked(); @@ -168,7 +168,7 @@ public: update(); } - int GetElementY(const Node::InputConnection& c) const; + int GetElementY(const NodeInput& c) const; public slots: void SetExpanded(bool e); diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp index bfa218d0c..61b3134c7 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp @@ -30,8 +30,7 @@ namespace olive { NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWidget *parent) : - QWidget(parent), - input_(nullptr) + QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); @@ -64,37 +63,36 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWi connect(next_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToNextKey); connect(toggle_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::ToggleKeyframe); connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable); - connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableBtnClicked); // Set defaults - SetInput(nullptr, -1); + SetInput(NodeInput()); ShowButtonsFromKeyframeEnable(false); } -void NodeParamViewKeyframeControl::SetInput(NodeInput *input, int element) +void NodeParamViewKeyframeControl::SetInput(const NodeInput& input) { - if (input_ != nullptr) { - disconnect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked); - disconnect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); - disconnect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); - disconnect(input_, &NodeInput::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); + if (input_.IsValid()) { + disconnect(input_.node(), &Node::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + disconnect(input_.node(), &Node::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); + disconnect(input_.node(), &Node::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); + disconnect(input_.node(), &Node::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); } input_ = input; - element_ = element; - SetButtonsEnabled(input_); + SetButtonsEnabled(input_.IsValid()); // Pick up keyframing value - enable_key_btn_->setChecked(input_ && input_->IsKeyframing(element_)); + enable_key_btn_->setChecked(input_.IsValid() && input_.IsKeyframing()); // Update buttons UpdateState(); - if (input_ != nullptr) { - connect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked); - connect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); - connect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); - connect(input_, &NodeInput::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); + if (input_.IsValid()) { + connect(input_.node(), &Node::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + connect(input_.node(), &Node::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); + connect(input_.node(), &Node::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); + connect(input_.node(), &Node::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); } } @@ -124,12 +122,12 @@ void NodeParamViewKeyframeControl::SetButtonsEnabled(bool e) rational NodeParamViewKeyframeControl::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); + return GetAdjustedTime(GetTimeTarget(), input_.node(), time_, true); } rational NodeParamViewKeyframeControl::ConvertToViewerTime(const rational &r) const { - return GetAdjustedTime(input_->parent(), GetTimeTarget(), r, false); + return GetAdjustedTime(input_.node(), GetTimeTarget(), r, false); } void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e) @@ -143,34 +141,33 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) { rational node_time = GetCurrentTimeAsNodeTime(); - QVector keys = input_->GetKeyframesAtTime(node_time, element_); + QVector keys = input_.node()->GetKeyframesAtTime(input_, node_time); MultiUndoCommand* command = new MultiUndoCommand(); - int nb_tracks = input_->GetNumberOfKeyframeTracks(); + int nb_tracks = input_.node()->GetNumberOfKeyframeTracks(input_); if (e && keys.isEmpty()) { // Add a keyframe here (one for each track) for (int i=0;iGetValueAtTimeForTrack(node_time, i, element_), - input_->GetBestKeyframeTypeForTime(node_time, i, element_), + input_.node()->GetSplitValueAtTimeOnTrack(input_, node_time, i), + input_.node()->GetBestKeyframeTypeForTimeOnTrack(input_, node_time, i), i, - element_); + input_.element(), + input_.input()); - command->add_child(new NodeParamInsertKeyframeCommand(input_, key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), key)); } } else if (!e && !keys.isEmpty()) { // Remove all keyframes at this time foreach (NodeKeyframe* key, keys) { command->add_child(new NodeParamRemoveKeyframeCommand(key)); - if (input_->GetKeyframeTracks(key->track()).size() == 1) { + if (input_.node()->GetKeyframeTracks(input_).size() == 1) { // If this was the last keyframe on this track, set the standard value to the value at this time too - command->add_child(new NodeParamSetStandardValueCommand(input_, - key->track(), - element_, - input_->GetValueAtTimeForTrack(node_time, key->track(), element_))); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, key->track()), + input_.node()->GetSplitValueAtTimeOnTrack(input_, node_time, key->track()))); } } } @@ -180,25 +177,25 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) void NodeParamViewKeyframeControl::UpdateState() { - if (!input_) { + if (!input_.IsValid()) { return; } - NodeKeyframe* earliest_key = input_->GetEarliestKeyframe(element_); - NodeKeyframe* latest_key = input_->GetLatestKeyframe(element_); + NodeKeyframe* earliest_key = input_.node()->GetEarliestKeyframe(input_); + NodeKeyframe* latest_key = input_.node()->GetLatestKeyframe(input_); rational node_time = GetCurrentTimeAsNodeTime(); prev_key_btn_->setEnabled(earliest_key && node_time > earliest_key->time()); next_key_btn_->setEnabled(latest_key && node_time < latest_key->time()); - toggle_key_btn_->setChecked(input_->HasKeyframeAtTime(node_time, element_)); + toggle_key_btn_->setChecked(input_.node()->HasKeyframeAtTime(input_, node_time)); } void NodeParamViewKeyframeControl::GoToPreviousKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframe* previous_key = input_->GetClosestKeyframeBeforeTime(node_time, element_); + NodeKeyframe* previous_key = input_.node()->GetClosestKeyframeBeforeTime(input_, node_time); if (previous_key) { rational key_time = ConvertToViewerTime(previous_key->time()); @@ -211,7 +208,7 @@ void NodeParamViewKeyframeControl::GoToNextKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframe* next_key = input_->GetClosestKeyframeAfterTime(node_time, element_); + NodeKeyframe* next_key = input_.node()->GetClosestKeyframeAfterTime(input_, node_time); if (next_key) { rational key_time = ConvertToViewerTime(next_key->time()); @@ -220,9 +217,9 @@ void NodeParamViewKeyframeControl::GoToNextKey() } } -void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) +void NodeParamViewKeyframeControl::KeyframeEnableBtnClicked(bool e) { - if (e == input_->IsKeyframing(element_)) { + if (e == input_.IsKeyframing()) { // No-op return; } @@ -231,19 +228,20 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) if (e) { // Enable keyframing - command->add_child(new NodeParamSetKeyframingCommand(input_, element_, true)); + command->add_child(new NodeParamSetKeyframingCommand(input_, true)); // Create one keyframe across all tracks here - const QVector& key_vals = input_->GetSplitStandardValue(element_); + const QVector& key_vals = input_.node()->GetSplitStandardValue(input_); for (int i=0;iadd_child(new NodeParamInsertKeyframeCommand(input_, key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), key)); } } else { // Confirm the user wants to clear all keyframes @@ -253,10 +251,10 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { // Store value at this time, we'll set this as the persistent value later - const QVector& stored_vals = input_->GetSplitValuesAtTime(GetCurrentTimeAsNodeTime(), element_); + const QVector& stored_vals = input_.node()->GetSplitValueAtTime(input_, GetCurrentTimeAsNodeTime()); // Delete all keyframes - foreach (const NodeKeyframeTrack& track, input_->GetKeyframeTracks(element_)) { + foreach (const NodeKeyframeTrack& track, input_.node()->GetKeyframeTracks(input_)) { for (int i=track.size()-1;i>=0;i--) { command->add_child(new NodeParamRemoveKeyframeCommand(track.at(i))); } @@ -264,11 +262,11 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) // Update standard value for (int i=0;iadd_child(new NodeParamSetStandardValueCommand(input_, i, element_, stored_vals.at(i))); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, i), stored_vals.at(i))); } // Disable keyframing - command->add_child(new NodeParamSetKeyframingCommand(input_, element_, false)); + command->add_child(new NodeParamSetKeyframingCommand(input_, false)); } else { // Disable action has effectively been ignored @@ -279,4 +277,11 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) Core::instance()->undo_stack()->pushIfHasChildren(command); } +void NodeParamViewKeyframeControl::KeyframeEnableChanged(const NodeInput &input, bool e) +{ + if (input_ == input) { + enable_key_btn_->setChecked(e); + } +} + } diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h index 581bb865c..93a963b84 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h @@ -24,7 +24,7 @@ #include #include -#include "node/input.h" +#include "node/param.h" #include "widget/timetarget/timetarget.h" namespace olive { @@ -35,17 +35,12 @@ class NodeParamViewKeyframeControl : public QWidget, public TimeTargetObject public: NodeParamViewKeyframeControl(bool right_align = true, QWidget* parent = nullptr); - NodeInput* GetConnectedInput() const + const NodeInput& GetConnectedInput() const { return input_; } - int GetConnectedElement() const - { - return element_; - } - - void SetInput(NodeInput* input, int element); + void SetInput(const NodeInput& input); void SetTime(const rational& time); @@ -66,8 +61,7 @@ private: QPushButton* next_key_btn_; QPushButton* enable_key_btn_; - NodeInput* input_; - int element_; + NodeInput input_; rational time_; @@ -82,7 +76,9 @@ private slots: void GoToNextKey(); - void KeyframeEnableChanged(bool e); + void KeyframeEnableBtnClicked(bool e); + + void KeyframeEnableChanged(const NodeInput& input, bool e); }; diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 89ca47444..99d45fd07 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -25,27 +25,26 @@ namespace olive { -NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, int element, bool setting) : +NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(const NodeInput &input, bool setting) : input_(input), - setting_(setting), - element_(element) + setting_(setting) { - Q_ASSERT(setting != input_->IsKeyframing()); + Q_ASSERT(setting != input_.IsKeyframing()); } Project *NodeParamSetKeyframingCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_.node()->parent()->project(); } void NodeParamSetKeyframingCommand::redo() { - input_->SetIsKeyframing(setting_, element_); + input_.node()->SetInputIsKeyframing(input_, setting_); } void NodeParamSetKeyframingCommand::undo() { - input_->SetIsKeyframing(!setting_, element_); + input_.node()->SetInputIsKeyframing(input_, !setting_); } NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value) : @@ -65,7 +64,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void NodeParamSetKeyframeValueCommand::redo() @@ -78,16 +77,17 @@ void NodeParamSetKeyframeValueCommand::undo() key_->set_value(old_value_); } -NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe) : - input_(input), +NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(Node* node, NodeKeyframe* keyframe) : + input_(node), keyframe_(keyframe) { - keyframe_->setParent(&memory_manager_); + // Take ownership of the keyframe + undo(); } Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_->parent()->project(); } void NodeParamInsertKeyframeCommand::redo() @@ -108,7 +108,7 @@ NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* key Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_->parent()->project(); } void NodeParamRemoveKeyframeCommand::redo() @@ -138,7 +138,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* k Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void NodeParamSetKeyframeTimeCommand::redo() @@ -151,19 +151,15 @@ void NodeParamSetKeyframeTimeCommand::undo() key_->set_time(old_time_); } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value) : - input_(input), - element_(element), - track_(track), - old_value_(input_->GetStandardValue(element_)), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &value) : + ref_(input), + old_value_(ref_.input().node()->GetStandardValue(ref_.input())), new_value_(value) { } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value) : - input_(input), - element_(element), - track_(track), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &new_value, const QVariant &old_value) : + ref_(input), old_value_(old_value), new_value_(new_value) { @@ -171,22 +167,22 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in Project *NodeParamSetStandardValueCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return ref_.input().node()->parent()->project(); } void NodeParamSetStandardValueCommand::redo() { - input_->SetStandardValueOnTrack(new_value_, track_, element_); + ref_.input().node()->SetSplitStandardValueOnTrack(ref_, new_value_); } void NodeParamSetStandardValueCommand::undo() { - input_->SetStandardValueOnTrack(old_value_, track_, element_); + ref_.input().node()->SetSplitStandardValueOnTrack(ref_, old_value_); } Project *NodeParamArrayInsertCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_.node()->parent()->project(); } } diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index 8f28600eb..7191369f8 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -21,7 +21,9 @@ #ifndef NODEPARAMVIEWUNDO_H #define NODEPARAMVIEWUNDO_H -#include "node/input.h" +#include "node/keyframe.h" +#include "node/node.h" +#include "node/param.h" #include "undo/undocommand.h" namespace olive { @@ -29,7 +31,7 @@ namespace olive { class NodeParamSetKeyframingCommand : public UndoCommand { public: - NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting); + NodeParamSetKeyframingCommand(const NodeInput& input, bool setting); virtual Project* GetRelevantProject() const override; @@ -37,16 +39,15 @@ public: virtual void undo() override; private: - NodeInput* input_; + NodeInput input_; bool setting_; - int element_; }; class NodeParamInsertKeyframeCommand : public UndoCommand { public: - NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe); + NodeParamInsertKeyframeCommand(Node *node, NodeKeyframe* keyframe); virtual Project* GetRelevantProject() const override; @@ -54,7 +55,7 @@ public: virtual void undo() override; private: - NodeInput* input_; + Node* input_; NodeKeyframe* keyframe_; @@ -73,7 +74,7 @@ public: virtual void undo() override; private: - NodeInput* input_; + Node* input_; NodeKeyframe* keyframe_; @@ -122,8 +123,8 @@ private: class NodeParamSetStandardValueCommand : public UndoCommand { public: - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value); - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& new_value, const QVariant& old_value); virtual Project* GetRelevantProject() const override; @@ -131,9 +132,7 @@ public: virtual void undo() override; private: - NodeInput* input_; - int element_; - int track_; + NodeKeyframeTrackReference ref_; QVariant old_value_; QVariant new_value_; @@ -143,7 +142,7 @@ private: class NodeParamArrayInsertCommand : public UndoCommand { public: - NodeParamArrayInsertCommand(NodeInput* input, int index) : + NodeParamArrayInsertCommand(const NodeInput& input, int index) : input_(input), index_(index) { @@ -153,16 +152,16 @@ public: virtual void redo() override { - input_->ArrayInsert(index_); + input_.node()->InputArrayInsert(input_.input(), index_); } virtual void undo() override { - input_->ArrayRemove(index_); + input_.node()->InputArrayRemove(input_.input(), index_); } private: - NodeInput* input_; + NodeInput input_; int index_; }; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index f8e70ded4..f402c67d6 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -40,43 +40,37 @@ namespace olive { -NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, int element, QObject *parent) : +NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(const NodeInput &input, QObject *parent) : QObject(parent), - input_(input), - element_(element) + input_(input) { CreateWidgets(); - connect(input_, &NodeInput::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged); - connect(input_, &NodeInput::PropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged); + connect(input_.node(), &Node::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged); + connect(input_.node(), &Node::InputPropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged); } void NodeParamViewWidgetBridge::SetTime(const rational &time) { time_ = time; - if (input_) { + if (input_.IsValid()) { UpdateWidgetValues(); } } -const QList &NodeParamViewWidgetBridge::widgets() const -{ - return widgets_; -} - void NodeParamViewWidgetBridge::CreateWidgets() { - if (input_->IsArray() && element_ == -1) { + if (input_.IsArray() && input_.element() == -1) { - NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_); + NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_.node(), input_.input()); connect(w, &NodeParamViewArrayWidget::DoubleClicked, this, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked); widgets_.append(w); } else { // We assume the first data type is the "primary" type - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -89,38 +83,34 @@ void NodeParamViewWidgetBridge::CreateWidgets() break; case NodeValue::kInt: { - IntegerSlider* slider = new IntegerSlider(); - slider->SetDefaultValue(input_->GetDefaultValue()); - slider->SetLadderElementCount(2); - widgets_.append(slider); - connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + CreateSliders(1); break; } case NodeValue::kFloat: { - CreateSliders(1); + CreateSliders(1); break; } case NodeValue::kVec2: { - CreateSliders(2); + CreateSliders(2); break; } case NodeValue::kVec3: { - CreateSliders(3); + CreateSliders(3); break; } case NodeValue::kVec4: { - CreateSliders(4); + CreateSliders(4); break; } case NodeValue::kCombo: { QComboBox* combobox = new QComboBox(); - QStringList items = input_->get_combobox_strings(); + QStringList items = input_.GetComboBoxStrings(); foreach (const QString& s, items) { combobox->addItem(s); } @@ -135,7 +125,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kColor: { // NOTE: Very convoluted way to get back to the project's color manager - ColorButton* color_button = new ColorButton(input_->parent()->parent()->project()->color_manager()); + ColorButton* color_button = new ColorButton(input_.node()->parent()->project()->color_manager()); widgets_.append(color_button); connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; @@ -164,7 +154,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kFootage: { FootageComboBox* footage_combobox = new FootageComboBox(); - footage_combobox->SetRoot(input_->parent()->parent()->project()->root()); + footage_combobox->SetRoot(input_.node()->parent()->project()->root()); connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); @@ -175,8 +165,9 @@ void NodeParamViewWidgetBridge::CreateWidgets() } // Check all properties - foreach (const QByteArray& key, input_->dynamicPropertyNames()) { - PropertyChanged(key, input_->property(key)); + auto input_properties = input_.node()->GetInputProperties(input_.input()); + for (auto it=input_properties.cbegin(); it!=input_properties.cend(); it++) { + PropertyChanged(input_.input(), it.key(), it.value()); } UpdateWidgetValues(); @@ -202,8 +193,8 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int { rational node_time = GetCurrentTimeAsNodeTime(); - if (input_->IsKeyframing(element_)) { - NodeKeyframe* existing_key = input_->GetKeyframeAtTimeOnTrack(node_time, track, element_); + if (input_.IsKeyframing()) { + NodeKeyframe* existing_key = input_.GetKeyframeAtTimeOnTrack(node_time, track); if (existing_key) { command->add_child(new NodeParamSetKeyframeValueCommand(existing_key, value)); @@ -211,14 +202,15 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int // No existing key, create a new one NodeKeyframe* new_key = new NodeKeyframe(node_time, value, - input_->GetBestKeyframeTypeForTime(node_time, track, element_), + input_.node()->GetBestKeyframeTypeForTimeOnTrack(NodeKeyframeTrackReference(input_, track), node_time), track, - element_); + input_.element(), + input_.input()); - command->add_child(new NodeParamInsertKeyframeCommand(input_, new_key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), new_key)); } } else { - command->add_child(new NodeParamSetStandardValueCommand(input_, track, element_, value)); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, track), value)); } } @@ -232,7 +224,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant // While we're dragging, we block the input's normal signalling and create our own if (!dragger_.IsStarted()) { - dragger_.Start(input_, node_time, slider_track, element_); + dragger_.Start(NodeKeyframeTrackReference(input_, slider_track), node_time); } dragger_.Drag(value); @@ -253,7 +245,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant void NodeParamViewWidgetBridge::WidgetCallback() { - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -269,7 +261,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a IntegerSlider IntegerSlider* slider = static_cast(sender()); - int64_t offset = input_->property("offset").toLongLong(); + int64_t offset = input_.GetProperty("offset").toLongLong(); ProcessSlider(slider, QVariant::fromValue(slider->GetValue() - offset)); break; @@ -279,7 +271,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - double offset = input_->property("offset").toDouble(); + double offset = input_.GetProperty("offset").toDouble(); ProcessSlider(slider, slider->GetValue() - offset); break; @@ -289,7 +281,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector2D offset = input_->property("offset").value(); + QVector2D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -299,7 +291,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector3D offset = input_->property("offset").value(); + QVector3D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -309,7 +301,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector4D offset = input_->property("offset").value(); + QVector4D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -329,12 +321,13 @@ void NodeParamViewWidgetBridge::WidgetCallback() SetInputValueInternal(c.blue(), 2, command); SetInputValueInternal(c.alpha(), 3, command); - input_->blockSignals(true); - input_->setProperty("col_input", c.color_input()); - input_->setProperty("col_display", c.color_output().display()); - input_->setProperty("col_view", c.color_output().view()); - input_->setProperty("col_look", c.color_output().look()); - input_->blockSignals(false); + Node* n = input_.node(); + n->blockSignals(true); + n->SetInputProperty(input_.input(), QStringLiteral("col_input"), c.color_input()); + n->SetInputProperty(input_.input(), QStringLiteral("col_display"), c.color_output().display()); + n->SetInputProperty(input_.input(), QStringLiteral("col_view"), c.color_output().view()); + n->SetInputProperty(input_.input(), QStringLiteral("col_look"), c.color_output().look()); + n->blockSignals(false); Core::instance()->undo_stack()->pushIfHasChildren(command); break; @@ -382,27 +375,28 @@ void NodeParamViewWidgetBridge::WidgetCallback() } } +template void NodeParamViewWidgetBridge::CreateSliders(int count) { for (int i=0;iSetDefaultValue(input_->GetDefaultValueForTrack(i)); + T* fs = new T(); + fs->SetDefaultValue(input_.GetSplitDefaultValueForTrack(i)); fs->SetLadderElementCount(2); widgets_.append(fs); - connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + connect(fs, &T::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); } } void NodeParamViewWidgetBridge::UpdateWidgetValues() { - if (input_->IsArray() && element_ == -1) { + if (input_.IsArray() && input_.element() == -1) { return; } rational node_time = GetCurrentTimeAsNodeTime(); // We assume the first data type is the "primary" type - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -415,22 +409,22 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() break; case NodeValue::kInt: { - int64_t offset = input_->property("offset").toLongLong(); + int64_t offset = input_.GetProperty("offset").toLongLong(); - static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toLongLong() + offset); + static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).toLongLong() + offset); break; } case NodeValue::kFloat: { - double offset = input_->property("offset").toDouble(); + double offset = input_.GetProperty("offset").toDouble(); - static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toDouble() + offset); + static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).toDouble() + offset); break; } case NodeValue::kVec2: { - QVector2D vec2 = input_->GetValueAtTime(node_time, element_).value(); - QVector2D offset = input_->property("offset").value(); + QVector2D vec2 = input_.GetValueAtTime(node_time).value(); + QVector2D offset = input_.GetProperty("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec2.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec2.y() + offset.y())); @@ -438,8 +432,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() } case NodeValue::kVec3: { - QVector3D vec3 = input_->GetValueAtTime(node_time, element_).value(); - QVector3D offset = input_->property("offset").value(); + QVector3D vec3 = input_.GetValueAtTime(node_time).value(); + QVector3D offset = input_.GetProperty("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec3.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec3.y() + offset.y())); @@ -448,8 +442,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() } case NodeValue::kVec4: { - QVector4D vec4 = input_->GetValueAtTime(node_time, element_).value(); - QVector4D offset = input_->property("offset").value(); + QVector4D vec4 = input_.GetValueAtTime(node_time).value(); + QVector4D offset = input_.GetProperty("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec4.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec4.y() + offset.y())); @@ -462,13 +456,13 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() break; case NodeValue::kColor: { - ManagedColor mc = input_->GetValueAtTime(node_time, element_).value(); + ManagedColor mc = input_.GetValueAtTime(node_time).value(); - mc.set_color_input(input_->property("col_input").toString()); + mc.set_color_input(input_.GetProperty("col_input").toString()); - QString d = input_->property("col_display").toString(); - QString v = input_->property("col_view").toString(); - QString l = input_->property("col_look").toString(); + QString d = input_.GetProperty("col_display").toString(); + QString v = input_.GetProperty("col_view").toString(); + QString l = input_.GetProperty("col_look").toString(); mc.set_color_output(ColorTransform(d, v, l)); @@ -478,17 +472,17 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() case NodeValue::kText: { NodeParamViewRichText* e = static_cast(widgets_.first()); - e->setTextPreservingCursor(input_->GetValueAtTime(node_time, element_).toString()); + e->setTextPreservingCursor(input_.GetValueAtTime(node_time).toString()); break; } case NodeValue::kBoolean: - static_cast(widgets_.first())->setChecked(input_->GetValueAtTime(node_time, element_).toBool()); + static_cast(widgets_.first())->setChecked(input_.GetValueAtTime(node_time).toBool()); break; case NodeValue::kFont: { QFontComboBox* fc = static_cast(widgets_.first()); fc->blockSignals(true); - fc->setCurrentFont(input_->GetValueAtTime(node_time, element_).toString()); + fc->setCurrentFont(input_.GetValueAtTime(node_time).toString()); fc->blockSignals(false); break; } @@ -496,24 +490,24 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() { QComboBox* cb = static_cast(widgets_.first()); cb->blockSignals(true); - cb->setCurrentIndex(input_->GetValueAtTime(node_time, element_).toInt()); + cb->setCurrentIndex(input_.GetValueAtTime(node_time).toInt()); cb->blockSignals(false); break; } case NodeValue::kFootage: - static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_->GetValueAtTime(node_time, element_))); + static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_.GetValueAtTime(node_time))); break; } } rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); + return GetAdjustedTime(GetTimeTarget(), input_.node(), time_, true); } -void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range, int element) +void NodeParamViewWidgetBridge::InputValueChanged(const NodeInput &input, const TimeRange &range) { - if (element == element_ + if (input_ == input && !dragger_.IsStarted() && range.in() <= time_ && range.out() >= time_) { // We'll need to update the widgets because the values have changed on our current time @@ -521,10 +515,16 @@ void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range, int el } } -void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVariant &value) +void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QString &key, const QVariant &value) { + if (input != input_.input()) { + return; + } + + NodeValue::Type data_type = input_.GetDataType(); + // Parameters for vectors only - if (NodeValue::type_is_vector(input_->GetDataType())) { + if (NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("disablex")) { static_cast(widgets_.at(0))->setEnabled(!value.toBool()); } else if (key == QStringLiteral("disabley")) { @@ -537,9 +537,9 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for integers, floats, and vectors - if (NodeValue::type_is_numeric(input_->GetDataType()) || NodeValue::type_is_vector(input_->GetDataType())) { + if (NodeValue::type_is_numeric(data_type) || NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("min")) { - switch (input_->GetDataType()) { + switch (data_type) { case NodeValue::kInt: static_cast(widgets_.first())->SetMinimum(value.value()); break; @@ -577,7 +577,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria break; } } else if (key == QStringLiteral("max")) { - switch (input_->GetDataType()) { + switch (data_type) { case NodeValue::kInt: static_cast(widgets_.first())->SetMaximum(value.value()); break; @@ -620,7 +620,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // ComboBox strings changing - if (input_->GetDataType() & NodeValue::kCombo) { + if (data_type & NodeValue::kCombo) { QComboBox* cb = static_cast(widgets_.first()); int old_index = cb->currentIndex(); @@ -630,7 +630,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria cb->clear(); - QStringList items = input_->get_combobox_strings(); + QStringList items = input_.GetComboBoxStrings(); foreach (const QString& s, items) { if (s.isEmpty()) { cb->insertSeparator(cb->count()); @@ -651,7 +651,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for floats and vectors only - if (input_->GetDataType() == NodeValue::kFloat || NodeValue::type_is_vector(input_->GetDataType())) { + if (data_type == NodeValue::kFloat || NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("view")) { FloatSlider::DisplayType display_type = static_cast(value.toInt()); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index b3a1c0736..7b191f143 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -23,7 +23,6 @@ #include -#include "node/input.h" #include "node/inputdragger.h" #include "widget/slider/sliderbase.h" #include "widget/timetarget/timetarget.h" @@ -41,11 +40,14 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject { Q_OBJECT public: - NodeParamViewWidgetBridge(NodeInput* input, int element, QObject* parent); + NodeParamViewWidgetBridge(const NodeInput& input, QObject* parent); void SetTime(const rational& time); - const QList& widgets() const; + const QVector& widgets() const + { + return widgets_; + } signals: void ArrayWidgetDoubleClicked(); @@ -59,17 +61,16 @@ private: void ProcessSlider(SliderBase* slider, const QVariant& value); + template void CreateSliders(int count); void UpdateWidgetValues(); rational GetCurrentTimeAsNodeTime() const; - NodeInput* input_; + NodeInput input_; - int element_; - - QList widgets_; + QVector widgets_; rational time_; @@ -80,9 +81,9 @@ private: private slots: void WidgetCallback(); - void InputValueChanged(const TimeRange& range, int element); + void InputValueChanged(const NodeInput& input, const TimeRange& range); - void PropertyChanged(const QString& key, const QVariant& value); + void PropertyChanged(const QString &input, const QString& key, const QVariant& value); }; diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index 7b58ad2ec..d67f23f8c 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -87,8 +87,7 @@ void NodeTableView::SetTime(const rational &time) for (l=db.begin(); l!=db.end(); l++) { const NodeValueTable& table = l.value(); - NodeInput* input = node->GetInputWithID(l.key()); - if (!input) { + if (!node->HasInputWithID(l.key())) { // Filters out table entries that aren't inputs (like "global") continue; } @@ -98,7 +97,7 @@ void NodeTableView::SetTime(const rational &time) for (int j=0; jchildCount(); j++) { QTreeWidgetItem* compare = item->child(j); - if (compare->data(0, Qt::UserRole).toString() == input->id()) { + if (compare->data(0, Qt::UserRole).toString() == l.key()) { input_item = compare; break; } @@ -106,8 +105,8 @@ void NodeTableView::SetTime(const rational &time) if (!input_item) { input_item = new QTreeWidgetItem(); - input_item->setText(0, input->name()); - input_item->setData(0, Qt::UserRole, input->id()); + input_item->setText(0, node->GetInputName(l.key())); + input_item->setData(0, Qt::UserRole, l.key()); input_item->setFirstColumnSpanned(true); item->addChild(input_item); } @@ -151,7 +150,7 @@ void NodeTableView::SetTime(const rational &time) } default: { - QVector split_values = NodeValue::split_normal_value_into_track_values(input->GetDataType(), value.data()); + QVector split_values = NodeValue::split_normal_value_into_track_values(node->GetInputDataType(l.key()), value.data()); for (int k=0;ksetText(2 + k, NodeValue::ValueToString(value.type(), split_values.at(k), true)); } diff --git a/app/widget/nodetableview/nodetablewidget.h b/app/widget/nodetableview/nodetablewidget.h index a68db7371..59e11d5af 100644 --- a/app/widget/nodetableview/nodetablewidget.h +++ b/app/widget/nodetableview/nodetablewidget.h @@ -42,7 +42,7 @@ public: } protected: - virtual void TimeChangedEvent(const int64_t& ts) override + virtual void TimeChangedEvent(const int64_t&) override { UpdateView(); } diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index 5772150f8..33a881ae1 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -18,12 +18,12 @@ bool NodeTreeView::IsNodeEnabled(Node *n) const return !disabled_nodes_.contains(n); } -bool NodeTreeView::IsInputEnabled(NodeInput *i, int element, int track) const +bool NodeTreeView::IsInputEnabled(const NodeKeyframeTrackReference &ref) const { - return !disabled_inputs_.contains({i, element, track}); + return !disabled_inputs_.contains(ref); } -void NodeTreeView::SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference &ref, const QColor &color) +void NodeTreeView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert into hashmap keyframe_colors_.insert(ref, color); @@ -47,21 +47,24 @@ void NodeTreeView::SetNodes(const QVector &nodes) node_item->setText(0, n->Name()); node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked); node_item->setData(0, kItemType, kItemTypeNode); - node_item->setData(0, kItemPointer, reinterpret_cast(n)); + node_item->setData(0, kItemNodePointer, Node::PtrToValue(n)); - foreach (NodeInput* input, n->inputs()) { - if (only_show_keyframable_ && !input->IsKeyframable()) { + foreach (const QString& input, n->inputs()) { + if (only_show_keyframable_ && !n->IsInputKeyframable(input)) { continue; } QTreeWidgetItem* input_item = nullptr; - for (int i=-1; iArraySize(); i++) { - const QVector& key_tracks = input->GetKeyframeTracks(i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& key_tracks = n->GetKeyframeTracks(input_ref); int this_element_track; - if (show_keyframe_tracks_as_rows_ && (key_tracks.size() == 1 || (i == -1 && input->IsArray()))) { + if (show_keyframe_tracks_as_rows_ + && (key_tracks.size() == 1 || (i == -1 && n->InputIsArray(input)))) { this_element_track = 0; } else { this_element_track = -1; @@ -70,14 +73,14 @@ void NodeTreeView::SetNodes(const QVector &nodes) QTreeWidgetItem* element_item; if (input_item) { - element_item = CreateItem(input_item, input, i, this_element_track); + element_item = CreateItem(input_item, NodeKeyframeTrackReference(input_ref, this_element_track)); } else { - input_item = CreateItem(node_item, input, i, this_element_track); + input_item = CreateItem(node_item, NodeKeyframeTrackReference(input_ref, this_element_track)); element_item = input_item; } - if (show_keyframe_tracks_as_rows_ && key_tracks.size() > 1 && (!input->IsArray() || i >= 0)) { - CreateItemsForTracks(element_item, input, i, key_tracks.size()); + if (show_keyframe_tracks_as_rows_ && key_tracks.size() > 1 && (!n->InputIsArray(input) || i >= 0)) { + CreateItemsForTracks(element_item, input_ref, key_tracks.size()); } } @@ -105,10 +108,10 @@ void NodeTreeView::mouseDoubleClickEvent(QMouseEvent *e) { QTreeWidget::mouseDoubleClickEvent(e); - NodeInput::KeyframeTrackReference ref = GetSelectedInput(); + NodeKeyframeTrackReference ref = GetSelectedInput(); - if (ref.input) { - emit InputDoubleClicked(ref.input, ref.element, ref.track); + if (ref.input().IsValid()) { + emit InputDoubleClicked(ref); } } @@ -117,36 +120,32 @@ void NodeTreeView::Retranslate() setHeaderLabel(tr("Nodes")); } -NodeInput::KeyframeTrackReference NodeTreeView::GetSelectedInput() +NodeKeyframeTrackReference NodeTreeView::GetSelectedInput() { QList sel = selectedItems(); - NodeInput* selected_input = nullptr; - int selected_element = -1; - int selected_track = -1; + NodeKeyframeTrackReference selected_ref; if (!sel.isEmpty()) { QTreeWidgetItem* item = sel.first(); if (item->data(0, kItemType).toInt() == kItemTypeInput) { - selected_input = reinterpret_cast(item->data(0, kItemPointer).value()); - selected_element = item->data(0, kItemElement).toInt(); - selected_track = item->data(0, kItemTrack).toInt(); + selected_ref = item->data(0, kItemInputReference).value(); } } - return {selected_input, selected_element, selected_track}; + return selected_ref; } -QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *input, int element, int track) +QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKeyframeTrackReference& ref) { QTreeWidgetItem* input_item = new QTreeWidgetItem(parent); QString item_name; - if (track == -1 || NodeValue::get_number_of_keyframe_tracks(input->GetDataType()) == 1) { - item_name = input->name(); + if (ref.track() == -1 || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1) { + item_name = ref.input().name(); } else { - switch (track) { + switch (ref.track()) { case 0: item_name = tr("X"); break; @@ -160,18 +159,14 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *in item_name = tr("W"); break; default: - item_name = QString::number(track); + item_name = QString::number(ref.track()); } } input_item->setText(0, item_name); - input_item->setCheckState(0, disabled_inputs_.contains({input, element, track}) ? Qt::Unchecked : Qt::Checked); + input_item->setCheckState(0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked); input_item->setData(0, kItemType, kItemTypeInput); - input_item->setData(0, kItemPointer, reinterpret_cast(input)); - input_item->setData(0, kItemElement, element); - input_item->setData(0, kItemTrack, track); - - NodeInput::KeyframeTrackReference ref = {input, element, track}; + input_item->setData(0, kItemInputReference, QVariant::fromValue(ref)); if (keyframe_colors_.contains(ref)) { input_item->setForeground(0, keyframe_colors_.value(ref)); @@ -182,10 +177,10 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *in return input_item; } -void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, NodeInput *input, int element, int track_count) +void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput& input, int track_count) { for (int j=0; jdata(0, kItemType).toInt()) { case kItemTypeNode: { - Node* n = reinterpret_cast(item->data(0, kItemPointer).value()); + Node* n = Node::ValueToPtr(item->data(0, kItemNodePointer)); if (item->checkState(0) == Qt::Checked) { if (disabled_nodes_.contains(n)) { @@ -211,19 +206,16 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) } case kItemTypeInput: { - NodeInput* input = reinterpret_cast(item->data(0, kItemPointer).value()); - int element = item->data(0, kItemElement).toInt(); - int track = item->data(0, kItemTrack).toInt(); - NodeInput::KeyframeTrackReference i = {input, element, track}; + NodeKeyframeTrackReference i = item->data(0, kItemInputReference).value(); if (item->checkState(0) == Qt::Checked) { if (disabled_inputs_.contains(i)) { disabled_inputs_.removeOne(i); - emit InputEnableChanged(input, element, track, true); + emit InputEnableChanged(i, true); } } else if (!disabled_inputs_.contains(i)) { disabled_inputs_.append(i); - emit InputEnableChanged(input, element, track, false); + emit InputEnableChanged(i, false); } break; } @@ -232,9 +224,7 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) void NodeTreeView::SelectionChanged() { - NodeInput::KeyframeTrackReference ref = GetSelectedInput(); - - emit InputSelectionChanged(ref.input, ref.element, ref.track); + emit InputSelectionChanged(GetSelectedInput()); } } diff --git a/app/widget/nodetreeview/nodetreeview.h b/app/widget/nodetreeview/nodetreeview.h index 32feb058f..6b66d15d4 100644 --- a/app/widget/nodetreeview/nodetreeview.h +++ b/app/widget/nodetreeview/nodetreeview.h @@ -15,9 +15,9 @@ public: bool IsNodeEnabled(Node* n) const; - bool IsInputEnabled(NodeInput* i, int element, int track) const; + bool IsInputEnabled(const NodeKeyframeTrackReference& ref) const; - void SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference& ref, const QColor& color); + void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); void SetOnlyShowKeyframable(bool e) { @@ -35,11 +35,11 @@ public slots: signals: void NodeEnableChanged(Node* n, bool e); - void InputEnableChanged(NodeInput* i, int element, int track, bool e); + void InputEnableChanged(const NodeKeyframeTrackReference& ref, bool e); - void InputSelectionChanged(NodeInput* input, int element, int track); + void InputSelectionChanged(const NodeKeyframeTrackReference& ref); - void InputDoubleClicked(NodeInput* input, int element, int track); + void InputDoubleClicked(const NodeKeyframeTrackReference& ref); protected: virtual void changeEvent(QEvent* e) override; @@ -49,11 +49,11 @@ protected: private: void Retranslate(); - NodeInput::KeyframeTrackReference GetSelectedInput(); + NodeKeyframeTrackReference GetSelectedInput(); - QTreeWidgetItem *CreateItem(QTreeWidgetItem* parent, NodeInput* input, int element, int track); + QTreeWidgetItem *CreateItem(QTreeWidgetItem* parent, const NodeKeyframeTrackReference& ref); - void CreateItemsForTracks(QTreeWidgetItem* parent, NodeInput* input, int element, int track_count); + void CreateItemsForTracks(QTreeWidgetItem* parent, const NodeInput& input, int track_count); enum ItemType { kItemTypeNode, @@ -61,23 +61,22 @@ private: }; static const int kItemType = Qt::UserRole; - static const int kItemPointer = Qt::UserRole + 1; - static const int kItemElement = Qt::UserRole + 2; - static const int kItemTrack = Qt::UserRole + 3; + static const int kItemInputReference = Qt::UserRole + 1; + static const int kItemNodePointer = Qt::UserRole + 1; QVector nodes_; QVector disabled_nodes_; - QVector disabled_inputs_; + QVector disabled_inputs_; - QHash item_map_; + QHash item_map_; bool only_show_keyframable_; bool show_keyframe_tracks_as_rows_; - QHash keyframe_colors_; + QHash keyframe_colors_; private slots: void ItemCheckStateChanged(QTreeWidgetItem* item, int column); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 46ad9fe91..0124e563a 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -40,7 +40,6 @@ NodeView::NodeView(QWidget *parent) : drop_edge_(nullptr), create_edge_(nullptr), create_edge_dst_(nullptr), - create_edge_dst_input_(nullptr), create_edge_dst_temp_expanded_(false), filter_mode_(kFilterShowSelectedBlocks), scale_(1.0) @@ -103,10 +102,8 @@ void NodeView::SetGraph(NodeGraph *graph) } foreach (Node* n, graph_->nodes()) { - foreach (NodeInput* input, n->parameters()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - scene_.AddEdge(it->second, input, it->first); - } + for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) { + scene_.AddEdge(it->second, it->first); } } } @@ -124,7 +121,7 @@ void NodeView::DeleteSelected() QVector selected_edges = scene_.GetSelectedEdges(); foreach (NodeViewEdge* edge, selected_edges) { - command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input(), edge->element())); + command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input())); } } @@ -426,18 +423,18 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) if (highlight_index >= 0) { create_edge_dst_input_ = create_edge_dst_->GetInputAtIndex(highlight_index); - create_edge_->SetPoints(create_edge_src_->GetOutputPoint(), - create_edge_dst_->GetInputPoint(highlight_index, create_edge_src_->pos()), + create_edge_->SetPoints(create_edge_src_->GetOutputPoint(Node::kDefaultOutput), + create_edge_dst_->GetInputPoint(create_edge_dst_input_.input(), create_edge_dst_input_.element(), create_edge_src_->pos()), true); } else { - create_edge_dst_input_ = nullptr; - create_edge_->SetPoints(create_edge_src_->GetOutputPoint(), + create_edge_dst_input_.Reset(); + create_edge_->SetPoints(create_edge_src_->GetOutputPoint(Node::kDefaultOutput), scene_pt, false); } // Set connected to whether we have a valid input destination - create_edge_->SetConnected(create_edge_dst_input_); + create_edge_->SetConnected(create_edge_dst_input_.IsValid()); return; } @@ -466,20 +463,22 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) new_drop_edge = dynamic_cast(item); if (new_drop_edge) { - drop_input_ = nullptr; + drop_input_.Reset(); - foreach (NodeInput* input, attached_node->parameters()) { - if (input->IsConnectable()) { - if (input->GetDataType() == new_drop_edge->input()->GetDataType()) { - drop_input_ = input; + foreach (const QString& input, attached_node->inputs()) { + NodeInput i(attached_node, input); + + if (attached_node->IsInputConnectable(input)) { + if (attached_node->GetInputDataType(input) == new_drop_edge->input().GetDataType()) { + drop_input_ = i; break; - } else if (!drop_input_) { - drop_input_ = input; + } else if (!drop_input_.IsValid()) { + drop_input_ = i; } } } - if (drop_input_) { + if (drop_input_.IsValid()) { break; } else { new_drop_edge = nullptr; @@ -519,10 +518,10 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) create_edge_dst_->SetExpanded(false); } - if (create_edge_dst_input_) { + if (create_edge_dst_input_.IsValid()) { // Make connection - Core::instance()->undo_stack()->push(new NodeEdgeAddCommand(create_edge_src_->GetNode(), create_edge_dst_input_, -1)); - create_edge_dst_input_ = nullptr; + Core::instance()->undo_stack()->push(new NodeEdgeAddCommand(create_edge_src_->GetNode(), create_edge_dst_input_)); + create_edge_dst_input_.Reset(); } create_edge_dst_ = nullptr; @@ -539,11 +538,11 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) MultiUndoCommand* command = new MultiUndoCommand(); // Remove old edge - command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input(), drop_edge_->element())); + command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input())); // Place new edges - command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_, -1)); - command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input(), drop_edge_->element())); + command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_)); + command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input())); Core::instance()->undo_stack()->push(command); } diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index a6247450f..7b3982d41 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -107,12 +107,12 @@ private: QList attached_items_; NodeViewEdge* drop_edge_; - NodeInput* drop_input_; + NodeInput drop_input_; NodeViewEdge* create_edge_; NodeViewItem* create_edge_src_; NodeViewItem* create_edge_dst_; - NodeInput* create_edge_dst_input_; + NodeInput create_edge_dst_input_; bool create_edge_dst_temp_expanded_; NodeViewScene scene_; diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index a52cb762b..34a5dc443 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -33,13 +33,12 @@ namespace olive { -NodeViewEdge::NodeViewEdge(Node* output, NodeInput *input, int element, +NodeViewEdge::NodeViewEdge(const NodeOutput &output, const NodeInput &input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent) : QGraphicsPathItem(parent), output_(output), input_(input), - element_(element), from_item_(from_item), to_item_(to_item) { @@ -56,8 +55,8 @@ NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : void NodeViewEdge::Adjust() { // Draw a line between the two - SetPoints(from_item()->GetOutputPoint(), - to_item()->GetInputPoint(input_, from_item()->pos()), + SetPoints(from_item()->GetOutputPoint(output_.output()), + to_item()->GetInputPoint(input_.input(), input_.element(), from_item()->pos()), to_item()->IsExpanded()); } diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 43dc1fdc6..9e19be610 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -39,18 +39,18 @@ class NodeViewItem; class NodeViewEdge : public QGraphicsPathItem { public: - NodeViewEdge(Node* output, NodeInput *input, int element, + NodeViewEdge(const NodeOutput& output, const NodeInput& input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent = nullptr); NodeViewEdge(QGraphicsItem* parent = nullptr); - Node* output() const + const NodeOutput& output() const { return output_; } - NodeInput* input() const + const NodeInput& input() const { return input_; } @@ -112,9 +112,9 @@ protected: private: void Init(); - Node* output_; + NodeOutput output_; - NodeInput* input_; + NodeInput input_; int element_; diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index cf6b8617e..9d47445a0 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -187,8 +187,8 @@ void NodeViewItem::SetNode(Node *n) if (node_) { node_->Retranslate(); - foreach (NodeInput* input, node_->parameters()) { - if (input->IsConnectable()) { + foreach (const QString& input, node_->inputs()) { + if (node_->IsInputConnectable(input)) { node_inputs_.append(input); } } @@ -258,7 +258,7 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti painter->fillRect(input_rect, highlight_col); } - painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name()); + painter->drawText(input_rect, Qt::AlignCenter, node_->GetInputName(node_inputs_.at(i))); } } @@ -430,17 +430,12 @@ QRectF NodeViewItem::GetInputRect(int index) const return r; } -QPointF NodeViewItem::GetInputPoint(NodeInput *input, const QPointF& source_pos) const +QPointF NodeViewItem::GetInputPoint(const QString &input, int element, const QPointF& source_pos) const { - return GetInputPoint(node_inputs_.indexOf(input), source_pos); + return pos() + GetInputPointInternal(node_inputs_.indexOf(input), source_pos); } -QPointF NodeViewItem::GetInputPoint(int input, const QPointF &source_pos) const -{ - return pos() + GetInputPointInternal(input, source_pos); -} - -QPointF NodeViewItem::GetOutputPoint() const +QPointF NodeViewItem::GetOutputPoint(const QString& output) const { switch (flow_dir_) { case NodeViewCommon::kLeftToRight: diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 349dfdff9..3e219cf78 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -78,10 +78,9 @@ public: /** * @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object */ - QPointF GetInputPoint(NodeInput* input, const QPointF &source_pos) const; - QPointF GetInputPoint(int input, const QPointF &source_pos) const; + QPointF GetInputPoint(const QString& input, int element, const QPointF &source_pos) const; - QPointF GetOutputPoint() const; + QPointF GetOutputPoint(const QString &output) const; /** * @brief Sets the direction nodes are flowing @@ -105,9 +104,9 @@ public: int GetIndexAt(QPointF pt) const; - NodeInput* GetInputAtIndex(int index) const + NodeInput GetInputAtIndex(int index) const { - return node_inputs_.at(index); + return NodeInput(node_, node_inputs_.at(index)); } void SetHighlightedIndex(int index); @@ -143,7 +142,7 @@ private: /** * @brief Cached list of node inputs */ - QList node_inputs_; + QVector node_inputs_; /** * @brief Rectangle of the Node's title bar (equal to rect() when collapsed) diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index 67dc1d73c..c71309c1c 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -98,10 +98,10 @@ NodeViewItem *NodeViewScene::NodeToUIObject(Node *n) return item_map_.value(n); } -NodeViewEdge *NodeViewScene::EdgeToUIObject(Node* output, NodeInput* input, int element) +NodeViewEdge *NodeViewScene::EdgeToUIObject(const NodeOutput& output, const NodeInput& input) { foreach (NodeViewEdge* edge, edges_) { - if (edge->output() == output && edge->input() == input && edge->element() == element) { + if (edge->output() == output && edge->input() == input) { return edge; } } @@ -174,14 +174,14 @@ void NodeViewScene::RemoveNode(Node *node) delete item_map_.take(node); } -void NodeViewScene::AddEdge(Node* output, NodeInput* input, int element) +void NodeViewScene::AddEdge(const NodeOutput &output, const NodeInput &input) { - AddEdgeInternal(output, input, element, NodeToUIObject(output), NodeToUIObject(input->parent())); + AddEdgeInternal(output, input, NodeToUIObject(output.node()), NodeToUIObject(input.node())); } -void NodeViewScene::RemoveEdge(Node* output, NodeInput* input, int element) +void NodeViewScene::RemoveEdge(const NodeOutput &output, const NodeInput &input) { - NodeViewEdge* edge = EdgeToUIObject(output, input, element); + NodeViewEdge* edge = EdgeToUIObject(output, input); edge->from_item()->RemoveEdge(edge); edge->to_item()->RemoveEdge(edge); edges_.removeOne(edge); @@ -203,9 +203,9 @@ int NodeViewScene::DetermineWeight(Node *n) return qMax(1, weight); } -void NodeViewScene::AddEdgeInternal(Node *output, NodeInput *input, int element, NodeViewItem *from, NodeViewItem *to) +void NodeViewScene::AddEdgeInternal(const NodeOutput& output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to) { - NodeViewEdge* edge_ui = new NodeViewEdge(output, input, element, from, to); + NodeViewEdge* edge_ui = new NodeViewEdge(output, input, from, to); edge_ui->SetFlowDirection(direction_); edge_ui->SetCurved(curved_edges_); diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h index d8362d4f2..d1b886507 100644 --- a/app/widget/nodeview/nodeviewscene.h +++ b/app/widget/nodeview/nodeviewscene.h @@ -53,7 +53,7 @@ public: * in this view/scene), this function returns nullptr. */ NodeViewItem* NodeToUIObject(Node* n); - NodeViewEdge *EdgeToUIObject(Node* output, NodeInput *input, int element); + NodeViewEdge *EdgeToUIObject(const NodeOutput &output, const NodeInput &input); QVector GetSelectedNodes() const; QVector GetSelectedItems() const; @@ -98,8 +98,8 @@ public slots: */ void RemoveNode(Node* node); - void AddEdge(Node* output, NodeInput* input, int element); - void RemoveEdge(Node* output, NodeInput* input, int element); + void AddEdge(const NodeOutput& output, const NodeInput& input); + void RemoveEdge(const NodeOutput& output, const NodeInput& input); /** * @brief Set whether edges in this scene should be curved or not @@ -109,7 +109,7 @@ public slots: private: static int DetermineWeight(Node* n); - void AddEdgeInternal(Node* output, NodeInput* input, int element, NodeViewItem* from, NodeViewItem* to); + void AddEdgeInternal(const NodeOutput &output, const NodeInput &input, NodeViewItem* from, NodeViewItem* to); QHash item_map_; diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 9e57694e9..1ac1b234c 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -25,10 +25,9 @@ namespace olive { -NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element) : +NodeEdgeAddCommand::NodeEdgeAddCommand(const NodeOutput &output, const NodeInput &input) : output_(output), input_(input), - element_(element), remove_command_(nullptr) { } @@ -40,22 +39,20 @@ NodeEdgeAddCommand::~NodeEdgeAddCommand() void NodeEdgeAddCommand::redo() { - if (input_->IsConnected(element_)) { + if (input_.IsConnected()) { if (!remove_command_) { - remove_command_ = new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), - input_, - element_); + remove_command_ = new NodeEdgeRemoveCommand(input_.GetConnectedOutput(), input_); } remove_command_->redo(); } - Node::ConnectEdge(output_, input_, element_); + Node::ConnectEdge(output_, input_); } void NodeEdgeAddCommand::undo() { - Node::DisconnectEdge(output_, input_, element_); + Node::DisconnectEdge(output_, input_); if (remove_command_) { remove_command_->undo(); @@ -64,29 +61,28 @@ void NodeEdgeAddCommand::undo() Project *NodeEdgeAddCommand::GetRelevantProject() const { - return output_->parent()->project(); + return output_.node()->parent()->project(); } -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element) : +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(const NodeOutput &output, const NodeInput &input) : output_(output), - input_(input), - element_(element) + input_(input) { } void NodeEdgeRemoveCommand::redo() { - Node::DisconnectEdge(output_, input_, element_); + Node::DisconnectEdge(output_, input_); } void NodeEdgeRemoveCommand::undo() { - Node::ConnectEdge(output_, input_, element_); + Node::ConnectEdge(output_, input_); } Project *NodeEdgeRemoveCommand::GetRelevantProject() const { - return output_->parent()->project(); + return output_.node()->parent()->project(); } NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node) : @@ -134,14 +130,12 @@ void NodeRemoveAndDisconnectCommand::prep() } // Disconnect everything - foreach (const Node::InputConnection& conn, node_->edges()) { - command_->add_child(new NodeEdgeRemoveCommand(node_, conn.input, conn.element)); + for (auto it=node_->input_connections().cbegin(); it!=node_->input_connections().cend(); it++) { + command_->add_child(new NodeEdgeRemoveCommand(it->second, it->first)); } - foreach (NodeInput* input, node_->inputs()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - command_->add_child(new NodeEdgeRemoveCommand(it->second, input, it->first)); - } + for (const Node::OutputConnection& conn : node_->output_connections()) { + command_->add_child(new NodeEdgeRemoveCommand(conn.first, conn.second)); } } diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index 19c117654..90ce7eb43 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -34,7 +34,7 @@ namespace olive { */ class NodeEdgeRemoveCommand : public UndoCommand { public: - NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element); + NodeEdgeRemoveCommand(const NodeOutput& output, const NodeInput& input); virtual Project* GetRelevantProject() const override; @@ -42,9 +42,8 @@ public: virtual void undo() override; private: - Node* output_; - NodeInput* input_; - int element_; + NodeOutput output_; + NodeInput input_; }; @@ -55,7 +54,7 @@ private: */ class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeAddCommand(Node* output, NodeInput* input, int element); + NodeEdgeAddCommand(const NodeOutput& output, const NodeInput& input); virtual ~NodeEdgeAddCommand() override; @@ -65,9 +64,8 @@ public: virtual void undo() override; private: - Node* output_; - NodeInput* input_; - int element_; + NodeOutput output_; + NodeInput input_; NodeEdgeRemoveCommand* remove_command_; diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index cf2d328c0..ad0921c79 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -41,7 +41,7 @@ namespace olive { inline bool NodeCanBeRemoved(Node* n) { //return n->edges().isEmpty() && n->GetExclusiveDependencies().isEmpty(); - return n->edges().empty(); + return n->output_connections().empty(); } inline UndoCommand* CreateRemoveCommand(Node* n) @@ -234,7 +234,7 @@ public: invalidate_range = block_->range(); } - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -294,7 +294,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } private: @@ -578,13 +578,13 @@ public: track->InsertBlockAfter(new_block(), block_); // If the block had an out transition, we move it to the new block - moved_transition_ = nullptr; + moved_transition_ = NodeInput(); TransitionBlock* potential_transition = dynamic_cast(new_block()->next()); if (potential_transition) { - foreach (const Node::InputConnection& conn, block_->edges()) { - if (conn.input->parent() == potential_transition) { - moved_transition_ = potential_transition->out_block_input(); + for (const Node::OutputConnection& output : block_->output_connections()) { + if (output.second.node() == potential_transition) { + moved_transition_ = NodeInput(potential_transition, TransitionBlock::kOutBlockInput); Node::DisconnectEdge(block_, moved_transition_); Node::ConnectEdge(new_block(), moved_transition_); break; @@ -601,9 +601,9 @@ public: track->BeginOperation(); - if (moved_transition_) { + if (moved_transition_.IsValid()) { Node::DisconnectEdge(new_block(), moved_transition_); - Node::DisconnectEdge(block_, moved_transition_); + Node::ConnectEdge(block_, moved_transition_); } block_->set_length_and_media_out(old_length_); @@ -630,7 +630,7 @@ private: UndoCommand* reconnect_tree_command_; - NodeInput* moved_transition_; + NodeInput moved_transition_; QVector src_nodes_; QVector added_nodes_; @@ -1144,8 +1144,7 @@ public: TrackListRippleToolCommand(TrackList* track_list, const QHash& info, const rational& ripple_movement, - const Timeline::MovementMode& movement_mode, - UndoCommand* parent = nullptr) : + const Timeline::MovementMode& movement_mode) : track_list_(track_list), info_(info), ripple_movement_(ripple_movement), @@ -1356,23 +1355,20 @@ private: class TimelineAddTrackCommand : public UndoCommand { public: TimelineAddTrackCommand(TrackList *timeline) : - timeline_(timeline), - direct_(nullptr) + timeline_(timeline) { track_ = new Track(); track_->setParent(&memory_manager_); if (timeline->GetTrackCount() > 0 && Config::Current()[QStringLiteral("AutoMergeTracks")].toBool()) { if (timeline_->type() == Track::kVideo) { - MergeNode* merge = new MergeNode(); - base_ = merge->base_in(); - blend_ = merge->blend_in(); - merge_ = merge; + merge_ = new MergeNode(); + base_ = NodeInput(merge_, MergeNode::kBaseIn); + blend_ = NodeInput(merge_, MergeNode::kBlendIn); } else { - MathNode* math = new MathNode(); - base_ = math->param_a_in(); - blend_ = math->param_b_in(); - merge_ = math; + merge_ = new MathNode(); + base_ = NodeInput(merge_, MathNode::kParamAIn); + blend_ = NodeInput(merge_, MathNode::kParamBIn); } merge_->setParent(&memory_manager_); } else { @@ -1394,8 +1390,8 @@ public: { // Add track track_->setParent(timeline_->GetParentGraph()); - timeline_->track_input()->ArrayAppend(); - Node::ConnectEdge(track_, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); + timeline_->ArrayAppend(); + Node::ConnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); // Add merge if applicable if (merge_) { @@ -1404,11 +1400,14 @@ public: Track* last_track = timeline_->GetTrackAt(timeline_->GetTrackCount()-2); // Whatever this track used to be connected to, connect the merge instead - std::vector edges = last_track->edges(); - foreach (const Node::InputConnection& ic, edges) { - if (ic.input != timeline_->track_input()) { - Node::DisconnectEdge(last_track, ic.input, ic.element); - Node::ConnectEdge(merge_, ic.input, ic.element); + const Node::OutputConnections edges = last_track->output_connections(); + for (const Node::OutputConnection& ic : edges) { + const NodeInput& i = ic.second; + + // Ignore the track input, but funnel everything else through our merge + if (i.node() != timeline_->parent() && i.input() != timeline_->track_input()) { + Node::DisconnectEdge(last_track, i); + Node::ConnectEdge(merge_, i); } } @@ -1417,18 +1416,20 @@ public: Node::ConnectEdge(last_track, base_); } else if (timeline_->GetTrackCount() == 1) { // If this was the first track we added, - NodeInput* relevant_input; + QString relevant_input; if (timeline_->type() == Track::kVideo) { - relevant_input = timeline_->parent()->texture_input(); + relevant_input = ViewerOutput::kTextureInput; } else { - relevant_input = timeline_->parent()->samples_input(); + relevant_input = ViewerOutput::kSamplesInput; } - if (!relevant_input->IsConnected()) { - Node::ConnectEdge(track_, relevant_input); + if (!timeline_->parent()->IsInputConnected(relevant_input)) { + direct_ = NodeInput(timeline_->parent(), relevant_input); - direct_ = relevant_input; + Node::ConnectEdge(track_, direct_); + } else { + direct_ = NodeInput(); } } } @@ -1443,20 +1444,23 @@ public: Node::DisconnectEdge(track_, blend_); Node::DisconnectEdge(last_track, base_); - std::vector edges = merge_->edges(); - foreach (const Node::InputConnection& ic, edges) { - Node::DisconnectEdge(merge_, ic.input, ic.element); - Node::ConnectEdge(last_track, ic.input, ic.element); + // Make copy of edges since the node's internal array will change as we disconnect things + const Node::OutputConnections edges = merge_->output_connections(); + for (const Node::OutputConnection& ic : edges) { + const NodeInput& i = ic.second; + + Node::DisconnectEdge(merge_, i); + Node::ConnectEdge(last_track, i); } merge_->setParent(&memory_manager_); - } else if (direct_) { + } else if (direct_.IsValid()) { Node::DisconnectEdge(track_, direct_); } // Remove track - Node::DisconnectEdge(track_, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); - timeline_->track_input()->ArrayRemoveLast(); + Node::DisconnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); + timeline_->ArrayRemoveLast(); track_->setParent(&memory_manager_); } @@ -1465,10 +1469,10 @@ private: Track* track_; Node* merge_; - NodeInput* base_; - NodeInput* blend_; + NodeInput base_; + NodeInput blend_; - NodeInput* direct_; + NodeInput direct_; QObject memory_manager_; @@ -1705,7 +1709,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -1762,7 +1766,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); + track_->InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput); } private: @@ -2047,7 +2051,7 @@ public: invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()), qMax(invalidate_range.out(), blocks_.last()->out())); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -2087,7 +2091,7 @@ public: invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()), qMax(invalidate_range.out(), blocks_.last()->out())); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } private: @@ -2315,7 +2319,7 @@ private: class TransitionRemoveCommand : public UndoCommand { public: - TransitionRemoveCommand(TransitionBlock* block, UndoCommand *parent = nullptr) : + TransitionRemoveCommand(TransitionBlock* block) : block_(block) { } @@ -2346,18 +2350,18 @@ public: } if (in_block_) { - Node::DisconnectEdge(in_block_, block_->in_block_input()); + Node::DisconnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::DisconnectEdge(out_block_, block_->out_block_input()); + Node::DisconnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); } track_->RippleRemoveBlock(block_); track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -2371,11 +2375,11 @@ public: } if (in_block_) { - Node::ConnectEdge(in_block_, block_->in_block_input()); + Node::ConnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::ConnectEdge(out_block_, block_->out_block_input()); + Node::ConnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); } // These if statements must be separated because in_offset and out_offset report different things @@ -2391,7 +2395,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); + track_->InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput); } private: diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 89b2992e7..44776322e 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -118,7 +118,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) command->add_child(new NodeAddCommand(graph, solid)); - command->add_child(new NodeEdgeAddCommand(solid, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(solid, NodeInput(clip, ClipBlock::kBufferIn))); break; } case olive::Tool::kAddableTitle: @@ -128,7 +128,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) command->add_child(new NodeAddCommand(graph, text)); - command->add_child(new NodeEdgeAddCommand(text, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(text, NodeInput(clip, ClipBlock::kBufferIn))); break; } case olive::Tool::kAddableBars: diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 9a1c4b483..a3783568c 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -434,8 +434,8 @@ void ImportTool::DropGhosts(bool insert) TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); - command->add_child(new NodeEdgeAddCommand(video_input, transform->texture_input(), -1)); - command->add_child(new NodeEdgeAddCommand(transform, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(video_input, NodeInput(transform, TransformDistortNode::kTextureInput))); + command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); break; } case Stream::kAudio: @@ -447,8 +447,8 @@ void ImportTool::DropGhosts(bool insert) VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); - command->add_child(new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1)); - command->add_child(new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(audio_input, NodeInput(volume_node, VolumeNode::kSamplesInput))); + command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); break; } default: diff --git a/app/widget/timelinewidget/tool/import.h b/app/widget/timelinewidget/tool/import.h index 40c653cf9..38f22448d 100644 --- a/app/widget/timelinewidget/tool/import.h +++ b/app/widget/timelinewidget/tool/import.h @@ -37,6 +37,12 @@ public: class DraggedFootage { public: + DraggedFootage() : + footage_(nullptr), + streams_(0) + { + } + DraggedFootage(Footage* f, quint64 streams) : footage_(f), streams_(streams) diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 541f2707a..0c8bb52bf 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -147,28 +147,25 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) // Connect block to transition command->add_child(new NodeEdgeAddCommand(out_block, - transition->out_block_input(), - -1)); + NodeInput(transition, TransitionBlock::kOutBlockInput))); command->add_child(new NodeEdgeAddCommand(in_block, - transition->in_block_input(), - -1)); + NodeInput(transition, TransitionBlock::kInBlockInput))); } else { Block* block_to_transition = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); - NodeInput* transition_input_to_connect; + QString transition_input_to_connect; if (ghost_->GetMode() == Timeline::kTrimIn) { transition->set_length_and_media_out(ghost_->GetAdjustedLength()); - transition_input_to_connect = transition->in_block_input(); + transition_input_to_connect = TransitionBlock::kInBlockInput; } else { transition->set_length_and_media_out(ghost_->GetAdjustedLength()); - transition_input_to_connect = transition->out_block_input(); + transition_input_to_connect = TransitionBlock::kOutBlockInput; } // Connect block to transition command->add_child(new NodeEdgeAddCommand(block_to_transition, - transition_input_to_connect, - -1)); + NodeInput(transition, transition_input_to_connect))); } Core::instance()->undo_stack()->push(command); diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 563d079e4..cea4a66e4 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -60,8 +60,8 @@ void FootageViewerWidget::SetFootage(Footage *footage) video_node_->SetStream(nullptr); audio_node_->SetStream(nullptr); - Node::DisconnectEdge(video_node_, sequence_.viewer_output()->texture_input()); - Node::DisconnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); + Node::DisconnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput)); + Node::DisconnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput)); } footage_ = footage; @@ -98,12 +98,12 @@ void FootageViewerWidget::SetFootage(Footage *footage) if (video_stream) { video_node_->SetStream(video_stream); - Node::ConnectEdge(video_node_, sequence_.viewer_output()->texture_input()); + Node::ConnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput)); } if (audio_stream) { audio_node_->SetStream(audio_stream); - Node::ConnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); + Node::ConnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput)); } ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager()); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 2f18aae2d..4d20618fc 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -178,8 +178,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); connect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); - connect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); - connect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); InterlacingChangedSlot(n->video_params().interlacing()); @@ -233,8 +232,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); disconnect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); - disconnect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); - disconnect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); ruler()->SetPlaybackCache(nullptr); @@ -398,8 +396,8 @@ void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &fn, bool ViewerWidget::ShouldForceWaveform() const { return GetConnectedNode() - && !GetConnectedNode()->texture_input()->IsConnected() - && GetConnectedNode()->samples_input()->IsConnected(); + && !GetConnectedNode()->IsInputConnected(ViewerOutput::kTextureInput) + && GetConnectedNode()->IsInputConnected(ViewerOutput::kSamplesInput); } void ViewerWidget::UpdateTextureFromNode(const rational& time)