From ac3f49a845245666be4313a0411f205d13302d0d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 04:11:24 +1000 Subject: [PATCH] nodes: moved matrix input on "video input" elsewhere Rather than plugging a matrix into the video input node, the matrix is now multiplied by the video input using a math node. This is probably more sensible from a user perspective. This also means the renderer is tolerant of texture sizes that are not equal to the sequence size, however most nodes will downsample the texture to the sequence size (and if not, it will be downsampled once it is cached). Textures will still *always* be in reference space and the sequence's format. This seems like the best compromise between backend and frontend congruity. --- app/node/CMakeLists.txt | 2 +- app/node/factory.cpp | 6 +- app/node/factory.h | 2 +- .../{distort => generator}/CMakeLists.txt | 2 +- .../matrix}/CMakeLists.txt | 4 +- .../matrix/matrix.cpp} | 30 +-- .../transform.h => generator/matrix/matrix.h} | 8 +- app/node/input/media/audio/audio.cpp | 4 - app/node/input/media/audio/audio.h | 3 +- app/node/input/media/media.cpp | 2 +- app/node/input/media/video/video.cpp | 33 --- app/node/input/media/video/video.h | 15 +- app/node/math/math/math.cpp | 194 ++++++++------- app/node/math/math/math.h | 16 +- app/node/output/track/track.cpp | 228 +++++++++--------- app/node/output/track/track.h | 9 +- app/node/output/track/tracklist.cpp | 2 +- app/render/backend/opengl/openglproxy.cpp | 4 +- app/render/backend/renderbackend.cpp | 2 +- app/shaders/{videoinput.vert => matrix.vert} | 8 +- app/shaders/shaders.qrc | 3 +- app/shaders/videoinput.frag | 11 - .../nodeparamviewconnectedlabel.cpp | 2 + app/widget/timelinewidget/tool/import.cpp | 19 +- app/widget/timelinewidget/undo/undo.cpp | 50 ++-- .../view/timelineviewblockitem.cpp | 6 +- 26 files changed, 307 insertions(+), 358 deletions(-) rename app/node/{distort => generator}/CMakeLists.txt (96%) rename app/node/{distort/transform => generator/matrix}/CMakeLists.txt (90%) rename app/node/{distort/transform/transform.cpp => generator/matrix/matrix.cpp} (81%) rename app/node/{distort/transform/transform.h => generator/matrix/matrix.h} (92%) rename app/shaders/{videoinput.vert => matrix.vert} (79%) delete mode 100644 app/shaders/videoinput.frag diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 89780c8dc..965982241 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory(audio) add_subdirectory(block) -add_subdirectory(distort) +add_subdirectory(generator) add_subdirectory(input) add_subdirectory(math) add_subdirectory(output) diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 2e2f79652..09791c6a5 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -25,7 +25,7 @@ #include "block/clip/clip.h" #include "block/gap/gap.h" #include "block/transition/externaltransition.h" -#include "distort/transform/transform.h" +#include "generator/matrix/matrix.h" #include "input/media/video/video.h" #include "input/media/audio/audio.h" #include "input/time/timeinput.h" @@ -141,8 +141,8 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new ClipBlock(); case kGapBlock: return new GapBlock(); - case kTransformDistort: - return new TransformDistort(); + case kMatrixGenerator: + return new MatrixGenerator(); case kVideoInput: return new VideoInput(); case kAudioInput: diff --git a/app/node/factory.h b/app/node/factory.h index 9cd683781..c6329de7e 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -36,7 +36,7 @@ public: kClipBlock, kGapBlock, kAudioInput, - kTransformDistort, + kMatrixGenerator, kVideoInput, kTrackOutput, kAudioVolume, diff --git a/app/node/distort/CMakeLists.txt b/app/node/generator/CMakeLists.txt similarity index 96% rename from app/node/distort/CMakeLists.txt rename to app/node/generator/CMakeLists.txt index f43e36675..9e8d25261 100644 --- a/app/node/distort/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(transform) +add_subdirectory(matrix) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/distort/transform/CMakeLists.txt b/app/node/generator/matrix/CMakeLists.txt similarity index 90% rename from app/node/distort/transform/CMakeLists.txt rename to app/node/generator/matrix/CMakeLists.txt index bfa0cfc5a..b986f2aca 100644 --- a/app/node/distort/transform/CMakeLists.txt +++ b/app/node/generator/matrix/CMakeLists.txt @@ -16,7 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/distort/transform/transform.h - node/distort/transform/transform.cpp + node/generator/matrix/matrix.h + node/generator/matrix/matrix.cpp PARENT_SCOPE ) diff --git a/app/node/distort/transform/transform.cpp b/app/node/generator/matrix/matrix.cpp similarity index 81% rename from app/node/distort/transform/transform.cpp rename to app/node/generator/matrix/matrix.cpp index 649aa065e..c967869ed 100644 --- a/app/node/distort/transform/transform.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -18,14 +18,14 @@ ***/ -#include "transform.h" +#include "matrix.h" #include #include OLIVE_NAMESPACE_ENTER -TransformDistort::TransformDistort() +MatrixGenerator::MatrixGenerator() { position_input_ = new NodeInput("pos_in", NodeParam::kVec2); AddInput(position_input_); @@ -42,39 +42,39 @@ TransformDistort::TransformDistort() uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true); uniform_scale_input_->set_is_keyframable(false); uniform_scale_input_->SetConnectable(false); - connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &TransformDistort::UniformScaleChanged); + connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); AddInput(uniform_scale_input_); anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2); AddInput(anchor_input_); } -Node *TransformDistort::copy() const +Node *MatrixGenerator::copy() const { - return new TransformDistort(); + return new MatrixGenerator(); } -QString TransformDistort::Name() const +QString MatrixGenerator::Name() const { - return tr("Transform"); + return tr("Orthographic Matrix"); } -QString TransformDistort::id() const +QString MatrixGenerator::id() const { return QStringLiteral("org.olivevideoeditor.Olive.transform"); } -QString TransformDistort::Category() const +QString MatrixGenerator::Category() const { - return tr("Distort"); + return tr("Generator"); } -QString TransformDistort::Description() const +QString MatrixGenerator::Description() const { - return tr("Apply transformations to position, rotation, and scale."); + return tr("Generate an orthographic matrix using position, rotation, and scale."); } -void TransformDistort::Retranslate() +void MatrixGenerator::Retranslate() { position_input_->set_name(tr("Position")); rotation_input_->set_name(tr("Rotation")); @@ -83,7 +83,7 @@ void TransformDistort::Retranslate() anchor_input_->set_name(tr("Anchor Point")); } -NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const +NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const { QMatrix4x4 mat; @@ -110,7 +110,7 @@ NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const return output; } -void TransformDistort::UniformScaleChanged() +void MatrixGenerator::UniformScaleChanged() { scale_input_->set_property("disabley", uniform_scale_input_->get_standard_value().toBool()); } diff --git a/app/node/distort/transform/transform.h b/app/node/generator/matrix/matrix.h similarity index 92% rename from app/node/distort/transform/transform.h rename to app/node/generator/matrix/matrix.h index 6e3acd6a4..53c5c5356 100644 --- a/app/node/distort/transform/transform.h +++ b/app/node/generator/matrix/matrix.h @@ -18,18 +18,18 @@ ***/ -#ifndef TRANSFORMDISTORT_H -#define TRANSFORMDISTORT_H +#ifndef MATRIXGENERATOR_H +#define MATRIXGENERATOR_H #include "node/node.h" OLIVE_NAMESPACE_ENTER -class TransformDistort : public Node +class MatrixGenerator : public Node { Q_OBJECT public: - TransformDistort(); + MatrixGenerator(); virtual Node* copy() const override; diff --git a/app/node/input/media/audio/audio.cpp b/app/node/input/media/audio/audio.cpp index c51b021d5..0dadd4b60 100644 --- a/app/node/input/media/audio/audio.cpp +++ b/app/node/input/media/audio/audio.cpp @@ -22,10 +22,6 @@ OLIVE_NAMESPACE_ENTER -AudioInput::AudioInput() -{ -} - Node *AudioInput::copy() const { return new AudioInput(); diff --git a/app/node/input/media/audio/audio.h b/app/node/input/media/audio/audio.h index 2bc6db0ef..b28500c53 100644 --- a/app/node/input/media/audio/audio.h +++ b/app/node/input/media/audio/audio.h @@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER class AudioInput : public MediaInput { public: - AudioInput(); + AudioInput() = default; virtual Node* copy() const override; @@ -36,7 +36,6 @@ public: virtual QString id() const override; virtual QString Description() const override; -private: }; OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index ff371da53..b7bad0efc 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -67,7 +67,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const } // Push buffer to the top of the stack - NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kSamples); + NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kBuffer); if (buffer.type() != NodeParam::kNone) { table.Push(buffer); } diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index 2820cb292..e6caab973 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -31,12 +31,6 @@ OLIVE_NAMESPACE_ENTER -VideoInput::VideoInput() -{ - matrix_input_ = new NodeInput("matrix_in", NodeInput::kMatrix); - AddInput(matrix_input_); -} - Node *VideoInput::copy() const { return new VideoInput(); @@ -57,31 +51,4 @@ QString VideoInput::Description() const return tr("Import a video footage stream."); } -NodeInput *VideoInput::matrix_input() const -{ - return matrix_input_; -} - -Node::Capabilities VideoInput::GetCapabilities(const NodeValueDatabase &) const -{ - return kShader; -} - -QString VideoInput::ShaderVertexCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.vert"); -} - -QString VideoInput::ShaderFragmentCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.frag"); -} - -void VideoInput::Retranslate() -{ - MediaInput::Retranslate(); - - matrix_input_->set_name(tr("Transform")); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index fdeb109fe..23a5698c8 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -31,7 +31,7 @@ OLIVE_NAMESPACE_ENTER class VideoInput : public MediaInput { public: - VideoInput(); + VideoInput() = default; virtual Node* copy() const override; @@ -39,19 +39,6 @@ public: virtual QString id() const override; virtual QString Description() const override; - NodeInput* matrix_input() const; - - virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; - virtual QString ShaderVertexCode(const NodeValueDatabase&) const override; - virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; - - virtual void Retranslate() override; - -protected: - -private: - NodeInput* matrix_input_; - }; OLIVE_NAMESPACE_EXIT diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index c90764eed..93e802481 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -131,9 +131,9 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const // Override the operation for this operation since we multiply texture COORDS by the matrix rather than NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; - NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; - operation = QStringLiteral("texture(%1, (vec4(ove_texcoord, 0.0, 1.0) * %2).xy)").arg(tex_in->id(), mat_in->id()); + // No-op frag shader (can we return QString() instead?) + operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id()); } else { switch (GetOperation()) { @@ -176,6 +176,25 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const operation); } +QString MathNode::ShaderVertexCode(const NodeValueDatabase &input) const +{ + PairingCalculator calc(input[param_a_in_], input[param_b_in_]); + + if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) { + + NodeParam::DataType type_a = calc.GetMostLikelyValueA().type(); + + // Override the operation for this operation since we multiply texture COORDS by the matrix rather than + NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; + NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; + + return ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id()); + + } + + return QString(); +} + NodeValue MathNode::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const { if (input == param_a_in_ || input == param_b_in_) { @@ -382,6 +401,11 @@ MathNode::Operation MathNode::GetOperation() const return static_cast(method_in_->get_standard_value().toInt()); } +void MathNode::SetOperation(MathNode::Operation o) +{ + method_in_->set_standard_value(o); +} + QString MathNode::GetShaderUniformType(const NodeParam::DataType &type) { switch (type) { @@ -405,6 +429,81 @@ QString MathNode::GetShaderVariableCall(const QString &input_id, const NodeParam return input_id; } +QVector4D MathNode::RetrieveVector(const NodeValue &val) +{ + // QVariant doesn't know that QVector*D can convert themselves so we do it here + switch (val.type()) { + case NodeParam::kVec2: + return val.data().value(); + case NodeParam::kVec3: + return val.data().value(); + case NodeParam::kVec4: + default: + return val.data().value(); + } +} + +void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) +{ + switch (type) { + case NodeParam::kVec2: + output->Push(type, QVector2D(vec)); + break; + case NodeParam::kVec3: + output->Push(type, QVector3D(vec)); + break; + case NodeParam::kVec4: + output->Push(type, vec); + break; + default: + break; + } +} + +float MathNode::RetrieveNumber(const NodeValue &val) +{ + if (val.type() == NodeParam::kRational) { + return val.data().value().toDouble(); + } else { + return val.data().toFloat(); + } +} + +MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) +{ + QVector pair_likelihood_a = GetPairLikelihood(table_a); + QVector pair_likelihood_b = GetPairLikelihood(table_b); + + int weight_a = qMax(0, table_b.Count() - table_a.Count()); + int weight_b = qMax(0, table_a.Count() - table_b.Count()); + + QVector likelihoods(kPairCount); + + for (int i=0;i -1) { + if (most_likely_pairing_ == kPairNone + || likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) { + most_likely_pairing_ = static_cast(i); + } + } + } + + if (most_likely_pairing_ != kPairNone) { + most_likely_value_a_ = table_a.At(pair_likelihood_a.at(most_likely_pairing_)); + most_likely_value_b_ = table_b.At(pair_likelihood_b.at(most_likely_pairing_)); + } +} + QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable &table) { // FIXME: When we introduce a manual override, placing it here would be the least problematic @@ -448,88 +547,6 @@ QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable return likelihood; } -MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector &a, - const QVector &b, - const int& weight_a, - const int& weight_b) -{ - QVector likelihoods(kPairCount); - - for (int i=0;i -1) { - if (pairing == kPairNone - || likelihoods.at(i) > likelihoods.at(pairing)) { - pairing = static_cast(i); - } - } - } - - return pairing; -} - -QVector4D MathNode::RetrieveVector(const NodeValue &val) -{ - // QVariant doesn't know that QVector*D can convert themselves so we do it here - switch (val.type()) { - case NodeParam::kVec2: - return val.data().value(); - case NodeParam::kVec3: - return val.data().value(); - case NodeParam::kVec4: - default: - return val.data().value(); - } -} - -void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) -{ - switch (type) { - case NodeParam::kVec2: - output->Push(type, QVector2D(vec)); - break; - case NodeParam::kVec3: - output->Push(type, QVector3D(vec)); - break; - case NodeParam::kVec4: - output->Push(type, vec); - break; - default: - break; - } -} - -float MathNode::RetrieveNumber(const NodeValue &val) -{ - if (val.type() == NodeParam::kRational) { - return val.data().value().toDouble(); - } else { - return val.data().toFloat(); - } -} - -MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) : - table_a_(table_a), - table_b_(table_b) -{ - pair_likelihood_a_ = GetPairLikelihood(table_a_); - pair_likelihood_b_ = GetPairLikelihood(table_b_); - - most_likely_pairing_ = GetMostLikelyPairingInternal(pair_likelihood_a_, - pair_likelihood_b_, - qMax(0, table_b_.Count() - table_a_.Count()), - qMax(0, table_a_.Count() - table_b_.Count())); -} - bool MathNode::PairingCalculator::FoundMostLikelyPairing() const { return (most_likely_pairing_ > kPairNone && most_likely_pairing_ < kPairCount); @@ -542,17 +559,12 @@ MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairing() const const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueA() const { - return GetMostLikelyValue(table_a_, pair_likelihood_a_); + return most_likely_value_a_; } const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueB() const { - return GetMostLikelyValue(table_b_, pair_likelihood_b_); -} - -const NodeValue& MathNode::PairingCalculator::GetMostLikelyValue(const NodeValueTable &table, const QVector &likelihood) const -{ - return table.At(likelihood.at(most_likely_pairing_)); + return most_likely_value_b_; } template diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 306dfc487..1909c4bd5 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -42,6 +42,7 @@ public: virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; virtual QString ShaderID(const NodeValueDatabase&) const override; virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + virtual QString ShaderVertexCode(const NodeValueDatabase&input) const override; virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const override; @@ -53,7 +54,6 @@ public: NodeInput* param_a_in() const; NodeInput* param_b_in() const; -private: enum Operation { kOpAdd, kOpSubtract, @@ -63,7 +63,9 @@ private: }; Operation GetOperation() const; + void SetOperation(Operation o); +private: enum Pairing { kPairNone = -1, @@ -96,21 +98,13 @@ private: const NodeValue& GetMostLikelyValueB() const; private: - static Pairing GetMostLikelyPairingInternal(const QVector &a, const QVector &b, const int &weight_a, const int &weight_b); - static QVector GetPairLikelihood(const NodeValueTable& table); - const NodeValue &GetMostLikelyValue(const NodeValueTable& table, const QVector& likelihood) const; - Pairing most_likely_pairing_; - const NodeValueTable& table_a_; + NodeValue most_likely_value_a_; - const NodeValueTable& table_b_; - - QVector pair_likelihood_a_; - - QVector pair_likelihood_b_; + NodeValue most_likely_value_b_; }; diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index ab77c7690..24497f684 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -40,7 +40,6 @@ TrackOutput::TrackOutput() : AddInput(block_input_); connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); - connect(block_input_, &NodeInputArray::SizeChanged, this, &TrackOutput::BlockListSizeChanged); muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); muted_input_->set_is_keyframable(false); @@ -233,7 +232,7 @@ QList TrackOutput::BlocksAtTimeRange(const TimeRange &range) const return list; } -const QVector &TrackOutput::Blocks() const +const QList &TrackOutput::Blocks() const { return block_cache_; } @@ -265,16 +264,25 @@ void TrackOutput::InsertBlockAfter(Block *block, Block *before) void TrackOutput::PrependBlock(Block *block) { - InsertBlockAtIndex(block, 0); + BlockInvalidateCache(); + + block_input_->Prepend(); + NodeParam::ConnectEdge(block->output(), block_input_->First()); + + UnblockInvalidateCache(); + + // Everything has shifted at this point + InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); } void TrackOutput::InsertBlockAtIndex(Block *block, int index) { BlockInvalidateCache(); - block_input_->InsertAt(index); + int insert_index = GetInputIndexFromCacheIndex(index); + block_input_->InsertAt(insert_index); NodeParam::ConnectEdge(block->output(), - block_input_->At(index)); + block_input_->At(insert_index)); UnblockInvalidateCache(); @@ -285,10 +293,8 @@ void TrackOutput::AppendBlock(Block *block) { BlockInvalidateCache(); - int last_index = block_input_->GetSize(); block_input_->Append(); - NodeParam::ConnectEdge(block->output(), - block_input_->At(last_index)); + NodeParam::ConnectEdge(block->output(), block_input_->Last()); UnblockInvalidateCache(); @@ -312,9 +318,7 @@ void TrackOutput::RippleRemoveBlock(Block *block) rational remove_in = block->in(); - int index_of_block_to_remove = block_cache_.indexOf(block); - - block_input_->RemoveAt(index_of_block_to_remove); + block_input_->RemoveAt(GetInputIndexFromCacheIndex(block)); UnblockInvalidateCache(); @@ -325,7 +329,7 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) { BlockInvalidateCache(); - int index_of_old_block = block_cache_.indexOf(old); + int index_of_old_block = GetInputIndexFromCacheIndex(old); NodeParam::DisconnectEdge(old->output(), block_input_->At(index_of_old_block)); @@ -432,108 +436,107 @@ void TrackOutput::SetLocked(bool e) void TrackOutput::UpdateInOutFrom(int index) { - Q_ASSERT(index >= 0); - Q_ASSERT(index < block_cache_.size()); - - rational new_track_length; - // Find block just before this one to find the last out point - for (int i=index-1;i>=0;i--) { + rational last_out = (index == 0) ? 0 : block_cache_.at(index - 1)->out(); + + // Iterate through all blocks updating their in/outs + for (int i=index; iout(); - break; - } - } + b->set_in(last_out); - for (int i=index;ilength(); - if (b) { - // Set in - b->set_in(new_track_length); + b->set_out(last_out); - // Set out - new_track_length += b->length(); - b->set_out(new_track_length); - - emit b->Refreshed(); - } + emit b->Refreshed(); } // Update track length - if (new_track_length != track_length_) { + if (last_out != track_length_) { rational old_track_length = track_length_; - track_length_ = new_track_length; + track_length_ = last_out; emit TrackLengthChanged(); - InvalidateCache(TimeRange(qMin(old_track_length, new_track_length), qMax(old_track_length, new_track_length)), + InvalidateCache(TimeRange(qMin(old_track_length, last_out), qMax(old_track_length, last_out)), block_input_, block_input_); } } -void TrackOutput::UpdatePreviousAndNextOfIndex(int index) +int TrackOutput::GetInputIndexFromCacheIndex(int cache_index) { - Block* ref = block_cache_.at(index); + return GetInputIndexFromCacheIndex(block_cache_.at(cache_index)); +} - Block* previous = nullptr; - Block* next = nullptr; - - // Find previous - for (int i=index-1;i>=0;i--) { - previous = block_cache_.at(i); - - if (previous) { - break; +int TrackOutput::GetInputIndexFromCacheIndex(Block *block) +{ + for (int i=0; iGetSize(); i++) { + if (block_input_->At(i)->get_connected_node() == block) { + return i; } } - // Find next - for (int i=index+1;iset_previous(previous); - ref->set_next(next); - - if (previous) - previous->set_next(ref); - - if (next) - next->set_previous(ref); - } else { - // Link previous and next together - if (previous) - previous->set_next(next); - - if (next) - next->set_previous(previous); - } + return -1; } void TrackOutput::BlockConnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - + // Determine what node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - block_cache_.replace(block_index, connected_block); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - if (connected_block) { - connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); + // If this node is a block, we can do something with it + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // See where this input falls in our internal "block cache" + Block* next = nullptr; + for (int i=block_input_->IndexOfSubParameter(edge->input())+1; iGetSize(); i++) { + Node* that_node = block_input_->At(i)->get_connected_node(); + + // If we find a block, this is the block that will follow the one just connected + if (that_node && that_node->IsBlock()) { + next = static_cast(that_node); + break; + } + } + + int real_block_index; + + // Either insert or append depending on if we found a "next" block + if (next) { + // Insert block before this next block + real_block_index = block_cache_.indexOf(next); + block_cache_.insert(real_block_index, connected_block); + + // Update values with next + next->set_previous(connected_block); + connected_block->set_next(next); + } else { + // No "next", this block must come at the end + real_block_index = block_cache_.size(); + block_cache_.append(connected_block); + + // Update next value + connected_block->set_next(nullptr); + } + + // For all blocks after the block we inserted (including it), update the "previous" and "next" + // fields as well as the in/out values + if (real_block_index == 0) { + connected_block->set_previous(nullptr); + } else { + Block* prev = block_cache_.at(real_block_index - 1); + + connected_block->set_previous(prev); + prev->set_next(connected_block); + } + + UpdateInOutFrom(real_block_index); + + // Make connections to this block + connect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); emit BlockAdded(connected_block); } @@ -541,46 +544,43 @@ void TrackOutput::BlockConnected(NodeEdgePtr edge) void TrackOutput::BlockDisconnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - - block_cache_.replace(block_index, nullptr); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - + // See what kind of node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - if (connected_block) { - disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); - // Update previous and next references + // If this was a block, we would have put it in our block cache in BlockConnected() + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // Determine what index this block was in our cache and remove it + int index_of_block = block_cache_.indexOf(connected_block); + block_cache_.removeAt(index_of_block); + + // If there were blocks following this one, update their ins/outs + if (index_of_block < block_cache_.size()) { + UpdateInOutFrom(index_of_block); + } + + // Join the previous and next blocks together + if (connected_block->previous()) { + connected_block->previous()->set_next(connected_block->next()); + } + + if (connected_block->next()) { + connected_block->next()->set_previous(connected_block->previous()); + } + + disconnect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); + emit BlockRemoved(connected_block); } } -void TrackOutput::BlockListSizeChanged(int size) -{ - int old_size = block_cache_.size(); - - block_cache_.resize(size); - - // Fill new slots with nullptr - for (int i=old_size;i(sender()); - int index = block_cache_.indexOf(b); - - Q_ASSERT(index >= 0); - - UpdateInOutFrom(index); + UpdateInOutFrom(block_cache_.indexOf(b)); } void TrackOutput::MutedInputValueChanged() diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 6bc342256..89f58903a 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -82,7 +82,7 @@ public: Block* BlockAtTime(const rational& time) const; QList BlocksAtTimeRange(const TimeRange& range) const; - const QVector& Blocks() const; + const QList &Blocks() const; virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override; @@ -195,9 +195,10 @@ protected: private: void UpdateInOutFrom(int index); - void UpdatePreviousAndNextOfIndex(int index); + int GetInputIndexFromCacheIndex(int cache_index); + int GetInputIndexFromCacheIndex(Block* block); - QVector block_cache_; + QList block_cache_; NodeInputArray* block_input_; @@ -222,8 +223,6 @@ private slots: void BlockDisconnected(NodeEdgePtr edge); - void BlockListSizeChanged(int size); - void BlockLengthChanged(); void MutedInputValueChanged(); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 646a8309a..743c1b50b 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -161,7 +161,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge) for (int i=track_index+1; iGetSize(); i++) { Node* that_track = track_input_->At(i)->get_connected_node(); - if (that_track->IsTrack()) { + if (that_track && that_track->IsTrack()) { next = static_cast(that_track); break; } diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 663096514..d1f4f7ddd 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -212,8 +212,8 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N if (!shader) { // Since we have shader code, compile it now - QString frag_code = node->ShaderFragmentCode(input_params); QString vert_code = node->ShaderVertexCode(input_params); + QString frag_code = node->ShaderFragmentCode(input_params); if (frag_code.isEmpty()) { frag_code = OpenGLShader::CodeDefaultFragment(); @@ -258,7 +258,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N NodeInput* input = static_cast(param); // Get value from database at this input - NodeValue meta_value = node->InputValueFromTable(input, input_params, true); + NodeValue meta_value = node->InputValueFromTable(input, input_params, false); const QVariant& value = meta_value.data(); NodeParam::DataType data_type; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 310a85431..38d07ad35 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -421,7 +421,7 @@ Node* RenderBackend::CopyNodeConnections(Node* src_node) void RenderBackend::CopyNodeMakeConnection(NodeInput* src_input, NodeInput* dst_input) { - qDebug() << "Copying input" << src_input->id() << "from" << src_input->parentNode()->id(); + //qDebug() << "Copying input" << src_input->id() << "from" << src_input->parentNode()->id(); if (src_input->IsConnected()) { Node* dst_node = CopyNodeConnections(src_input->get_connected_node()); diff --git a/app/shaders/videoinput.vert b/app/shaders/matrix.vert similarity index 79% rename from app/shaders/videoinput.vert rename to app/shaders/matrix.vert index 59efb9515..dcfc4741f 100644 --- a/app/shaders/videoinput.vert +++ b/app/shaders/matrix.vert @@ -1,8 +1,8 @@ #version 150 -uniform mat4 matrix_in; +uniform mat4 %1; -uniform vec2 footage_in_resolution; +uniform vec2 %2_resolution; uniform vec2 ove_resolution; in vec4 a_position; @@ -27,10 +27,10 @@ void main() { transform *= scale_mat4(vec3(1.0 / ove_resolution, 1.0)); // Multiply by received matrix - transform *= matrix_in; + transform *= %1; // Scale back out to footage size - transform *= scale_mat4(vec3(footage_in_resolution, 1.0)); + transform *= scale_mat4(vec3(%2_resolution, 1.0)); gl_Position = transform * a_position; ove_texcoord = a_texcoord; diff --git a/app/shaders/shaders.qrc b/app/shaders/shaders.qrc index ab4e87535..7e6f4876c 100644 --- a/app/shaders/shaders.qrc +++ b/app/shaders/shaders.qrc @@ -17,7 +17,6 @@ solid.xml stroke.frag stroke.xml - videoinput.frag - videoinput.vert + matrix.vert diff --git a/app/shaders/videoinput.frag b/app/shaders/videoinput.frag deleted file mode 100644 index 01eb57b2e..000000000 --- a/app/shaders/videoinput.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 150 - -uniform sampler2D footage_in; - -in vec2 ove_texcoord; - -out vec4 fragColor; - -void main(void) { - fragColor = texture(footage_in, ove_texcoord); -} diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 9c48bc888..65ed2504d 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -42,6 +42,8 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked); layout->addWidget(connected_to_lbl_); + layout->addStretch(); + // Set up "link" font QFont link_font = connected_to_lbl_->font(); link_font.setUnderline(true); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 73264b243..fc8662f49 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -30,9 +30,10 @@ #include "core.h" #include "dialog/sequence/sequence.h" #include "node/audio/volume/volume.h" -#include "node/distort/transform/transform.h" +#include "node/generator/matrix/matrix.h" #include "node/input/media/audio/audio.h" #include "node/input/media/video/video.h" +#include "node/math/math/math.h" #include "project/item/sequence/sequence.h" #include "widget/nodeview/nodeviewundo.h" #include "window/mainwindow/mainwindow.h" @@ -397,15 +398,17 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert) VideoInput* video_input = new VideoInput(); video_input->SetFootage(footage_stream); new NodeAddCommand(dst_graph, video_input, command); - new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command); - TransformDistort* transform = new TransformDistort(); - new NodeAddCommand(dst_graph, transform, command); - new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command); + MatrixGenerator* matrix = new MatrixGenerator(); + new NodeAddCommand(dst_graph, matrix, command); - //OpacityNode* opacity = new OpacityNode(); - //NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); - //NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); + MathNode* multiply = new MathNode(); + multiply->SetOperation(MathNode::kOpMultiply); + new NodeAddCommand(dst_graph, multiply, command); + + new NodeEdgeAddCommand(video_input->output(), multiply->param_a_in(), command); + new NodeEdgeAddCommand(matrix->output(), multiply->param_b_in(), command); + new NodeEdgeAddCommand(multiply->output(), clip->texture_input(), command); break; } case Stream::kAudio: diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 992a69a89..6833b6c67 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -613,33 +613,31 @@ void TrackCleanGapsCommand::redo_internal() TrackOutput* track = track_list_->GetTrackAt(track_index_); foreach (Block* b, track->Blocks()) { - if (b) { - if (b->type() == Block::kGap) { - if (on_gap) { - consecutive_gaps.append(static_cast(b)); - } else { - on_gap = static_cast(b); - } - } else if (on_gap) { - merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); - - // Remove each gap and add to the length of the merged - // We can block the IC signal because merging gaps won't actually change anything - track->BlockInvalidateCache(); - rational new_gap_length = on_gap->length(); - foreach (GapBlock* gap, consecutive_gaps) { - track->RippleRemoveBlock(gap); - static_cast(track->parent())->TakeNode(gap, &memory_manager_); - - new_gap_length += gap->length(); - } - on_gap->set_length_and_media_out(new_gap_length); - track->UnblockInvalidateCache(); - - // Reset state - on_gap = nullptr; - consecutive_gaps.clear(); + if (b->type() == Block::kGap) { + if (on_gap) { + consecutive_gaps.append(static_cast(b)); + } else { + on_gap = static_cast(b); } + } else if (on_gap) { + merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); + + // Remove each gap and add to the length of the merged + // We can block the IC signal because merging gaps won't actually change anything + track->BlockInvalidateCache(); + rational new_gap_length = on_gap->length(); + foreach (GapBlock* gap, consecutive_gaps) { + track->RippleRemoveBlock(gap); + static_cast(track->parent())->TakeNode(gap, &memory_manager_); + + new_gap_length += gap->length(); + } + on_gap->set_length_and_media_out(new_gap_length); + track->UnblockInvalidateCache(); + + // Reset state + on_gap = nullptr; + consecutive_gaps.clear(); } } diff --git a/app/widget/timelinewidget/view/timelineviewblockitem.cpp b/app/widget/timelinewidget/view/timelineviewblockitem.cpp index 1f022450a..7e182657f 100644 --- a/app/widget/timelinewidget/view/timelineviewblockitem.cpp +++ b/app/widget/timelinewidget/view/timelineviewblockitem.cpp @@ -134,7 +134,11 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI } else { painter->setPen(Qt::lightGray); } - painter->drawText(rect(), static_cast(Qt::AlignLeft | Qt::AlignTop), block_->block_name()); + + int text_top = TrackOutput::GetTrackHeightMinimum() / 2 - painter->fontMetrics().height() / 2; + QRectF text_rect = rect(); + text_rect.adjust(0, text_top, 0, 0); + painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, block_->block_name()); // Linked clips are underlined if (block_->HasLinks()) {