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:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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}
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AudioInput::AudioInput()
|
||||
{
|
||||
}
|
||||
|
||||
Node *AudioInput::copy() const
|
||||
{
|
||||
return new AudioInput();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user