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()) {