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