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.
This commit is contained in:
itsmattkc
2020-04-27 04:11:24 +10:00
parent f61321d68b
commit ac3f49a845
26 changed files with 307 additions and 358 deletions
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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:
+1 -1
View File
@@ -36,7 +36,7 @@ public:
kClipBlock,
kGapBlock,
kAudioInput,
kTransformDistort,
kMatrixGenerator,
kVideoInput,
kTrackOutput,
kAudioVolume,
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(transform)
add_subdirectory(matrix)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
@@ -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
)
@@ -18,14 +18,14 @@
***/
#include "transform.h"
#include "matrix.h"
#include <QMatrix4x4>
#include <QVector2D>
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());
}
@@ -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;
-4
View File
@@ -22,10 +22,6 @@
OLIVE_NAMESPACE_ENTER
AudioInput::AudioInput()
{
}
Node *AudioInput::copy() const
{
return new AudioInput();
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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);
}
-33
View File
@@ -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
+1 -14
View File
@@ -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
+103 -91
View File
@@ -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<Operation>(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<QVector2D>();
case NodeParam::kVec3:
return val.data().value<QVector3D>();
case NodeParam::kVec4:
default:
return val.data().value<QVector4D>();
}
}
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<rational>().toDouble();
} else {
return val.data().toFloat();
}
}
MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b)
{
QVector<int> pair_likelihood_a = GetPairLikelihood(table_a);
QVector<int> 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<int> likelihoods(kPairCount);
for (int i=0;i<kPairCount;i++) {
if (pair_likelihood_a.at(i) == -1 || pair_likelihood_b.at(i) == -1) {
likelihoods.replace(i, -1);
} else {
likelihoods.replace(i, pair_likelihood_a.at(i) + weight_a + pair_likelihood_b.at(i) + weight_b);
}
}
most_likely_pairing_ = kPairNone;
for (int i=0;i<likelihoods.size();i++) {
if (likelihoods.at(i) > -1) {
if (most_likely_pairing_ == kPairNone
|| likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) {
most_likely_pairing_ = static_cast<Pairing>(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<int> 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<int> MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable
return likelihood;
}
MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector<int> &a,
const QVector<int> &b,
const int& weight_a,
const int& weight_b)
{
QVector<int> likelihoods(kPairCount);
for (int i=0;i<likelihoods.size();i++) {
if (a.at(i) == -1 || b.at(i) == -1) {
likelihoods.replace(i, -1);
} else {
likelihoods.replace(i, a.at(i) + weight_a + b.at(i) + weight_b);
}
}
Pairing pairing = kPairNone;
for (int i=0;i<likelihoods.size();i++) {
if (likelihoods.at(i) > -1) {
if (pairing == kPairNone
|| likelihoods.at(i) > likelihoods.at(pairing)) {
pairing = static_cast<Pairing>(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<QVector2D>();
case NodeParam::kVec3:
return val.data().value<QVector3D>();
case NodeParam::kVec4:
default:
return val.data().value<QVector4D>();
}
}
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<rational>().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<int> &likelihood) const
{
return table.At(likelihood.at(most_likely_pairing_));
return most_likely_value_b_;
}
template<typename T, typename U>
+5 -11
View File
@@ -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<int> &a, const QVector<int> &b, const int &weight_a, const int &weight_b);
static QVector<int> GetPairLikelihood(const NodeValueTable& table);
const NodeValue &GetMostLikelyValue(const NodeValueTable& table, const QVector<int>& likelihood) const;
Pairing most_likely_pairing_;
const NodeValueTable& table_a_;
NodeValue most_likely_value_a_;
const NodeValueTable& table_b_;
QVector<int> pair_likelihood_a_;
QVector<int> pair_likelihood_b_;
NodeValue most_likely_value_b_;
};
+114 -114
View File
@@ -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<Block *> TrackOutput::BlocksAtTimeRange(const TimeRange &range) const
return list;
}
const QVector<Block *> &TrackOutput::Blocks() const
const QList<Block *> &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; i<block_cache_.size(); i++) {
Block* b = block_cache_.at(i);
if (b) {
new_track_length = b->out();
break;
}
}
b->set_in(last_out);
for (int i=index;i<block_cache_.size();i++) {
Block* b = block_cache_.at(i);
last_out += b->length();
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; i<block_input_->GetSize(); i++) {
if (block_input_->At(i)->get_connected_node() == block) {
return i;
}
}
// Find next
for (int i=index+1;i<block_cache_.size();i++) {
next = block_cache_.at(i);
if (next) {
break;
}
}
if (ref) {
// Link blocks together
ref->set_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<Block*>(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<Block*>(connected_node);
// See where this input falls in our internal "block cache"
Block* next = nullptr;
for (int i=block_input_->IndexOfSubParameter(edge->input())+1; i<block_input_->GetSize(); 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<Block*>(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<Block*>(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<Block*>(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<size;i++) {
block_cache_.replace(i, nullptr);
}
}
void TrackOutput::BlockLengthChanged()
{
// Assumes sender is a Block
Block* b = static_cast<Block*>(sender());
int index = block_cache_.indexOf(b);
Q_ASSERT(index >= 0);
UpdateInOutFrom(index);
UpdateInOutFrom(block_cache_.indexOf(b));
}
void TrackOutput::MutedInputValueChanged()
+4 -5
View File
@@ -82,7 +82,7 @@ public:
Block* BlockAtTime(const rational& time) const;
QList<Block*> BlocksAtTimeRange(const TimeRange& range) const;
const QVector<Block*>& Blocks() const;
const QList<Block *> &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*> block_cache_;
QList<Block*> block_cache_;
NodeInputArray* block_input_;
@@ -222,8 +223,6 @@ private slots:
void BlockDisconnected(NodeEdgePtr edge);
void BlockListSizeChanged(int size);
void BlockLengthChanged();
void MutedInputValueChanged();
+1 -1
View File
@@ -161,7 +161,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge)
for (int i=track_index+1; i<track_input_->GetSize(); i++) {
Node* that_track = track_input_->At(i)->get_connected_node();
if (that_track->IsTrack()) {
if (that_track && that_track->IsTrack()) {
next = static_cast<TrackOutput*>(that_track);
break;
}
+2 -2
View File
@@ -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<NodeInput*>(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;
+1 -1
View File
@@ -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());
@@ -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;
+1 -2
View File
@@ -17,7 +17,6 @@
<file>solid.xml</file>
<file>stroke.frag</file>
<file>stroke.xml</file>
<file>videoinput.frag</file>
<file>videoinput.vert</file>
<file>matrix.vert</file>
</qresource>
</RCC>
-11
View File
@@ -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);
}
@@ -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);
+11 -8
View File
@@ -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:
+24 -26
View File
@@ -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<GapBlock*>(b));
} else {
on_gap = static_cast<GapBlock*>(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<NodeGraph*>(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<GapBlock*>(b));
} else {
on_gap = static_cast<GapBlock*>(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<NodeGraph*>(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();
}
}
@@ -134,7 +134,11 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
} else {
painter->setPen(Qt::lightGray);
}
painter->drawText(rect(), static_cast<int>(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()) {