use strings to connect nodes
This makes the node system somewhat more high-level with the intent of making working with them far more flexible and stable. By making the architecture more abstracted, it becomes far less rigid which should allow us to do even more with it and make it much less crash prone.
This commit is contained in:
@@ -220,6 +220,8 @@ QString FFmpegDecoder::id()
|
|||||||
|
|
||||||
Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
|
Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(cancelled)
|
||||||
|
|
||||||
// Variable for receiving errors from FFmpeg
|
// Variable for receiving errors from FFmpeg
|
||||||
int error_code;
|
int error_code;
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ namespace olive {
|
|||||||
void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command)
|
void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command)
|
||||||
{
|
{
|
||||||
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
|
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
|
||||||
Node* out = xml_node_data.node_ptrs.value(con.output);
|
NodeOutput out(xml_node_data.node_ptrs.value(con.output_node), con.output);
|
||||||
|
|
||||||
if (out) {
|
if (out.IsValid()) {
|
||||||
if (command) {
|
if (command) {
|
||||||
command->add_child(new NodeEdgeAddCommand(out, con.input, con.element));
|
command->add_child(new NodeEdgeAddCommand(out, con.input));
|
||||||
} else {
|
} else {
|
||||||
Node::ConnectEdge(out, con.input, con.element);
|
Node::ConnectEdge(out, con.input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-11
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
#include "node/param.h"
|
||||||
#include "project/item/footage/stream.h"
|
#include "project/item/footage/stream.h"
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
|
|
||||||
@@ -38,15 +39,9 @@ class NodeInput;
|
|||||||
|
|
||||||
struct XMLNodeData {
|
struct XMLNodeData {
|
||||||
struct SerializedConnection {
|
struct SerializedConnection {
|
||||||
NodeInput* input;
|
NodeInput input;
|
||||||
int element;
|
quintptr output_node;
|
||||||
quintptr output;
|
QString output;
|
||||||
};
|
|
||||||
|
|
||||||
struct FootageConnection {
|
|
||||||
NodeInput* input;
|
|
||||||
int element;
|
|
||||||
quintptr footage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockLink {
|
struct BlockLink {
|
||||||
@@ -56,8 +51,6 @@ struct XMLNodeData {
|
|||||||
|
|
||||||
QHash<quintptr, Node*> node_ptrs;
|
QHash<quintptr, Node*> node_ptrs;
|
||||||
QList<SerializedConnection> desired_connections;
|
QList<SerializedConnection> desired_connections;
|
||||||
QHash<quintptr, Stream*> footage_ptrs;
|
|
||||||
QList<FootageConnection> footage_connections;
|
|
||||||
QList<BlockLink> block_links;
|
QList<BlockLink> block_links;
|
||||||
QHash<quintptr, Item*> item_ptrs;
|
QHash<quintptr, Item*> item_ptrs;
|
||||||
|
|
||||||
|
|||||||
@@ -892,6 +892,8 @@ QString Core::GetProjectFilter(bool include_any_filter)
|
|||||||
if (include_any_filter) {
|
if (include_any_filter) {
|
||||||
filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;"));
|
filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;"));
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(include_any_filter)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Append standard filter
|
// Append standard filter
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class CodecSection : public QWidget
|
|||||||
public:
|
public:
|
||||||
CodecSection(QWidget* parent = nullptr);
|
CodecSection(QWidget* parent = nullptr);
|
||||||
|
|
||||||
virtual void AddOpts(EncodingParams* params){}
|
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "node/input.h"
|
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "widget/menu/menu.h"
|
#include "widget/menu/menu.h"
|
||||||
|
|||||||
+13
-13
@@ -25,28 +25,28 @@ add_subdirectory(output)
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
node/connectable.cpp
|
|
||||||
node/connectable.h
|
|
||||||
node/factory.h
|
|
||||||
node/factory.cpp
|
node/factory.cpp
|
||||||
node/graph.h
|
node/factory.h
|
||||||
node/graph.cpp
|
node/graph.cpp
|
||||||
node/input.h
|
node/graph.h
|
||||||
node/input.cpp
|
|
||||||
node/inputdragger.h
|
|
||||||
node/inputdragger.cpp
|
node/inputdragger.cpp
|
||||||
node/inputimmediate.h
|
node/inputdragger.h
|
||||||
node/inputimmediate.cpp
|
node/inputimmediate.cpp
|
||||||
node/keyframe.h
|
node/inputimmediate.h
|
||||||
node/keyframe.cpp
|
node/keyframe.cpp
|
||||||
node/node.h
|
node/keyframe.h
|
||||||
node/node.cpp
|
node/node.cpp
|
||||||
node/nodecopypaste.h
|
node/node.h
|
||||||
node/nodecopypaste.cpp
|
node/nodecopypaste.cpp
|
||||||
|
node/nodecopypaste.h
|
||||||
|
node/param.cpp
|
||||||
|
node/param.h
|
||||||
node/splitvalue.h
|
node/splitvalue.h
|
||||||
node/traverser.h
|
|
||||||
node/traverser.cpp
|
node/traverser.cpp
|
||||||
node/value.h
|
node/traverser.h
|
||||||
node/value.cpp
|
node/value.cpp
|
||||||
|
node/value.h
|
||||||
|
node/valuedatabase.cpp
|
||||||
|
node/valuedatabase.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
+18
-13
@@ -24,14 +24,17 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString PanNode::kSamplesInput = QStringLiteral("samples_in");
|
||||||
|
const QString PanNode::kPanningInput = QStringLiteral("panning_in");
|
||||||
|
|
||||||
PanNode::PanNode()
|
PanNode::PanNode()
|
||||||
{
|
{
|
||||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
panning_input_ = new NodeInput(this, QStringLiteral("panning_in"), NodeValue::kFloat, 0.0);
|
AddInput(kPanningInput, NodeValue::kFloat, 0.0);
|
||||||
panning_input_->setProperty("min", -1.0);
|
SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0);
|
||||||
panning_input_->setProperty("max", 1.0);
|
SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0);
|
||||||
panning_input_->setProperty("view", FloatSlider::kPercentage);
|
SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *PanNode::copy() const
|
Node *PanNode::copy() const
|
||||||
@@ -59,18 +62,20 @@ QString PanNode::Description() const
|
|||||||
return tr("Adjust the stereo panning of an audio source.");
|
return tr("Adjust the stereo panning of an audio source.");
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable PanNode::Value(NodeValueDatabase &value) const
|
NodeValueTable PanNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
// Create a sample job
|
// Create a sample job
|
||||||
SampleJob job(samples_input_, value);
|
SampleJob job(kSamplesInput, value);
|
||||||
job.InsertValue(panning_input_, value);
|
job.InsertValue(this, kPanningInput, value);
|
||||||
|
|
||||||
// Push it to our table
|
// Push it to our table
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (job.HasSamples()) {
|
if (job.HasSamples()) {
|
||||||
float pan_volume = job.GetValue(panning_input_).data().toFloat();
|
float pan_volume = job.GetValue(kPanningInput).data().toFloat();
|
||||||
if (panning_input_->IsStatic()) {
|
if (IsInputStatic(kPanningInput)) {
|
||||||
if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) {
|
if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) {
|
||||||
if (pan_volume > 0) {
|
if (pan_volume > 0) {
|
||||||
job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume);
|
job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume);
|
||||||
@@ -95,7 +100,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float pan_val = values[panning_input_].Get(NodeValue::kFloat).toFloat();
|
float pan_val = values[kPanningInput].Get(NodeValue::kFloat).toFloat();
|
||||||
|
|
||||||
for (int i=0;i<input->audio_params().channel_count();i++) {
|
for (int i=0;i<input->audio_params().channel_count();i++) {
|
||||||
output->data()[i][index] = input->data()[i][index];
|
output->data()[i][index] = input->data()[i][index];
|
||||||
@@ -110,8 +115,8 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in
|
|||||||
|
|
||||||
void PanNode::Retranslate()
|
void PanNode::Retranslate()
|
||||||
{
|
{
|
||||||
samples_input_->set_name(tr("Samples"));
|
SetInputName(kSamplesInput, tr("Samples"));
|
||||||
panning_input_->set_name(tr("Pan"));
|
SetInputName(kPanningInput, tr("Pan"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,15 @@ public:
|
|||||||
virtual QVector<CategoryID> Category() const override;
|
virtual QVector<CategoryID> Category() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
static const QString kSamplesInput;
|
||||||
|
static const QString kPanningInput;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeInput* samples_input_;
|
NodeInput* samples_input_;
|
||||||
NodeInput* panning_input_;
|
NodeInput* panning_input_;
|
||||||
|
|||||||
@@ -24,14 +24,16 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in");
|
||||||
|
const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in");
|
||||||
|
|
||||||
VolumeNode::VolumeNode()
|
VolumeNode::VolumeNode()
|
||||||
{
|
{
|
||||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||||
samples_input_->SetKeyframable(false);
|
|
||||||
|
|
||||||
volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0);
|
AddInput(kVolumeInput, NodeValue::kFloat, 1.0);
|
||||||
volume_input_->setProperty("min", 0.0);
|
SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0);
|
||||||
volume_input_->setProperty("view", FloatSlider::kDecibel);
|
SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *VolumeNode::copy() const
|
Node *VolumeNode::copy() const
|
||||||
@@ -59,26 +61,28 @@ QString VolumeNode::Description() const
|
|||||||
return tr("Adjusts the volume of an audio source.");
|
return tr("Adjusts the volume of an audio source.");
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const
|
NodeValueTable VolumeNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
return ValueInternal(value,
|
return ValueInternal(value,
|
||||||
kOpMultiply,
|
kOpMultiply,
|
||||||
kPairSampleNumber,
|
kPairSampleNumber,
|
||||||
samples_input_,
|
kSamplesInput,
|
||||||
value[samples_input_].TakeWithMeta(NodeValue::kSamples),
|
value[kSamplesInput].TakeWithMeta(NodeValue::kSamples),
|
||||||
volume_input_,
|
kVolumeInput,
|
||||||
value[volume_input_].TakeWithMeta(NodeValue::kFloat));
|
value[kVolumeInput].TakeWithMeta(NodeValue::kFloat));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
||||||
{
|
{
|
||||||
return ProcessSamplesInternal(values, kOpMultiply, samples_input_, volume_input_, input, output, index);
|
return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeNode::Retranslate()
|
void VolumeNode::Retranslate()
|
||||||
{
|
{
|
||||||
samples_input_->set_name(tr("Samples"));
|
SetInputName(kSamplesInput, tr("Samples"));
|
||||||
volume_input_->set_name(tr("Volume"));
|
SetInputName(kVolumeInput, tr("Volume"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,20 +38,14 @@ public:
|
|||||||
virtual QVector<CategoryID> Category() const override;
|
virtual QVector<CategoryID> Category() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
NodeInput* samples_input() const
|
static const QString kSamplesInput;
|
||||||
{
|
static const QString kVolumeInput;
|
||||||
return samples_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* samples_input_;
|
|
||||||
NodeInput* volume_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+37
-39
@@ -28,6 +28,11 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString Block::kLengthInput = QStringLiteral("length_in");
|
||||||
|
const QString Block::kMediaInInput = QStringLiteral("media_in_in");
|
||||||
|
const QString Block::kEnabledInput = QStringLiteral("enabled_in");
|
||||||
|
const QString Block::kSpeedInput = QStringLiteral("speed_in");
|
||||||
|
|
||||||
Block::Block() :
|
Block::Block() :
|
||||||
previous_(nullptr),
|
previous_(nullptr),
|
||||||
next_(nullptr),
|
next_(nullptr),
|
||||||
@@ -36,24 +41,18 @@ Block::Block() :
|
|||||||
in_transition_(nullptr),
|
in_transition_(nullptr),
|
||||||
out_transition_(nullptr)
|
out_transition_(nullptr)
|
||||||
{
|
{
|
||||||
length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational);
|
AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
length_input_->SetConnectable(false);
|
IgnoreInvalidationsFrom(kLengthInput);
|
||||||
length_input_->SetKeyframable(false);
|
IgnoreHashingFrom(kLengthInput);
|
||||||
IgnoreInvalidationsFrom(length_input_);
|
|
||||||
connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthChanged);
|
|
||||||
|
|
||||||
media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational);
|
AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
media_in_input_->SetConnectable(false);
|
IgnoreHashingFrom(kMediaInInput);
|
||||||
media_in_input_->SetKeyframable(false);
|
|
||||||
|
|
||||||
enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean);
|
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
enabled_input_->SetConnectable(false);
|
|
||||||
enabled_input_->SetKeyframable(false);
|
|
||||||
enabled_input_->SetStandardValue(true);
|
|
||||||
|
|
||||||
speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat);
|
AddInput(kSpeedInput, NodeValue::kFloat, 1.0);
|
||||||
speed_input_->SetStandardValue(1.0);
|
SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
speed_input_->setProperty("view", FloatSlider::kPercentage);
|
IgnoreHashingFrom(kSpeedInput);
|
||||||
|
|
||||||
// A block's length must be greater than 0
|
// A block's length must be greater than 0
|
||||||
set_length_and_media_out(1);
|
set_length_and_media_out(1);
|
||||||
@@ -66,7 +65,7 @@ QVector<Node::CategoryID> Block::Category() const
|
|||||||
|
|
||||||
rational Block::length() const
|
rational Block::length() const
|
||||||
{
|
{
|
||||||
return length_input_->GetStandardValue().value<rational>();
|
return GetStandardValue(kLengthInput).value<rational>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::set_length_and_media_out(const rational &length)
|
void Block::set_length_and_media_out(const rational &length)
|
||||||
@@ -97,22 +96,22 @@ void Block::set_length_and_media_in(const rational &length)
|
|||||||
|
|
||||||
rational Block::media_in() const
|
rational Block::media_in() const
|
||||||
{
|
{
|
||||||
return media_in_input_->GetStandardValue().value<rational>();
|
return GetStandardValue(kMediaInInput).value<rational>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::set_media_in(const rational &media_in)
|
void Block::set_media_in(const rational &media_in)
|
||||||
{
|
{
|
||||||
media_in_input_->SetStandardValue(QVariant::fromValue(media_in));
|
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Block::is_enabled() const
|
bool Block::is_enabled() const
|
||||||
{
|
{
|
||||||
return enabled_input_->GetStandardValue().toBool();
|
return GetStandardValue(kEnabledInput).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::set_enabled(bool e)
|
void Block::set_enabled(bool e)
|
||||||
{
|
{
|
||||||
enabled_input_->SetStandardValue(e);
|
SetStandardValue(kEnabledInput, e);
|
||||||
|
|
||||||
emit EnabledChanged();
|
emit EnabledChanged();
|
||||||
}
|
}
|
||||||
@@ -127,10 +126,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
|
|||||||
rational local_time = sequence_time;
|
rational local_time = sequence_time;
|
||||||
|
|
||||||
// FIXME: Doesn't handle reversing
|
// FIXME: Doesn't handle reversing
|
||||||
if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) {
|
if (IsInputStatic(kSpeedInput)) {
|
||||||
// FIXME: We'll need to calculate the speed hoo boy
|
double speed_value = GetStandardValue(kSpeedInput).toDouble();
|
||||||
} else {
|
|
||||||
double speed_value = speed_input_->GetStandardValue().toDouble();
|
|
||||||
|
|
||||||
if (qIsNull(speed_value)) {
|
if (qIsNull(speed_value)) {
|
||||||
// Effectively holds the frame at the in point
|
// Effectively holds the frame at the in point
|
||||||
@@ -139,6 +136,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
|
|||||||
// Multiply time
|
// Multiply time
|
||||||
local_time = rational::fromDouble(local_time.toDouble() * speed_value);
|
local_time = rational::fromDouble(local_time.toDouble() * speed_value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME: We'll need to calculate the speed hoo boy
|
||||||
}
|
}
|
||||||
|
|
||||||
return local_time + media_in();
|
return local_time + media_in();
|
||||||
@@ -154,10 +153,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
|
|||||||
rational sequence_time = media_time - media_in();
|
rational sequence_time = media_time - media_in();
|
||||||
|
|
||||||
// FIXME: Doesn't handle reversing
|
// FIXME: Doesn't handle reversing
|
||||||
if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) {
|
if (IsInputKeyframing(kSpeedInput) || IsInputConnected(kSpeedInput)) {
|
||||||
// FIXME: We'll need to calculate the speed hoo boy
|
// FIXME: We'll need to calculate the speed hoo boy
|
||||||
} else {
|
} else {
|
||||||
double speed_value = speed_input_->GetStandardValue().toDouble();
|
double speed_value = GetStandardValue(kSpeedInput).toDouble();
|
||||||
|
|
||||||
if (qIsNull(speed_value)) {
|
if (qIsNull(speed_value)) {
|
||||||
// Effectively holds the frame at the in point, also prevents divide by zero
|
// Effectively holds the frame at the in point, also prevents divide by zero
|
||||||
@@ -171,16 +170,15 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
|
|||||||
return sequence_time;
|
return sequence_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<NodeInput *> Block::GetInputsToHash() const
|
void Block::InputValueChangedEvent(const QString &input, int element)
|
||||||
{
|
{
|
||||||
QVector<NodeInput*> inputs = Node::GetInputsToHash();
|
Q_UNUSED(element)
|
||||||
|
|
||||||
// Ignore these inputs
|
if (input == kLengthInput) {
|
||||||
inputs.removeOne(media_in_input_);
|
emit LengthChanged();
|
||||||
inputs.removeOne(speed_input_);
|
} else if (input == kEnabledInput) {
|
||||||
inputs.removeOne(length_input_);
|
emit EnabledChanged();
|
||||||
|
}
|
||||||
return inputs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::LinkChangeEvent()
|
void Block::LinkChangeEvent()
|
||||||
@@ -198,17 +196,17 @@ void Block::LinkChangeEvent()
|
|||||||
|
|
||||||
void Block::set_length_internal(const rational &length)
|
void Block::set_length_internal(const rational &length)
|
||||||
{
|
{
|
||||||
length_input_->SetStandardValue(QVariant::fromValue(length));
|
SetStandardValue(kLengthInput, QVariant::fromValue(length));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::Retranslate()
|
void Block::Retranslate()
|
||||||
{
|
{
|
||||||
Node::Retranslate();
|
Node::Retranslate();
|
||||||
|
|
||||||
length_input_->set_name(tr("Length"));
|
SetInputName(kLengthInput, tr("Length"));
|
||||||
media_in_input_->set_name(tr("Media In"));
|
SetInputName(kMediaInInput, tr("Media In"));
|
||||||
enabled_input_->set_name(tr("Enabled"));
|
SetInputName(kEnabledInput, tr("Enabled"));
|
||||||
speed_input_->set_name(tr("Speed"));
|
SetInputName(kSpeedInput, tr("Speed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::Hash(QCryptographicHash &, const rational &) const
|
void Block::Hash(QCryptographicHash &, const rational &) const
|
||||||
|
|||||||
+6
-21
@@ -114,21 +114,6 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
NodeInput* length_input() const
|
|
||||||
{
|
|
||||||
return length_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* media_in_input() const
|
|
||||||
{
|
|
||||||
return media_in_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* speed_input() const
|
|
||||||
{
|
|
||||||
return speed_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
TransitionBlock* in_transition()
|
TransitionBlock* in_transition()
|
||||||
{
|
{
|
||||||
return in_transition_;
|
return in_transition_;
|
||||||
@@ -166,6 +151,11 @@ public:
|
|||||||
|
|
||||||
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
||||||
|
|
||||||
|
static const QString kLengthInput;
|
||||||
|
static const QString kMediaInInput;
|
||||||
|
static const QString kEnabledInput;
|
||||||
|
static const QString kSpeedInput;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -178,7 +168,7 @@ protected:
|
|||||||
|
|
||||||
rational MediaToSequenceTime(const rational& media_time) const;
|
rational MediaToSequenceTime(const rational& media_time) const;
|
||||||
|
|
||||||
virtual QVector<NodeInput*> GetInputsToHash() const override;
|
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||||
|
|
||||||
virtual void LinkChangeEvent() override;
|
virtual void LinkChangeEvent() override;
|
||||||
|
|
||||||
@@ -188,11 +178,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void set_length_internal(const rational &length);
|
void set_length_internal(const rational &length);
|
||||||
|
|
||||||
NodeInput* length_input_;
|
|
||||||
NodeInput* media_in_input_;
|
|
||||||
NodeInput* speed_input_;
|
|
||||||
NodeInput* enabled_input_;
|
|
||||||
|
|
||||||
rational in_point_;
|
rational in_point_;
|
||||||
rational out_point_;
|
rational out_point_;
|
||||||
Track* track_;
|
Track* track_;
|
||||||
|
|||||||
@@ -22,10 +22,11 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in");
|
||||||
|
|
||||||
ClipBlock::ClipBlock()
|
ClipBlock::ClipBlock()
|
||||||
{
|
{
|
||||||
texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone);
|
AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||||
texture_input_->SetKeyframable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *ClipBlock::copy() const
|
Node *ClipBlock::copy() const
|
||||||
@@ -53,15 +54,12 @@ QString ClipBlock::Description() const
|
|||||||
return tr("A time-based node that represents a media source.");
|
return tr("A time-based node that represents a media source.");
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput *ClipBlock::texture_input() const
|
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||||
{
|
{
|
||||||
return texture_input_;
|
Q_UNUSED(element)
|
||||||
}
|
|
||||||
|
|
||||||
void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
|
||||||
{
|
|
||||||
// If signal is from texture input, transform all times from media time to sequence time
|
// If signal is from texture input, transform all times from media time to sequence time
|
||||||
if (from.input == texture_input_) {
|
if (from == kBufferIn) {
|
||||||
// Adjust range from media time to sequence time
|
// Adjust range from media time to sequence time
|
||||||
rational start = MediaToSequenceTime(range.in());
|
rational start = MediaToSequenceTime(range.in());
|
||||||
rational end = MediaToSequenceTime(range.out());
|
rational end = MediaToSequenceTime(range.out());
|
||||||
@@ -73,32 +71,34 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeRange ClipBlock::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(element)
|
Q_UNUSED(element)
|
||||||
|
|
||||||
if (input == texture_input_) {
|
if (input == kBufferIn) {
|
||||||
return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out()));
|
return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Block::InputTimeAdjustment(input, element, input_time);
|
return Block::InputTimeAdjustment(input, element, input_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeRange ClipBlock::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(element)
|
Q_UNUSED(element)
|
||||||
|
|
||||||
if (input == texture_input_) {
|
if (input == kBufferIn) {
|
||||||
return TimeRange(MediaToSequenceTime(input_time.in()), MediaToSequenceTime(input_time.out()));
|
return TimeRange(MediaToSequenceTime(input_time.in()), MediaToSequenceTime(input_time.out()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Block::OutputTimeAdjustment(input, element, input_time);
|
return Block::OutputTimeAdjustment(input, element, input_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable ClipBlock::Value(NodeValueDatabase &value) const
|
NodeValueTable ClipBlock::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
// We discard most values here except for the buffer we received
|
// We discard most values here except for the buffer we received
|
||||||
NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer);
|
NodeValue data = value[kBufferIn].GetWithMeta(NodeValue::kBuffer);
|
||||||
|
|
||||||
NodeValueTable table;
|
NodeValueTable table;
|
||||||
if (data.type() != NodeValue::kNone) {
|
if (data.type() != NodeValue::kNone) {
|
||||||
@@ -111,15 +111,15 @@ void ClipBlock::Retranslate()
|
|||||||
{
|
{
|
||||||
Block::Retranslate();
|
Block::Retranslate();
|
||||||
|
|
||||||
texture_input_->set_name(tr("Buffer"));
|
SetInputName(kBufferIn, tr("Buffer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const
|
void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const
|
||||||
{
|
{
|
||||||
if (texture_input_->IsConnected()) {
|
if (IsInputConnected(kBufferIn)) {
|
||||||
rational t = InputTimeAdjustment(texture_input_, -1, TimeRange(time, time)).in();
|
rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in();
|
||||||
|
|
||||||
texture_input_->GetConnectedNode()->Hash(hash, t);
|
GetConnectedNode(kBufferIn)->Hash(hash, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,22 +42,19 @@ public:
|
|||||||
virtual QString id() const override;
|
virtual QString id() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
NodeInput* texture_input() const;
|
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
|
||||||
|
|
||||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||||
|
|
||||||
virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||||
|
|
||||||
virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
||||||
|
|
||||||
private:
|
static const QString kBufferIn;
|
||||||
NodeInput* texture_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ public:
|
|||||||
virtual QString id() const override;
|
virtual QString id() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) cons
|
|||||||
|
|
||||||
void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const
|
void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(value)
|
||||||
|
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,11 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString DipToColorTransition::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
DipToColorTransition::DipToColorTransition()
|
DipToColorTransition::DipToColorTransition()
|
||||||
{
|
{
|
||||||
color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0)));
|
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *DipToColorTransition::copy() const
|
Node *DipToColorTransition::copy() const
|
||||||
@@ -61,7 +63,7 @@ ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) const
|
|||||||
|
|
||||||
void DipToColorTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const
|
void DipToColorTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const
|
||||||
{
|
{
|
||||||
job.InsertValue(color_input_, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,11 @@ public:
|
|||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||||
|
|
||||||
|
static const QString kColorInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const override;
|
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const override;
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* color_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,23 +24,19 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString TransitionBlock::kOutBlockInput = QStringLiteral("out_block_in");
|
||||||
|
const QString TransitionBlock::kInBlockInput = QStringLiteral("in_block_in");
|
||||||
|
const QString TransitionBlock::kCurveInput = QStringLiteral("curve_in");
|
||||||
|
|
||||||
TransitionBlock::TransitionBlock() :
|
TransitionBlock::TransitionBlock() :
|
||||||
connected_out_block_(nullptr),
|
connected_out_block_(nullptr),
|
||||||
connected_in_block_(nullptr)
|
connected_in_block_(nullptr)
|
||||||
{
|
{
|
||||||
out_block_input_ = new NodeInput(this, QStringLiteral("out_block_in"), NodeValue::kNone);
|
AddInput(kOutBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||||
out_block_input_->SetKeyframable(false);
|
|
||||||
connect(out_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::InBlockConnected);
|
|
||||||
connect(out_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::InBlockDisconnected);
|
|
||||||
|
|
||||||
in_block_input_ = new NodeInput(this, QStringLiteral("in_block_in"), NodeValue::kNone);
|
AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||||
in_block_input_->SetKeyframable(false);
|
|
||||||
connect(in_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::OutBlockConnected);
|
|
||||||
connect(in_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::OutBlockDisconnected);
|
|
||||||
|
|
||||||
curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo);
|
AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||||
curve_input_->SetKeyframable(false);
|
|
||||||
curve_input_->SetConnectable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Block::Type TransitionBlock::type() const
|
Block::Type TransitionBlock::type() const
|
||||||
@@ -48,26 +44,16 @@ Block::Type TransitionBlock::type() const
|
|||||||
return kTransition;
|
return kTransition;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput *TransitionBlock::out_block_input() const
|
|
||||||
{
|
|
||||||
return out_block_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput *TransitionBlock::in_block_input() const
|
|
||||||
{
|
|
||||||
return in_block_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionBlock::Retranslate()
|
void TransitionBlock::Retranslate()
|
||||||
{
|
{
|
||||||
Block::Retranslate();
|
Block::Retranslate();
|
||||||
|
|
||||||
out_block_input_->set_name(tr("From"));
|
SetInputName(kOutBlockInput, tr("From"));
|
||||||
in_block_input_->set_name(tr("To"));
|
SetInputName(kInBlockInput, tr("To"));
|
||||||
curve_input_->set_name(tr("Curve"));
|
SetInputName(kCurveInput, tr("Curve"));
|
||||||
|
|
||||||
// These must correspond to the CurveType enum
|
// These must correspond to the CurveType enum
|
||||||
curve_input_->set_combobox_strings({ tr("Linear"), tr("Exponential"), tr("Logarithmic") });
|
SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") });
|
||||||
}
|
}
|
||||||
|
|
||||||
rational TransitionBlock::in_offset() const
|
rational TransitionBlock::in_offset() const
|
||||||
@@ -169,56 +155,16 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t
|
|||||||
NodeValue(NodeValue::kFloat, GetInProgress(time), this));
|
NodeValue(NodeValue::kFloat, GetInProgress(time), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransitionBlock::OutBlockConnected(Node *node)
|
NodeValueTable TransitionBlock::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
// If node is not a block, this will just be null
|
Q_UNUSED(output)
|
||||||
if ((connected_out_block_ = dynamic_cast<Block*>(node))) {
|
|
||||||
|
|
||||||
Q_ASSERT(connected_out_block_->type() != Block::kTransition
|
|
||||||
&& !connected_out_block_->out_transition()
|
|
||||||
&& connected_out_block_ == this->previous());
|
|
||||||
|
|
||||||
connected_out_block_->set_out_transition(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionBlock::OutBlockDisconnected()
|
|
||||||
{
|
|
||||||
if (connected_out_block_) {
|
|
||||||
connected_out_block_->set_in_transition(nullptr);
|
|
||||||
connected_out_block_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionBlock::InBlockConnected(Node *node)
|
|
||||||
{
|
|
||||||
// If node is not a block, this will just be null
|
|
||||||
if ((connected_in_block_ = dynamic_cast<Block*>(node))) {
|
|
||||||
|
|
||||||
Q_ASSERT(connected_in_block_->type() != Block::kTransition
|
|
||||||
&& !connected_in_block_->in_transition()
|
|
||||||
&& connected_in_block_ == this->next());
|
|
||||||
|
|
||||||
connected_in_block_->set_in_transition(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionBlock::InBlockDisconnected()
|
|
||||||
{
|
|
||||||
if (connected_in_block_) {
|
|
||||||
connected_in_block_->set_in_transition(nullptr);
|
|
||||||
connected_in_block_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
|
|
||||||
{
|
|
||||||
NodeValue::Type data_type;
|
NodeValue::Type data_type;
|
||||||
|
|
||||||
if (out_block_input()->IsConnected()) {
|
if (IsInputConnected(kOutBlockInput)) {
|
||||||
data_type = value[out_block_input()].GetWithMeta(NodeValue::kBuffer).type();
|
data_type = value[kOutBlockInput].GetWithMeta(NodeValue::kBuffer).type();
|
||||||
} else if (in_block_input()->IsConnected()) {
|
} else if (IsInputConnected(kInBlockInput)) {
|
||||||
data_type = value[in_block_input()].GetWithMeta(NodeValue::kBuffer).type();
|
data_type = value[kInBlockInput].GetWithMeta(NodeValue::kBuffer).type();
|
||||||
} else {
|
} else {
|
||||||
data_type = NodeValue::kNone;
|
data_type = NodeValue::kNone;
|
||||||
}
|
}
|
||||||
@@ -230,9 +176,9 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
|
|||||||
// This must be a visual transition
|
// This must be a visual transition
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
|
|
||||||
job.InsertValue(out_block_input(), value);
|
job.InsertValue(this, kOutBlockInput, value);
|
||||||
job.InsertValue(in_block_input(), value);
|
job.InsertValue(this, kInBlockInput, value);
|
||||||
job.InsertValue(curve_input_, value);
|
job.InsertValue(this, kCurveInput, value);
|
||||||
|
|
||||||
double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
|
double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
|
||||||
InsertTransitionTimes(&job, time);
|
InsertTransitionTimes(&job, time);
|
||||||
@@ -243,8 +189,8 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
|
|||||||
push_job = QVariant::fromValue(job);
|
push_job = QVariant::fromValue(job);
|
||||||
} else if (data_type == NodeValue::kSamples) {
|
} else if (data_type == NodeValue::kSamples) {
|
||||||
// This must be an audio transition
|
// This must be an audio transition
|
||||||
SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
SampleBufferPtr from_samples = value[kOutBlockInput].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||||
SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
SampleBufferPtr to_samples = value[kInBlockInput].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||||
|
|
||||||
if (from_samples || to_samples) {
|
if (from_samples || to_samples) {
|
||||||
double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
|
double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
|
||||||
@@ -287,7 +233,7 @@ void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferP
|
|||||||
|
|
||||||
double TransitionBlock::TransformCurve(double linear) const
|
double TransitionBlock::TransformCurve(double linear) const
|
||||||
{
|
{
|
||||||
switch (static_cast<CurveType>(curve_input_->GetStandardValue().toInt())) {
|
switch (static_cast<CurveType>(GetStandardValue(kCurveInput).toInt())) {
|
||||||
case kLinear:
|
case kLinear:
|
||||||
break;
|
break;
|
||||||
case kExponential:
|
case kExponential:
|
||||||
@@ -301,4 +247,49 @@ double TransitionBlock::TransformCurve(double linear) const
|
|||||||
return linear;
|
return linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionBlock::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
Q_UNUSED(element)
|
||||||
|
|
||||||
|
if (input == kOutBlockInput) {
|
||||||
|
// If node is not a block, this will just be null
|
||||||
|
if ((connected_out_block_ = dynamic_cast<Block*>(output.node()))) {
|
||||||
|
|
||||||
|
Q_ASSERT(connected_out_block_->type() != Block::kTransition
|
||||||
|
&& !connected_out_block_->out_transition()
|
||||||
|
&& connected_out_block_ == this->previous());
|
||||||
|
|
||||||
|
connected_out_block_->set_out_transition(this);
|
||||||
|
}
|
||||||
|
} else if (input == kInBlockInput) {
|
||||||
|
// If node is not a block, this will just be null
|
||||||
|
if ((connected_in_block_ = dynamic_cast<Block*>(output.node()))) {
|
||||||
|
|
||||||
|
Q_ASSERT(connected_in_block_->type() != Block::kTransition
|
||||||
|
&& !connected_in_block_->in_transition()
|
||||||
|
&& connected_in_block_ == this->next());
|
||||||
|
|
||||||
|
connected_in_block_->set_in_transition(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
Q_UNUSED(element)
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
|
if (input == kOutBlockInput) {
|
||||||
|
if (connected_out_block_) {
|
||||||
|
connected_out_block_->set_in_transition(nullptr);
|
||||||
|
connected_out_block_ = nullptr;
|
||||||
|
}
|
||||||
|
} else if (input == kInBlockInput) {
|
||||||
|
if (connected_in_block_) {
|
||||||
|
connected_in_block_->set_in_transition(nullptr);
|
||||||
|
connected_in_block_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,6 @@ public:
|
|||||||
|
|
||||||
virtual Type type() const override;
|
virtual Type type() const override;
|
||||||
|
|
||||||
NodeInput* out_block_input() const;
|
|
||||||
NodeInput* in_block_input() const;
|
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
rational in_offset() const;
|
rational in_offset() const;
|
||||||
@@ -50,7 +47,11 @@ public:
|
|||||||
|
|
||||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
|
static const QString kOutBlockInput;
|
||||||
|
static const QString kInBlockInput;
|
||||||
|
static const QString kCurveInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const;
|
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const;
|
||||||
@@ -59,6 +60,10 @@ protected:
|
|||||||
|
|
||||||
double TransformCurve(double linear) const;
|
double TransformCurve(double linear) const;
|
||||||
|
|
||||||
|
virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||||
|
|
||||||
|
virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum CurveType {
|
enum CurveType {
|
||||||
kLinear,
|
kLinear,
|
||||||
@@ -70,25 +75,10 @@ private:
|
|||||||
|
|
||||||
void InsertTransitionTimes(AcceleratedJob* job, const double& time) const;
|
void InsertTransitionTimes(AcceleratedJob* job, const double& time) const;
|
||||||
|
|
||||||
NodeInput* out_block_input_;
|
|
||||||
|
|
||||||
NodeInput* in_block_input_;
|
|
||||||
|
|
||||||
NodeInput* curve_input_;
|
|
||||||
|
|
||||||
Block* connected_out_block_;
|
Block* connected_out_block_;
|
||||||
|
|
||||||
Block* connected_in_block_;
|
Block* connected_in_block_;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void OutBlockConnected(Node* node);
|
|
||||||
|
|
||||||
void OutBlockDisconnected();
|
|
||||||
|
|
||||||
void InBlockConnected(Node* node);
|
|
||||||
|
|
||||||
void InBlockDisconnected();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2020 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "connectable.h"
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
#include "node.h"
|
|
||||||
|
|
||||||
namespace olive {
|
|
||||||
|
|
||||||
void NodeConnectable::ConnectEdge(Node *output, NodeInput *input, int element)
|
|
||||||
{
|
|
||||||
InputConnection conn_to_in = {input, element};
|
|
||||||
|
|
||||||
// Connection exists
|
|
||||||
if (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) != output->output_connections_.end()) {
|
|
||||||
qDebug() << "Ignored connect that already exists:" << output << input->parent() << input->id() << element;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a connection isn't getting overwritten
|
|
||||||
Q_ASSERT(input->input_connections_.find(element) == input->input_connections_.end());
|
|
||||||
|
|
||||||
// Insert connections in both sides
|
|
||||||
output->output_connections_.push_back(conn_to_in);
|
|
||||||
input->input_connections_[element] = output;
|
|
||||||
|
|
||||||
// Emit signals
|
|
||||||
emit input->InputConnected(output, element);
|
|
||||||
emit output->OutputConnected(input, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeConnectable::DisconnectEdge(Node *output, NodeInput *input, int element)
|
|
||||||
{
|
|
||||||
InputConnection conn_to_in = {input, element};
|
|
||||||
|
|
||||||
// Connection exists
|
|
||||||
if (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) == output->output_connections_.end()) {
|
|
||||||
qDebug() << "Ignored disconnect that doesn't exist:" << output << input->parent() << input->id() << element;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assertions to ensure connection exists
|
|
||||||
Q_ASSERT(input->input_connections_.at(element) == output);
|
|
||||||
|
|
||||||
// Remove connections from both sides
|
|
||||||
output->output_connections_.erase(std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in));
|
|
||||||
input->input_connections_.erase(input->input_connections_.find(element));
|
|
||||||
|
|
||||||
// Emit signals
|
|
||||||
emit input->InputDisconnected(output, element);
|
|
||||||
emit output->OutputDisconnected(input, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const NodeConnectable::InputConnection &r, uint seed)
|
|
||||||
{
|
|
||||||
return ::qHash(r.input, seed) ^ ::qHash(r.element, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2020 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef CONNECTABLE_H
|
|
||||||
#define CONNECTABLE_H
|
|
||||||
|
|
||||||
#include <QMap>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
namespace olive {
|
|
||||||
|
|
||||||
class Node;
|
|
||||||
class NodeInput;
|
|
||||||
|
|
||||||
class NodeConnectable : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
NodeConnectable() = default;
|
|
||||||
|
|
||||||
struct InputConnection {
|
|
||||||
InputConnection(NodeInput* i = nullptr, int e = -1)
|
|
||||||
{
|
|
||||||
input = i;
|
|
||||||
element = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const InputConnection& rhs) const
|
|
||||||
{
|
|
||||||
return input == rhs.input && element == rhs.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* input;
|
|
||||||
int element;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ConnectEdge(Node* output, NodeInput* input, int element = -1);
|
|
||||||
|
|
||||||
static void DisconnectEdge(Node* output, NodeInput* input, int element = -1);
|
|
||||||
|
|
||||||
struct Edge {
|
|
||||||
Node* output;
|
|
||||||
NodeInput* input;
|
|
||||||
int element;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const std::vector<InputConnection>& output_connections() const
|
|
||||||
{
|
|
||||||
return output_connections_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::map<int, Node*>& input_connections() const
|
|
||||||
{
|
|
||||||
return input_connections_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<InputConnection> output_connections_;
|
|
||||||
|
|
||||||
std::map<int, Node*> input_connections_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
uint qHash(const NodeConnectable::InputConnection& r, uint seed = 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CONNECTABLE_H
|
|
||||||
@@ -25,67 +25,61 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString CropDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString CropDistortNode::kLeftInput = QStringLiteral("left_in");
|
||||||
|
const QString CropDistortNode::kTopInput = QStringLiteral("top_in");
|
||||||
|
const QString CropDistortNode::kRightInput = QStringLiteral("right_in");
|
||||||
|
const QString CropDistortNode::kBottomInput = QStringLiteral("bottom_in");
|
||||||
|
const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in");
|
||||||
|
|
||||||
CropDistortNode::CropDistortNode()
|
CropDistortNode::CropDistortNode()
|
||||||
{
|
{
|
||||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
left_input_ = new NodeInput(this, QStringLiteral("left_in"), NodeValue::kFloat, 0.0);
|
CreateCropSideInput(kLeftInput);
|
||||||
left_input_->setProperty("min", 0.0);
|
CreateCropSideInput(kTopInput);
|
||||||
left_input_->setProperty("max", 1.0);
|
CreateCropSideInput(kRightInput);
|
||||||
left_input_->setProperty("view", FloatSlider::kPercentage);
|
CreateCropSideInput(kBottomInput);
|
||||||
|
|
||||||
top_input_ = new NodeInput(this, QStringLiteral("top_in"), NodeValue::kFloat, 0.0);
|
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
||||||
top_input_->setProperty("min", 0.0);
|
SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0);
|
||||||
top_input_->setProperty("max", 1.0);
|
|
||||||
top_input_->setProperty("view", FloatSlider::kPercentage);
|
|
||||||
|
|
||||||
right_input_ = new NodeInput(this, QStringLiteral("right_in"), NodeValue::kFloat, 0.0);
|
|
||||||
right_input_->setProperty("min", 0.0);
|
|
||||||
right_input_->setProperty("max", 1.0);
|
|
||||||
right_input_->setProperty("view", FloatSlider::kPercentage);
|
|
||||||
|
|
||||||
bottom_input_ = new NodeInput(this, QStringLiteral("bottom_in"), NodeValue::kFloat, 0.0);
|
|
||||||
bottom_input_->setProperty("min", 0.0);
|
|
||||||
bottom_input_->setProperty("max", 1.0);
|
|
||||||
bottom_input_->setProperty("view", FloatSlider::kPercentage);
|
|
||||||
|
|
||||||
feather_input_ = new NodeInput(this, QStringLiteral("feather_in"), NodeValue::kFloat, 0.0);
|
|
||||||
feather_input_->setProperty("min", 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CropDistortNode::Retranslate()
|
void CropDistortNode::Retranslate()
|
||||||
{
|
{
|
||||||
texture_input_->set_name(tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
left_input_->set_name(tr("Left"));
|
SetInputName(kLeftInput, tr("Left"));
|
||||||
top_input_->set_name(tr("Top"));
|
SetInputName(kTopInput, tr("Top"));
|
||||||
right_input_->set_name(tr("Right"));
|
SetInputName(kRightInput, tr("Right"));
|
||||||
bottom_input_->set_name(tr("Bottom"));
|
SetInputName(kBottomInput, tr("Bottom"));
|
||||||
feather_input_->set_name(tr("Feather"));
|
SetInputName(kFeatherInput, tr("Feather"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const
|
NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.InsertValue(texture_input_, value);
|
job.InsertValue(this, kTextureInput, value);
|
||||||
job.InsertValue(left_input_, value);
|
job.InsertValue(this, kLeftInput, value);
|
||||||
job.InsertValue(top_input_, value);
|
job.InsertValue(this, kTopInput, value);
|
||||||
job.InsertValue(right_input_, value);
|
job.InsertValue(this, kRightInput, value);
|
||||||
job.InsertValue(bottom_input_, value);
|
job.InsertValue(this, kBottomInput, value);
|
||||||
job.InsertValue(feather_input_, value);
|
job.InsertValue(this, kFeatherInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"),
|
job.InsertValue(QStringLiteral("resolution_in"),
|
||||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(true);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (!job.GetValue(texture_input_).data().isNull()) {
|
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||||
if (!qIsNull(job.GetValue(left_input_).data().toDouble())
|
if (!qIsNull(job.GetValue(kLeftInput).data().toDouble())
|
||||||
|| !qIsNull(job.GetValue(right_input_).data().toDouble())
|
|| !qIsNull(job.GetValue(kRightInput).data().toDouble())
|
||||||
|| !qIsNull(job.GetValue(top_input_).data().toDouble())
|
|| !qIsNull(job.GetValue(kTopInput).data().toDouble())
|
||||||
|| !qIsNull(job.GetValue(bottom_input_).data().toDouble())) {
|
|| !qIsNull(job.GetValue(kBottomInput).data().toDouble())) {
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
} else {
|
} else {
|
||||||
table.Push(NodeValue::kTexture, job.GetValue(texture_input_).data(), this);
|
table.Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,10 +100,10 @@ void CropDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
|||||||
|
|
||||||
p->setPen(QPen(Qt::white, 0));
|
p->setPen(QPen(Qt::white, 0));
|
||||||
|
|
||||||
double left_pt = resolution.x() * db[left_input_].Get(NodeValue::kFloat).toDouble();
|
double left_pt = resolution.x() * db[kLeftInput].Get(NodeValue::kFloat).toDouble();
|
||||||
double top_pt = resolution.y() * db[top_input_].Get(NodeValue::kFloat).toDouble();
|
double top_pt = resolution.y() * db[kTopInput].Get(NodeValue::kFloat).toDouble();
|
||||||
double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeValue::kFloat).toDouble());
|
double right_pt = resolution.x() * (1.0 - db[kRightInput].Get(NodeValue::kFloat).toDouble());
|
||||||
double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeValue::kFloat).toDouble());
|
double bottom_pt = resolution.y() * (1.0 - db[kBottomInput].Get(NodeValue::kFloat).toDouble());
|
||||||
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
||||||
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
||||||
|
|
||||||
@@ -156,7 +150,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
|| in_rect) {
|
|| in_rect) {
|
||||||
gizmo_drag_ |= kGizmoLeft;
|
gizmo_drag_ |= kGizmoLeft;
|
||||||
|
|
||||||
gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat));
|
gizmo_start_.append(db[kLeftInput].Get(NodeValue::kFloat));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_active[kGizmoScaleTopLeft]
|
if (gizmo_active[kGizmoScaleTopLeft]
|
||||||
@@ -165,7 +159,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
|| in_rect) {
|
|| in_rect) {
|
||||||
gizmo_drag_ |= kGizmoTop;
|
gizmo_drag_ |= kGizmoTop;
|
||||||
|
|
||||||
gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat));
|
gizmo_start_.append(db[kTopInput].Get(NodeValue::kFloat));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_active[kGizmoScaleTopRight]
|
if (gizmo_active[kGizmoScaleTopRight]
|
||||||
@@ -174,7 +168,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
|| in_rect) {
|
|| in_rect) {
|
||||||
gizmo_drag_ |= kGizmoRight;
|
gizmo_drag_ |= kGizmoRight;
|
||||||
|
|
||||||
gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat));
|
gizmo_start_.append(db[kRightInput].Get(NodeValue::kFloat));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_active[kGizmoScaleBottomLeft]
|
if (gizmo_active[kGizmoScaleBottomLeft]
|
||||||
@@ -183,7 +177,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
|| in_rect) {
|
|| in_rect) {
|
||||||
gizmo_drag_ |= kGizmoBottom;
|
gizmo_drag_ |= kGizmoBottom;
|
||||||
|
|
||||||
gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat));
|
gizmo_start_.append(db[kBottomInput].Get(NodeValue::kFloat));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_drag_ > kGizmoNone) {
|
if (gizmo_drag_ > kGizmoNone) {
|
||||||
@@ -204,22 +198,22 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
|||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
if (gizmo_drag_ & kGizmoLeft) {
|
if (gizmo_drag_ & kGizmoLeft) {
|
||||||
gizmo_dragger_[counter].Start(left_input_, time, 0);
|
gizmo_dragger_[counter].Start(NodeInput(this, kLeftInput), time);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_drag_ & kGizmoTop) {
|
if (gizmo_drag_ & kGizmoTop) {
|
||||||
gizmo_dragger_[counter].Start(top_input_, time, 0);
|
gizmo_dragger_[counter].Start(NodeInput(this, kTopInput), time);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_drag_ & kGizmoRight) {
|
if (gizmo_drag_ & kGizmoRight) {
|
||||||
gizmo_dragger_[counter].Start(right_input_, time, 0);
|
gizmo_dragger_[counter].Start(NodeInput(this, kRightInput), time);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_drag_ & kGizmoBottom) {
|
if (gizmo_drag_ & kGizmoBottom) {
|
||||||
gizmo_dragger_[counter].Start(bottom_input_, time, 0);
|
gizmo_dragger_[counter].Start(NodeInput(this, kBottomInput), time);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,4 +254,12 @@ void CropDistortNode::GizmoRelease()
|
|||||||
gizmo_start_.clear();
|
gizmo_start_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CropDistortNode::CreateCropSideInput(const QString &id)
|
||||||
|
{
|
||||||
|
AddInput(id, NodeValue::kFloat, 0.0);
|
||||||
|
SetInputProperty(id, QStringLiteral("min"), 0.0);
|
||||||
|
SetInputProperty(id, QStringLiteral("max"), 1.0);
|
||||||
|
SetInputProperty(id, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
@@ -75,15 +75,15 @@ public:
|
|||||||
virtual void GizmoMove(const QPointF &p, const rational &time) override;
|
virtual void GizmoMove(const QPointF &p, const rational &time) override;
|
||||||
virtual void GizmoRelease() override;
|
virtual void GizmoRelease() override;
|
||||||
|
|
||||||
|
static const QString kTextureInput;
|
||||||
|
static const QString kLeftInput;
|
||||||
|
static const QString kTopInput;
|
||||||
|
static const QString kRightInput;
|
||||||
|
static const QString kBottomInput;
|
||||||
|
static const QString kFeatherInput;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeInput* texture_input_;
|
void CreateCropSideInput(const QString& id);
|
||||||
|
|
||||||
NodeInput* left_input_;
|
|
||||||
NodeInput* top_input_;
|
|
||||||
NodeInput* right_input_;
|
|
||||||
NodeInput* bottom_input_;
|
|
||||||
|
|
||||||
NodeInput* feather_input_;
|
|
||||||
|
|
||||||
// Gizmo variables
|
// Gizmo variables
|
||||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
||||||
|
|||||||
@@ -26,35 +26,40 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in");
|
||||||
|
const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in");
|
||||||
|
|
||||||
TransformDistortNode::TransformDistortNode()
|
TransformDistortNode::TransformDistortNode()
|
||||||
{
|
{
|
||||||
autoscale_input_ = new NodeInput(this, QStringLiteral("autoscale_in"), NodeValue::kCombo, 0);
|
AddInput(kAutoscaleInput, NodeValue::kCombo, 0);
|
||||||
|
|
||||||
interpolation_input_ = new NodeInput(this, QStringLiteral("interpolation_in"), NodeValue::kCombo, 2);
|
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
|
||||||
|
|
||||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
texture_input_->SetKeyframable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformDistortNode::Retranslate()
|
void TransformDistortNode::Retranslate()
|
||||||
{
|
{
|
||||||
MatrixGenerator::Retranslate();
|
MatrixGenerator::Retranslate();
|
||||||
|
|
||||||
autoscale_input_->set_name(tr("Auto-Scale"));
|
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
|
||||||
texture_input_->set_name(tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
interpolation_input_->set_name(tr("Interpolation"));
|
SetInputName(kInterpolationInput, tr("Interpolation"));
|
||||||
|
|
||||||
autoscale_input_->set_combobox_strings({tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")});
|
SetComboBoxStrings(kAutoscaleInput, {tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")});
|
||||||
interpolation_input_->set_combobox_strings({tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")});
|
SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")});
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
|
NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
// Generate matrix
|
// Generate matrix
|
||||||
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
|
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
|
||||||
|
|
||||||
// Pop texture
|
// Pop texture
|
||||||
TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value<TexturePtr>();
|
TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value<TexturePtr>();
|
||||||
|
|
||||||
// Merge table
|
// Merge table
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
@@ -64,7 +69,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
|
|||||||
// Adjust our matrix by the resolutions involved
|
// Adjust our matrix by the resolutions involved
|
||||||
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
|
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
|
||||||
QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height());
|
QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height());
|
||||||
AutoScaleType autoscale = static_cast<AutoScaleType>(value[autoscale_input_].Get(NodeValue::kCombo).toInt());
|
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt());
|
||||||
|
|
||||||
QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix,
|
QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix,
|
||||||
sequence_res,
|
sequence_res,
|
||||||
@@ -79,7 +84,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
|
|||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
|
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
|
||||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
|
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
|
||||||
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[interpolation_input_].Get(NodeValue::kCombo).toInt()));
|
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[kInterpolationInput].Get(NodeValue::kCombo).toInt()));
|
||||||
|
|
||||||
// FIXME: This should be optimized, we can use matrix math to determine if this operation will
|
// FIXME: This should be optimized, we can use matrix math to determine if this operation will
|
||||||
// end up with gaps in the screen that will require an alpha channel.
|
// end up with gaps in the screen that will require an alpha channel.
|
||||||
@@ -121,10 +126,10 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
if (scaling) {
|
if (scaling) {
|
||||||
|
|
||||||
// Dragging scale handle
|
// Dragging scale handle
|
||||||
gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)};
|
gizmo_start_ = {db[kScaleInput].Get(NodeValue::kVec2)};
|
||||||
gizmo_drag_ = scale_input();
|
gizmo_drag_ = kScaleInput;
|
||||||
|
|
||||||
gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeValue::kBoolean).toBool();
|
gizmo_scale_uniform_ = db[kUniformScaleInput].Get(NodeValue::kBoolean).toBool();
|
||||||
|
|
||||||
if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight]
|
if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight]
|
||||||
|| gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) {
|
|| gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) {
|
||||||
@@ -136,8 +141,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store texture size
|
// Store texture size
|
||||||
QVector2D texture_sz = db[texture_input()].Get(NodeValue::kTexture).value<QVector2D>();
|
QVector2D texture_sz = db[kTextureInput].Get(NodeValue::kTexture).value<QVector2D>();
|
||||||
gizmo_scale_anchor_ = db[anchor_input()].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
|
gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
|
||||||
|
|
||||||
if (gizmo_scale_active[kGizmoScaleTopRight]
|
if (gizmo_scale_active[kGizmoScaleTopRight]
|
||||||
|| gizmo_scale_active[kGizmoScaleBottomRight]
|
|| gizmo_scale_active[kGizmoScaleBottomRight]
|
||||||
@@ -161,9 +166,9 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
} else if (gizmo_anchor_pt_.contains(p)) {
|
} else if (gizmo_anchor_pt_.contains(p)) {
|
||||||
|
|
||||||
// Dragging the anchor point specifically
|
// Dragging the anchor point specifically
|
||||||
gizmo_start_ = {db[anchor_input()].Get(NodeValue::kVec2),
|
gizmo_start_ = {db[kAnchorInput].Get(NodeValue::kVec2),
|
||||||
db[position_input()].Get(NodeValue::kVec2)};
|
db[kPositionInput].Get(NodeValue::kVec2)};
|
||||||
gizmo_drag_ = anchor_input();
|
gizmo_drag_ = kAnchorInput;
|
||||||
|
|
||||||
// Store current matrix
|
// Store current matrix
|
||||||
gizmo_matrix_ = GenerateMatrix(db, false, true, true, false);
|
gizmo_matrix_ = GenerateMatrix(db, false, true, true, false);
|
||||||
@@ -173,16 +178,16 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
} else if (gizmo_rect_.containsPoint(p, Qt::OddEvenFill)) {
|
} else if (gizmo_rect_.containsPoint(p, Qt::OddEvenFill)) {
|
||||||
|
|
||||||
// Dragging the main rectangle
|
// Dragging the main rectangle
|
||||||
gizmo_start_ = {db[position_input()].Get(NodeValue::kVec2)};
|
gizmo_start_ = {db[kPositionInput].Get(NodeValue::kVec2)};
|
||||||
gizmo_drag_ = position_input();
|
gizmo_drag_ = kPositionInput;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Dragging rotation
|
// Dragging rotation
|
||||||
gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)};
|
gizmo_start_ = {db[kRotationInput].Get(NodeValue::kFloat)};
|
||||||
gizmo_drag_ = rotation_input();
|
gizmo_drag_ = kRotationInput;
|
||||||
gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(),
|
gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(),
|
||||||
gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x());
|
gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x());
|
||||||
|
|
||||||
@@ -198,15 +203,15 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
|||||||
QPointF movement = (p - gizmo_drag_pos_);
|
QPointF movement = (p - gizmo_drag_pos_);
|
||||||
QVector2D vec_movement(movement);
|
QVector2D vec_movement(movement);
|
||||||
|
|
||||||
if (gizmo_drag_ == anchor_input()) {
|
if (gizmo_drag_ == kAnchorInput) {
|
||||||
|
|
||||||
// Dragging the anchor point around
|
// Dragging the anchor point around
|
||||||
if (gizmo_dragger_.isEmpty()) {
|
if (gizmo_dragger_.isEmpty()) {
|
||||||
gizmo_dragger_.resize(4);
|
gizmo_dragger_.resize(4);
|
||||||
gizmo_dragger_[0].Start(anchor_input(), time, 0);
|
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0), time);
|
||||||
gizmo_dragger_[1].Start(anchor_input(), time, 1);
|
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1), time);
|
||||||
gizmo_dragger_[2].Start(position_input(), time, 0);
|
gizmo_dragger_[2].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||||
gizmo_dragger_[3].Start(position_input(), time, 1);
|
gizmo_dragger_[3].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector2D inverted_movement(gizmo_matrix_.toTransform().inverted().map(movement));
|
QVector2D inverted_movement(gizmo_matrix_.toTransform().inverted().map(movement));
|
||||||
@@ -218,13 +223,13 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
|||||||
gizmo_dragger_[2].Drag(position_cont.x());
|
gizmo_dragger_[2].Drag(position_cont.x());
|
||||||
gizmo_dragger_[3].Drag(position_cont.y());
|
gizmo_dragger_[3].Drag(position_cont.y());
|
||||||
|
|
||||||
} else if (gizmo_drag_ == position_input()) {
|
} else if (gizmo_drag_ == kPositionInput) {
|
||||||
|
|
||||||
// Dragging the main rectangle around
|
// Dragging the main rectangle around
|
||||||
if (gizmo_dragger_.isEmpty()) {
|
if (gizmo_dragger_.isEmpty()) {
|
||||||
gizmo_dragger_.resize(2);
|
gizmo_dragger_.resize(2);
|
||||||
gizmo_dragger_[0].Start(position_input(), time, 0);
|
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||||
gizmo_dragger_[1].Start(position_input(), time, 1);
|
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector2D position_cont = gizmo_start_[0].value<QVector2D>() + vec_movement;
|
QVector2D position_cont = gizmo_start_[0].value<QVector2D>() + vec_movement;
|
||||||
@@ -232,20 +237,20 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
|||||||
gizmo_dragger_[0].Drag(position_cont.x());
|
gizmo_dragger_[0].Drag(position_cont.x());
|
||||||
gizmo_dragger_[1].Drag(position_cont.y());
|
gizmo_dragger_[1].Drag(position_cont.y());
|
||||||
|
|
||||||
} else if (gizmo_drag_ == scale_input()) {
|
} else if (gizmo_drag_ == kScaleInput) {
|
||||||
|
|
||||||
// Dragging a resize handle
|
// Dragging a resize handle
|
||||||
if (gizmo_dragger_.isEmpty()) {
|
if (gizmo_dragger_.isEmpty()) {
|
||||||
if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) {
|
if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) {
|
||||||
gizmo_dragger_.resize(1);
|
gizmo_dragger_.resize(1);
|
||||||
gizmo_dragger_[0].Start(scale_input(), time, 0);
|
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time);
|
||||||
} else if (gizmo_scale_axes_ == kGizmoScaleYOnly) {
|
} else if (gizmo_scale_axes_ == kGizmoScaleYOnly) {
|
||||||
gizmo_dragger_.resize(1);
|
gizmo_dragger_.resize(1);
|
||||||
gizmo_dragger_[0].Start(scale_input(), time, 1);
|
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||||
} else {
|
} else {
|
||||||
gizmo_dragger_.resize(2);
|
gizmo_dragger_.resize(2);
|
||||||
gizmo_dragger_[0].Start(scale_input(), time, 0);
|
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time);
|
||||||
gizmo_dragger_[1].Start(scale_input(), time, 1);
|
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,12 +279,12 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (gizmo_drag_ == rotation_input()) {
|
} else if (gizmo_drag_ == kRotationInput) {
|
||||||
|
|
||||||
// Dragging outside the rectangle to rotate
|
// Dragging outside the rectangle to rotate
|
||||||
if (gizmo_dragger_.isEmpty()) {
|
if (gizmo_dragger_.isEmpty()) {
|
||||||
gizmo_dragger_.resize(1);
|
gizmo_dragger_.resize(1);
|
||||||
gizmo_dragger_[0].Start(rotation_input(), time, 0);
|
gizmo_dragger_[0].Start(NodeInput(this, kRotationInput), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
double current_angle = qAtan2(p.y() - gizmo_anchor_pt_.center().y(),
|
double current_angle = qAtan2(p.y() - gizmo_anchor_pt_.center().y(),
|
||||||
@@ -367,10 +372,10 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
|||||||
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
||||||
|
|
||||||
// GizmoTraverser just returns the sizes of the textures and no other data
|
// GizmoTraverser just returns the sizes of the textures and no other data
|
||||||
QVector2D tex_sz = db[texture_input_].Get(NodeValue::kTexture).value<QVector2D>();
|
QVector2D tex_sz = db[kTextureInput].Get(NodeValue::kTexture).value<QVector2D>();
|
||||||
|
|
||||||
// Retrieve autoscale value
|
// Retrieve autoscale value
|
||||||
AutoScaleType autoscale = static_cast<AutoScaleType>(db[autoscale_input_].Get(NodeValue::kCombo).toInt());
|
AutoScaleType autoscale = static_cast<AutoScaleType>(db[kAutoscaleInput].Get(NodeValue::kCombo).toInt());
|
||||||
|
|
||||||
// Fold values into a matrix for the rectangle
|
// Fold values into a matrix for the rectangle
|
||||||
QMatrix4x4 rectangle_matrix;
|
QMatrix4x4 rectangle_matrix;
|
||||||
@@ -435,8 +440,8 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
|||||||
|
|
||||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||||
// really anchor around the center
|
// really anchor around the center
|
||||||
position_input()->setProperty("offset", sequence_res * 0.5);
|
SetInputProperty(kPositionInput, QStringLiteral("offset"), sequence_res * 0.5);
|
||||||
anchor_input()->setProperty("offset", tex_sz * 0.5);
|
SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||||
|
|
||||||
@@ -78,11 +78,6 @@ public:
|
|||||||
virtual void GizmoMove(const QPointF &p, const rational &time) override;
|
virtual void GizmoMove(const QPointF &p, const rational &time) override;
|
||||||
virtual void GizmoRelease() override;
|
virtual void GizmoRelease() override;
|
||||||
|
|
||||||
NodeInput* texture_input() const
|
|
||||||
{
|
|
||||||
return texture_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AutoScaleType {
|
enum AutoScaleType {
|
||||||
kAutoScaleNone,
|
kAutoScaleNone,
|
||||||
kAutoScaleFit,
|
kAutoScaleFit,
|
||||||
@@ -95,15 +90,15 @@ public:
|
|||||||
const QVector2D& texture_res,
|
const QVector2D& texture_res,
|
||||||
AutoScaleType autoscale_type = kAutoScaleNone);
|
AutoScaleType autoscale_type = kAutoScaleNone);
|
||||||
|
|
||||||
|
static const QString kTextureInput;
|
||||||
|
static const QString kAutoscaleInput;
|
||||||
|
static const QString kInterpolationInput;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat);
|
static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat);
|
||||||
|
|
||||||
NodeInput* texture_input_;
|
|
||||||
NodeInput* autoscale_input_;
|
|
||||||
NodeInput* interpolation_input_;
|
|
||||||
|
|
||||||
// Gizmo variables
|
// Gizmo variables
|
||||||
NodeInput* gizmo_drag_;
|
QString gizmo_drag_;
|
||||||
QVector<QVariant> gizmo_start_;
|
QVector<QVariant> gizmo_start_;
|
||||||
QVector<NodeInputDragger> gizmo_dragger_;
|
QVector<NodeInputDragger> gizmo_dragger_;
|
||||||
QPointF gizmo_drag_pos_;
|
QPointF gizmo_drag_pos_;
|
||||||
|
|||||||
+11
-6
@@ -52,13 +52,18 @@ void NodeFactory::Initialize()
|
|||||||
|
|
||||||
// Add internal types
|
// Add internal types
|
||||||
for (int i=0;i<kInternalNodeCount;i++) {
|
for (int i=0;i<kInternalNodeCount;i++) {
|
||||||
library_.append(CreateFromFactoryIndex(static_cast<InternalID>(i)));
|
Node* created_node = CreateFromFactoryIndex(static_cast<InternalID>(i));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
library_.append(created_node);
|
||||||
library_.append(new ExternalTransition(":/shaders/crossdissolve.xml"));
|
|
||||||
library_.append(new ExternalTransition(":/shaders/diptoblack.xml"));
|
if (created_node->inputs().isEmpty()) {
|
||||||
*/
|
qWarning() << "Node" << created_node->id() << "has no inputs";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (created_node->outputs().isEmpty()) {
|
||||||
|
qWarning() << "Node" << created_node->id() << "has no outputs";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeFactory::Destroy()
|
void NodeFactory::Destroy()
|
||||||
|
|||||||
@@ -22,20 +22,27 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString BlurFilterNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString BlurFilterNode::kMethodInput = QStringLiteral("method_in");
|
||||||
|
const QString BlurFilterNode::kRadiusInput = QStringLiteral("radius_in");
|
||||||
|
const QString BlurFilterNode::kHorizInput = QStringLiteral("horiz_in");
|
||||||
|
const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in");
|
||||||
|
const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in");
|
||||||
|
|
||||||
BlurFilterNode::BlurFilterNode()
|
BlurFilterNode::BlurFilterNode()
|
||||||
{
|
{
|
||||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
method_input_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo, 0);
|
AddInput(kMethodInput, NodeValue::kCombo, 0);
|
||||||
|
|
||||||
radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f);
|
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
|
||||||
radius_input_->setProperty("min", 0.0f);
|
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
|
||||||
|
|
||||||
horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kBoolean, true);
|
AddInput(kHorizInput, NodeValue::kBoolean, true);
|
||||||
|
|
||||||
vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kBoolean, true);
|
AddInput(kVertInput, NodeValue::kBoolean, true);
|
||||||
|
|
||||||
repeat_edge_pixels_input_ = new NodeInput(this, QStringLiteral("repeat_edge_pixels_in"), NodeValue::kBoolean, false);
|
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *BlurFilterNode::copy() const
|
Node *BlurFilterNode::copy() const
|
||||||
@@ -65,13 +72,13 @@ QString BlurFilterNode::Description() const
|
|||||||
|
|
||||||
void BlurFilterNode::Retranslate()
|
void BlurFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
texture_input_->set_name(tr("Input"));
|
SetInputName(kTextureInput, tr("Input"));
|
||||||
method_input_->set_name(tr("Method"));
|
SetInputName(kMethodInput, tr("Method"));
|
||||||
method_input_->set_combobox_strings({ tr("Box"), tr("Gaussian") });
|
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") });
|
||||||
radius_input_->set_name(tr("Radius"));
|
SetInputName(kRadiusInput, tr("Radius"));
|
||||||
horiz_input_->set_name(tr("Horizontal"));
|
SetInputName(kHorizInput, tr("Horizontal"));
|
||||||
vert_input_->set_name(tr("Vertical"));
|
SetInputName(kVertInput, tr("Vertical"));
|
||||||
repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels"));
|
SetInputName(kRepeatEdgePixelsInput, tr("Repeat Edge Pixels"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const
|
ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const
|
||||||
@@ -80,35 +87,37 @@ ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const
|
|||||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag"));
|
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
|
|
||||||
job.InsertValue(texture_input_, value);
|
job.InsertValue(this, kTextureInput, value);
|
||||||
job.InsertValue(method_input_, value);
|
job.InsertValue(this, kMethodInput, value);
|
||||||
job.InsertValue(radius_input_, value);
|
job.InsertValue(this, kRadiusInput, value);
|
||||||
job.InsertValue(horiz_input_, value);
|
job.InsertValue(this, kHorizInput, value);
|
||||||
job.InsertValue(vert_input_, value);
|
job.InsertValue(this, kVertInput, value);
|
||||||
job.InsertValue(repeat_edge_pixels_input_, value);
|
job.InsertValue(this, kRepeatEdgePixelsInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"),
|
job.InsertValue(QStringLiteral("resolution_in"),
|
||||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
// If there's no texture, no need to run an operation
|
// If there's no texture, no need to run an operation
|
||||||
if (!job.GetValue(texture_input_).data().isNull()) {
|
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||||
|
|
||||||
// Check if radius > 0, and both "horiz" and/or "vert" are enabled
|
// Check if radius > 0, and both "horiz" and/or "vert" are enabled
|
||||||
if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool())
|
if ((job.GetValue(kHorizInput).data().toBool() || job.GetValue(kVertInput).data().toBool())
|
||||||
&& job.GetValue(radius_input_).data().toDouble() > 0.0) {
|
&& job.GetValue(kRadiusInput).data().toDouble() > 0.0) {
|
||||||
|
|
||||||
// Set iteration count to 2 if we're blurring both horizontally and vertically
|
// Set iteration count to 2 if we're blurring both horizontally and vertically
|
||||||
if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) {
|
if (job.GetValue(kHorizInput).data().toBool() && job.GetValue(kVertInput).data().toBool()) {
|
||||||
job.SetIterations(2, texture_input_);
|
job.SetIterations(2, kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not repeating pixels, expect an alpha channel to appear
|
// If we're not repeating pixels, expect an alpha channel to appear
|
||||||
if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) {
|
if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) {
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +125,7 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If we're not performing the blur job, just push the texture
|
// If we're not performing the blur job, just push the texture
|
||||||
table.Push(job.GetValue(texture_input_));
|
table.Push(job.GetValue(kTextureInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,20 +41,14 @@ public:
|
|||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
private:
|
static const QString kTextureInput;
|
||||||
NodeInput* texture_input_;
|
static const QString kMethodInput;
|
||||||
|
static const QString kRadiusInput;
|
||||||
NodeInput* method_input_;
|
static const QString kHorizInput;
|
||||||
|
static const QString kVertInput;
|
||||||
NodeInput* radius_input_;
|
static const QString kRepeatEdgePixelsInput;
|
||||||
|
|
||||||
NodeInput* horiz_input_;
|
|
||||||
|
|
||||||
NodeInput* vert_input_;
|
|
||||||
|
|
||||||
NodeInput* repeat_edge_pixels_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,46 +22,52 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString MosaicFilterNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString MosaicFilterNode::kHorizInput = QStringLiteral("horiz_in");
|
||||||
|
const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in");
|
||||||
|
|
||||||
MosaicFilterNode::MosaicFilterNode()
|
MosaicFilterNode::MosaicFilterNode()
|
||||||
{
|
{
|
||||||
tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kFloat, 32.0);
|
AddInput(kHorizInput, NodeValue::kFloat, 32.0);
|
||||||
horiz_input_->setProperty("min", 1.0);
|
SetInputProperty(kHorizInput, QStringLiteral("min"), 1.0);
|
||||||
|
|
||||||
vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0);
|
AddInput(kVertInput, NodeValue::kFloat, 18.0);
|
||||||
vert_input_->setProperty("min", 1.0);
|
SetInputProperty(kVertInput, QStringLiteral("min"), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MosaicFilterNode::Retranslate()
|
void MosaicFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
tex_input_->set_name(tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
horiz_input_->set_name(tr("Horizontal"));
|
SetInputName(kHorizInput, tr("Horizontal"));
|
||||||
vert_input_->set_name(tr("Vertical"));
|
SetInputName(kVertInput, tr("Vertical"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const
|
NodeValueTable MosaicFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
|
|
||||||
job.InsertValue(tex_input_, value);
|
job.InsertValue(this, kTextureInput, value);
|
||||||
job.InsertValue(horiz_input_, value);
|
job.InsertValue(this, kHorizInput, value);
|
||||||
job.InsertValue(vert_input_, value);
|
job.InsertValue(this, kVertInput, value);
|
||||||
|
|
||||||
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
||||||
job.SetInterpolation(tex_input_, Texture::kLinear);
|
job.SetInterpolation(kTextureInput, Texture::kLinear);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (!job.GetValue(tex_input_).data().isNull()) {
|
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||||
TexturePtr texture = job.GetValue(tex_input_).data().value<TexturePtr>();
|
TexturePtr texture = job.GetValue(kTextureInput).data().value<TexturePtr>();
|
||||||
|
|
||||||
if (texture
|
if (texture
|
||||||
&& job.GetValue(horiz_input_).data().toInt() != texture->width()
|
&& job.GetValue(kHorizInput).data().toInt() != texture->width()
|
||||||
&& job.GetValue(vert_input_).data().toInt() != texture->height()) {
|
&& job.GetValue(kVertInput).data().toInt() != texture->height()) {
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
} else {
|
} else {
|
||||||
table.Push(job.GetValue(tex_input_));
|
table.Push(job.GetValue(kTextureInput));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,15 +58,12 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
private:
|
static const QString kTextureInput;
|
||||||
NodeInput* tex_input_;
|
static const QString kHorizInput;
|
||||||
|
static const QString kVertInput;
|
||||||
NodeInput* horiz_input_;
|
|
||||||
|
|
||||||
NodeInput* vert_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,24 +25,27 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString StrokeFilterNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString StrokeFilterNode::kColorInput = QStringLiteral("color_in");
|
||||||
|
const QString StrokeFilterNode::kRadiusInput = QStringLiteral("radius_in");
|
||||||
|
const QString StrokeFilterNode::kOpacityInput = QStringLiteral("opacity_in");
|
||||||
|
const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in");
|
||||||
|
|
||||||
StrokeFilterNode::StrokeFilterNode()
|
StrokeFilterNode::StrokeFilterNode()
|
||||||
{
|
{
|
||||||
tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
color_input_ = new NodeInput(this,
|
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f)));
|
||||||
QStringLiteral("color_in"),
|
|
||||||
NodeValue::kColor,
|
|
||||||
QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f)));
|
|
||||||
|
|
||||||
radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f);
|
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
|
||||||
radius_input_->setProperty("min", 0.0f);
|
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
|
||||||
|
|
||||||
opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f);
|
AddInput(kOpacityInput, NodeValue::kFloat, 1.0f);
|
||||||
opacity_input_->setProperty("view", FloatSlider::kPercentage);
|
SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
opacity_input_->setProperty("min", 0.0f);
|
SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.0f);
|
||||||
opacity_input_->setProperty("max", 1.0f);
|
SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f);
|
||||||
|
|
||||||
inner_input_ = new NodeInput(this, QStringLiteral("inner_in"), NodeValue::kBoolean, false);
|
AddInput(kInnerInput, NodeValue::kBoolean, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *StrokeFilterNode::copy() const
|
Node *StrokeFilterNode::copy() const
|
||||||
@@ -72,33 +75,35 @@ QString StrokeFilterNode::Description() const
|
|||||||
|
|
||||||
void StrokeFilterNode::Retranslate()
|
void StrokeFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
tex_input_->set_name(tr("Input"));
|
SetInputName(kTextureInput, tr("Input"));
|
||||||
color_input_->set_name(tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
radius_input_->set_name(tr("Radius"));
|
SetInputName(kRadiusInput, tr("Radius"));
|
||||||
opacity_input_->set_name(tr("Opacity"));
|
SetInputName(kOpacityInput, tr("Opacity"));
|
||||||
inner_input_->set_name(tr("Inner"));
|
SetInputName(kInnerInput, tr("Inner"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const
|
NodeValueTable StrokeFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
|
|
||||||
job.InsertValue(tex_input_, value);
|
job.InsertValue(this, kTextureInput, value);
|
||||||
job.InsertValue(color_input_, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
job.InsertValue(radius_input_, value);
|
job.InsertValue(this, kRadiusInput, value);
|
||||||
job.InsertValue(opacity_input_, value);
|
job.InsertValue(this, kOpacityInput, value);
|
||||||
job.InsertValue(inner_input_, value);
|
job.InsertValue(this, kInnerInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"),
|
job.InsertValue(QStringLiteral("resolution_in"),
|
||||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (!job.GetValue(tex_input_).data().isNull()) {
|
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||||
if (job.GetValue(radius_input_).data().toDouble() > 0.0
|
if (job.GetValue(kRadiusInput).data().toDouble() > 0.0
|
||||||
&& job.GetValue(opacity_input_).data().toDouble() > 0.0) {
|
&& job.GetValue(kOpacityInput).data().toDouble() > 0.0) {
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
} else {
|
} else {
|
||||||
table.Push(job.GetValue(tex_input_));
|
table.Push(job.GetValue(kTextureInput));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,19 +40,14 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
private:
|
static const QString kTextureInput;
|
||||||
NodeInput* tex_input_;
|
static const QString kColorInput;
|
||||||
|
static const QString kRadiusInput;
|
||||||
NodeInput* color_input_;
|
static const QString kOpacityInput;
|
||||||
|
static const QString kInnerInput;
|
||||||
NodeInput* radius_input_;
|
|
||||||
|
|
||||||
NodeInput* opacity_input_;
|
|
||||||
|
|
||||||
NodeInput* inner_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,23 +27,26 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString MatrixGenerator::kPositionInput = QStringLiteral("pos_in");
|
||||||
|
const QString MatrixGenerator::kRotationInput = QStringLiteral("rot_in");
|
||||||
|
const QString MatrixGenerator::kScaleInput = QStringLiteral("scale_in");
|
||||||
|
const QString MatrixGenerator::kUniformScaleInput = QStringLiteral("uniform_scale_in");
|
||||||
|
const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in");
|
||||||
|
|
||||||
MatrixGenerator::MatrixGenerator()
|
MatrixGenerator::MatrixGenerator()
|
||||||
{
|
{
|
||||||
position_input_ = new NodeInput(this, QStringLiteral("pos_in"), NodeValue::kVec2, QVector2D());
|
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||||
|
|
||||||
rotation_input_ = new NodeInput(this, QStringLiteral("rot_in"), NodeValue::kFloat, 0.0f);
|
AddInput(kRotationInput, NodeValue::kFloat, 0.0);
|
||||||
|
|
||||||
scale_input_ = new NodeInput(this, QStringLiteral("scale_in"), NodeValue::kVec2, QVector2D(1.0f, 1.0f));
|
AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f));
|
||||||
scale_input_->setProperty("min", QVector2D(0, 0));
|
SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0));
|
||||||
scale_input_->setProperty("view", FloatSlider::kPercentage);
|
SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
scale_input_->setProperty("disabley", true);
|
SetInputProperty(kScaleInput, QStringLiteral("disabley"), true);
|
||||||
|
|
||||||
uniform_scale_input_ = new NodeInput(this, QStringLiteral("uniform_scale_in"), NodeValue::kBoolean, true);
|
AddInput(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
uniform_scale_input_->SetKeyframable(false);
|
|
||||||
uniform_scale_input_->SetConnectable(false);
|
|
||||||
connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged);
|
|
||||||
|
|
||||||
anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D());
|
AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *MatrixGenerator::copy() const
|
Node *MatrixGenerator::copy() const
|
||||||
@@ -78,20 +81,22 @@ QString MatrixGenerator::Description() const
|
|||||||
|
|
||||||
void MatrixGenerator::Retranslate()
|
void MatrixGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
position_input_->set_name(tr("Position"));
|
SetInputName(kPositionInput, tr("Position"));
|
||||||
rotation_input_->set_name(tr("Rotation"));
|
SetInputName(kRotationInput, tr("Rotation"));
|
||||||
scale_input_->set_name(tr("Scale"));
|
SetInputName(kScaleInput, tr("Scale"));
|
||||||
uniform_scale_input_->set_name(tr("Uniform Scale"));
|
SetInputName(kUniformScaleInput, tr("Uniform Scale"));
|
||||||
anchor_input_->set_name(tr("Anchor Point"));
|
SetInputName(kAnchorInput, tr("Anchor Point"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const
|
NodeValueTable MatrixGenerator::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
// Push matrix output
|
// Push matrix output
|
||||||
QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false);
|
QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false);
|
||||||
NodeValueTable output = value.Merge();
|
NodeValueTable out = value.Merge();
|
||||||
output.Push(NodeValue::kMatrix, mat, this);
|
out.Push(NodeValue::kMatrix, mat, this);
|
||||||
return output;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const
|
QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const
|
||||||
@@ -103,47 +108,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take,
|
|||||||
if (!ignore_anchor) {
|
if (!ignore_anchor) {
|
||||||
if (take) {
|
if (take) {
|
||||||
// Take and store
|
// Take and store
|
||||||
anchor = value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
anchor = value[kAnchorInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
} else {
|
} else {
|
||||||
// Get and store
|
// Get and store
|
||||||
anchor = value[anchor_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
anchor = value[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
} else if (take) {
|
} else if (take) {
|
||||||
// Just take
|
// Just take
|
||||||
value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
value[kAnchorInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore_scale) {
|
if (!ignore_scale) {
|
||||||
if (take) {
|
if (take) {
|
||||||
scale = value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
scale = value[kScaleInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
} else {
|
} else {
|
||||||
scale = value[scale_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
scale = value[kScaleInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
} else if (take) {
|
} else if (take) {
|
||||||
value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
value[kScaleInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore_position) {
|
if (!ignore_position) {
|
||||||
if (take) {
|
if (take) {
|
||||||
position = value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
position = value[kPositionInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
} else {
|
} else {
|
||||||
position = value[position_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
position = value[kPositionInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
} else if (take) {
|
} else if (take) {
|
||||||
value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
value[kPositionInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (take) {
|
if (take) {
|
||||||
return GenerateMatrix(position,
|
return GenerateMatrix(position,
|
||||||
value[rotation_input_].Take(NodeValue::kFloat).toFloat(),
|
value[kRotationInput].Take(NodeValue::kFloat).toFloat(),
|
||||||
scale,
|
scale,
|
||||||
value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(),
|
value[kUniformScaleInput].Take(NodeValue::kBoolean).toBool(),
|
||||||
anchor);
|
anchor);
|
||||||
} else {
|
} else {
|
||||||
return GenerateMatrix(position,
|
return GenerateMatrix(position,
|
||||||
value[rotation_input_].Get(NodeValue::kFloat).toFloat(),
|
value[kRotationInput].Get(NodeValue::kFloat).toFloat(),
|
||||||
scale,
|
scale,
|
||||||
value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(),
|
value[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(),
|
||||||
anchor);
|
anchor);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -178,9 +183,13 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
|
|||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatrixGenerator::UniformScaleChanged()
|
void MatrixGenerator::InputValueChangedEvent(const QString &input, int element)
|
||||||
{
|
{
|
||||||
scale_input_->setProperty("disabley", uniform_scale_input_->GetStandardValue().toBool());
|
Q_UNUSED(element)
|
||||||
|
|
||||||
|
if (input == kUniformScaleInput) {
|
||||||
|
SetInputProperty(kScaleInput, QStringLiteral("disabley"), GetStandardValue(kUniformScaleInput).toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,13 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
|
static const QString kPositionInput;
|
||||||
|
static const QString kRotationInput;
|
||||||
|
static const QString kScaleInput;
|
||||||
|
static const QString kUniformScaleInput;
|
||||||
|
static const QString kAnchorInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const;
|
QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const;
|
||||||
@@ -54,44 +60,7 @@ protected:
|
|||||||
bool uniform_scale,
|
bool uniform_scale,
|
||||||
const QVector2D &anchor);
|
const QVector2D &anchor);
|
||||||
|
|
||||||
NodeInput* position_input() const
|
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||||
{
|
|
||||||
return position_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* rotation_input() const
|
|
||||||
{
|
|
||||||
return rotation_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* scale_input() const
|
|
||||||
{
|
|
||||||
return scale_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* uniform_scale_input() const
|
|
||||||
{
|
|
||||||
return uniform_scale_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* anchor_input() const
|
|
||||||
{
|
|
||||||
return anchor_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* position_input_;
|
|
||||||
|
|
||||||
NodeInput* rotation_input_;
|
|
||||||
|
|
||||||
NodeInput* scale_input_;
|
|
||||||
|
|
||||||
NodeInput* uniform_scale_input_;
|
|
||||||
|
|
||||||
NodeInput* anchor_input_;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void UniformScaleChanged();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,25 +25,27 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in");
|
||||||
|
const QString PolygonGenerator::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
PolygonGenerator::PolygonGenerator()
|
PolygonGenerator::PolygonGenerator()
|
||||||
{
|
{
|
||||||
points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0));
|
AddInput(kPointsInput, NodeValue::kVec2, QVector2D(0, 0), InputFlags(kInputFlagArray));
|
||||||
points_input_->SetIsArray(true);
|
|
||||||
|
|
||||||
color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0)));
|
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0)));
|
||||||
|
|
||||||
// The Default Pentagon(tm)
|
// The Default Pentagon(tm)
|
||||||
points_input_->ArrayResize(5);
|
InputArrayResize(kPointsInput, 5);
|
||||||
points_input_->SetStandardValueOnTrack(960, 0, 0);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, 960, 0);
|
||||||
points_input_->SetStandardValueOnTrack(240, 1, 0);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, 240, 0);
|
||||||
points_input_->SetStandardValueOnTrack(640, 0, 1);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, 640, 1);
|
||||||
points_input_->SetStandardValueOnTrack(480, 1, 1);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 1);
|
||||||
points_input_->SetStandardValueOnTrack(760, 0, 2);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, 760, 2);
|
||||||
points_input_->SetStandardValueOnTrack(800, 1, 2);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 2);
|
||||||
points_input_->SetStandardValueOnTrack(1100, 0, 3);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, 1100, 3);
|
||||||
points_input_->SetStandardValueOnTrack(800, 1, 3);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 3);
|
||||||
points_input_->SetStandardValueOnTrack(1280, 0, 4);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, 1200, 4);
|
||||||
points_input_->SetStandardValueOnTrack(480, 1, 4);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *PolygonGenerator::copy() const
|
Node *PolygonGenerator::copy() const
|
||||||
@@ -73,8 +75,8 @@ QString PolygonGenerator::Description() const
|
|||||||
|
|
||||||
void PolygonGenerator::Retranslate()
|
void PolygonGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
points_input_->set_name(tr("Points"));
|
SetInputName(kPointsInput, tr("Points"));
|
||||||
color_input_->set_name(tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const
|
ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const
|
||||||
@@ -84,12 +86,14 @@ ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const
|
|||||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/polygon.frag")));
|
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/polygon.frag")));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const
|
NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
|
|
||||||
job.InsertValue(points_input_, value);
|
job.InsertValue(this, kPointsInput, value);
|
||||||
job.InsertValue(color_input_, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(true);
|
||||||
|
|
||||||
@@ -168,10 +172,10 @@ void PolygonGenerator::GizmoRelease()
|
|||||||
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
|
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
|
||||||
{
|
{
|
||||||
// FIXME: Should Get() use a `kArray` type instead of a `kVec2` type?
|
// FIXME: Should Get() use a `kArray` type instead of a `kVec2` type?
|
||||||
QVector<NodeValueTable> array_tbl = db[points_input_].Get(NodeValue::kVec2).value< QVector<NodeValueTable> >();
|
QVector<NodeValueTable> array_tbl = db[kPointsInput].Get(NodeValue::kVec2).value< QVector<NodeValueTable> >();
|
||||||
QVector<QPointF> points(array_tbl.size());
|
QVector<QPointF> points(array_tbl.size());
|
||||||
|
|
||||||
for (int i=0;i<points_input_->ArraySize();i++) {
|
for (int i=0;i<array_tbl.size();i++) {
|
||||||
QVector2D v = array_tbl.at(i).Get(NodeValue::kVec2).value<QVector2D>();
|
QVector2D v = array_tbl.at(i).Get(NodeValue::kVec2).value<QVector2D>();
|
||||||
|
|
||||||
v *= scale;
|
v *= scale;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public:
|
|||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
virtual bool HasGizmos() const override;
|
virtual bool HasGizmos() const override;
|
||||||
//virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p) const override;
|
//virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p) const override;
|
||||||
@@ -51,15 +51,14 @@ public:
|
|||||||
//virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override;
|
//virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override;
|
||||||
//virtual void GizmoRelease() override;
|
//virtual void GizmoRelease() override;
|
||||||
|
|
||||||
|
static const QString kPointsInput;
|
||||||
|
static const QString kColorInput;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QPointF> GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const;
|
QVector<QPointF> GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const;
|
||||||
|
|
||||||
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
|
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
|
||||||
|
|
||||||
NodeInput* points_input_;
|
|
||||||
|
|
||||||
NodeInput* color_input_;
|
|
||||||
|
|
||||||
NodeInput* gizmo_drag_;
|
NodeInput* gizmo_drag_;
|
||||||
QPointF gizmo_drag_start_;
|
QPointF gizmo_drag_start_;
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,12 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString SolidGenerator::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
SolidGenerator::SolidGenerator()
|
SolidGenerator::SolidGenerator()
|
||||||
{
|
{
|
||||||
// Default to a color that isn't black
|
// Default to a color that isn't black
|
||||||
color_input_ = new NodeInput(this,
|
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f)));
|
||||||
QStringLiteral("color_in"),
|
|
||||||
NodeValue::kColor,
|
|
||||||
QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *SolidGenerator::copy() const
|
Node *SolidGenerator::copy() const
|
||||||
@@ -60,13 +59,15 @@ QString SolidGenerator::Description() const
|
|||||||
|
|
||||||
void SolidGenerator::Retranslate()
|
void SolidGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
color_input_->set_name(tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const
|
NodeValueTable SolidGenerator::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.InsertValue(color_input_, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
|
|||||||
@@ -40,11 +40,10 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
private:
|
static const QString kColorInput;
|
||||||
NodeInput* color_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,31 +30,23 @@ enum TextVerticalAlign {
|
|||||||
kVerticalAlignBottom,
|
kVerticalAlignBottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QString TextGenerator::kTextInput = QStringLiteral("text_in");
|
||||||
|
const QString TextGenerator::kColorInput = QStringLiteral("color_in");
|
||||||
|
const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in");
|
||||||
|
const QString TextGenerator::kFontInput = QStringLiteral("font_in");
|
||||||
|
const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in");
|
||||||
|
|
||||||
TextGenerator::TextGenerator()
|
TextGenerator::TextGenerator()
|
||||||
{
|
{
|
||||||
text_input_ = new NodeInput(this,
|
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||||
QStringLiteral("text_in"),
|
|
||||||
NodeValue::kText,
|
|
||||||
tr("Sample Text"));
|
|
||||||
|
|
||||||
color_input_ = new NodeInput(this,
|
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0)));
|
||||||
QStringLiteral("color_in"),
|
|
||||||
NodeValue::kColor,
|
|
||||||
QVariant::fromValue(Color(1.0f, 1.0f, 1.0)));
|
|
||||||
|
|
||||||
valign_input_ = new NodeInput(this,
|
AddInput(kVAlignInput, NodeValue::kCombo, 1);
|
||||||
QStringLiteral("valign_in"),
|
|
||||||
NodeValue::kCombo,
|
|
||||||
1);
|
|
||||||
|
|
||||||
font_input_ = new NodeInput(this,
|
AddInput(kFontInput, NodeValue::kFont);
|
||||||
QStringLiteral("font_in"),
|
|
||||||
NodeValue::kFont);
|
|
||||||
|
|
||||||
font_size_input_ = new NodeInput(this,
|
AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f);
|
||||||
QStringLiteral("font_size_in"),
|
|
||||||
NodeValue::kFloat,
|
|
||||||
72.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *TextGenerator::copy() const
|
Node *TextGenerator::copy() const
|
||||||
@@ -84,27 +76,29 @@ QString TextGenerator::Description() const
|
|||||||
|
|
||||||
void TextGenerator::Retranslate()
|
void TextGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
text_input_->set_name(tr("Text"));
|
SetInputName(kTextInput, tr("Text"));
|
||||||
font_input_->set_name(tr("Font"));
|
SetInputName(kFontInput, tr("Font"));
|
||||||
font_size_input_->set_name(tr("Font Size"));
|
SetInputName(kFontSizeInput, tr("Font Size"));
|
||||||
color_input_->set_name(tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
valign_input_->set_name(tr("Vertical Align"));
|
SetInputName(kVAlignInput, tr("Vertical Align"));
|
||||||
valign_input_->set_combobox_strings({tr("Top"), tr("Center"), tr("Bottom")});
|
SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")});
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const
|
NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
GenerateJob job;
|
GenerateJob job;
|
||||||
job.InsertValue(text_input_, value);
|
job.InsertValue(this, kTextInput, value);
|
||||||
job.InsertValue(color_input_, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
job.InsertValue(valign_input_, value);
|
job.InsertValue(this, kVAlignInput, value);
|
||||||
job.InsertValue(font_input_, value);
|
job.InsertValue(this, kFontInput, value);
|
||||||
job.InsertValue(font_size_input_, value);
|
job.InsertValue(this, kFontSizeInput, value);
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(true);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (!job.GetValue(text_input_).data().toString().isEmpty()) {
|
if (!job.GetValue(kTextInput).data().toString().isEmpty()) {
|
||||||
table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +118,14 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
|||||||
|
|
||||||
// Set default font
|
// Set default font
|
||||||
QFont default_font;
|
QFont default_font;
|
||||||
default_font.setFamily(job.GetValue(font_input_).data().toString());
|
default_font.setFamily(job.GetValue(kFontInput).data().toString());
|
||||||
default_font.setPointSizeF(job.GetValue(font_size_input_).data().toFloat());
|
default_font.setPointSizeF(job.GetValue(kFontSizeInput).data().toFloat());
|
||||||
text_doc.setDefaultFont(default_font);
|
text_doc.setDefaultFont(default_font);
|
||||||
|
|
||||||
// Center by default
|
// Center by default
|
||||||
text_doc.setDefaultTextOption(QTextOption(Qt::AlignCenter));
|
text_doc.setDefaultTextOption(QTextOption(Qt::AlignCenter));
|
||||||
|
|
||||||
text_doc.setHtml(job.GetValue(text_input_).data().toString());
|
text_doc.setHtml(job.GetValue(kTextInput).data().toString());
|
||||||
|
|
||||||
// Align to 80% width because that's considered the "title safe" area
|
// Align to 80% width because that's considered the "title safe" area
|
||||||
int tenth_of_width = frame->video_params().width() / 10;
|
int tenth_of_width = frame->video_params().width() / 10;
|
||||||
@@ -144,7 +138,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
|||||||
// Push 10% inwards to compensate for title safe area
|
// Push 10% inwards to compensate for title safe area
|
||||||
p.translate(tenth_of_width, 0);
|
p.translate(tenth_of_width, 0);
|
||||||
|
|
||||||
TextVerticalAlign valign = static_cast<TextVerticalAlign>(job.GetValue(valign_input_).data().toInt());
|
TextVerticalAlign valign = static_cast<TextVerticalAlign>(job.GetValue(kVAlignInput).data().toInt());
|
||||||
int doc_height = text_doc.size().height();
|
int doc_height = text_doc.size().height();
|
||||||
|
|
||||||
switch (valign) {
|
switch (valign) {
|
||||||
@@ -165,7 +159,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
|||||||
text_doc.drawContents(&p);
|
text_doc.drawContents(&p);
|
||||||
|
|
||||||
// Transplant alpha channel to frame
|
// Transplant alpha channel to frame
|
||||||
Color rgb = job.GetValue(color_input_).data().value<Color>();
|
Color rgb = job.GetValue(kColorInput).data().value<Color>();
|
||||||
for (int x=0; x<frame->width(); x++) {
|
for (int x=0; x<frame->width(); x++) {
|
||||||
for (int y=0; y<frame->height(); y++) {
|
for (int y=0; y<frame->height(); y++) {
|
||||||
uchar src_alpha = img.bits()[img.bytesPerLine() * y + x];
|
uchar src_alpha = img.bits()[img.bytesPerLine() * y + x];
|
||||||
|
|||||||
@@ -40,20 +40,15 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
||||||
|
|
||||||
private:
|
static const QString kTextInput;
|
||||||
NodeInput* text_input_;
|
static const QString kColorInput;
|
||||||
|
static const QString kVAlignInput;
|
||||||
NodeInput* color_input_;
|
static const QString kFontInput;
|
||||||
|
static const QString kFontSizeInput;
|
||||||
NodeInput* valign_input_;
|
|
||||||
|
|
||||||
NodeInput* font_input_;
|
|
||||||
|
|
||||||
NodeInput* font_size_input_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -65,11 +65,11 @@ signals:
|
|||||||
*/
|
*/
|
||||||
void NodeRemoved(Node* node);
|
void NodeRemoved(Node* node);
|
||||||
|
|
||||||
void InputConnected(Node* output, NodeInput* input, int element);
|
void InputConnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void InputDisconnected(Node* output, NodeInput* input, int element);
|
void InputDisconnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void ValueChanged(NodeInput* input, int element);
|
void ValueChanged(const NodeInput& input);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void childEvent(QChildEvent* event) override;
|
virtual void childEvent(QChildEvent* event) override;
|
||||||
|
|||||||
@@ -1,905 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2020 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
#include "common/bezier.h"
|
|
||||||
#include "common/lerp.h"
|
|
||||||
#include "common/xmlutils.h"
|
|
||||||
#include "core.h"
|
|
||||||
#include "node.h"
|
|
||||||
#include "project/item/footage/stream.h"
|
|
||||||
#include "widget/nodeview/nodeviewundo.h"
|
|
||||||
|
|
||||||
#define super NodeConnectable
|
|
||||||
|
|
||||||
namespace olive {
|
|
||||||
|
|
||||||
NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const SplitValue &default_value)
|
|
||||||
{
|
|
||||||
Init(parent, id, type, default_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, const QVariant &default_value)
|
|
||||||
{
|
|
||||||
Init(parent, id, type, NodeValue::split_normal_value_into_track_values(type, default_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type)
|
|
||||||
{
|
|
||||||
Init(parent, id, type, SplitValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput::~NodeInput()
|
|
||||||
{
|
|
||||||
// Disconnect everything
|
|
||||||
DisconnectAll();
|
|
||||||
|
|
||||||
// Delete instances
|
|
||||||
delete primary_;
|
|
||||||
qDeleteAll(subinputs_);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString NodeInput::name() const
|
|
||||||
{
|
|
||||||
if (name_.isEmpty()) {
|
|
||||||
return tr("Input");
|
|
||||||
}
|
|
||||||
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::DisconnectAll()
|
|
||||||
{
|
|
||||||
std::map<int, Node*> copied_edges = edges();
|
|
||||||
|
|
||||||
for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) {
|
|
||||||
DisconnectEdge(it->second, this, it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
|
|
||||||
{
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (cancelled && *cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader->name() == QStringLiteral("primary")) {
|
|
||||||
// Load primary immediate
|
|
||||||
LoadImmediate(reader, -1, xml_node_data, cancelled);
|
|
||||||
} else if (reader->name() == QStringLiteral("subelements")) {
|
|
||||||
// Load subelements
|
|
||||||
XMLAttributeLoop(reader, attr) {
|
|
||||||
if (attr.name() == QStringLiteral("count")) {
|
|
||||||
ArrayResize(attr.value().toInt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int element_counter = 0;
|
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (reader->name() == QStringLiteral("element")) {
|
|
||||||
LoadImmediate(reader, element_counter, xml_node_data, cancelled);
|
|
||||||
|
|
||||||
element_counter++;
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (reader->name() == QStringLiteral("connections")) {
|
|
||||||
// Load connections
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (reader->name() == QStringLiteral("connection")) {
|
|
||||||
int ele = -1;
|
|
||||||
|
|
||||||
XMLAttributeLoop(reader, attr) {
|
|
||||||
if (attr.name() == QStringLiteral("element")) {
|
|
||||||
ele = attr.value().toInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xml_node_data.desired_connections.append({this, ele, reader->readElementText().toULongLong()});
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::Save(QXmlStreamWriter *writer) const
|
|
||||||
{
|
|
||||||
writer->writeAttribute(QStringLiteral("id"), id());
|
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("primary"));
|
|
||||||
|
|
||||||
SaveImmediate(writer, -1);
|
|
||||||
|
|
||||||
writer->writeEndElement(); // primary
|
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("subelements"));
|
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("count"), QString::number(array_size_));
|
|
||||||
|
|
||||||
for (int i=0; i<array_size_; i++) {
|
|
||||||
writer->writeStartElement(QStringLiteral("element"));
|
|
||||||
|
|
||||||
SaveImmediate(writer, i);
|
|
||||||
|
|
||||||
writer->writeEndElement(); // element
|
|
||||||
}
|
|
||||||
|
|
||||||
writer->writeEndElement(); // subelements
|
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("connections"));
|
|
||||||
|
|
||||||
for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) {
|
|
||||||
writer->writeStartElement(QStringLiteral("connection"));
|
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it->first));
|
|
||||||
|
|
||||||
writer->writeCharacters(QString::number(reinterpret_cast<quintptr>(it->second)));
|
|
||||||
|
|
||||||
writer->writeEndElement(); // connection
|
|
||||||
}
|
|
||||||
|
|
||||||
writer->writeEndElement(); // connections
|
|
||||||
}
|
|
||||||
|
|
||||||
Node *NodeInput::parent() const
|
|
||||||
{
|
|
||||||
return static_cast<Node*>(QObject::parent());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeInput::event(QEvent *e)
|
|
||||||
{
|
|
||||||
if (e->type() == QEvent::DynamicPropertyChange) {
|
|
||||||
QByteArray key = static_cast<QDynamicPropertyChangeEvent*>(e)->propertyName();
|
|
||||||
emit PropertyChanged(key, property(key));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QObject::event(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::childEvent(QChildEvent *event)
|
|
||||||
{
|
|
||||||
super::childEvent(event);
|
|
||||||
|
|
||||||
NodeKeyframe* key = dynamic_cast<NodeKeyframe*>(event->child());
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
if (event->type() == QEvent::ChildAdded) {
|
|
||||||
GetImmediate(key->element())->insert_keyframe(key);
|
|
||||||
|
|
||||||
connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::InvalidateFromKeyframeTimeChange);
|
|
||||||
connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
|
|
||||||
connect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange);
|
|
||||||
connect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged);
|
|
||||||
connect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange);
|
|
||||||
connect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange);
|
|
||||||
|
|
||||||
emit KeyframeAdded(key);
|
|
||||||
emit ValueChanged(get_range_affected_by_keyframe(key), key->element());
|
|
||||||
} else if (event->type() == QEvent::ChildRemoved) {
|
|
||||||
TimeRange time_affected = get_range_affected_by_keyframe(key);
|
|
||||||
|
|
||||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::InvalidateFromKeyframeTimeChange);
|
|
||||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
|
|
||||||
disconnect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange);
|
|
||||||
disconnect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged);
|
|
||||||
disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange);
|
|
||||||
disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange);
|
|
||||||
|
|
||||||
GetImmediate(key->element())->remove_keyframe(key);
|
|
||||||
|
|
||||||
emit KeyframeRemoved(key);
|
|
||||||
emit ValueChanged(time_affected, key->element());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ClearElement(int index)
|
|
||||||
{
|
|
||||||
GetImmediate(index)->delete_all_keyframes();
|
|
||||||
|
|
||||||
if (IsKeyframable()) {
|
|
||||||
SetIsKeyframing(false, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetSplitStandardValue(default_value_, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_val)
|
|
||||||
{
|
|
||||||
setParent(parent);
|
|
||||||
|
|
||||||
id_ = id;
|
|
||||||
keyframable_ = true;
|
|
||||||
connectable_ = true;
|
|
||||||
default_value_ = default_val;
|
|
||||||
array_size_ = 0;
|
|
||||||
data_type_ = type;
|
|
||||||
is_array_ = false;
|
|
||||||
|
|
||||||
primary_ = CreateImmediate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
|
|
||||||
{
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (reader->name() == QStringLiteral("standard")) {
|
|
||||||
// Load standard value
|
|
||||||
int val_index = 0;
|
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (cancelled && *cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader->name() == QStringLiteral("track")) {
|
|
||||||
QString value_text = reader->readElementText();
|
|
||||||
QVariant value_on_track;
|
|
||||||
|
|
||||||
if (!value_text.isEmpty()) {
|
|
||||||
value_on_track = StringToValue(value_text, xml_node_data.footage_connections, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetStandardValueOnTrack(value_on_track, val_index, element);
|
|
||||||
|
|
||||||
val_index++;
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (reader->name() == QStringLiteral("keyframing") && IsKeyframable()) {
|
|
||||||
SetIsKeyframing(reader->readElementText().toInt(), element);
|
|
||||||
} else if (reader->name() == QStringLiteral("keyframes")) {
|
|
||||||
int track = 0;
|
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (cancelled && *cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader->name() == QStringLiteral("track")) {
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
|
||||||
if (cancelled && *cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader->name() == QStringLiteral("key")) {
|
|
||||||
rational key_time;
|
|
||||||
NodeKeyframe::Type key_type;
|
|
||||||
QVariant key_value;
|
|
||||||
QPointF key_in_handle;
|
|
||||||
QPointF key_out_handle;
|
|
||||||
|
|
||||||
XMLAttributeLoop(reader, attr) {
|
|
||||||
if (cancelled && *cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr.name() == QStringLiteral("time")) {
|
|
||||||
key_time = rational::fromString(attr.value().toString());
|
|
||||||
} else if (attr.name() == QStringLiteral("type")) {
|
|
||||||
key_type = static_cast<NodeKeyframe::Type>(attr.value().toInt());
|
|
||||||
} else if (attr.name() == QStringLiteral("inhandlex")) {
|
|
||||||
key_in_handle.setX(attr.value().toDouble());
|
|
||||||
} else if (attr.name() == QStringLiteral("inhandley")) {
|
|
||||||
key_in_handle.setY(attr.value().toDouble());
|
|
||||||
} else if (attr.name() == QStringLiteral("outhandlex")) {
|
|
||||||
key_out_handle.setX(attr.value().toDouble());
|
|
||||||
} else if (attr.name() == QStringLiteral("outhandley")) {
|
|
||||||
key_out_handle.setY(attr.value().toDouble());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key_value = StringToValue(reader->readElementText(), xml_node_data.footage_connections, element);
|
|
||||||
|
|
||||||
NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, this);
|
|
||||||
key->set_bezier_control_in(key_in_handle);
|
|
||||||
key->set_bezier_control_out(key_out_handle);
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
track++;
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (reader->name() == QStringLiteral("csinput")) {
|
|
||||||
setProperty("col_input", reader->readElementText());
|
|
||||||
} else if (reader->name() == QStringLiteral("csdisplay")) {
|
|
||||||
setProperty("col_display", reader->readElementText());
|
|
||||||
} else if (reader->name() == QStringLiteral("csview")) {
|
|
||||||
setProperty("col_view", reader->readElementText());
|
|
||||||
} else if (reader->name() == QStringLiteral("cslook")) {
|
|
||||||
setProperty("col_look", reader->readElementText());
|
|
||||||
} else {
|
|
||||||
reader->skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::SaveImmediate(QXmlStreamWriter* writer, int element) const
|
|
||||||
{
|
|
||||||
if (IsKeyframable()) {
|
|
||||||
writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsKeyframing(element)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write standard value
|
|
||||||
writer->writeStartElement("standard");
|
|
||||||
|
|
||||||
foreach (const QVariant& v, GetSplitStandardValue(element)) {
|
|
||||||
writer->writeTextElement("track", ValueToString(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
writer->writeEndElement(); // standard
|
|
||||||
|
|
||||||
// Write keyframes
|
|
||||||
writer->writeStartElement("keyframes");
|
|
||||||
|
|
||||||
foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(element)) {
|
|
||||||
writer->writeStartElement("track");
|
|
||||||
|
|
||||||
foreach (NodeKeyframe* key, track) {
|
|
||||||
writer->writeStartElement("key");
|
|
||||||
|
|
||||||
writer->writeAttribute("time", key->time().toString());
|
|
||||||
writer->writeAttribute("type", QString::number(key->type()));
|
|
||||||
writer->writeAttribute("inhandlex", QString::number(key->bezier_control_in().x()));
|
|
||||||
writer->writeAttribute("inhandley", QString::number(key->bezier_control_in().y()));
|
|
||||||
writer->writeAttribute("outhandlex", QString::number(key->bezier_control_out().x()));
|
|
||||||
writer->writeAttribute("outhandley", QString::number(key->bezier_control_out().y()));
|
|
||||||
|
|
||||||
writer->writeCharacters(ValueToString(key->value()));
|
|
||||||
|
|
||||||
writer->writeEndElement(); // key
|
|
||||||
}
|
|
||||||
|
|
||||||
writer->writeEndElement(); // track
|
|
||||||
}
|
|
||||||
|
|
||||||
writer->writeEndElement(); // keyframes
|
|
||||||
|
|
||||||
if (data_type_ == NodeValue::kColor) {
|
|
||||||
// Save color management information
|
|
||||||
writer->writeTextElement(QStringLiteral("csinput"), property("col_input").toString());
|
|
||||||
writer->writeTextElement(QStringLiteral("csdisplay"), property("col_display").toString());
|
|
||||||
writer->writeTextElement(QStringLiteral("csview"), property("col_view").toString());
|
|
||||||
writer->writeTextElement(QStringLiteral("cslook"), property("col_look").toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayResizeInternal(int size)
|
|
||||||
{
|
|
||||||
if (array_size_ != size) {
|
|
||||||
// Update array size
|
|
||||||
if (array_size_ < size) {
|
|
||||||
// Size is larger, create any immediates that don't exist
|
|
||||||
for (int i=subinputs_.size(); i<size; i++) {
|
|
||||||
subinputs_.append(CreateImmediate());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that we do not delete any immediates when decreasing size since the user might still
|
|
||||||
// want that data. Therefore it's important to note that array_size_ does NOT necessarily
|
|
||||||
// equal subinputs_.size()
|
|
||||||
}
|
|
||||||
|
|
||||||
array_size_ = size;
|
|
||||||
emit ArraySizeChanged(array_size_);
|
|
||||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInputImmediate *NodeInput::CreateImmediate()
|
|
||||||
{
|
|
||||||
return new NodeInputImmediate(data_type_, default_value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::GetDependencies(QVector<Node *> &list, bool traverse, bool exclusive_only) const
|
|
||||||
{
|
|
||||||
for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) {
|
|
||||||
Node* connected = it->second;
|
|
||||||
|
|
||||||
if (connected->edges().size() == 1 || !exclusive_only) {
|
|
||||||
if (!list.contains(connected)) {
|
|
||||||
list.append(connected);
|
|
||||||
|
|
||||||
if (traverse) {
|
|
||||||
foreach (NodeInput* i, connected->parameters()) {
|
|
||||||
i->GetDependencies(list, traverse, exclusive_only);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant NodeInput::GetDefaultValueForTrack(int track) const
|
|
||||||
{
|
|
||||||
if (default_value_.isEmpty()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
return default_value_.at(track);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Node *> NodeInput::GetDependencies(bool traverse, bool exclusive_only) const
|
|
||||||
{
|
|
||||||
QVector<Node *> list;
|
|
||||||
|
|
||||||
GetDependencies(list, traverse, exclusive_only);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Node *> NodeInput::GetExclusiveDependencies() const
|
|
||||||
{
|
|
||||||
return GetDependencies(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Node *> NodeInput::GetImmediateDependencies() const
|
|
||||||
{
|
|
||||||
return GetDependencies(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayAppend(bool undoable)
|
|
||||||
{
|
|
||||||
ArrayResize(ArraySize() + 1, undoable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayInsert(int index, bool undoable)
|
|
||||||
{
|
|
||||||
if (undoable) {
|
|
||||||
Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, index));
|
|
||||||
} else {
|
|
||||||
// Add new input
|
|
||||||
ArrayResizeInternal(ArraySize() + 1);
|
|
||||||
|
|
||||||
// Move connections down
|
|
||||||
std::map<int, Node*> copied_edges = edges();
|
|
||||||
for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) {
|
|
||||||
if (it->first >= index) {
|
|
||||||
// Disconnect this and reconnect it one element down
|
|
||||||
DisconnectEdge(it->second, this, it->first);
|
|
||||||
ConnectEdge(it->second, this, it->first + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift values and keyframes up one element
|
|
||||||
for (int i=ArraySize()-1; i>index; i--) {
|
|
||||||
CopyValuesOfElement(this, this, i-1, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset value of element we just "inserted"
|
|
||||||
ClearElement(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayRemove(int index, bool undoable)
|
|
||||||
{
|
|
||||||
if (undoable) {
|
|
||||||
Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, index));
|
|
||||||
} else {
|
|
||||||
// Remove input
|
|
||||||
ArrayResizeInternal(ArraySize() - 1);
|
|
||||||
|
|
||||||
// Move connections up
|
|
||||||
std::map<int, Node*> copied_edges = edges();
|
|
||||||
for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) {
|
|
||||||
if (it->first >= index) {
|
|
||||||
// Disconnect this and reconnect it one element up if it's not the element being removed
|
|
||||||
DisconnectEdge(it->second, this, it->first);
|
|
||||||
|
|
||||||
if (it->first > index) {
|
|
||||||
ConnectEdge(it->second, this, it->first - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift values and keyframes down one element
|
|
||||||
for (int i=index; i<ArraySize(); i++) {
|
|
||||||
// Copying ArraySize()+1 is actually legal because immediates are never deleted
|
|
||||||
CopyValuesOfElement(this, this, i+1, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset value of last element
|
|
||||||
ClearElement(ArraySize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayPrepend(bool undoable)
|
|
||||||
{
|
|
||||||
ArrayInsert(0, undoable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayResize(int size, bool undoable)
|
|
||||||
{
|
|
||||||
if (array_size_ == size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayResizeCommand* c = new ArrayResizeCommand(this, size);
|
|
||||||
|
|
||||||
if (undoable) {
|
|
||||||
Core::instance()->undo_stack()->push(c);
|
|
||||||
} else {
|
|
||||||
c->redo();
|
|
||||||
delete c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::ArrayRemoveLast(bool undoable)
|
|
||||||
{
|
|
||||||
ArrayResize(ArraySize() - 1, undoable);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant NodeInput::GetValueAtTime(const rational &time, int element) const
|
|
||||||
{
|
|
||||||
return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitValuesAtTime(time, element));
|
|
||||||
}
|
|
||||||
|
|
||||||
SplitValue NodeInput::GetSplitValuesAtTime(const rational &time, int element) const
|
|
||||||
{
|
|
||||||
SplitValue vals;
|
|
||||||
|
|
||||||
int nb_tracks = GetNumberOfKeyframeTracks();
|
|
||||||
|
|
||||||
for (int i=0;i<nb_tracks;i++) {
|
|
||||||
if (IsUsingStandardValue(i, element)) {
|
|
||||||
vals.append(GetStandardValueOnTrack(i, element));
|
|
||||||
} else {
|
|
||||||
vals.append(GetValueAtTimeForTrack(time, i, element));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vals;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant NodeInput::GetValueAtTimeForTrack(const rational &time, int track, int element) const
|
|
||||||
{
|
|
||||||
if (!IsUsingStandardValue(track, element)) {
|
|
||||||
const NodeKeyframeTrack& key_track = GetKeyframeTracks(element).at(track);
|
|
||||||
|
|
||||||
if (key_track.first()->time() >= time) {
|
|
||||||
// This time precedes any keyframe, so we just return the first value
|
|
||||||
return key_track.first()->value();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_track.last()->time() <= time) {
|
|
||||||
// This time is after any keyframes so we return the last value
|
|
||||||
return key_track.last()->value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're here, the time must be somewhere in between the keyframes
|
|
||||||
for (int i=0;i<key_track.size()-1;i++) {
|
|
||||||
NodeKeyframe* before = key_track.at(i);
|
|
||||||
NodeKeyframe* after = key_track.at(i+1);
|
|
||||||
|
|
||||||
if (before->time() == time
|
|
||||||
|| !NodeValue::type_can_be_interpolated(data_type_)
|
|
||||||
|| (before->time() < time && before->type() == NodeKeyframe::kHold)) {
|
|
||||||
|
|
||||||
// Time == keyframe time, so value is precise
|
|
||||||
return before->value();
|
|
||||||
|
|
||||||
} else if (after->time() == time) {
|
|
||||||
|
|
||||||
// Time == keyframe time, so value is precise
|
|
||||||
return after->value();
|
|
||||||
|
|
||||||
} else if (before->time() < time && after->time() > time) {
|
|
||||||
// We must interpolate between these keyframes
|
|
||||||
|
|
||||||
if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
|
|
||||||
// Perform a cubic bezier with two control points
|
|
||||||
|
|
||||||
double t = Bezier::CubicXtoT(time.toDouble(),
|
|
||||||
before->time().toDouble(),
|
|
||||||
before->time().toDouble() + before->valid_bezier_control_out().x(),
|
|
||||||
after->time().toDouble() + after->valid_bezier_control_in().x(),
|
|
||||||
after->time().toDouble());
|
|
||||||
|
|
||||||
double y = Bezier::CubicTtoY(before->value().toDouble(),
|
|
||||||
before->value().toDouble() + before->valid_bezier_control_out().y(),
|
|
||||||
after->value().toDouble() + after->valid_bezier_control_in().y(),
|
|
||||||
after->value().toDouble(),
|
|
||||||
t);
|
|
||||||
|
|
||||||
return y;
|
|
||||||
|
|
||||||
} else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) {
|
|
||||||
// Perform a quadratic bezier with only one control point
|
|
||||||
|
|
||||||
QPointF control_point;
|
|
||||||
double control_point_time;
|
|
||||||
double control_point_value;
|
|
||||||
|
|
||||||
if (before->type() == NodeKeyframe::kBezier) {
|
|
||||||
control_point = before->valid_bezier_control_out();
|
|
||||||
control_point_time = before->time().toDouble() + control_point.x();
|
|
||||||
control_point_value = before->value().toDouble() + control_point.y();
|
|
||||||
} else {
|
|
||||||
control_point = after->valid_bezier_control_in();
|
|
||||||
control_point_time = after->time().toDouble() + control_point.x();
|
|
||||||
control_point_value = after->value().toDouble() + control_point.y();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate T from time values - used to determine bezier progress
|
|
||||||
double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble());
|
|
||||||
|
|
||||||
// Generate value using T
|
|
||||||
double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t);
|
|
||||||
|
|
||||||
return y;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// To have arrived here, the keyframes must both be linear
|
|
||||||
qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble());
|
|
||||||
|
|
||||||
return lerp(before->value().toDouble(), after->value().toDouble(), period_progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetStandardValueOnTrack(track, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeInput::IsKeyframable() const
|
|
||||||
{
|
|
||||||
return keyframable_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::SetKeyframable(bool k)
|
|
||||||
{
|
|
||||||
keyframable_ = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool traverse_arrays)
|
|
||||||
{
|
|
||||||
Q_ASSERT(source->id() == dest->id());
|
|
||||||
|
|
||||||
CopyValuesOfElement(source, dest, -1);
|
|
||||||
|
|
||||||
// Copy array size
|
|
||||||
dest->ArrayResize(source->ArraySize());
|
|
||||||
|
|
||||||
// If enabled, copy arrays too
|
|
||||||
if (traverse_arrays) {
|
|
||||||
for (int i=0; i<source->ArraySize(); i++) {
|
|
||||||
CopyValuesOfElement(source, dest, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy connections
|
|
||||||
if (include_connections) {
|
|
||||||
if (traverse_arrays) {
|
|
||||||
// Copy all connections
|
|
||||||
for (auto it=source->input_connections().cbegin(); it!=source->input_connections().cend(); it++) {
|
|
||||||
ConnectEdge(it->second, dest, it->first);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Just copy the primary connection (at -1)
|
|
||||||
if (source->IsConnected()) {
|
|
||||||
ConnectEdge(source->GetConnectedNode(), dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int src_element, int dst_element)
|
|
||||||
{
|
|
||||||
if (dst_element >= dst->subinputs_.size()) {
|
|
||||||
qDebug() << "Ignored destination element that was out of array bounds";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy standard value
|
|
||||||
dst->SetSplitStandardValue(src->GetSplitStandardValue(src_element), dst_element);
|
|
||||||
|
|
||||||
// Copy keyframes
|
|
||||||
dst->GetImmediate(dst_element)->delete_all_keyframes();
|
|
||||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(src_element)->keyframe_tracks()) {
|
|
||||||
foreach (NodeKeyframe* key, track) {
|
|
||||||
key->copy(dst_element, dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy keyframing state
|
|
||||||
if (src->IsKeyframable()) {
|
|
||||||
dst->SetIsKeyframing(src->IsKeyframing(src_element), dst_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the root of an array, copy the array size
|
|
||||||
if (src_element == -1 && dst_element == -1) {
|
|
||||||
dst->ArrayResize(src->ArraySize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList NodeInput::get_combobox_strings() const
|
|
||||||
{
|
|
||||||
return property("combo_str").toStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::set_combobox_strings(const QStringList &strings)
|
|
||||||
{
|
|
||||||
setProperty("combo_str", strings);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::InvalidateFromKeyframeBezierInChange()
|
|
||||||
{
|
|
||||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
|
||||||
const NodeKeyframeTrack& track = GetTrackFromKeyframe(key);
|
|
||||||
int keyframe_index = track.indexOf(key);
|
|
||||||
|
|
||||||
rational start = RATIONAL_MIN;
|
|
||||||
rational end = key->time();
|
|
||||||
|
|
||||||
if (keyframe_index > 0) {
|
|
||||||
start = track.at(keyframe_index - 1)->time();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit ValueChanged(TimeRange(start, end), key->element());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::InvalidateFromKeyframeBezierOutChange()
|
|
||||||
{
|
|
||||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
|
||||||
const NodeKeyframeTrack& track = GetTrackFromKeyframe(key);
|
|
||||||
int keyframe_index = track.indexOf(key);
|
|
||||||
|
|
||||||
rational start = key->time();
|
|
||||||
rational end = RATIONAL_MAX;
|
|
||||||
|
|
||||||
if (keyframe_index < track.size() - 1) {
|
|
||||||
end = track.at(keyframe_index + 1)->time();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit ValueChanged(TimeRange(start, end), key->element());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::InvalidateFromKeyframeTimeChange()
|
|
||||||
{
|
|
||||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
|
||||||
NodeInputImmediate* immediate = GetImmediate(key->element());
|
|
||||||
TimeRange original_range = get_range_affected_by_keyframe(key);
|
|
||||||
|
|
||||||
TimeRangeList invalidate_range;
|
|
||||||
invalidate_range.insert(original_range);
|
|
||||||
|
|
||||||
if (!(original_range.in() < key->time() && original_range.out() > key->time())) {
|
|
||||||
// This keyframe needs resorting, store it and remove it from the list
|
|
||||||
immediate->remove_keyframe(key);
|
|
||||||
|
|
||||||
// Automatically insertion sort
|
|
||||||
immediate->insert_keyframe(key);
|
|
||||||
|
|
||||||
// Invalidate new area that the keyframe has been moved to
|
|
||||||
invalidate_range.insert(get_range_affected_by_keyframe(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalidate entire area surrounding the keyframe (either where it currently is, or where it used to be before it
|
|
||||||
// was resorted in the if block above)
|
|
||||||
foreach (const TimeRange& r, invalidate_range) {
|
|
||||||
emit ValueChanged(r, key->element());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::InvalidateFromKeyframeValueChange()
|
|
||||||
{
|
|
||||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
|
||||||
emit ValueChanged(get_range_affected_by_keyframe(key), key->element());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeInput::InvalidateFromKeyframeTypeChanged()
|
|
||||||
{
|
|
||||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
|
||||||
const NodeKeyframeTrack& track = GetTrackFromKeyframe(key);
|
|
||||||
|
|
||||||
if (track.size() == 1) {
|
|
||||||
// If there are no other frames, the interpolation won't do anything
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalidate entire range
|
|
||||||
emit ValueChanged(get_range_around_index(track.indexOf(key), key->track(), key->element()), key->element());
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeRange NodeInput::get_range_affected_by_keyframe(NodeKeyframe *key) const
|
|
||||||
{
|
|
||||||
const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key);
|
|
||||||
int keyframe_index = key_track.indexOf(key);
|
|
||||||
|
|
||||||
TimeRange range = get_range_around_index(keyframe_index, key->track(), key->element());
|
|
||||||
|
|
||||||
// If a previous key exists and it's a hold, we don't need to invalidate those frames
|
|
||||||
if (key_track.size() > 1
|
|
||||||
&& keyframe_index > 0
|
|
||||||
&& key_track.at(keyframe_index - 1)->type() == NodeKeyframe::kHold) {
|
|
||||||
range.set_in(key->time());
|
|
||||||
}
|
|
||||||
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeRange NodeInput::get_range_around_index(int index, int track, int element) const
|
|
||||||
{
|
|
||||||
rational range_begin = RATIONAL_MIN;
|
|
||||||
rational range_end = RATIONAL_MAX;
|
|
||||||
|
|
||||||
const NodeKeyframeTrack& key_track = GetImmediate(element)->keyframe_tracks().at(track);
|
|
||||||
|
|
||||||
if (key_track.size() > 1) {
|
|
||||||
if (index > 0) {
|
|
||||||
// If this is not the first key, we'll need to limit it to the key just before
|
|
||||||
range_begin = key_track.at(index - 1)->time();
|
|
||||||
}
|
|
||||||
if (index < key_track.size() - 1) {
|
|
||||||
// If this is not the last key, we'll need to limit it to the key just after
|
|
||||||
range_end = key_track.at(index + 1)->time();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TimeRange(range_begin, range_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString NodeInput::ValueToString(const QVariant &value) const
|
|
||||||
{
|
|
||||||
return NodeValue::ValueToString(data_type_, value, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant NodeInput::StringToValue(const QString &string, QList<XMLNodeData::FootageConnection>& footage_connections, int element)
|
|
||||||
{
|
|
||||||
if (data_type_ == NodeValue::kFootage) {
|
|
||||||
footage_connections.append({this, element, string.toULongLong()});
|
|
||||||
}
|
|
||||||
|
|
||||||
return NodeValue::StringToValue(data_type_, string, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const NodeInput::KeyframeTrackReference &ref, uint seed)
|
|
||||||
{
|
|
||||||
return qHash(ref.input, seed) ^ qHash(ref.element, seed) ^ qHash(ref.track, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
Project *NodeInput::ArrayRemoveCommand::GetRelevantProject() const
|
|
||||||
{
|
|
||||||
return input_->parent()->parent()->project();
|
|
||||||
}
|
|
||||||
|
|
||||||
Project *NodeInput::ArrayResizeCommand::GetRelevantProject() const
|
|
||||||
{
|
|
||||||
return input_->parent()->parent()->project();
|
|
||||||
}
|
|
||||||
|
|
||||||
Project *NodeInput::ArrayInsertCommand::GetRelevantProject() const
|
|
||||||
{
|
|
||||||
return input_->parent()->parent()->project();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,629 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2020 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef NODEINPUT_H
|
|
||||||
#define NODEINPUT_H
|
|
||||||
|
|
||||||
#include "common/timerange.h"
|
|
||||||
#include "keyframe.h"
|
|
||||||
#include "node/connectable.h"
|
|
||||||
#include "node/inputimmediate.h"
|
|
||||||
#include "node/value.h"
|
|
||||||
#include "splitvalue.h"
|
|
||||||
#include "undo/undocommand.h"
|
|
||||||
|
|
||||||
namespace olive {
|
|
||||||
|
|
||||||
class Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A node parameter designed to take either user input or data from another node
|
|
||||||
*/
|
|
||||||
class NodeInput : public NodeConnectable
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief NodeInput Constructor
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*
|
|
||||||
* Unique ID associated with this parameter for this Node. This ID only has to be unique within this Node. Used for
|
|
||||||
* saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an
|
|
||||||
* older version. This of course assumes that parameters don't change their ID.
|
|
||||||
*/
|
|
||||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_value);
|
|
||||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVariant& default_value);
|
|
||||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type);
|
|
||||||
|
|
||||||
virtual ~NodeInput() override;
|
|
||||||
|
|
||||||
const QString& id() const
|
|
||||||
{
|
|
||||||
return id_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString name() const;
|
|
||||||
|
|
||||||
void set_name(const QString& name)
|
|
||||||
{
|
|
||||||
name_ = name;
|
|
||||||
|
|
||||||
emit NameChanged(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsArray() const
|
|
||||||
{
|
|
||||||
return is_array_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetIsArray(bool e)
|
|
||||||
{
|
|
||||||
is_array_ = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisconnectAll();
|
|
||||||
|
|
||||||
void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
|
|
||||||
|
|
||||||
void Save(QXmlStreamWriter* writer) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deliberately shadow QObject parent, since we only expect NodeInput to have Node as a parent
|
|
||||||
*/
|
|
||||||
Node* parent() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The data type this parameter outputs
|
|
||||||
*
|
|
||||||
* This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be
|
|
||||||
* connected to it.
|
|
||||||
*/
|
|
||||||
NodeValue::Type GetDataType() const
|
|
||||||
{
|
|
||||||
return data_type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDataType(NodeValue::Type type)
|
|
||||||
{
|
|
||||||
data_type_ = type;
|
|
||||||
|
|
||||||
emit DataTypeChanged(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::map<int, Node*>& edges() const
|
|
||||||
{
|
|
||||||
return input_connections();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsConnected(int element = -1) const
|
|
||||||
{
|
|
||||||
return input_connections().find(element) != input_connections().end();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns TRUE if the value is expected to always be the same (i.e. no keyframes and
|
|
||||||
* not connected to anything)
|
|
||||||
*/
|
|
||||||
bool IsStatic(int element = -1) const
|
|
||||||
{
|
|
||||||
return !IsConnected(element) && !GetImmediate(element)->is_keyframing();
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* GetConnectedNode(int element = -1) const
|
|
||||||
{
|
|
||||||
return input_connections().at(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsConnectable() const
|
|
||||||
{
|
|
||||||
return connectable_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetConnectable(bool e)
|
|
||||||
{
|
|
||||||
connectable_ = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<NodeKeyframeTrack> &GetKeyframeTracks(int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->keyframe_tracks();
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeKeyframe* GetKeyframeAtTimeOnTrack(const rational& time, int track, int element) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_keyframe_at_time_on_track(time, track);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeKeyframe::Type GetBestKeyframeTypeForTime(const rational& time, int track, int element) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_best_keyframe_type_for_time(time, track);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Return whether this input can be keyframed or not
|
|
||||||
*/
|
|
||||||
bool IsKeyframable() const;
|
|
||||||
|
|
||||||
bool IsKeyframing(int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->is_keyframing();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get non-keyframed value
|
|
||||||
*/
|
|
||||||
QVariant GetStandardValue(int element = -1) const
|
|
||||||
{
|
|
||||||
return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitStandardValue(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get non-keyframed value split into components (the way it's stored)
|
|
||||||
*/
|
|
||||||
const SplitValue& GetSplitStandardValue(int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_split_standard_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant GetStandardValueOnTrack(int track, int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_split_standard_value().at(track);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set non-keyframed value
|
|
||||||
*
|
|
||||||
* This is the value used if keyframing is not enabled. If keyframing is enabled, it is
|
|
||||||
* overwritten.
|
|
||||||
*/
|
|
||||||
void SetStandardValue(const QVariant& value, int element = -1)
|
|
||||||
{
|
|
||||||
SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSplitStandardValue(const SplitValue& value, int element = -1)
|
|
||||||
{
|
|
||||||
GetImmediate(element)->set_split_standard_value(value);
|
|
||||||
|
|
||||||
for (int i=0; i<value.size(); i++) {
|
|
||||||
if (IsUsingStandardValue(i, element)) {
|
|
||||||
// If this standard value is being used, we need to send a value changed signal
|
|
||||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), element);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetStandardValueOnTrack(const QVariant& value, int track = 0, int element = -1)
|
|
||||||
{
|
|
||||||
GetImmediate(element)->set_standard_value_on_track(value, track);
|
|
||||||
|
|
||||||
if (IsUsingStandardValue(track, element)) {
|
|
||||||
// If this standard value is being used, we need to send a value changed signal
|
|
||||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set whether this input can be keyframed or not
|
|
||||||
*/
|
|
||||||
void SetKeyframable(bool k);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copy all values including keyframe information and connections from another NodeInput
|
|
||||||
*/
|
|
||||||
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool traverse_arrays = true);
|
|
||||||
|
|
||||||
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int src_element, int dst_element);
|
|
||||||
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element)
|
|
||||||
{
|
|
||||||
CopyValuesOfElement(source, dst, element, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList get_combobox_strings() const;
|
|
||||||
|
|
||||||
void set_combobox_strings(const QStringList& strings);
|
|
||||||
|
|
||||||
void GetDependencies(QVector<Node *> &list, bool traverse, bool exclusive_only) const;
|
|
||||||
|
|
||||||
QVariant GetDefaultValue() const
|
|
||||||
{
|
|
||||||
return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SplitValue& GetSplitDefaultValue() const
|
|
||||||
{
|
|
||||||
return default_value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant GetDefaultValueForTrack(int track) const;
|
|
||||||
|
|
||||||
QVector<Node*> GetDependencies(bool traverse = true, bool exclusive_only = false) const;
|
|
||||||
|
|
||||||
QVector<Node*> GetExclusiveDependencies() const;
|
|
||||||
|
|
||||||
QVector<Node*> GetImmediateDependencies() const;
|
|
||||||
|
|
||||||
NodeKeyframe* GetEarliestKeyframe(int element = -1)
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_earliest_keyframe();
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeKeyframe* GetLatestKeyframe(int element = -1)
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_latest_keyframe();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasKeyframeAtTime(const rational& time, int element = -1)
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->has_keyframe_at_time(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<NodeKeyframe*> GetKeyframesAtTime(const rational& time, int element = -1)
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_keyframe_at_time(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetIsKeyframing(bool keyframing, int element)
|
|
||||||
{
|
|
||||||
if (!IsKeyframable()) {
|
|
||||||
qDebug() << "Ignored set keyframing because this input is not keyframable";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetImmediate(element)->set_is_keyframing(keyframing);
|
|
||||||
|
|
||||||
emit KeyframeEnableChanged(keyframing, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for ArrayResize(ArraySize() + 1)
|
|
||||||
void ArrayAppend(bool undoable = false);
|
|
||||||
|
|
||||||
void ArrayInsert(int index, bool undoable = false);
|
|
||||||
|
|
||||||
void ArrayRemove(int index, bool undoable = false);
|
|
||||||
|
|
||||||
/// Alias for ArrayInsert(0)
|
|
||||||
void ArrayPrepend(bool undoable = false);
|
|
||||||
|
|
||||||
void ArrayResize(int size, bool undoable = false);
|
|
||||||
|
|
||||||
/// Alias for ArrayResize(ArraySize() - 1)
|
|
||||||
void ArrayRemoveLast(bool undoable = false);
|
|
||||||
|
|
||||||
int ArraySize() const
|
|
||||||
{
|
|
||||||
return array_size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetNumberOfKeyframeTracks() const
|
|
||||||
{
|
|
||||||
return NodeValue::get_number_of_keyframe_tracks(data_type_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate what the stored value should be at a certain time
|
|
||||||
*
|
|
||||||
* If this is a multi-track data type (e.g. kVec2), this will automatically combine the result into a QVector2D.
|
|
||||||
*/
|
|
||||||
QVariant GetValueAtTime(const rational& time, int element = -1) const;
|
|
||||||
|
|
||||||
SplitValue GetSplitValuesAtTime(const rational& time, int element = -1) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate the stored value for a specific track
|
|
||||||
*
|
|
||||||
* For most data types, there is only one track (e.g. `track == 0`), but multi-track data types like kVec2 will
|
|
||||||
* produce the X value on track 0 and the Y value on track 1.
|
|
||||||
*/
|
|
||||||
QVariant GetValueAtTimeForTrack(const rational& time, int track, int element = -1) const;
|
|
||||||
|
|
||||||
NodeKeyframe* GetClosestKeyframeBeforeTime(const rational& time, int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_closest_keyframe_before_time(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeKeyframe* GetClosestKeyframeAfterTime(const rational& time, int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->get_closest_keyframe_after_time(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct KeyframeTrackReference {
|
|
||||||
NodeInput* input;
|
|
||||||
int element;
|
|
||||||
int track;
|
|
||||||
|
|
||||||
bool operator==(const KeyframeTrackReference& rhs) const
|
|
||||||
{
|
|
||||||
return input == rhs.input && element == rhs.element && track == rhs.track;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void NameChanged(const QString& name);
|
|
||||||
|
|
||||||
void ValueChanged(const olive::TimeRange& range, int element);
|
|
||||||
|
|
||||||
void KeyframeAdded(NodeKeyframe* key);
|
|
||||||
|
|
||||||
void KeyframeRemoved(NodeKeyframe* key);
|
|
||||||
|
|
||||||
void KeyframeTimeChanged();
|
|
||||||
|
|
||||||
void PropertyChanged(const QString& s, const QVariant& v);
|
|
||||||
|
|
||||||
void ArraySizeChanged(int size);
|
|
||||||
|
|
||||||
void KeyframeEnableChanged(bool enabled, int element);
|
|
||||||
|
|
||||||
void DataTypeChanged(NodeValue::Type type);
|
|
||||||
|
|
||||||
void InputConnected(Node* source, int element);
|
|
||||||
|
|
||||||
void InputDisconnected(Node* source, int element);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool event(QEvent* e) override;
|
|
||||||
|
|
||||||
virtual void childEvent(QChildEvent* e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class ArrayInsertCommand : public UndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArrayInsertCommand(NodeInput* input, int index) :
|
|
||||||
input_(input),
|
|
||||||
index_(index)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Project* GetRelevantProject() const override;
|
|
||||||
|
|
||||||
virtual void redo() override
|
|
||||||
{
|
|
||||||
input_->ArrayInsert(index_, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void undo() override
|
|
||||||
{
|
|
||||||
input_->ArrayRemove(index_, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* input_;
|
|
||||||
int index_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class ArrayRemoveCommand : public UndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArrayRemoveCommand(NodeInput* input, int index) :
|
|
||||||
input_(input),
|
|
||||||
index_(index)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Project* GetRelevantProject() const override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void redo() override
|
|
||||||
{
|
|
||||||
// Save immediate data
|
|
||||||
if (input_->IsKeyframable()) {
|
|
||||||
is_keyframing_ = input_->IsKeyframing(index_);
|
|
||||||
}
|
|
||||||
standard_value_ = input_->GetSplitStandardValue(index_);
|
|
||||||
keyframes_ = input_->GetImmediate(index_)->keyframe_tracks();
|
|
||||||
input_->GetImmediate(index_)->delete_all_keyframes(&memory_manager_);
|
|
||||||
|
|
||||||
input_->ArrayRemove(index_, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void undo() override
|
|
||||||
{
|
|
||||||
input_->ArrayInsert(index_, false);
|
|
||||||
|
|
||||||
// Restore keyframes
|
|
||||||
foreach (const NodeKeyframeTrack& track, keyframes_) {
|
|
||||||
foreach (NodeKeyframe* key, track) {
|
|
||||||
key->setParent(input_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input_->SetSplitStandardValue(standard_value_, index_);
|
|
||||||
|
|
||||||
if (input_->IsKeyframable()) {
|
|
||||||
input_->SetIsKeyframing(is_keyframing_, index_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* input_;
|
|
||||||
int index_;
|
|
||||||
|
|
||||||
SplitValue standard_value_;
|
|
||||||
bool is_keyframing_;
|
|
||||||
QVector<NodeKeyframeTrack> keyframes_;
|
|
||||||
QObject memory_manager_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class ArrayResizeCommand : public UndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArrayResizeCommand(NodeInput* input, int size) :
|
|
||||||
input_(input),
|
|
||||||
size_(size)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual void redo() override
|
|
||||||
{
|
|
||||||
old_size_ = input_->array_size_;
|
|
||||||
|
|
||||||
if (old_size_ > size_) {
|
|
||||||
// Decreasing in size, disconnect any extraneous edges
|
|
||||||
for (int i=size_; i<old_size_; i++) {
|
|
||||||
try {
|
|
||||||
Node* output = input_->edges().at(i);
|
|
||||||
|
|
||||||
removed_connections_.insert(i, output);
|
|
||||||
|
|
||||||
DisconnectEdge(output, input_, i);
|
|
||||||
} catch (std::out_of_range&) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input_->ArrayResizeInternal(size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void undo() override
|
|
||||||
{
|
|
||||||
for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) {
|
|
||||||
ConnectEdge(it.value(), input_, it.key());
|
|
||||||
}
|
|
||||||
removed_connections_.clear();
|
|
||||||
|
|
||||||
input_->ArrayResizeInternal(old_size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Project* GetRelevantProject() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeInput* input_;
|
|
||||||
int size_;
|
|
||||||
int old_size_;
|
|
||||||
|
|
||||||
QHash<int, Node*> removed_connections_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void ClearElement(int index);
|
|
||||||
|
|
||||||
void Init(Node *parent, const QString& id, NodeValue::Type type, const SplitValue &default_val);
|
|
||||||
|
|
||||||
void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
|
|
||||||
|
|
||||||
void SaveImmediate(QXmlStreamWriter *writer, int element) const;
|
|
||||||
|
|
||||||
void ArrayResizeInternal(int size);
|
|
||||||
|
|
||||||
NodeInputImmediate* CreateImmediate();
|
|
||||||
|
|
||||||
const NodeInputImmediate* GetImmediate(int element = -1) const
|
|
||||||
{
|
|
||||||
return element > -1 ? subinputs_.at(element) : primary_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInputImmediate* GetImmediate(int element = -1)
|
|
||||||
{
|
|
||||||
return element > -1 ? subinputs_[element] : primary_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const
|
|
||||||
{
|
|
||||||
return GetImmediate(key->element())->keyframe_tracks().at(key->track());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUsingStandardValue(int track = 0, int element = -1) const
|
|
||||||
{
|
|
||||||
return GetImmediate(element)->is_using_standard_value(track);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ValueToString(const QVariant& value) const;
|
|
||||||
|
|
||||||
QVariant StringToValue(const QString &string, QList<XMLNodeData::FootageConnection> &footage_connections, int element);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Intelligently determine how what time range is affected by a keyframe
|
|
||||||
*/
|
|
||||||
TimeRange get_range_affected_by_keyframe(NodeKeyframe *key) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets a time range between the previous and next keyframes of index
|
|
||||||
*/
|
|
||||||
TimeRange get_range_around_index(int index, int track, int element) const;
|
|
||||||
|
|
||||||
NodeInputImmediate* primary_;
|
|
||||||
|
|
||||||
QVector<NodeInputImmediate*> subinputs_;
|
|
||||||
|
|
||||||
SplitValue default_value_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unique identifier of this input within this node
|
|
||||||
*/
|
|
||||||
QString id_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief User displayable name of input
|
|
||||||
*/
|
|
||||||
QString name_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Internal keyframable value
|
|
||||||
*/
|
|
||||||
bool keyframable_;
|
|
||||||
|
|
||||||
bool connectable_;
|
|
||||||
|
|
||||||
bool is_array_;
|
|
||||||
|
|
||||||
int array_size_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default data type
|
|
||||||
*/
|
|
||||||
NodeValue::Type data_type_;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
/**
|
|
||||||
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
|
|
||||||
*/
|
|
||||||
void InvalidateFromKeyframeTimeChange();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slot when a keyframe's value changes to signal that the cache needs updating
|
|
||||||
*/
|
|
||||||
void InvalidateFromKeyframeValueChange();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slot when a keyframe's type changes to signal that the cache needs updating
|
|
||||||
*/
|
|
||||||
void InvalidateFromKeyframeTypeChanged();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating
|
|
||||||
*/
|
|
||||||
void InvalidateFromKeyframeBezierInChange();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating
|
|
||||||
*/
|
|
||||||
void InvalidateFromKeyframeBezierOutChange();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
uint qHash(const NodeInput::KeyframeTrackReference& ref, uint seed = 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // NODEINPUT_H
|
|
||||||
@@ -25,13 +25,12 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString MediaInput::kFootageInput = QStringLiteral("footage_in");
|
||||||
|
|
||||||
MediaInput::MediaInput() :
|
MediaInput::MediaInput() :
|
||||||
connected_footage_(nullptr)
|
connected_footage_(nullptr)
|
||||||
{
|
{
|
||||||
footage_input_ = new NodeInput(this, QStringLiteral("footage_in"), NodeValue::kFootage);
|
AddInput(kFootageInput, NodeValue::kFootage, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
footage_input_->SetConnectable(false);
|
|
||||||
footage_input_->SetKeyframable(false);
|
|
||||||
connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Node::CategoryID> MediaInput::Category() const
|
QVector<Node::CategoryID> MediaInput::Category() const
|
||||||
@@ -41,21 +40,23 @@ QVector<Node::CategoryID> MediaInput::Category() const
|
|||||||
|
|
||||||
Stream *MediaInput::stream() const
|
Stream *MediaInput::stream() const
|
||||||
{
|
{
|
||||||
return Node::ValueToPtr<Stream>(footage_input_->GetStandardValue());
|
return Node::ValueToPtr<Stream>(GetStandardValue(kFootageInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaInput::SetStream(Stream* s)
|
void MediaInput::SetStream(Stream* s)
|
||||||
{
|
{
|
||||||
footage_input_->SetStandardValue(Node::PtrToValue(s));
|
SetStandardValue(kFootageInput, Node::PtrToValue(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaInput::Retranslate()
|
void MediaInput::Retranslate()
|
||||||
{
|
{
|
||||||
footage_input_->set_name(tr("Media"));
|
SetInputName(kFootageInput, tr("Media"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
|
NodeValueTable MediaInput::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
if (connected_footage_) {
|
if (connected_footage_) {
|
||||||
@@ -68,28 +69,32 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaInput::FootageChanged()
|
void MediaInput::InputValueChangedEvent(const QString &input, int element)
|
||||||
{
|
{
|
||||||
Stream* new_footage = footage_input_->GetStandardValue().value<Stream*>();
|
Q_UNUSED(element)
|
||||||
|
|
||||||
if (new_footage == connected_footage_) {
|
if (input == kFootageInput) {
|
||||||
return;
|
Stream* new_footage = stream();
|
||||||
}
|
|
||||||
|
|
||||||
if (connected_footage_) {
|
if (new_footage == connected_footage_) {
|
||||||
disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connected_footage_ = new_footage;
|
if (connected_footage_) {
|
||||||
|
disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
||||||
|
}
|
||||||
|
|
||||||
if (connected_footage_) {
|
connected_footage_ = new_footage;
|
||||||
connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
|
||||||
|
if (connected_footage_) {
|
||||||
|
connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaInput::FootageParametersChanged()
|
void MediaInput::FootageParametersChanged()
|
||||||
{
|
{
|
||||||
InvalidateCache(TimeRange(0, RATIONAL_MAX), InputConnection(footage_input_));
|
InvalidateCache(TimeRange(0, RATIONAL_MAX), kFootageInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,16 +63,16 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
|
static const QString kFootageInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NodeInput* footage_input_;
|
virtual void InputValueChangedEvent(const QString& input, int element);
|
||||||
|
|
||||||
Stream* connected_footage_;
|
Stream* connected_footage_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void FootageChanged();
|
|
||||||
|
|
||||||
void FootageParametersChanged();
|
void FootageParametersChanged();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,8 +51,10 @@ QString TimeInput::Description() const
|
|||||||
return tr("Generates the time (in seconds) at this frame");
|
return tr("Generates the time (in seconds) at this frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable TimeInput::Value(NodeValueDatabase &value) const
|
NodeValueTable TimeInput::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
table.Push(NodeValue::kFloat,
|
table.Push(NodeValue::kFloat,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public:
|
|||||||
virtual QVector<CategoryID> Category() const override;
|
virtual QVector<CategoryID> Category() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||||
|
|
||||||
virtual void Hash(QCryptographicHash& hash, const rational& time) const override;
|
virtual void Hash(QCryptographicHash& hash, const rational& time) const override;
|
||||||
|
|
||||||
|
|||||||
+28
-25
@@ -26,61 +26,64 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
NodeInputDragger::NodeInputDragger() :
|
NodeInputDragger::NodeInputDragger()
|
||||||
input_(nullptr)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NodeInputDragger::IsStarted() const
|
bool NodeInputDragger::IsStarted() const
|
||||||
{
|
{
|
||||||
return input_;
|
return input_.IsValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeInputDragger::Start(NodeInput *input, const rational &time, int track, int element)
|
void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rational &time)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!input_);
|
Q_ASSERT(!IsStarted());
|
||||||
|
|
||||||
// Set up new drag
|
// Set up new drag
|
||||||
input_ = input;
|
input_ = input;
|
||||||
time_ = time;
|
time_ = time;
|
||||||
track_ = track;
|
|
||||||
element_ = element;
|
Node* node = input_.input().node();
|
||||||
|
|
||||||
// Cache current value
|
// Cache current value
|
||||||
start_value_ = input_->GetValueAtTimeForTrack(time, track, element_);
|
start_value_ = node->GetSplitValueAtTimeOnTrack(input_, time);
|
||||||
|
|
||||||
// Determine whether we are creating a keyframe or not
|
// Determine whether we are creating a keyframe or not
|
||||||
if (input_->IsKeyframing(element_)) {
|
if (input_.input().IsKeyframing()) {
|
||||||
dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_);
|
dragging_key_ = node->GetKeyframeAtTimeOnTrack(input_, time);
|
||||||
drag_created_key_ = !dragging_key_;
|
drag_created_key_ = !dragging_key_;
|
||||||
|
|
||||||
if (drag_created_key_) {
|
if (drag_created_key_) {
|
||||||
dragging_key_ = new NodeKeyframe(time,
|
dragging_key_ = new NodeKeyframe(time,
|
||||||
start_value_,
|
start_value_,
|
||||||
input_->GetBestKeyframeTypeForTime(time, track, element_),
|
node->GetBestKeyframeTypeForTimeOnTrack(input_, time),
|
||||||
track,
|
input_.track(),
|
||||||
element_,
|
input_.input().element(),
|
||||||
input_);
|
input_.input().input(),
|
||||||
|
node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeInputDragger::Drag(QVariant value)
|
void NodeInputDragger::Drag(QVariant value)
|
||||||
{
|
{
|
||||||
Q_ASSERT(input_);
|
Q_ASSERT(IsStarted());
|
||||||
|
|
||||||
if (input_->property("min").isValid()) {
|
Node* node = input_.input().node();
|
||||||
|
const QString& input = input_.input().input();
|
||||||
|
|
||||||
|
if (node->HasInputProperty(input, QStringLiteral("min"))) {
|
||||||
// Assumes the value is a double of some kind
|
// Assumes the value is a double of some kind
|
||||||
double min = input_->property("min").toDouble();
|
double min = node->GetInputProperty(input, QStringLiteral("min")).toDouble();
|
||||||
double v = value.toDouble();
|
double v = value.toDouble();
|
||||||
if (v < min) {
|
if (v < min) {
|
||||||
value = min;
|
value = min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_->property("max").isValid()) {
|
if (node->HasInputProperty(input, QStringLiteral("max"))) {
|
||||||
double max = input_->property("max").toDouble();
|
double max = node->GetInputProperty(input, QStringLiteral("max")).toDouble();
|
||||||
double v = value.toDouble();
|
double v = value.toDouble();
|
||||||
if (v > max) {
|
if (v > max) {
|
||||||
value = max;
|
value = max;
|
||||||
@@ -91,10 +94,10 @@ void NodeInputDragger::Drag(QVariant value)
|
|||||||
|
|
||||||
//input_->blockSignals(true);
|
//input_->blockSignals(true);
|
||||||
|
|
||||||
if (input_->IsKeyframing(element_)) {
|
if (input_.input().IsKeyframing()) {
|
||||||
dragging_key_->set_value(value);
|
dragging_key_->set_value(value);
|
||||||
} else {
|
} else {
|
||||||
input_->SetStandardValueOnTrack(value, track_, element_);
|
node->SetSplitStandardValueOnTrack(input_, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//input_->blockSignals(false);
|
//input_->blockSignals(false);
|
||||||
@@ -108,10 +111,10 @@ void NodeInputDragger::End()
|
|||||||
|
|
||||||
MultiUndoCommand* command = new MultiUndoCommand();
|
MultiUndoCommand* command = new MultiUndoCommand();
|
||||||
|
|
||||||
if (input_->IsKeyframing(element_)) {
|
if (input_.input().node()->IsInputKeyframing(input_.input())) {
|
||||||
if (drag_created_key_) {
|
if (drag_created_key_) {
|
||||||
// We created a keyframe in this process
|
// We created a keyframe in this process
|
||||||
command->add_child(new NodeParamInsertKeyframeCommand(input_, dragging_key_));
|
command->add_child(new NodeParamInsertKeyframeCommand(input_.input().node(), dragging_key_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We just set a keyframe's value
|
// We just set a keyframe's value
|
||||||
@@ -120,12 +123,12 @@ void NodeInputDragger::End()
|
|||||||
command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_));
|
command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_));
|
||||||
} else {
|
} else {
|
||||||
// We just set the standard value
|
// We just set the standard value
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_));
|
command->add_child(new NodeParamSetStandardValueCommand(input_, end_value_, start_value_));
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command);
|
Core::instance()->undo_stack()->push(command);
|
||||||
|
|
||||||
input_ = nullptr;
|
input_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
#ifndef NODEINPUTDRAGGER_H
|
#ifndef NODEINPUTDRAGGER_H
|
||||||
#define NODEINPUTDRAGGER_H
|
#define NODEINPUTDRAGGER_H
|
||||||
|
|
||||||
#include "node/input.h"
|
#include "common/rational.h"
|
||||||
|
#include "node/keyframe.h"
|
||||||
|
#include "node/param.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
@@ -32,21 +34,17 @@ public:
|
|||||||
|
|
||||||
bool IsStarted() const;
|
bool IsStarted() const;
|
||||||
|
|
||||||
void Start(NodeInput* input, const rational& time, int track, int element = -1);
|
void Start(const NodeKeyframeTrackReference& input, const rational& time);
|
||||||
|
|
||||||
void Drag(QVariant value);
|
void Drag(QVariant value);
|
||||||
|
|
||||||
void End();
|
void End();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeInput* input_;
|
NodeKeyframeTrackReference input_;
|
||||||
|
|
||||||
int track_;
|
|
||||||
|
|
||||||
rational time_;
|
rational time_;
|
||||||
|
|
||||||
int element_;
|
|
||||||
|
|
||||||
QVariant start_value_;
|
QVariant start_value_;
|
||||||
|
|
||||||
QVariant end_value_;
|
QVariant end_value_;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include "common/bezier.h"
|
#include "common/bezier.h"
|
||||||
#include "common/lerp.h"
|
#include "common/lerp.h"
|
||||||
#include "common/tohex.h"
|
#include "common/tohex.h"
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,12 @@ public:
|
|||||||
return standard_value_;
|
return standard_value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_standard_value_on_track(const QVariant &value, int track = 0);
|
const QVariant& get_split_standard_value_on_track(int track) const
|
||||||
|
{
|
||||||
|
return standard_value_.at(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_standard_value_on_track(const QVariant &value, int track);
|
||||||
|
|
||||||
void set_split_standard_value(const SplitValue& value);
|
void set_split_standard_value(const SplitValue& value);
|
||||||
|
|
||||||
|
|||||||
@@ -20,18 +20,19 @@
|
|||||||
|
|
||||||
#include "keyframe.h"
|
#include "keyframe.h"
|
||||||
|
|
||||||
#include "input.h"
|
#include "node.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear;
|
const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear;
|
||||||
|
|
||||||
NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track, int element, QObject *parent) :
|
NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, Type type, int track, int element, const QString &input, QObject *parent) :
|
||||||
time_(time),
|
time_(time),
|
||||||
value_(value),
|
value_(value),
|
||||||
type_(type),
|
type_(type),
|
||||||
bezier_control_in_(QPointF(0.0, 0.0)),
|
bezier_control_in_(QPointF(0.0, 0.0)),
|
||||||
bezier_control_out_(QPointF(0.0, 0.0)),
|
bezier_control_out_(QPointF(0.0, 0.0)),
|
||||||
|
input_(input),
|
||||||
track_(track),
|
track_(track),
|
||||||
element_(element),
|
element_(element),
|
||||||
previous_(nullptr),
|
previous_(nullptr),
|
||||||
@@ -47,7 +48,7 @@ NodeKeyframe::~NodeKeyframe()
|
|||||||
|
|
||||||
NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const
|
NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const
|
||||||
{
|
{
|
||||||
NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, parent);
|
NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, input_, parent);
|
||||||
copy->bezier_control_in_ = bezier_control_in_;
|
copy->bezier_control_in_ = bezier_control_in_;
|
||||||
copy->bezier_control_out_ = bezier_control_out_;
|
copy->bezier_control_out_ = bezier_control_out_;
|
||||||
return copy;
|
return copy;
|
||||||
@@ -58,9 +59,9 @@ NodeKeyframe *NodeKeyframe::copy(QObject* parent) const
|
|||||||
return copy(element_, parent);
|
return copy(element_, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput *NodeKeyframe::parent() const
|
Node *NodeKeyframe::parent() const
|
||||||
{
|
{
|
||||||
return static_cast<NodeInput*>(QObject::parent());
|
return static_cast<Node*>(QObject::parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
const rational &NodeKeyframe::time() const
|
const rational &NodeKeyframe::time() const
|
||||||
|
|||||||
+15
-4
@@ -26,11 +26,11 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "common/rational.h"
|
#include "common/rational.h"
|
||||||
|
#include "node/param.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class NodeInput;
|
class Node;
|
||||||
class NodeInputImmediate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A point of data to be used at a certain time and interpolated with other data
|
* @brief A point of data to be used at a certain time and interpolated with other data
|
||||||
@@ -61,14 +61,23 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief NodeKeyframe Constructor
|
* @brief NodeKeyframe Constructor
|
||||||
*/
|
*/
|
||||||
NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track, int element, QObject* parent = nullptr);
|
NodeKeyframe(const rational& time, const QVariant& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr);
|
||||||
|
|
||||||
virtual ~NodeKeyframe() override;
|
virtual ~NodeKeyframe() override;
|
||||||
|
|
||||||
NodeKeyframe* copy(int element, QObject* parent = nullptr) const;
|
NodeKeyframe* copy(int element, QObject* parent = nullptr) const;
|
||||||
NodeKeyframe* copy(QObject* parent = nullptr) const;
|
NodeKeyframe* copy(QObject* parent = nullptr) const;
|
||||||
|
|
||||||
NodeInput* parent() const;
|
Node* parent() const;
|
||||||
|
const QString& input() const
|
||||||
|
{
|
||||||
|
return input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframeTrackReference key_track_ref() const
|
||||||
|
{
|
||||||
|
return NodeKeyframeTrackReference(NodeInput(parent(), input(), element()), track());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The time this keyframe is set at
|
* @brief The time this keyframe is set at
|
||||||
@@ -194,6 +203,8 @@ private:
|
|||||||
|
|
||||||
QPointF bezier_control_out_;
|
QPointF bezier_control_out_;
|
||||||
|
|
||||||
|
QString input_;
|
||||||
|
|
||||||
int track_;
|
int track_;
|
||||||
|
|
||||||
int element_;
|
int element_;
|
||||||
|
|||||||
+26
-21
@@ -22,19 +22,22 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString MathNode::kMethodIn = QStringLiteral("method_in");
|
||||||
|
const QString MathNode::kParamAIn = QStringLiteral("param_a_in");
|
||||||
|
const QString MathNode::kParamBIn = QStringLiteral("param_b_in");
|
||||||
|
const QString MathNode::kParamCIn = QStringLiteral("param_c_in");
|
||||||
|
|
||||||
MathNode::MathNode()
|
MathNode::MathNode()
|
||||||
{
|
{
|
||||||
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
|
AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
method_in_->SetConnectable(false);
|
|
||||||
method_in_->SetKeyframable(false);
|
|
||||||
|
|
||||||
param_a_in_ = new NodeInput(this, QStringLiteral("param_a_in"), NodeValue::kFloat, 0.0);
|
AddInput(kParamAIn, NodeValue::kFloat, 0.0);
|
||||||
param_a_in_->setProperty("decimalplaces", 8);
|
SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8);
|
||||||
param_a_in_->setProperty("autotrim", true);
|
SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true);
|
||||||
|
|
||||||
param_b_in_ = new NodeInput(this, QStringLiteral("param_b_in"), NodeValue::kFloat, 0.0);
|
AddInput(kParamBIn, NodeValue::kFloat, 0.0);
|
||||||
param_b_in_->setProperty("decimalplaces", 8);
|
SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8);
|
||||||
param_b_in_->setProperty("autotrim", true);
|
SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *MathNode::copy() const
|
Node *MathNode::copy() const
|
||||||
@@ -66,9 +69,9 @@ void MathNode::Retranslate()
|
|||||||
{
|
{
|
||||||
Node::Retranslate();
|
Node::Retranslate();
|
||||||
|
|
||||||
method_in_->set_name(tr("Method"));
|
SetInputName(kMethodIn, tr("Method"));
|
||||||
param_a_in_->set_name(tr("Value"));
|
SetInputName(kParamAIn, tr("Value"));
|
||||||
param_b_in_->set_name(tr("Value"));
|
SetInputName(kParamBIn, tr("Value"));
|
||||||
|
|
||||||
QStringList operations = {tr("Add"),
|
QStringList operations = {tr("Add"),
|
||||||
tr("Subtract"),
|
tr("Subtract"),
|
||||||
@@ -77,19 +80,21 @@ void MathNode::Retranslate()
|
|||||||
QString(),
|
QString(),
|
||||||
tr("Power")};
|
tr("Power")};
|
||||||
|
|
||||||
method_in_->set_combobox_strings(operations);
|
SetComboBoxStrings(kMethodIn, operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderCode MathNode::GetShaderCode(const QString &shader_id) const
|
ShaderCode MathNode::GetShaderCode(const QString &shader_id) const
|
||||||
{
|
{
|
||||||
return GetShaderCodeInternal(shader_id, param_a_in_, param_b_in_);
|
return GetShaderCodeInternal(shader_id, kParamAIn, kParamBIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MathNode::Value(NodeValueDatabase &value) const
|
NodeValueTable MathNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
// Auto-detect what values to operate with
|
// Auto-detect what values to operate with
|
||||||
// FIXME: Add manual override for this
|
// FIXME: Add manual override for this
|
||||||
PairingCalculator calc(value[param_a_in_], value[param_b_in_]);
|
PairingCalculator calc(value[kParamAIn], value[kParamBIn]);
|
||||||
|
|
||||||
// Do nothing if no pairing was found
|
// Do nothing if no pairing was found
|
||||||
if (!calc.FoundMostLikelyPairing()) {
|
if (!calc.FoundMostLikelyPairing()) {
|
||||||
@@ -97,23 +102,23 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
NodeValue val_a = calc.GetMostLikelyValueA();
|
NodeValue val_a = calc.GetMostLikelyValueA();
|
||||||
value[param_a_in_].Remove(val_a);
|
value[kParamAIn].Remove(val_a);
|
||||||
|
|
||||||
NodeValue val_b = calc.GetMostLikelyValueB();
|
NodeValue val_b = calc.GetMostLikelyValueB();
|
||||||
value[param_b_in_].Remove(val_b);
|
value[kParamBIn].Remove(val_b);
|
||||||
|
|
||||||
return ValueInternal(value,
|
return ValueInternal(value,
|
||||||
GetOperation(),
|
GetOperation(),
|
||||||
calc.GetMostLikelyPairing(),
|
calc.GetMostLikelyPairing(),
|
||||||
param_a_in_,
|
kParamAIn,
|
||||||
val_a,
|
val_a,
|
||||||
param_b_in_,
|
kParamBIn,
|
||||||
val_b);
|
val_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MathNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void MathNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
||||||
{
|
{
|
||||||
return ProcessSamplesInternal(values, GetOperation(), param_a_in_, param_b_in_, input, output, index);
|
return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,34 +44,22 @@ public:
|
|||||||
|
|
||||||
Operation GetOperation() const
|
Operation GetOperation() const
|
||||||
{
|
{
|
||||||
return static_cast<Operation>(method_in_->GetStandardValue().toInt());
|
return static_cast<Operation>(GetStandardValue(kMethodIn).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOperation(Operation o)
|
void SetOperation(Operation o)
|
||||||
{
|
{
|
||||||
method_in_->SetStandardValue(o);
|
SetStandardValue(kMethodIn, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput* param_a_in() const
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
{
|
|
||||||
return param_a_in_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* param_b_in() const
|
|
||||||
{
|
|
||||||
return param_b_in_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
|
||||||
|
|
||||||
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
||||||
|
|
||||||
private:
|
static const QString kMethodIn;
|
||||||
NodeInput* method_in_;
|
static const QString kParamAIn;
|
||||||
|
static const QString kParamBIn;
|
||||||
NodeInput* param_a_in_;
|
static const QString kParamCIn;
|
||||||
|
|
||||||
NodeInput* param_b_in_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInput *param_a_in, olive::NodeInput *param_b_in) const
|
ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const QString& param_a_in, const QString& param_b_in) const
|
||||||
{
|
{
|
||||||
QStringList code_id = shader_id.split('.');
|
QStringList code_id = shader_id.split('.');
|
||||||
|
|
||||||
@@ -43,11 +43,11 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
|||||||
if (pairing == kPairTextureMatrix && op == kOpMultiply) {
|
if (pairing == kPairTextureMatrix && op == kOpMultiply) {
|
||||||
|
|
||||||
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
|
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
|
||||||
NodeInput* tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in;
|
const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in;
|
||||||
NodeInput* mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
|
const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
|
||||||
|
|
||||||
// No-op frag shader (can we return QString() instead?)
|
// No-op frag shader (can we return QString() instead?)
|
||||||
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id());
|
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in);
|
||||||
|
|
||||||
vert = QStringLiteral("uniform mat4 %1;\n"
|
vert = QStringLiteral("uniform mat4 %1;\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -59,7 +59,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
|||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" gl_Position = %1 * a_position;\n"
|
" gl_Position = %1 * a_position;\n"
|
||||||
" ove_texcoord = a_texcoord;\n"
|
" ove_texcoord = a_texcoord;\n"
|
||||||
"}\n").arg(mat_in->id());
|
"}\n").arg(mat_in);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
@@ -89,8 +89,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
operation = operation.arg(GetShaderVariableCall(param_a_in->id(), type_a),
|
operation = operation.arg(GetShaderVariableCall(param_a_in, type_a),
|
||||||
GetShaderVariableCall(param_b_in->id(), type_b));
|
GetShaderVariableCall(param_b_in, type_b));
|
||||||
}
|
}
|
||||||
|
|
||||||
frag = QStringLiteral("uniform %1 %3;\n"
|
frag = QStringLiteral("uniform %1 %3;\n"
|
||||||
@@ -104,8 +104,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
|||||||
" fragColor = %5;\n"
|
" fragColor = %5;\n"
|
||||||
"}\n").arg(GetShaderUniformType(type_a),
|
"}\n").arg(GetShaderUniformType(type_a),
|
||||||
GetShaderUniformType(type_b),
|
GetShaderUniformType(type_b),
|
||||||
param_a_in->id(),
|
param_a_in,
|
||||||
param_b_in->id(),
|
param_b_in,
|
||||||
operation);
|
operation);
|
||||||
|
|
||||||
return ShaderCode(frag, vert);
|
return ShaderCode(frag, vert);
|
||||||
@@ -165,7 +165,7 @@ void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type typ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput *param_a_in, const NodeValue& val_a, NodeInput *param_b_in, const NodeValue& val_b) const
|
NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b) const
|
||||||
{
|
{
|
||||||
NodeValueTable output = value.Merge();
|
NodeValueTable output = value.Merge();
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
|||||||
{
|
{
|
||||||
// Queue a sample job
|
// Queue a sample job
|
||||||
const NodeValue& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a;
|
const NodeValue& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a;
|
||||||
NodeInput* number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in;
|
const QString& number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in;
|
||||||
|
|
||||||
float number = RetrieveNumber(number_val);
|
float number = RetrieveNumber(number_val);
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
|||||||
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
|
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
|
||||||
|
|
||||||
if (job.HasSamples()) {
|
if (job.HasSamples()) {
|
||||||
if (number_param->IsStatic()) {
|
if (IsInputStatic(number_param)) {
|
||||||
if (!NumberIsNoOp(operation, number)) {
|
if (!NumberIsNoOp(operation, number)) {
|
||||||
for (int i=0;i<job.samples()->audio_params().channel_count();i++) {
|
for (int i=0;i<job.samples()->audio_params().channel_count();i++) {
|
||||||
for (int j=0;j<job.samples()->sample_count();j++) {
|
for (int j=0;j<job.samples()->sample_count();j++) {
|
||||||
@@ -375,7 +375,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, NodeInput *param_a_in, NodeInput *param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
||||||
{
|
{
|
||||||
// This function is only used for sample+number pairing
|
// This function is only used for sample+number pairing
|
||||||
NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber);
|
NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber);
|
||||||
|
|||||||
@@ -109,13 +109,13 @@ protected:
|
|||||||
|
|
||||||
static bool NumberIsNoOp(const Operation& op, const float& number);
|
static bool NumberIsNoOp(const Operation& op, const float& number);
|
||||||
|
|
||||||
ShaderCode GetShaderCodeInternal(const QString &shader_id, NodeInput* param_a_in, NodeInput* param_b_in) const;
|
ShaderCode GetShaderCodeInternal(const QString &shader_id, const QString ¶m_a_in, const QString ¶m_b_in) const;
|
||||||
|
|
||||||
void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const;
|
void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const;
|
||||||
|
|
||||||
NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput* param_a_in, const NodeValue &val_a, NodeInput* param_b_in, const NodeValue& val_b) const;
|
NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b) const;
|
||||||
|
|
||||||
void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, NodeInput* param_a_in, NodeInput* param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
|
void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,14 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString MergeNode::kBaseIn = QStringLiteral("base_in");
|
||||||
|
const QString MergeNode::kBlendIn = QStringLiteral("blend_in");
|
||||||
|
|
||||||
MergeNode::MergeNode()
|
MergeNode::MergeNode()
|
||||||
{
|
{
|
||||||
base_in_ = new NodeInput(this, QStringLiteral("base_in"), NodeValue::kTexture);
|
AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
|
||||||
blend_in_ = new NodeInput(this, QStringLiteral("blend_in"), NodeValue::kTexture);
|
AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *MergeNode::copy() const
|
Node *MergeNode::copy() const
|
||||||
@@ -56,8 +59,9 @@ QString MergeNode::Description() const
|
|||||||
|
|
||||||
void MergeNode::Retranslate()
|
void MergeNode::Retranslate()
|
||||||
{
|
{
|
||||||
base_in_->set_name(tr("Base"));
|
SetInputName(kBaseIn, tr("Base"));
|
||||||
blend_in_->set_name(tr("Blend"));
|
|
||||||
|
SetInputName(kBlendIn, tr("Blend"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const
|
ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const
|
||||||
@@ -67,24 +71,26 @@ ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const
|
|||||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag"));
|
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(output)
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.InsertValue(base_in_, value);
|
job.InsertValue(this, kBaseIn, value);
|
||||||
job.InsertValue(blend_in_, value);
|
job.InsertValue(this, kBlendIn, value);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
TexturePtr base_tex = job.GetValue(base_in_).data().value<TexturePtr>();
|
TexturePtr base_tex = job.GetValue(kBaseIn).data().value<TexturePtr>();
|
||||||
TexturePtr blend_tex = job.GetValue(blend_in_).data().value<TexturePtr>();
|
TexturePtr blend_tex = job.GetValue(kBlendIn).data().value<TexturePtr>();
|
||||||
|
|
||||||
if (base_tex || blend_tex) {
|
if (base_tex || blend_tex) {
|
||||||
if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) {
|
if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) {
|
||||||
// We only have a blend texture or the blend texture is RGB only, no need to alpha over
|
// We only have a blend texture or the blend texture is RGB only, no need to alpha over
|
||||||
table.Push(job.GetValue(blend_in_));
|
table.Push(job.GetValue(kBlendIn));
|
||||||
} else if (!blend_tex) {
|
} else if (!blend_tex) {
|
||||||
// We only have a base texture, no need to alpha over
|
// We only have a base texture, no need to alpha over
|
||||||
table.Push(job.GetValue(base_in_));
|
table.Push(job.GetValue(kBaseIn));
|
||||||
} else {
|
} else {
|
||||||
// We have both textures, push the job
|
// We have both textures, push the job
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
@@ -94,16 +100,6 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput *MergeNode::base_in() const
|
|
||||||
{
|
|
||||||
return base_in_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput *MergeNode::blend_in() const
|
|
||||||
{
|
|
||||||
return blend_in_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
|
void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
|
||||||
{
|
{
|
||||||
// We do some hash optimization here. If only one of the inputs is connected, this node
|
// We do some hash optimization here. If only one of the inputs is connected, this node
|
||||||
@@ -116,16 +112,16 @@ void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
|
|||||||
bool base_changed_hash = false;
|
bool base_changed_hash = false;
|
||||||
bool blend_changed_hash = false;
|
bool blend_changed_hash = false;
|
||||||
|
|
||||||
if (base_in_->IsConnected()) {
|
if (IsInputConnected(kBaseIn)) {
|
||||||
base_in_->GetConnectedNode()->Hash(hash, time);
|
GetConnectedNode(kBaseIn)->Hash(hash, time);
|
||||||
|
|
||||||
QByteArray post_base_hash = hash.result();
|
QByteArray post_base_hash = hash.result();
|
||||||
base_changed_hash = (post_base_hash != current_result);
|
base_changed_hash = (post_base_hash != current_result);
|
||||||
current_result = post_base_hash;
|
current_result = post_base_hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blend_in_->IsConnected()) {
|
if(IsInputConnected(kBlendIn)) {
|
||||||
blend_in_->GetConnectedNode()->Hash(hash, time);
|
GetConnectedNode(kBlendIn)->Hash(hash, time);
|
||||||
|
|
||||||
blend_changed_hash = (hash.result() != current_result);
|
blend_changed_hash = (hash.result() != current_result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ public:
|
|||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
NodeInput* base_in() const;
|
static const QString kBaseIn;
|
||||||
NodeInput* blend_in() const;
|
static const QString kBlendIn;
|
||||||
|
|
||||||
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,14 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString TrigonometryNode::kMethodIn = QStringLiteral("method_in");
|
||||||
|
const QString TrigonometryNode::kXIn = QStringLiteral("x_in");
|
||||||
|
|
||||||
TrigonometryNode::TrigonometryNode()
|
TrigonometryNode::TrigonometryNode()
|
||||||
{
|
{
|
||||||
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
|
AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
method_in_->SetConnectable(false);
|
|
||||||
method_in_->SetKeyframable(false);
|
|
||||||
|
|
||||||
x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0);
|
AddInput(kXIn, NodeValue::kFloat, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
olive::Node *olive::TrigonometryNode::copy() const
|
olive::Node *olive::TrigonometryNode::copy() const
|
||||||
@@ -70,18 +71,20 @@ void TrigonometryNode::Retranslate()
|
|||||||
tr("Hyperbolic Cosine"),
|
tr("Hyperbolic Cosine"),
|
||||||
tr("Hyperbolic Tangent")};
|
tr("Hyperbolic Tangent")};
|
||||||
|
|
||||||
method_in_->set_combobox_strings(strings);
|
SetComboBoxStrings(kMethodIn, strings);
|
||||||
|
|
||||||
method_in_->set_name(tr("Method"));
|
SetInputName(kMethodIn, tr("Method"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const
|
NodeValueTable TrigonometryNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||||
{
|
{
|
||||||
float x = value[x_in_].Take(NodeValue::kFloat).toFloat();
|
Q_UNUSED(output)
|
||||||
|
|
||||||
|
float x = value[kXIn].Take(NodeValue::kFloat).toFloat();
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
switch (static_cast<Operation>(method_in_->GetStandardValue().toInt())) {
|
switch (static_cast<Operation>(GetStandardValue(kMethodIn).toInt())) {
|
||||||
case kOpSine:
|
case kOpSine:
|
||||||
x = qSin(x);
|
x = qSin(x);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
|
||||||
|
|
||||||
|
static const QString kMethodIn;
|
||||||
|
static const QString kXIn;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Operation {
|
enum Operation {
|
||||||
@@ -55,10 +58,6 @@ private:
|
|||||||
kOpHypTangent
|
kOpHypTangent
|
||||||
};
|
};
|
||||||
|
|
||||||
NodeInput* method_in_;
|
|
||||||
|
|
||||||
NodeInput* x_in_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1472
-193
File diff suppressed because it is too large
Load Diff
+655
-72
@@ -21,6 +21,7 @@
|
|||||||
#ifndef NODE_H
|
#ifndef NODE_H
|
||||||
#define NODE_H
|
#define NODE_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -30,15 +31,17 @@
|
|||||||
#include "codec/frame.h"
|
#include "codec/frame.h"
|
||||||
#include "codec/samplebuffer.h"
|
#include "codec/samplebuffer.h"
|
||||||
#include "common/rational.h"
|
#include "common/rational.h"
|
||||||
|
#include "common/timerange.h"
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "node/connectable.h"
|
#include "node/keyframe.h"
|
||||||
#include "node/input.h"
|
#include "node/inputimmediate.h"
|
||||||
#include "node/value.h"
|
#include "node/param.h"
|
||||||
#include "render/audioparams.h"
|
#include "render/audioparams.h"
|
||||||
#include "render/job/generatejob.h"
|
#include "render/job/generatejob.h"
|
||||||
#include "render/job/samplejob.h"
|
#include "render/job/samplejob.h"
|
||||||
#include "render/job/shaderjob.h"
|
#include "render/job/shaderjob.h"
|
||||||
#include "render/shadercode.h"
|
#include "render/shadercode.h"
|
||||||
|
#include "splitvalue.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ class NodeGraph;
|
|||||||
* This is a simple base class designed to contain all the functionality for this kind of processing connective unit.
|
* This is a simple base class designed to contain all the functionality for this kind of processing connective unit.
|
||||||
* It is an abstract class intended to be subclassed to create nodes with actual functionality.
|
* It is an abstract class intended to be subclassed to create nodes with actual functionality.
|
||||||
*/
|
*/
|
||||||
class Node : public NodeConnectable
|
class Node : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -80,7 +83,7 @@ public:
|
|||||||
kCategoryCount
|
kCategoryCount
|
||||||
};
|
};
|
||||||
|
|
||||||
Node();
|
Node(bool create_default_output = true);
|
||||||
|
|
||||||
virtual ~Node() override;
|
virtual ~Node() override;
|
||||||
|
|
||||||
@@ -154,6 +157,31 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void Retranslate();
|
virtual void Retranslate();
|
||||||
|
|
||||||
|
const QVector<QString>& inputs() const
|
||||||
|
{
|
||||||
|
return input_ids_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<QString>& outputs() const
|
||||||
|
{
|
||||||
|
return outputs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasInputWithID(const QString& id) const
|
||||||
|
{
|
||||||
|
return input_ids_.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasOutputWithID(const QString& id) const
|
||||||
|
{
|
||||||
|
return outputs_.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasParamWithID(const QString& id) const
|
||||||
|
{
|
||||||
|
return HasInputWithID(id) || HasOutputWithID(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the color of this node
|
* @brief Retrieve the color of this node
|
||||||
*/
|
*/
|
||||||
@@ -180,26 +208,266 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void ConnectEdge(const NodeOutput& output, const NodeInput& input);
|
||||||
* @brief Return a list of NodeParams
|
|
||||||
*/
|
static void DisconnectEdge(const NodeOutput& output, const NodeInput& input);
|
||||||
const QVector<NodeInput*>& parameters() const
|
|
||||||
|
QString GetInputName(const QString& id) const;
|
||||||
|
|
||||||
|
void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
|
||||||
|
void SaveInput(QXmlStreamWriter* writer, const QString& id) const;
|
||||||
|
|
||||||
|
bool IsInputConnectable(const QString& input) const;
|
||||||
|
bool IsInputKeyframable(const QString& input) const;
|
||||||
|
|
||||||
|
bool IsInputKeyframing(const QString& input, int element = -1) const;
|
||||||
|
bool IsInputKeyframing(const NodeInput& input) const
|
||||||
{
|
{
|
||||||
return inputs_;
|
return IsInputKeyframing(input.input(), input.element());
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<NodeInput*>& inputs() const
|
void SetInputIsKeyframing(const QString& input, bool e, int element = -1);
|
||||||
|
void SetInputIsKeyframing(const NodeInput& input, bool e)
|
||||||
{
|
{
|
||||||
return inputs_;
|
SetInputIsKeyframing(input.input(), e, input.element());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool IsInputConnected(const QString& input, int element = -1) const;
|
||||||
* @brief Return the index of a parameter
|
bool IsInputConnected(const NodeInput& input) const
|
||||||
* @return Parameter index or -1 if this parameter is not part of this Node
|
|
||||||
*/
|
|
||||||
int IndexOfParameter(NodeInput* param) const
|
|
||||||
{
|
{
|
||||||
return inputs_.indexOf(param);
|
return IsInputConnected(input.input(), input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInputStatic(const QString& input, int element = -1) const
|
||||||
|
{
|
||||||
|
return !IsInputConnected(input, element) && !IsInputKeyframing(input, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInputStatic(const NodeInput& input) const
|
||||||
|
{
|
||||||
|
return IsInputStatic(input.input(), input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeOutput GetConnectedOutput(const QString& input, int element = -1) const;
|
||||||
|
|
||||||
|
NodeOutput GetConnectedOutput(const NodeInput& input) const
|
||||||
|
{
|
||||||
|
return GetConnectedOutput(input.input(), input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* GetConnectedNode(const QString& input, int element = -1) const
|
||||||
|
{
|
||||||
|
return GetConnectedOutput(input, element).node();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* GetConnectedNode(const NodeInput& input) const
|
||||||
|
{
|
||||||
|
return GetConnectedNode(input.input(), input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUsingStandardValue(const QString& input, int track, int element = -1) const;
|
||||||
|
|
||||||
|
NodeValue::Type GetInputDataType(const QString& id) const;
|
||||||
|
void SetInputDataType(const QString& id, const NodeValue::Type& type);
|
||||||
|
|
||||||
|
bool HasInputProperty(const QString& id, const QString& name) const;
|
||||||
|
QHash<QString, QVariant> GetInputProperties(const QString& id) const;
|
||||||
|
QVariant GetInputProperty(const QString& id, const QString& name) const;
|
||||||
|
void SetInputProperty(const QString& id, const QString& name, const QVariant& value);
|
||||||
|
|
||||||
|
QVariant GetValueAtTime(const QString& input, const rational& time, int element = -1) const
|
||||||
|
{
|
||||||
|
NodeValue::Type type = GetInputDataType(input);
|
||||||
|
|
||||||
|
return NodeValue::combine_track_values_into_normal_value(type, GetSplitValueAtTime(input, time, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetValueAtTime(const NodeInput& input, const rational& time)
|
||||||
|
{
|
||||||
|
return GetValueAtTime(input.input(), time, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitValue GetSplitValueAtTime(const QString& input, const rational& time, int element = -1) const;
|
||||||
|
|
||||||
|
SplitValue GetSplitValueAtTime(const NodeInput& input, const rational& time)
|
||||||
|
{
|
||||||
|
return GetSplitValueAtTime(input.input(), time, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetSplitValueAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const;
|
||||||
|
QVariant GetSplitValueAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const
|
||||||
|
{
|
||||||
|
return GetSplitValueAtTimeOnTrack(input.input(), time, track, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetSplitValueAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetSplitValueAtTimeOnTrack(input.input(), time, input.track());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetDefaultValue(const QString& input) const;
|
||||||
|
SplitValue GetSplitDefaultValue(const QString& input) const;
|
||||||
|
QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const;
|
||||||
|
|
||||||
|
const QVector<NodeKeyframeTrack>& GetKeyframeTracks(const QString& input, int element) const;
|
||||||
|
const QVector<NodeKeyframeTrack>& GetKeyframeTracks(const NodeInput& input) const
|
||||||
|
{
|
||||||
|
return GetKeyframeTracks(input.input(), input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<NodeKeyframe*> GetKeyframesAtTime(const QString& input, const rational& time, int element = -1) const;
|
||||||
|
QVector<NodeKeyframe*> GetKeyframesAtTime(const NodeInput& input, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetKeyframesAtTime(input.input(), time, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetKeyframeAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const;
|
||||||
|
NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const
|
||||||
|
{
|
||||||
|
return GetKeyframeAtTimeOnTrack(input.input(), time, track, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetKeyframeAtTimeOnTrack(input.input(), time, input.track());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const;
|
||||||
|
|
||||||
|
NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeInput& input, const rational& time, int track) const
|
||||||
|
{
|
||||||
|
return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, track, input.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, input.track());
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetNumberOfKeyframeTracks(const QString& id) const;
|
||||||
|
int GetNumberOfKeyframeTracks(const NodeInput& id) const
|
||||||
|
{
|
||||||
|
return GetNumberOfKeyframeTracks(id.input());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetEarliestKeyframe(const QString& id, int element = -1) const;
|
||||||
|
NodeKeyframe* GetEarliestKeyframe(const NodeInput& id) const
|
||||||
|
{
|
||||||
|
return GetEarliestKeyframe(id.input(), id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetLatestKeyframe(const QString& id, int element = -1) const;
|
||||||
|
NodeKeyframe* GetLatestKeyframe(const NodeInput& id) const
|
||||||
|
{
|
||||||
|
return GetLatestKeyframe(id.input(), id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetClosestKeyframeBeforeTime(const QString& id, const rational& time, int element = -1) const;
|
||||||
|
NodeKeyframe* GetClosestKeyframeBeforeTime(const NodeInput& id, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetClosestKeyframeBeforeTime(id.input(), time, id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* GetClosestKeyframeAfterTime(const QString& id, const rational& time, int element = -1) const;
|
||||||
|
NodeKeyframe* GetClosestKeyframeAfterTime(const NodeInput& id, const rational& time) const
|
||||||
|
{
|
||||||
|
return GetClosestKeyframeAfterTime(id.input(), time, id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasKeyframeAtTime(const QString& id, const rational& time, int element = -1) const;
|
||||||
|
bool HasKeyframeAtTime(const NodeInput& id, const rational& time) const
|
||||||
|
{
|
||||||
|
return HasKeyframeAtTime(id.input(), time, id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GetComboBoxStrings(const QString& id) const;
|
||||||
|
|
||||||
|
QVariant GetStandardValue(const QString& id, int element = -1) const;
|
||||||
|
QVariant GetStandardValue(const NodeInput& id) const
|
||||||
|
{
|
||||||
|
return GetStandardValue(id.input(), id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitValue GetSplitStandardValue(const QString& id, int element = -1) const;
|
||||||
|
SplitValue GetSplitStandardValue(const NodeInput& id) const
|
||||||
|
{
|
||||||
|
return GetSplitStandardValue(id.input(), id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetSplitStandardValueOnTrack(const QString& input, int track, int element = -1) const;
|
||||||
|
QVariant GetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id) const
|
||||||
|
{
|
||||||
|
return GetSplitStandardValueOnTrack(id.input().input(), id.track(), id.input().element());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStandardValue(const QString& id, const QVariant& value, int element = -1);
|
||||||
|
void SetStandardValue(const NodeInput& id, const QVariant& value)
|
||||||
|
{
|
||||||
|
SetStandardValue(id.input(), value, id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSplitStandardValue(const QString& id, const SplitValue& value, int element = -1);
|
||||||
|
void SetSplitStandardValue(const NodeInput& id, const SplitValue& value)
|
||||||
|
{
|
||||||
|
SetSplitStandardValue(id.input(), value, id.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSplitStandardValueOnTrack(const QString& id, int track, const QVariant& value, int element = -1);
|
||||||
|
void SetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id, const QVariant& value)
|
||||||
|
{
|
||||||
|
SetSplitStandardValueOnTrack(id.input().input(), id.track(), value, id.input().element());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputIsArray(const QString& id) const;
|
||||||
|
|
||||||
|
void InputArrayInsert(const QString& id, int index, bool undoable = false);
|
||||||
|
void InputArrayResize(const QString& id, int size, bool undoable = false);
|
||||||
|
void InputArrayRemove(const QString& id, int index, bool undoable = false);
|
||||||
|
|
||||||
|
void InputArrayAppend(const QString& id, bool undoable = false)
|
||||||
|
{
|
||||||
|
InputArrayResize(id, InputArraySize(id) + 1, undoable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputArrayPrepend(const QString& id, bool undoable = false)
|
||||||
|
{
|
||||||
|
InputArrayInsert(id, 0, undoable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputArrayRemoveLast(const QString& id, bool undoable = false)
|
||||||
|
{
|
||||||
|
InputArrayResize(id, InputArraySize(id) - 1, undoable);
|
||||||
|
}
|
||||||
|
|
||||||
|
int InputArraySize(const QString& id) const;
|
||||||
|
|
||||||
|
const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const;
|
||||||
|
|
||||||
|
using InputConnections = std::map<NodeInput, NodeOutput>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return map of input connections
|
||||||
|
*
|
||||||
|
* Inputs can only have one connection, so the key is the input connected and the value is the
|
||||||
|
* output that it's connected to.
|
||||||
|
*/
|
||||||
|
const InputConnections& input_connections() const
|
||||||
|
{
|
||||||
|
return input_connections_;
|
||||||
|
}
|
||||||
|
|
||||||
|
using OutputConnection = std::pair<NodeOutput, NodeInput>;
|
||||||
|
using OutputConnections = std::vector<OutputConnection>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return list of output connections
|
||||||
|
*
|
||||||
|
* An output can connect an infinite amount of inputs, so in this map, the key is the output and
|
||||||
|
* the value is a vector of inputs.
|
||||||
|
*/
|
||||||
|
const OutputConnections& output_connections() const
|
||||||
|
{
|
||||||
|
return output_connections_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,11 +507,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const;
|
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the input with the specified ID (or nullptr if it doesn't exist)
|
|
||||||
*/
|
|
||||||
NodeInput* GetInputWithID(const QString& id) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns whether this Node outputs to `n`
|
* @brief Returns whether this Node outputs to `n`
|
||||||
*
|
*
|
||||||
@@ -257,7 +520,6 @@ public:
|
|||||||
* (FALSE).
|
* (FALSE).
|
||||||
*/
|
*/
|
||||||
bool OutputsTo(Node* n, bool recursively) const;
|
bool OutputsTo(Node* n, bool recursively) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
|
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
|
||||||
*/
|
*/
|
||||||
@@ -266,7 +528,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node.
|
* @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node.
|
||||||
*/
|
*/
|
||||||
bool OutputsTo(NodeInput* input, bool recursively) const;
|
bool OutputsTo(const NodeInput &input, bool recursively) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns whether this node ever receives an input from a particular node instance
|
* @brief Returns whether this node ever receives an input from a particular node instance
|
||||||
@@ -330,7 +592,11 @@ public:
|
|||||||
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
||||||
* call this function with transformed time and relay the signal that way.
|
* call this function with transformed time and relay the signal that way.
|
||||||
*/
|
*/
|
||||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection());
|
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1);
|
||||||
|
void InvalidateCache(const TimeRange& range, const NodeInput& from)
|
||||||
|
{
|
||||||
|
InvalidateCache(range, from.input(), from.element());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Limits cache invalidation temporarily
|
* @brief Limits cache invalidation temporarily
|
||||||
@@ -351,12 +617,12 @@ public:
|
|||||||
* If this node modifies the `time` (i.e. a clip converting sequence time to media time), this function should be
|
* If this node modifies the `time` (i.e. a clip converting sequence time to media time), this function should be
|
||||||
* overridden to do so. Also make sure to override OutputTimeAdjustment() to provide the inverse function.
|
* overridden to do so. Also make sure to override OutputTimeAdjustment() to provide the inverse function.
|
||||||
*/
|
*/
|
||||||
virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const;
|
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The inverse of InputTimeAdjustment()
|
* @brief The inverse of InputTimeAdjustment()
|
||||||
*/
|
*/
|
||||||
virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const;
|
virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies inputs from from Node to another including connections
|
* @brief Copies inputs from from Node to another including connections
|
||||||
@@ -365,6 +631,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void CopyInputs(Node* source, Node* destination, bool include_connections = true);
|
static void CopyInputs(Node* source, Node* destination, bool include_connections = true);
|
||||||
|
|
||||||
|
static void CopyInput(Node* src, Node* dst, const QString& input, bool include_connections, bool traverse_arrays);
|
||||||
|
|
||||||
|
static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int src_element, int dst_element);
|
||||||
|
static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int element)
|
||||||
|
{
|
||||||
|
return CopyValuesOfElement(src, dst, input, element, element);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clones a set of nodes and connects the new ones the way the old ones were
|
* @brief Clones a set of nodes and connects the new ones the way the old ones were
|
||||||
*/
|
*/
|
||||||
@@ -394,12 +668,7 @@ public:
|
|||||||
* corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior
|
* corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior
|
||||||
* of the NodeParam objects will handle everything related to it automatically.
|
* of the NodeParam objects will handle everything related to it automatically.
|
||||||
*/
|
*/
|
||||||
virtual NodeValueTable Value(NodeValueDatabase& value) const;
|
virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Return whether a parameter with ID `id` has already been added to this Node
|
|
||||||
*/
|
|
||||||
bool HasParamWithID(const QString& id) const;
|
|
||||||
|
|
||||||
const QPointF& GetPosition() const;
|
const QPointF& GetPosition() const;
|
||||||
|
|
||||||
@@ -418,12 +687,7 @@ public:
|
|||||||
|
|
||||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const;
|
virtual void Hash(QCryptographicHash& hash, const rational &time) const;
|
||||||
|
|
||||||
const std::vector<InputConnection>& edges() const
|
void InvalidateAll(const QString& input, int element = -1);
|
||||||
{
|
|
||||||
return output_connections();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InvalidateAll(NodeInput *input, int element);
|
|
||||||
|
|
||||||
bool HasLinks() const
|
bool HasLinks() const
|
||||||
{
|
{
|
||||||
@@ -439,7 +703,58 @@ public:
|
|||||||
static bool Unlink(Node* a, Node* b);
|
static bool Unlink(Node* a, Node* b);
|
||||||
static bool AreLinked(Node* a, Node* b);
|
static bool AreLinked(Node* a, Node* b);
|
||||||
|
|
||||||
|
static const QString kDefaultOutput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
enum InputFlag {
|
||||||
|
/// By default, inputs are keyframable, connectable, and NOT arrays
|
||||||
|
kInputFlagNormal = 0x0,
|
||||||
|
kInputFlagArray = 0x1,
|
||||||
|
kInputFlagNotKeyframable = 0x2,
|
||||||
|
kInputFlagNotConnectable = 0x4
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputFlags {
|
||||||
|
public:
|
||||||
|
explicit InputFlags()
|
||||||
|
{
|
||||||
|
f_ = kInputFlagNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit InputFlags(uint64_t flags)
|
||||||
|
{
|
||||||
|
f_ = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator&(const InputFlag& f) const
|
||||||
|
{
|
||||||
|
return f_ & f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t f_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal));
|
||||||
|
void AddInput(const QString& id, NodeValue::Type type, InputFlags flags = InputFlags(kInputFlagNormal))
|
||||||
|
{
|
||||||
|
AddInput(id, type, QVariant(), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveInput(const QString& id);
|
||||||
|
|
||||||
|
void AddOutput(const QString& id = kDefaultOutput);
|
||||||
|
|
||||||
|
void RemoveOutput(const QString& id);
|
||||||
|
|
||||||
|
void SetInputName(const QString& id, const QString& name);
|
||||||
|
|
||||||
|
void SetComboBoxStrings(const QString& id, const QStringList& strings)
|
||||||
|
{
|
||||||
|
SetInputProperty(id, QStringLiteral("combo_str"), strings);
|
||||||
|
}
|
||||||
|
|
||||||
void SendInvalidateCache(const TimeRange &range);
|
void SendInvalidateCache(const TimeRange &range);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -449,14 +764,14 @@ protected:
|
|||||||
* parameters has changed throughout the duration of the clip (essential from 0 to infinity).
|
* parameters has changed throughout the duration of the clip (essential from 0 to infinity).
|
||||||
* In some scenarios, it may be preferable to handle this signal separately in order to
|
* In some scenarios, it may be preferable to handle this signal separately in order to
|
||||||
*/
|
*/
|
||||||
void IgnoreInvalidationsFrom(NodeInput* input);
|
void IgnoreInvalidationsFrom(const QString &input_id);
|
||||||
|
|
||||||
|
void IgnoreHashingFrom(const QString& input_id);
|
||||||
|
|
||||||
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data);
|
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data);
|
||||||
|
|
||||||
virtual void SaveInternal(QXmlStreamWriter* writer) const;
|
virtual void SaveInternal(QXmlStreamWriter* writer) const;
|
||||||
|
|
||||||
virtual QVector<NodeInput*> GetInputsToHash() const;
|
|
||||||
|
|
||||||
enum GizmoScaleHandles {
|
enum GizmoScaleHandles {
|
||||||
kGizmoScaleTopLeft,
|
kGizmoScaleTopLeft,
|
||||||
kGizmoScaleTopCenter,
|
kGizmoScaleTopCenter,
|
||||||
@@ -475,10 +790,16 @@ protected:
|
|||||||
|
|
||||||
static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count);
|
static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count);
|
||||||
|
|
||||||
virtual void childEvent(QChildEvent* event) override;
|
|
||||||
|
|
||||||
virtual void LinkChangeEvent(){}
|
virtual void LinkChangeEvent(){}
|
||||||
|
|
||||||
|
virtual void InputValueChangedEvent(const QString& input, int element);
|
||||||
|
|
||||||
|
virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output);
|
||||||
|
|
||||||
|
virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output);
|
||||||
|
|
||||||
|
virtual void childEvent(QChildEvent *event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted whenever the position is set through SetPosition()
|
* @brief Signal emitted whenever the position is set through SetPosition()
|
||||||
@@ -492,19 +813,231 @@ signals:
|
|||||||
|
|
||||||
void ColorChanged();
|
void ColorChanged();
|
||||||
|
|
||||||
void ValueChanged(NodeInput* input, int element);
|
void ValueChanged(const NodeInput& input, const TimeRange& range);
|
||||||
|
|
||||||
void InputConnected(Node* output, NodeInput* input, int element);
|
void InputConnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void InputDisconnected(Node* output, NodeInput* input, int element);
|
void InputDisconnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void OutputConnected(NodeInput* destination, int element);
|
void OutputConnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void OutputDisconnected(NodeInput* destination, int element);
|
void OutputDisconnected(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
|
void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value);
|
||||||
|
|
||||||
void LinksChanged();
|
void LinksChanged();
|
||||||
|
|
||||||
|
void InputArraySizeChanged(const QString& input, int new_size);
|
||||||
|
|
||||||
|
void KeyframeAdded(NodeKeyframe* key);
|
||||||
|
|
||||||
|
void KeyframeRemoved(NodeKeyframe* key);
|
||||||
|
|
||||||
|
void KeyframeTimeChanged();
|
||||||
|
|
||||||
|
void KeyframeEnableChanged(const NodeInput& input, bool enabled);
|
||||||
|
|
||||||
|
void InputAdded(const QString& id);
|
||||||
|
|
||||||
|
void InputRemoved(const QString& id);
|
||||||
|
|
||||||
|
void OutputAdded(const QString& id);
|
||||||
|
|
||||||
|
void OutputRemoved(const QString& id);
|
||||||
|
|
||||||
|
void InputNameChanged(const QString& id, const QString& name);
|
||||||
|
|
||||||
|
void InputDataTypeChanged(const QString& id, NodeValue::Type type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class ArrayInsertCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArrayInsertCommand(Node* node, const QString& input, int index) :
|
||||||
|
node_(node),
|
||||||
|
input_(input),
|
||||||
|
index_(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Project* GetRelevantProject() const override;
|
||||||
|
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
node_->InputArrayInsert(input_, index_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
node_->InputArrayRemove(input_, index_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* node_;
|
||||||
|
QString input_;
|
||||||
|
int index_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArrayRemoveCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArrayRemoveCommand(Node* node, const QString& input, int index) :
|
||||||
|
node_(node),
|
||||||
|
input_(input),
|
||||||
|
index_(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Project* GetRelevantProject() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
// Save immediate data
|
||||||
|
if (node_->IsInputKeyframable(input_)) {
|
||||||
|
is_keyframing_ = node_->IsInputKeyframing(input_, index_);
|
||||||
|
}
|
||||||
|
standard_value_ = node_->GetSplitStandardValue(input_, index_);
|
||||||
|
keyframes_ = node_->GetKeyframeTracks(input_, index_);
|
||||||
|
node_->GetImmediate(input_, index_)->delete_all_keyframes(&memory_manager_);
|
||||||
|
|
||||||
|
node_->InputArrayRemove(input_, index_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
node_->InputArrayInsert(input_, index_, false);
|
||||||
|
|
||||||
|
// Restore keyframes
|
||||||
|
foreach (const NodeKeyframeTrack& track, keyframes_) {
|
||||||
|
foreach (NodeKeyframe* key, track) {
|
||||||
|
key->setParent(node_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node_->SetSplitStandardValue(input_, standard_value_, index_);
|
||||||
|
|
||||||
|
if (node_->IsInputKeyframable(input_)) {
|
||||||
|
node_->SetInputIsKeyframing(input_, is_keyframing_, index_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* node_;
|
||||||
|
QString input_;
|
||||||
|
int index_;
|
||||||
|
|
||||||
|
SplitValue standard_value_;
|
||||||
|
bool is_keyframing_;
|
||||||
|
QVector<NodeKeyframeTrack> keyframes_;
|
||||||
|
QObject memory_manager_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArrayResizeCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArrayResizeCommand(Node* node, const QString& input, int size) :
|
||||||
|
node_(node),
|
||||||
|
input_(input),
|
||||||
|
size_(size)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
old_size_ = node_->GetInternalInputArraySize(input_);
|
||||||
|
|
||||||
|
if (old_size_ > size_) {
|
||||||
|
// Decreasing in size, disconnect any extraneous edges
|
||||||
|
for (int i=size_; i<old_size_; i++) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
NodeInput input(node_, input_, i);
|
||||||
|
NodeOutput output = node_->input_connections().at(input);
|
||||||
|
|
||||||
|
removed_connections_[input] = output;
|
||||||
|
|
||||||
|
DisconnectEdge(output, input);
|
||||||
|
} catch (std::out_of_range&) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_->ArrayResizeInternal(input_, size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) {
|
||||||
|
ConnectEdge(it->second, it->first);
|
||||||
|
}
|
||||||
|
removed_connections_.clear();
|
||||||
|
|
||||||
|
node_->ArrayResizeInternal(input_, old_size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Project* GetRelevantProject() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* node_;
|
||||||
|
QString input_;
|
||||||
|
int size_;
|
||||||
|
int old_size_;
|
||||||
|
|
||||||
|
InputConnections removed_connections_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
NodeValue::Type type;
|
||||||
|
InputFlags flags;
|
||||||
|
SplitValue default_value;
|
||||||
|
QHash<QString, QVariant> properties;
|
||||||
|
QString human_name;
|
||||||
|
int array_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeInputImmediate* CreateImmediate(const QString& input);
|
||||||
|
|
||||||
|
NodeInputImmediate* GetImmediate(const QString& input, int element) const;
|
||||||
|
|
||||||
|
int GetInternalInputIndex(const QString& input) const
|
||||||
|
{
|
||||||
|
return input_ids_.indexOf(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputFlags GetInputFlags(const QString& input) const;
|
||||||
|
|
||||||
|
Input* GetInternalInputData(const QString& input)
|
||||||
|
{
|
||||||
|
int i = GetInternalInputIndex(input);
|
||||||
|
|
||||||
|
if (i == -1) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return &input_data_[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Input* GetInternalInputData(const QString& input) const
|
||||||
|
{
|
||||||
|
int i = GetInternalInputIndex(input);
|
||||||
|
|
||||||
|
if (i == -1) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return &input_data_.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportInvalidInput(const char* attempted_action, const QString &id) const;
|
||||||
|
|
||||||
|
void ArrayResizeInternal(const QString& id, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Immediates aren't deleted, so the actual array size may be larger than ArraySize()
|
||||||
|
*/
|
||||||
|
int GetInternalInputArraySize(const QString& input);
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static void FindInputNodeInternal(const Node* n, QVector<T *>& list);
|
static void FindInputNodeInternal(const Node* n, QVector<T *>& list);
|
||||||
|
|
||||||
@@ -513,11 +1046,29 @@ private:
|
|||||||
|
|
||||||
QVector<Node*> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
QVector<Node*> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
||||||
|
|
||||||
void HashInputElement(QCryptographicHash& hash, NodeInput* input, int element, const rational& time) const;
|
void HashInputElement(QCryptographicHash& hash, const QString &input, int element, const rational& time) const;
|
||||||
|
|
||||||
QVector<NodeInput*> inputs_;
|
void ParameterValueChanged(const QString &input, int element, const olive::TimeRange &range);
|
||||||
|
|
||||||
QVector<NodeInput*> ignore_connections_;
|
void LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
|
||||||
|
|
||||||
|
void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Intelligently determine how what time range is affected by a keyframe
|
||||||
|
*/
|
||||||
|
TimeRange GetRangeAffectedByKeyframe(NodeKeyframe *key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a time range between the previous and next keyframes of index
|
||||||
|
*/
|
||||||
|
TimeRange GetRangeAroundIndex(const QString& input, int index, int track, int element) const;
|
||||||
|
|
||||||
|
void ClearElement(const QString &input, int index);
|
||||||
|
|
||||||
|
QVector<QString> ignore_connections_;
|
||||||
|
|
||||||
|
QVector<QString> ignore_when_hashing_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal variable for whether this Node can be deleted or not
|
* @brief Internal variable for whether this Node can be deleted or not
|
||||||
@@ -544,29 +1095,59 @@ private:
|
|||||||
*/
|
*/
|
||||||
QVector<Node*> links_;
|
QVector<Node*> links_;
|
||||||
|
|
||||||
|
QVector<QString> input_ids_;
|
||||||
|
QVector<Input> input_data_;
|
||||||
|
|
||||||
|
QVector<QString> outputs_;
|
||||||
|
|
||||||
|
QMap<QString, NodeInputImmediate*> standard_immediates_;
|
||||||
|
|
||||||
|
QMap<QString, QVector<NodeInputImmediate*> > array_immediates_;
|
||||||
|
|
||||||
|
InputConnections input_connections_;
|
||||||
|
|
||||||
|
OutputConnections output_connections_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ParameterValueChanged(const olive::TimeRange &range, int element);
|
/**
|
||||||
|
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
|
||||||
|
*/
|
||||||
|
void InvalidateFromKeyframeTimeChange();
|
||||||
|
|
||||||
void ParameterConnected(Node* source, int element);
|
/**
|
||||||
|
* @brief Slot when a keyframe's value changes to signal that the cache needs updating
|
||||||
|
*/
|
||||||
|
void InvalidateFromKeyframeValueChange();
|
||||||
|
|
||||||
void ParameterDisconnected(Node* source, int element);
|
/**
|
||||||
|
* @brief Slot when a keyframe's type changes to signal that the cache needs updating
|
||||||
|
*/
|
||||||
|
void InvalidateFromKeyframeTypeChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating
|
||||||
|
*/
|
||||||
|
void InvalidateFromKeyframeBezierInChange();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating
|
||||||
|
*/
|
||||||
|
void InvalidateFromKeyframeBezierOutChange();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void Node::FindInputNodeInternal(const Node* n, QVector<T *> &list)
|
void Node::FindInputNodeInternal(const Node* n, QVector<T *> &list)
|
||||||
{
|
{
|
||||||
foreach (NodeInput* input, n->inputs_) {
|
for (auto it=n->input_connections_.cbegin(); it!=n->input_connections_.cend(); it++) {
|
||||||
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
|
Node* edge = it->second.node();
|
||||||
Node* edge = it->second;
|
T* cast_test = dynamic_cast<T*>(edge);
|
||||||
T* cast_test = dynamic_cast<T*>(edge);
|
|
||||||
|
|
||||||
if (cast_test) {
|
if (cast_test) {
|
||||||
list.append(cast_test);
|
list.append(cast_test);
|
||||||
}
|
|
||||||
|
|
||||||
FindInputNodeInternal<T>(edge, list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FindInputNodeInternal<T>(edge, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,15 +1170,17 @@ T* Node::ValueToPtr(const QVariant &ptr)
|
|||||||
template<class T>
|
template<class T>
|
||||||
void Node::FindOutputNodeInternal(const Node* n, QVector<T *>& list)
|
void Node::FindOutputNodeInternal(const Node* n, QVector<T *>& list)
|
||||||
{
|
{
|
||||||
foreach (const InputConnection& edge, n->edges()) {
|
foreach (const std::vector<NodeInput>& outputs, n->output_connections_) {
|
||||||
Node* connected = static_cast<Node*>(edge.input->parent());
|
foreach (const NodeInput& output, outputs) {
|
||||||
T* cast_test = dynamic_cast<T*>(connected);
|
Node* connected = output.node();
|
||||||
|
T* cast_test = dynamic_cast<T*>(connected);
|
||||||
|
|
||||||
if (cast_test) {
|
if (cast_test) {
|
||||||
list.append(cast_test);
|
list.append(cast_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
FindOutputNodeInternal<T>(connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
FindOutputNodeInternal<T>(connected);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,37 +132,6 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
|||||||
// Link blocks
|
// Link blocks
|
||||||
XMLLinkBlocks(xml_node_data);
|
XMLLinkBlocks(xml_node_data);
|
||||||
|
|
||||||
// Connect footage to existing footage if it exists
|
|
||||||
if (!xml_node_data.footage_connections.isEmpty()) {
|
|
||||||
// Get list of all footage from project
|
|
||||||
QVector<Item*> footage = graph->project()->get_items_of_type(Item::kFootage);
|
|
||||||
|
|
||||||
if (!footage.isEmpty()) {
|
|
||||||
foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) {
|
|
||||||
if (con.footage) {
|
|
||||||
// Assume this is a pointer to a Stream*
|
|
||||||
Stream* loaded_stream = reinterpret_cast<Stream*>(con.footage);
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
foreach (Item* item, footage) {
|
|
||||||
foreach (Stream* s, static_cast<Footage*>(item)->streams()) {
|
|
||||||
if (s == loaded_stream) {
|
|
||||||
con.input->SetStandardValue(Node::PtrToValue(s), con.element);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pasted_nodes;
|
return pasted_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +141,7 @@ void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, void*
|
|||||||
|
|
||||||
void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
|
void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(xml_node_data)
|
||||||
reader->skipCurrentElement();
|
reader->skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+169
-169
@@ -33,24 +33,21 @@ const double Track::kTrackHeightDefault = 3.0;
|
|||||||
const double Track::kTrackHeightMinimum = 1.5;
|
const double Track::kTrackHeightMinimum = 1.5;
|
||||||
const double Track::kTrackHeightInterval = 0.5;
|
const double Track::kTrackHeightInterval = 0.5;
|
||||||
|
|
||||||
|
const QString Track::kBlockInput = QStringLiteral("block_in");
|
||||||
|
const QString Track::kMutedInput = QStringLiteral("muted_in");
|
||||||
|
|
||||||
Track::Track() :
|
Track::Track() :
|
||||||
track_type_(Track::kNone),
|
track_type_(Track::kNone),
|
||||||
index_(-1),
|
index_(-1),
|
||||||
locked_(false)
|
locked_(false)
|
||||||
{
|
{
|
||||||
block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone);
|
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
|
||||||
block_input_->SetKeyframable(false);
|
|
||||||
block_input_->SetIsArray(true);
|
|
||||||
connect(block_input_, &NodeInput::InputConnected, this, &Track::BlockConnected);
|
|
||||||
connect(block_input_, &NodeInput::InputDisconnected, this, &Track::BlockDisconnected);
|
|
||||||
|
|
||||||
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
||||||
// on our end
|
// on our end
|
||||||
IgnoreInvalidationsFrom(block_input_);
|
IgnoreInvalidationsFrom(kBlockInput);
|
||||||
|
|
||||||
muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean);
|
AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
muted_input_->SetKeyframable(false);
|
|
||||||
connect(muted_input_, &NodeInput::ValueChanged, this, &Track::MutedInputValueChanged);
|
|
||||||
|
|
||||||
// Set default height
|
// Set default height
|
||||||
track_height_ = kTrackHeightDefault;
|
track_height_ = kTrackHeightDefault;
|
||||||
@@ -97,9 +94,9 @@ QString Track::Description() const
|
|||||||
"a Sequence.");
|
"a Sequence.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||||
{
|
{
|
||||||
if (input == block_input_ && element >= 0) {
|
if (input == kBlockInput && element >= 0) {
|
||||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||||
|
|
||||||
return TransformRangeForBlock(blocks_.at(cache_index), input_time);
|
return TransformRangeForBlock(blocks_.at(cache_index), input_time);
|
||||||
@@ -108,9 +105,9 @@ TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRa
|
|||||||
return Node::InputTimeAdjustment(input, element, input_time);
|
return Node::InputTimeAdjustment(input, element, input_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeRange Track::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
TimeRange Track::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||||
{
|
{
|
||||||
if (input == block_input_ && element >= 0) {
|
if (input == kBlockInput && element >= 0) {
|
||||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||||
const rational& block_in = blocks_.at(cache_index)->in();
|
const rational& block_in = blocks_.at(cache_index)->in();
|
||||||
|
|
||||||
@@ -157,12 +154,153 @@ void Track::SaveInternal(QXmlStreamWriter *writer) const
|
|||||||
writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight()));
|
writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Track::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
if (input == kBlockInput) {
|
||||||
|
if (element == -1) {
|
||||||
|
// User has replaced the entire array, we will invalidate everything
|
||||||
|
InvalidateAll(kBlockInput, element);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a block was connected, if not, ignore
|
||||||
|
Block* block = dynamic_cast<Block*>(output.node());
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine where in the cache this block will be
|
||||||
|
int cache_index = -1;
|
||||||
|
Block *previous = nullptr, *next = nullptr;
|
||||||
|
|
||||||
|
int arr_sz = InputArraySize(kBlockInput);
|
||||||
|
for (int i=element+1; i<arr_sz; i++) {
|
||||||
|
// Find next block because this will be the index that we want to insert at
|
||||||
|
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||||
|
|
||||||
|
if (cache_index >= 0) {
|
||||||
|
next = blocks_.at(cache_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was no next, this will be inserted at the end
|
||||||
|
if (cache_index == -1) {
|
||||||
|
cache_index = blocks_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine previous block, either by using next's previous or the last block if there was no
|
||||||
|
// next. If there are neither, they'll both remain null
|
||||||
|
if (next) {
|
||||||
|
previous = next->previous();
|
||||||
|
} else if (!blocks_.isEmpty()) {
|
||||||
|
previous = blocks_.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert at index
|
||||||
|
blocks_.insert(cache_index, block);
|
||||||
|
block_array_indexes_.insert(cache_index, element);
|
||||||
|
|
||||||
|
// Update previous/next
|
||||||
|
if (previous) {
|
||||||
|
previous->set_next(block);
|
||||||
|
block->set_previous(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
block->set_next(next);
|
||||||
|
next->set_previous(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
block->set_track(this);
|
||||||
|
|
||||||
|
// Update ins/outs
|
||||||
|
UpdateInOutFrom(cache_index);
|
||||||
|
|
||||||
|
// Connect to the block
|
||||||
|
connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||||
|
|
||||||
|
// Invalidate cache now that block should have an in point
|
||||||
|
InvalidateCache(TimeRange(block->in(), track_length()));
|
||||||
|
|
||||||
|
// Emit block added signal
|
||||||
|
emit BlockAdded(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Track::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
if (input == kBlockInput) {
|
||||||
|
if (element == -1) {
|
||||||
|
// User has replaced the entire array, we will invalidate everything
|
||||||
|
InvalidateAll(kBlockInput, element);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* b = dynamic_cast<Block*>(output.node());
|
||||||
|
|
||||||
|
if (!b) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BlockRemoved(b);
|
||||||
|
|
||||||
|
TimeRange invalidate_range(b->in(), track_length());
|
||||||
|
|
||||||
|
// Get cache index
|
||||||
|
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||||
|
|
||||||
|
// Remove block here
|
||||||
|
blocks_.removeAt(cache_index);
|
||||||
|
block_array_indexes_.removeAt(cache_index);
|
||||||
|
|
||||||
|
// Update previous/nexts
|
||||||
|
Block* previous = b->previous();
|
||||||
|
Block* next = b->next();
|
||||||
|
|
||||||
|
if (previous) {
|
||||||
|
previous->set_next(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
next->set_previous(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
b->set_previous(nullptr);
|
||||||
|
b->set_next(nullptr);
|
||||||
|
b->set_track(nullptr);
|
||||||
|
|
||||||
|
// Update lengths
|
||||||
|
if (next) {
|
||||||
|
UpdateInOutFrom(blocks_.indexOf(next));
|
||||||
|
} else if (blocks_.isEmpty()) {
|
||||||
|
SetLengthInternal(rational());
|
||||||
|
} else {
|
||||||
|
SetLengthInternal(blocks_.last()->out());
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||||
|
|
||||||
|
InvalidateCache(invalidate_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Track::InputValueChangedEvent(const QString &input, int element)
|
||||||
|
{
|
||||||
|
Q_UNUSED(element)
|
||||||
|
|
||||||
|
if (input == kMutedInput) {
|
||||||
|
emit MutedChanged(IsMuted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Track::Retranslate()
|
void Track::Retranslate()
|
||||||
{
|
{
|
||||||
Node::Retranslate();
|
Node::Retranslate();
|
||||||
|
|
||||||
block_input_->set_name(tr("Blocks"));
|
SetInputName(kBlockInput, tr("Blocks"));
|
||||||
muted_input_->set_name(tr("Muted"));
|
SetInputName(kMutedInput, tr("Muted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::SetIndex(const int &index)
|
void Track::SetIndex(const int &index)
|
||||||
@@ -276,15 +414,15 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||||
{
|
{
|
||||||
TimeRange limited;
|
TimeRange limited;
|
||||||
|
|
||||||
Block* b;
|
const Block* b;
|
||||||
|
|
||||||
if (from.input == block_input_
|
if (from == kBlockInput
|
||||||
&& from.element >= 0
|
&& element >= 0
|
||||||
&& (b = dynamic_cast<Block*>(from.input->GetConnectedNode(from.element)))) {
|
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))) {
|
||||||
// Limit the range signal to the corresponding block
|
// Limit the range signal to the corresponding block
|
||||||
if (range.out() <= b->in() || range.in() >= b->out()) {
|
if (range.out() <= b->in() || range.in() >= b->out()) {
|
||||||
return;
|
return;
|
||||||
@@ -329,8 +467,8 @@ void Track::PrependBlock(Block *block)
|
|||||||
{
|
{
|
||||||
BeginOperation();
|
BeginOperation();
|
||||||
|
|
||||||
block_input_->ArrayPrepend();
|
InputArrayPrepend(kBlockInput);
|
||||||
Node::ConnectEdge(block, block_input_, 0);
|
Node::ConnectEdge(block, NodeInput(this, kBlockInput, 0));
|
||||||
|
|
||||||
EndOperation();
|
EndOperation();
|
||||||
|
|
||||||
@@ -343,8 +481,8 @@ void Track::InsertBlockAtIndex(Block *block, int index)
|
|||||||
BeginOperation();
|
BeginOperation();
|
||||||
|
|
||||||
int insert_index = GetArrayIndexFromCacheIndex(index);
|
int insert_index = GetArrayIndexFromCacheIndex(index);
|
||||||
block_input_->ArrayInsert(insert_index);
|
InputArrayInsert(kBlockInput, insert_index);
|
||||||
Node::ConnectEdge(block, block_input_, insert_index);
|
Node::ConnectEdge(block, NodeInput(this, kBlockInput, insert_index));
|
||||||
|
|
||||||
EndOperation();
|
EndOperation();
|
||||||
|
|
||||||
@@ -355,8 +493,8 @@ void Track::AppendBlock(Block *block)
|
|||||||
{
|
{
|
||||||
BeginOperation();
|
BeginOperation();
|
||||||
|
|
||||||
block_input_->ArrayAppend();
|
InputArrayAppend(kBlockInput);
|
||||||
Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1);
|
Node::ConnectEdge(block, NodeInput(this, kBlockInput, InputArraySize(kBlockInput) - 1));
|
||||||
|
|
||||||
EndOperation();
|
EndOperation();
|
||||||
|
|
||||||
@@ -371,7 +509,7 @@ void Track::RippleRemoveBlock(Block *block)
|
|||||||
rational remove_in = block->in();
|
rational remove_in = block->in();
|
||||||
rational remove_out = block->out();
|
rational remove_out = block->out();
|
||||||
|
|
||||||
block_input_->ArrayRemove(GetArrayIndexFromBlock(block));
|
InputArrayRemove(kBlockInput, GetArrayIndexFromBlock(block));
|
||||||
|
|
||||||
EndOperation();
|
EndOperation();
|
||||||
|
|
||||||
@@ -384,9 +522,9 @@ void Track::ReplaceBlock(Block *old, Block *replace)
|
|||||||
|
|
||||||
int index_of_old_block = GetArrayIndexFromBlock(old);
|
int index_of_old_block = GetArrayIndexFromBlock(old);
|
||||||
|
|
||||||
DisconnectEdge(old, block_input_, index_of_old_block);
|
DisconnectEdge(old, NodeInput(this, kBlockInput, index_of_old_block));
|
||||||
|
|
||||||
ConnectEdge(replace, block_input_, index_of_old_block);
|
ConnectEdge(replace, NodeInput(this, kBlockInput, index_of_old_block));
|
||||||
|
|
||||||
EndOperation();
|
EndOperation();
|
||||||
|
|
||||||
@@ -421,7 +559,7 @@ QString Track::GetDefaultTrackName(Track::Type type, int index)
|
|||||||
|
|
||||||
bool Track::IsMuted() const
|
bool Track::IsMuted() const
|
||||||
{
|
{
|
||||||
return muted_input_->GetStandardValue().toBool();
|
return GetStandardValue(kMutedInput).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Track::IsLocked() const
|
bool Track::IsLocked() const
|
||||||
@@ -429,11 +567,6 @@ bool Track::IsLocked() const
|
|||||||
return locked_;
|
return locked_;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput *Track::block_input() const
|
|
||||||
{
|
|
||||||
return block_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
||||||
{
|
{
|
||||||
Block* b = BlockAtTime(time);
|
Block* b = BlockAtTime(time);
|
||||||
@@ -446,8 +579,7 @@ void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
|||||||
|
|
||||||
void Track::SetMuted(bool e)
|
void Track::SetMuted(bool e)
|
||||||
{
|
{
|
||||||
muted_input_->SetStandardValue(e);
|
SetStandardValue(kMutedInput, e);
|
||||||
InvalidateCache(TimeRange(0, track_length()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::SetLocked(bool e)
|
void Track::SetLocked(bool e)
|
||||||
@@ -511,133 +643,6 @@ void Track::SetLengthInternal(const rational &r, bool invalidate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::BlockConnected(Node *node, int element)
|
|
||||||
{
|
|
||||||
if (element == -1) {
|
|
||||||
// User has replaced the entire array, we will invalidate everything
|
|
||||||
InvalidateAll(block_input_, element);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a block was connected, if not, ignore
|
|
||||||
Block* block = dynamic_cast<Block*>(node);
|
|
||||||
|
|
||||||
if (!block) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine where in the cache this block will be
|
|
||||||
int cache_index = -1;
|
|
||||||
Block *previous = nullptr, *next = nullptr;
|
|
||||||
|
|
||||||
for (int i=element+1; i<block_input_->ArraySize(); i++) {
|
|
||||||
// Find next block because this will be the index that we want to insert at
|
|
||||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
|
||||||
|
|
||||||
if (cache_index >= 0) {
|
|
||||||
next = blocks_.at(cache_index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there was no next, this will be inserted at the end
|
|
||||||
if (cache_index == -1) {
|
|
||||||
cache_index = blocks_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine previous block, either by using next's previous or the last block if there was no
|
|
||||||
// next. If there are neither, they'll both remain null
|
|
||||||
if (next) {
|
|
||||||
previous = next->previous();
|
|
||||||
} else if (!blocks_.isEmpty()) {
|
|
||||||
previous = blocks_.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert at index
|
|
||||||
blocks_.insert(cache_index, block);
|
|
||||||
block_array_indexes_.insert(cache_index, element);
|
|
||||||
|
|
||||||
// Update previous/next
|
|
||||||
if (previous) {
|
|
||||||
previous->set_next(block);
|
|
||||||
block->set_previous(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next) {
|
|
||||||
block->set_next(next);
|
|
||||||
next->set_previous(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
block->set_track(this);
|
|
||||||
|
|
||||||
// Update ins/outs
|
|
||||||
UpdateInOutFrom(cache_index);
|
|
||||||
|
|
||||||
// Connect to the block
|
|
||||||
connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
|
||||||
|
|
||||||
// Invalidate cache now that block should have an in point
|
|
||||||
InvalidateCache(TimeRange(block->in(), track_length()));
|
|
||||||
|
|
||||||
// Emit block added signal
|
|
||||||
emit BlockAdded(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Track::BlockDisconnected(Node* node, int element)
|
|
||||||
{
|
|
||||||
if (element == -1) {
|
|
||||||
// User has replaced the entire array, we will invalidate everything
|
|
||||||
InvalidateAll(block_input_, element);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block* b = dynamic_cast<Block*>(node);
|
|
||||||
|
|
||||||
if (!b) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit BlockRemoved(b);
|
|
||||||
|
|
||||||
TimeRange invalidate_range(b->in(), track_length());
|
|
||||||
|
|
||||||
// Get cache index
|
|
||||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
|
||||||
|
|
||||||
// Remove block here
|
|
||||||
blocks_.removeAt(cache_index);
|
|
||||||
block_array_indexes_.removeAt(cache_index);
|
|
||||||
|
|
||||||
// Update previous/nexts
|
|
||||||
Block* previous = b->previous();
|
|
||||||
Block* next = b->next();
|
|
||||||
|
|
||||||
if (previous) {
|
|
||||||
previous->set_next(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next) {
|
|
||||||
next->set_previous(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
b->set_previous(nullptr);
|
|
||||||
b->set_next(nullptr);
|
|
||||||
b->set_track(nullptr);
|
|
||||||
|
|
||||||
// Update lengths
|
|
||||||
if (next) {
|
|
||||||
UpdateInOutFrom(blocks_.indexOf(next));
|
|
||||||
} else if (blocks_.isEmpty()) {
|
|
||||||
SetLengthInternal(rational());
|
|
||||||
} else {
|
|
||||||
SetLengthInternal(blocks_.last()->out());
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
|
||||||
|
|
||||||
InvalidateCache(invalidate_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Track::BlockLengthChanged()
|
void Track::BlockLengthChanged()
|
||||||
{
|
{
|
||||||
// Assumes sender is a Block
|
// Assumes sender is a Block
|
||||||
@@ -654,11 +659,6 @@ void Track::BlockLengthChanged()
|
|||||||
InvalidateCache(invalidate_region);
|
InvalidateCache(invalidate_region);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::MutedInputValueChanged()
|
|
||||||
{
|
|
||||||
emit MutedChanged(IsMuted());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const Track::Reference &r, uint seed)
|
uint qHash(const Track::Reference &r, uint seed)
|
||||||
{
|
{
|
||||||
// Not super efficient, but couldn't think of any better way to ensure a different hash each time
|
// Not super efficient, but couldn't think of any better way to ensure a different hash each time
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ public:
|
|||||||
virtual QVector<CategoryID> Category() const override;
|
virtual QVector<CategoryID> Category() const override;
|
||||||
virtual QString Description() const override;
|
virtual QString Description() const override;
|
||||||
|
|
||||||
virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||||
|
|
||||||
virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||||
|
|
||||||
static rational TransformTimeForBlock(Block* block, const rational& time);
|
static rational TransformTimeForBlock(Block* block, const rational& time);
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ public:
|
|||||||
return blocks_;
|
return blocks_;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()) override;
|
virtual void InvalidateCache(const TimeRange& range, const QString& from = QString(), int element = -1) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||||
@@ -274,8 +274,6 @@ public:
|
|||||||
|
|
||||||
bool IsLocked() const;
|
bool IsLocked() const;
|
||||||
|
|
||||||
NodeInput* block_input() const;
|
|
||||||
|
|
||||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
||||||
|
|
||||||
AudioVisualWaveform& waveform()
|
AudioVisualWaveform& waveform()
|
||||||
@@ -287,6 +285,9 @@ public:
|
|||||||
static const double kTrackHeightMinimum;
|
static const double kTrackHeightMinimum;
|
||||||
static const double kTrackHeightInterval;
|
static const double kTrackHeightInterval;
|
||||||
|
|
||||||
|
static const QString kBlockInput;
|
||||||
|
static const QString kMutedInput;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetMuted(bool e);
|
void SetMuted(bool e);
|
||||||
|
|
||||||
@@ -338,6 +339,12 @@ protected:
|
|||||||
|
|
||||||
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
|
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
|
||||||
|
|
||||||
|
virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||||
|
|
||||||
|
virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||||
|
|
||||||
|
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateInOutFrom(int index);
|
void UpdateInOutFrom(int index);
|
||||||
|
|
||||||
@@ -352,10 +359,6 @@ private:
|
|||||||
QVector<Block*> blocks_;
|
QVector<Block*> blocks_;
|
||||||
QVector<int> block_array_indexes_;
|
QVector<int> block_array_indexes_;
|
||||||
|
|
||||||
NodeInput* block_input_;
|
|
||||||
|
|
||||||
NodeInput* muted_input_;
|
|
||||||
|
|
||||||
Track::Type track_type_;
|
Track::Type track_type_;
|
||||||
|
|
||||||
rational track_length_;
|
rational track_length_;
|
||||||
@@ -371,14 +374,8 @@ private:
|
|||||||
AudioVisualWaveform waveform_;
|
AudioVisualWaveform waveform_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void BlockConnected(Node* node, int element);
|
|
||||||
|
|
||||||
void BlockDisconnected(Node* node, int element);
|
|
||||||
|
|
||||||
void BlockLengthChanged();
|
void BlockLengthChanged();
|
||||||
|
|
||||||
void MutedInputValueChanged();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint qHash(const Track::Reference& r, uint seed = 0);
|
uint qHash(const Track::Reference& r, uint seed = 0);
|
||||||
|
|||||||
@@ -27,13 +27,11 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, NodeInput *track_input) :
|
TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, const QString &track_input) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
track_input_(track_input),
|
track_input_(track_input),
|
||||||
type_(type)
|
type_(type)
|
||||||
{
|
{
|
||||||
connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected);
|
|
||||||
connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Track *TrackList::GetTrackAt(int index) const
|
Track *TrackList::GetTrackAt(int index) const
|
||||||
@@ -48,7 +46,7 @@ Track *TrackList::GetTrackAt(int index) const
|
|||||||
void TrackList::TrackConnected(Node *node, int element)
|
void TrackList::TrackConnected(Node *node, int element)
|
||||||
{
|
{
|
||||||
if (element == -1) {
|
if (element == -1) {
|
||||||
parent()->InvalidateAll(track_input_, element);
|
parent()->InvalidateAll(track_input(), element);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +58,7 @@ void TrackList::TrackConnected(Node *node, int element)
|
|||||||
|
|
||||||
// Determine where in the cache this block will be
|
// Determine where in the cache this block will be
|
||||||
int cache_index = -1;
|
int cache_index = -1;
|
||||||
for (int i=element+1; i<track_input_->ArraySize(); i++) {
|
for (int i=element+1; i<ArraySize(); i++) {
|
||||||
// Find next track because this will be the index we insert at
|
// Find next track because this will be the index we insert at
|
||||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||||
|
|
||||||
@@ -97,7 +95,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
|
|||||||
{
|
{
|
||||||
if (element == -1) {
|
if (element == -1) {
|
||||||
// User has replaced the entire array, we will invalidate everything
|
// User has replaced the entire array, we will invalidate everything
|
||||||
parent()->InvalidateAll(track_input_, element);
|
parent()->InvalidateAll(track_input(), element);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,11 +139,36 @@ NodeGraph *TrackList::GetParentGraph() const
|
|||||||
return static_cast<NodeGraph*>(parent()->parent());
|
return static_cast<NodeGraph*>(parent()->parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString& TrackList::track_input() const
|
||||||
|
{
|
||||||
|
return track_input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeInput TrackList::track_input(int element) const
|
||||||
|
{
|
||||||
|
return NodeInput(parent(), track_input(), element);
|
||||||
|
}
|
||||||
|
|
||||||
ViewerOutput *TrackList::parent() const
|
ViewerOutput *TrackList::parent() const
|
||||||
{
|
{
|
||||||
return static_cast<ViewerOutput*>(QObject::parent());
|
return static_cast<ViewerOutput*>(QObject::parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TrackList::ArraySize() const
|
||||||
|
{
|
||||||
|
return parent()->InputArraySize(track_input());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackList::ArrayAppend(bool undoable)
|
||||||
|
{
|
||||||
|
parent()->InputArrayAppend(track_input(), undoable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackList::ArrayRemoveLast(bool undoable)
|
||||||
|
{
|
||||||
|
parent()->InputArrayRemoveLast(track_input(), undoable);
|
||||||
|
}
|
||||||
|
|
||||||
void TrackList::UpdateTotalLength()
|
void TrackList::UpdateTotalLength()
|
||||||
{
|
{
|
||||||
total_length_ = 0;
|
total_length_ = 0;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TrackList : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TrackList(ViewerOutput *parent, const Track::Type& type, NodeInput* track_input);
|
TrackList(ViewerOutput *parent, const Track::Type& type, const QString& track_input);
|
||||||
|
|
||||||
const Track::Type& type() const
|
const Track::Type& type() const
|
||||||
{
|
{
|
||||||
@@ -61,13 +61,27 @@ public:
|
|||||||
|
|
||||||
NodeGraph* GetParentGraph() const;
|
NodeGraph* GetParentGraph() const;
|
||||||
|
|
||||||
NodeInput* track_input() const
|
const QString &track_input() const;
|
||||||
{
|
NodeInput track_input(int element) const;
|
||||||
return track_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewerOutput* parent() const;
|
ViewerOutput* parent() const;
|
||||||
|
|
||||||
|
int ArraySize() const;
|
||||||
|
|
||||||
|
void ArrayAppend(bool undoable = false);
|
||||||
|
void ArrayRemoveLast(bool undoable = false);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/**
|
||||||
|
* @brief Slot for when the track connection is added
|
||||||
|
*/
|
||||||
|
void TrackConnected(Node* node, int element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Slot for when the track connection is removed
|
||||||
|
*/
|
||||||
|
void TrackDisconnected(Node* node, int element);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TrackListChanged();
|
void TrackListChanged();
|
||||||
|
|
||||||
@@ -96,23 +110,13 @@ private:
|
|||||||
return track_array_indexes_.indexOf(index);
|
return track_array_indexes_.indexOf(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInput* track_input_;
|
QString track_input_;
|
||||||
|
|
||||||
rational total_length_;
|
rational total_length_;
|
||||||
|
|
||||||
enum Track::Type type_;
|
enum Track::Type type_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
/**
|
|
||||||
* @brief Slot for when the track connection is added
|
|
||||||
*/
|
|
||||||
void TrackConnected(Node* node, int element);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slot for when the track connection is removed
|
|
||||||
*/
|
|
||||||
void TrackDisconnected(Node* node, int element);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Slot for when any of the track's length changes so we can update the length of the tracklist
|
* @brief Slot for when any of the track's length changes so we can update the length of the tracklist
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -24,31 +24,31 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
|
||||||
|
const QString ViewerOutput::kTrackInputFormat = QStringLiteral("track_in_%1");
|
||||||
|
|
||||||
ViewerOutput::ViewerOutput() :
|
ViewerOutput::ViewerOutput() :
|
||||||
video_frame_cache_(this),
|
video_frame_cache_(this),
|
||||||
audio_playback_cache_(this),
|
audio_playback_cache_(this),
|
||||||
operation_stack_(0)
|
operation_stack_(0)
|
||||||
{
|
{
|
||||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
texture_input_->SetKeyframable(false);
|
|
||||||
|
|
||||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||||
samples_input_->SetKeyframable(false);
|
|
||||||
|
|
||||||
// Create TrackList instances
|
// Create TrackList instances
|
||||||
track_inputs_.resize(Track::kCount);
|
|
||||||
track_lists_.resize(Track::kCount);
|
track_lists_.resize(Track::kCount);
|
||||||
|
|
||||||
for (int i=0;i<Track::kCount;i++) {
|
for (int i=0;i<Track::kCount;i++) {
|
||||||
// Create track input
|
// Create track input
|
||||||
NodeInput* track_input = new NodeInput(this, QStringLiteral("track_in_%1").arg(i), NodeValue::kNone);
|
QString track_input_id = kTrackInputFormat.arg(i);
|
||||||
track_input->SetIsArray(true);
|
|
||||||
track_input->SetKeyframable(false);
|
|
||||||
|
|
||||||
IgnoreInvalidationsFrom(track_input);
|
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
|
||||||
track_inputs_.replace(i, track_input);
|
|
||||||
|
|
||||||
TrackList* list = new TrackList(this, static_cast<Track::Type>(i), track_input);
|
IgnoreInvalidationsFrom(track_input_id);
|
||||||
|
|
||||||
|
TrackList* list = new TrackList(this, static_cast<Track::Type>(i), track_input_id);
|
||||||
track_lists_.replace(i, list);
|
track_lists_.replace(i, list);
|
||||||
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
|
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
|
||||||
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
|
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
|
||||||
@@ -110,15 +110,17 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
|||||||
ShiftAudioCache(from, to);
|
ShiftAudioCache(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(element)
|
||||||
|
|
||||||
if (operation_stack_ == 0) {
|
if (operation_stack_ == 0) {
|
||||||
if (from.input == texture_input_ || from.input == samples_input_) {
|
if (from == kTextureInput || from == kSamplesInput) {
|
||||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||||
qMin(GetLength(), range.out()));
|
qMin(GetLength(), range.out()));
|
||||||
|
|
||||||
if (invalidated_range.in() != invalidated_range.out()) {
|
if (invalidated_range.in() != invalidated_range.out()) {
|
||||||
if (from.input == texture_input_) {
|
if (from == kTextureInput) {
|
||||||
video_frame_cache_.Invalidate(invalidated_range);
|
video_frame_cache_.Invalidate(invalidated_range);
|
||||||
} else {
|
} else {
|
||||||
audio_playback_cache_.Invalidate(invalidated_range);
|
audio_playback_cache_.Invalidate(invalidated_range);
|
||||||
@@ -216,8 +218,8 @@ void ViewerOutput::VerifyLength()
|
|||||||
{
|
{
|
||||||
video_length = track_lists_.at(Track::kVideo)->GetTotalLength();
|
video_length = track_lists_.at(Track::kVideo)->GetTotalLength();
|
||||||
|
|
||||||
if (video_length.isNull() && texture_input_->IsConnected()) {
|
if (video_length.isNull() && IsInputConnected(kTextureInput)) {
|
||||||
NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0);
|
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0));
|
||||||
video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +229,8 @@ void ViewerOutput::VerifyLength()
|
|||||||
{
|
{
|
||||||
audio_length = track_lists_.at(Track::kAudio)->GetTotalLength();
|
audio_length = track_lists_.at(Track::kAudio)->GetTotalLength();
|
||||||
|
|
||||||
if (audio_length.isNull() && samples_input_->IsConnected()) {
|
if (audio_length.isNull() && IsInputConnected(kSamplesInput)) {
|
||||||
NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0);
|
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0));
|
||||||
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,11 +253,11 @@ void ViewerOutput::Retranslate()
|
|||||||
{
|
{
|
||||||
Node::Retranslate();
|
Node::Retranslate();
|
||||||
|
|
||||||
texture_input_->set_name(tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
|
|
||||||
samples_input_->set_name(tr("Samples"));
|
SetInputName(kSamplesInput, tr("Samples"));
|
||||||
|
|
||||||
for (int i=0;i<track_inputs_.size();i++) {
|
for (int i=0;i<Track::kCount;i++) {
|
||||||
QString input_name;
|
QString input_name;
|
||||||
|
|
||||||
switch (static_cast<Track::Type>(i)) {
|
switch (static_cast<Track::Type>(i)) {
|
||||||
@@ -274,7 +276,7 @@ void ViewerOutput::Retranslate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!input_name.isEmpty()) {
|
if (!input_name.isEmpty()) {
|
||||||
track_inputs_.at(i)->set_name(input_name);
|
SetInputName(kTrackInputFormat.arg(i), input_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,4 +295,32 @@ void ViewerOutput::EndOperation()
|
|||||||
Node::EndOperation();
|
Node::EndOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
if (input == kTextureInput) {
|
||||||
|
emit TextureInputChanged();
|
||||||
|
} else {
|
||||||
|
foreach (TrackList* list, track_lists_) {
|
||||||
|
if (list->track_input() == input) {
|
||||||
|
list->TrackConnected(output.node(), element);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||||
|
{
|
||||||
|
if (input == kTextureInput) {
|
||||||
|
emit TextureInputChanged();
|
||||||
|
} else {
|
||||||
|
foreach (TrackList* list, track_lists_) {
|
||||||
|
if (list->track_input() == input) {
|
||||||
|
list->TrackDisconnected(output.node(), element);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,17 +59,7 @@ public:
|
|||||||
void ShiftAudioCache(const rational& from, const rational& to);
|
void ShiftAudioCache(const rational& from, const rational& to);
|
||||||
void ShiftCache(const rational& from, const rational& to);
|
void ShiftCache(const rational& from, const rational& to);
|
||||||
|
|
||||||
NodeInput* texture_input() const
|
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
|
||||||
{
|
|
||||||
return texture_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeInput* samples_input() const
|
|
||||||
{
|
|
||||||
return samples_input_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
|
||||||
|
|
||||||
const VideoParams& video_params() const
|
const VideoParams& video_params() const
|
||||||
{
|
{
|
||||||
@@ -106,11 +96,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
QVector<Track *> GetUnlockedTracks() const;
|
QVector<Track *> GetUnlockedTracks() const;
|
||||||
|
|
||||||
NodeInput* track_input(Track::Type type) const
|
|
||||||
{
|
|
||||||
return track_inputs_.at(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackList* track_list(Track::Type type) const
|
TrackList* track_list(Track::Type type) const
|
||||||
{
|
{
|
||||||
return track_lists_.at(type);
|
return track_lists_.at(type);
|
||||||
@@ -132,6 +117,10 @@ public:
|
|||||||
|
|
||||||
virtual void EndOperation() override;
|
virtual void EndOperation() override;
|
||||||
|
|
||||||
|
static const QString kTextureInput;
|
||||||
|
static const QString kSamplesInput;
|
||||||
|
static const QString kTrackInputFormat;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TimebaseChanged(const rational&);
|
void TimebaseChanged(const rational&);
|
||||||
|
|
||||||
@@ -149,19 +138,20 @@ signals:
|
|||||||
void TrackAdded(Track* track);
|
void TrackAdded(Track* track);
|
||||||
void TrackRemoved(Track* track);
|
void TrackRemoved(Track* track);
|
||||||
|
|
||||||
|
void TextureInputChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override;
|
||||||
|
|
||||||
|
void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUuid uuid_;
|
QUuid uuid_;
|
||||||
|
|
||||||
NodeInput* texture_input_;
|
|
||||||
|
|
||||||
NodeInput* samples_input_;
|
|
||||||
|
|
||||||
VideoParams video_params_;
|
VideoParams video_params_;
|
||||||
|
|
||||||
AudioParams audio_params_;
|
AudioParams audio_params_;
|
||||||
|
|
||||||
QVector<NodeInput*> track_inputs_;
|
|
||||||
|
|
||||||
QVector<TrackList*> track_lists_;
|
QVector<TrackList*> track_lists_;
|
||||||
|
|
||||||
QVector<Track*> track_cache_;
|
QVector<Track*> track_cache_;
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2020 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "param.h"
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
QString NodeInput::name() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetInputName(input_);
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeInput::IsConnected() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->IsInputConnected(*this);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeInput::IsKeyframing() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->IsInputKeyframing(*this);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeInput::IsArray() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->InputIsArray(input_);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeOutput NodeInput::GetConnectedOutput() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetConnectedOutput(*this);
|
||||||
|
} else {
|
||||||
|
return NodeOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeValue::Type NodeInput::GetDataType() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetInputDataType(input_);
|
||||||
|
} else {
|
||||||
|
return NodeValue::kNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList NodeInput::GetComboBoxStrings() const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetComboBoxStrings(input_);
|
||||||
|
} else {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant NodeInput::GetProperty(const QString &key) const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetInputProperty(input_, key);
|
||||||
|
} else {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant NodeInput::GetValueAtTime(const rational &time) const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetValueAtTime(*this, time);
|
||||||
|
} else {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframe* NodeInput::GetKeyframeAtTimeOnTrack(const rational &time, int track) const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetKeyframeAtTimeOnTrack(*this, time, track);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const
|
||||||
|
{
|
||||||
|
if (IsValid()) {
|
||||||
|
return node_->GetSplitDefaultValueOnTrack(input_, track);
|
||||||
|
} else {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const NodeInput &i)
|
||||||
|
{
|
||||||
|
return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeOutput::NodeOutput(Node *n)
|
||||||
|
{
|
||||||
|
node_ = n;
|
||||||
|
output_ = Node::kDefaultOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const NodeKeyframeTrackReference &i)
|
||||||
|
{
|
||||||
|
return qHash(i.input()) & qHash(i.track());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const NodeInputPair &i)
|
||||||
|
{
|
||||||
|
return qHash(i.node) & qHash(i.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2020 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef NODEPARAM_H
|
||||||
|
#define NODEPARAM_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "common/rational.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
class NodeKeyframe;
|
||||||
|
|
||||||
|
struct NodeInputPair {
|
||||||
|
bool operator==(const NodeInputPair& rhs) const
|
||||||
|
{
|
||||||
|
return node == rhs.node && input == rhs.input;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* node;
|
||||||
|
QString input;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines a node output
|
||||||
|
*/
|
||||||
|
class NodeOutput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeOutput(Node* n, const QString& o)
|
||||||
|
{
|
||||||
|
node_ = n;
|
||||||
|
output_ = o;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeOutput(Node* n);
|
||||||
|
|
||||||
|
NodeOutput()
|
||||||
|
{
|
||||||
|
node_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const NodeOutput& rhs) const
|
||||||
|
{
|
||||||
|
return node_ == rhs.node_ && output_ == rhs.output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* node() const
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& output() const
|
||||||
|
{
|
||||||
|
return output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* node_;
|
||||||
|
QString output_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines a Node input
|
||||||
|
*/
|
||||||
|
class NodeInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeInput()
|
||||||
|
{
|
||||||
|
node_ = nullptr;
|
||||||
|
element_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeInput(Node* n, const QString& i, int e = -1)
|
||||||
|
{
|
||||||
|
node_ = n;
|
||||||
|
input_ = i;
|
||||||
|
element_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const NodeInput& rhs) const
|
||||||
|
{
|
||||||
|
return node_ == rhs.node_ && input_ == rhs.input_ && element_ == rhs.element_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const NodeInput& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const NodeInput& rhs) const
|
||||||
|
{
|
||||||
|
if (node_ != rhs.node_) {
|
||||||
|
return node_ < rhs.node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_ != rhs.input_) {
|
||||||
|
return input_ < rhs.input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return element_ < rhs.element_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* node() const
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeInputPair input_pair() const
|
||||||
|
{
|
||||||
|
return {node_, input_};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& input() const
|
||||||
|
{
|
||||||
|
return input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int element() const
|
||||||
|
{
|
||||||
|
return element_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_element(int e)
|
||||||
|
{
|
||||||
|
element_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return node_ && !input_.isEmpty() && element_ >= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsConnected() const;
|
||||||
|
|
||||||
|
bool IsKeyframing() const;
|
||||||
|
|
||||||
|
bool IsArray() const;
|
||||||
|
|
||||||
|
NodeOutput GetConnectedOutput() const;
|
||||||
|
|
||||||
|
NodeValue::Type GetDataType() const;
|
||||||
|
|
||||||
|
QStringList GetComboBoxStrings() const;
|
||||||
|
|
||||||
|
QVariant GetProperty(const QString& key) const;
|
||||||
|
|
||||||
|
QVariant GetValueAtTime(const rational& time) const;
|
||||||
|
|
||||||
|
NodeKeyframe *GetKeyframeAtTimeOnTrack(const rational& time, int track) const;
|
||||||
|
|
||||||
|
QVariant GetSplitDefaultValueForTrack(int track) const;
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
*this = NodeInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node* node_;
|
||||||
|
QString input_;
|
||||||
|
int element_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NodeKeyframeTrackReference {
|
||||||
|
public:
|
||||||
|
NodeKeyframeTrackReference()
|
||||||
|
{
|
||||||
|
track_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeKeyframeTrackReference(const NodeInput& input, int track = 0)
|
||||||
|
{
|
||||||
|
input_ = input;
|
||||||
|
track_ = track;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const NodeKeyframeTrackReference& rhs) const
|
||||||
|
{
|
||||||
|
return input_ == rhs.input_ && track_ == rhs.track_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeInput& input() const
|
||||||
|
{
|
||||||
|
return input_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track() const
|
||||||
|
{
|
||||||
|
return track_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return input_.IsValid() && track_ >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
*this = NodeKeyframeTrackReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeInput input_;
|
||||||
|
int track_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
uint qHash(const NodeInputPair& i);
|
||||||
|
uint qHash(const NodeInput& i);
|
||||||
|
uint qHash(const NodeKeyframeTrackReference& i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference)
|
||||||
|
|
||||||
|
#endif // NODEPARAM_H
|
||||||
+15
-22
@@ -29,12 +29,12 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
|
|||||||
NodeValueDatabase database;
|
NodeValueDatabase database;
|
||||||
|
|
||||||
// We need to insert tables into the database for each input
|
// We need to insert tables into the database for each input
|
||||||
foreach (NodeInput* input, node->parameters()) {
|
foreach (const QString& input, node->inputs()) {
|
||||||
if (IsCancelled()) {
|
if (IsCancelled()) {
|
||||||
return NodeValueDatabase();
|
return NodeValueDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
database.Insert(input, ProcessInput(input, range));
|
database.Insert(input, ProcessInput(node, input, range));
|
||||||
}
|
}
|
||||||
|
|
||||||
AddGlobalsToDatabase(database, range);
|
AddGlobalsToDatabase(database, range);
|
||||||
@@ -42,37 +42,35 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
|
|||||||
return database;
|
return database;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& range)
|
NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range)
|
||||||
{
|
{
|
||||||
// If input is connected, retrieve value directly
|
// If input is connected, retrieve value directly
|
||||||
Node* node = input->parent();
|
if (node->IsInputConnected(input)) {
|
||||||
|
|
||||||
if (input->IsConnected()) {
|
|
||||||
|
|
||||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range);
|
TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range);
|
||||||
|
|
||||||
// Value will equal something from the connected node, follow it
|
// Value will equal something from the connected node, follow it
|
||||||
return GenerateTable(input->GetConnectedNode(), adjusted_range);
|
return GenerateTable(node->GetConnectedOutput(input), adjusted_range);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Store node
|
// Store node
|
||||||
QVariant return_val;
|
QVariant return_val;
|
||||||
|
|
||||||
if (input->IsArray()) {
|
if (node->InputIsArray(input)) {
|
||||||
|
|
||||||
// Value is an array, we will return a list of NodeValueTables
|
// Value is an array, we will return a list of NodeValueTables
|
||||||
QVector<NodeValueTable> array_tbl(input->ArraySize());
|
QVector<NodeValueTable> array_tbl(node->InputArraySize(input));
|
||||||
|
|
||||||
for (int i=0; i<array_tbl.size(); i++) {
|
for (int i=0; i<array_tbl.size(); i++) {
|
||||||
NodeValueTable& sub_tbl = array_tbl[i];
|
NodeValueTable& sub_tbl = array_tbl[i];
|
||||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, i, range);
|
TimeRange adjusted_range = node->InputTimeAdjustment(input, i, range);
|
||||||
|
|
||||||
if (input->IsConnected(i)) {
|
if (node->IsInputConnected(input, i)) {
|
||||||
sub_tbl = GenerateTable(input->GetConnectedNode(i), adjusted_range);
|
sub_tbl = GenerateTable(node->GetConnectedOutput(input, i), adjusted_range);
|
||||||
} else {
|
} else {
|
||||||
QVariant input_value = input->GetValueAtTime(adjusted_range.in(), i);
|
QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), i);
|
||||||
sub_tbl.Push(input->GetDataType(), input_value, node);
|
sub_tbl.Push(node->GetInputDataType(input), input_value, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,18 +81,18 @@ NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& ra
|
|||||||
// Not connected or an array, just pull the immediate
|
// Not connected or an array, just pull the immediate
|
||||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range);
|
TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range);
|
||||||
|
|
||||||
return_val = input->GetValueAtTime(adjusted_range.in());
|
return_val = node->GetValueAtTime(input, adjusted_range.in());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable return_table;
|
NodeValueTable return_table;
|
||||||
return_table.Push(input->GetDataType(), return_val, node, true);
|
return_table.Push(node->GetInputDataType(input), return_val, node, true);
|
||||||
return return_table;
|
return return_table;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range)
|
NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output, const TimeRange& range)
|
||||||
{
|
{
|
||||||
const Track* track = dynamic_cast<const Track*>(n);
|
const Track* track = dynamic_cast<const Track*>(n);
|
||||||
if (track) {
|
if (track) {
|
||||||
@@ -108,18 +106,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
|||||||
NodeValueDatabase database = GenerateDatabase(n, range);
|
NodeValueDatabase database = GenerateDatabase(n, range);
|
||||||
|
|
||||||
// By this point, the node should have all the inputs it needs to render correctly
|
// By this point, the node should have all the inputs it needs to render correctly
|
||||||
NodeValueTable table = n->Value(database);
|
NodeValueTable table = n->Value(output, database);
|
||||||
|
|
||||||
PostProcessTable(n, range, table);
|
PostProcessTable(n, range, table);
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable NodeTraverser::GenerateTable(const Node *n, const rational &in, const rational &out)
|
|
||||||
{
|
|
||||||
return GenerateTable(n, TimeRange(in, out));
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeRange &range)
|
NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeRange &range)
|
||||||
{
|
{
|
||||||
// By default, just follow the in point
|
// By default, just follow the in point
|
||||||
|
|||||||
@@ -36,13 +36,16 @@ class NodeTraverser : public CancelableObject
|
|||||||
public:
|
public:
|
||||||
NodeTraverser() = default;
|
NodeTraverser() = default;
|
||||||
|
|
||||||
NodeValueTable GenerateTable(const Node *n, const TimeRange &range);
|
NodeValueTable GenerateTable(const Node *n, const QString &output, const TimeRange &range);
|
||||||
NodeValueTable GenerateTable(const Node *n, const rational &in, const rational& out);
|
NodeValueTable GenerateTable(const NodeOutput& output, const TimeRange &range)
|
||||||
|
{
|
||||||
|
return GenerateTable(output.node(), output.output(), range);
|
||||||
|
}
|
||||||
|
|
||||||
NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range);
|
NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NodeValueTable ProcessInput(NodeInput *input, const TimeRange &range);
|
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);
|
||||||
|
|
||||||
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range);
|
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range);
|
||||||
|
|
||||||
|
|||||||
+1
-21
@@ -20,13 +20,13 @@
|
|||||||
|
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QMatrix4x4>
|
#include <QMatrix4x4>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QVector3D>
|
#include <QVector3D>
|
||||||
#include <QVector4D>
|
#include <QVector4D>
|
||||||
|
|
||||||
#include "common/tohex.h"
|
#include "common/tohex.h"
|
||||||
#include "node/input.h"
|
|
||||||
#include "render/color.h"
|
#include "render/color.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
@@ -311,26 +311,6 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
|||||||
return QCoreApplication::translate("NodeValue", "Unknown");
|
return QCoreApplication::translate("NodeValue", "Unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable &NodeValueDatabase::operator[](const NodeInput *input)
|
|
||||||
{
|
|
||||||
return tables_[input->id()];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeValueDatabase::Insert(const NodeInput *key, const NodeValueTable &value)
|
|
||||||
{
|
|
||||||
tables_.insert(key->id(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValueTable NodeValueDatabase::Merge() const
|
|
||||||
{
|
|
||||||
QHash<QString, NodeValueTable> copy = tables_;
|
|
||||||
|
|
||||||
// Kinda hacky, but we don't need this table to slipstream
|
|
||||||
copy.remove(QStringLiteral("global"));
|
|
||||||
|
|
||||||
return NodeValueTable::Merge(copy.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
|
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
|
||||||
{
|
{
|
||||||
int value_index = GetInternal(type, tag);
|
int value_index = GetInternal(type, tag);
|
||||||
|
|||||||
+5
-51
@@ -18,16 +18,16 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#ifndef VALUE_H
|
#ifndef NODEVALUE_H
|
||||||
#define VALUE_H
|
#define NODEVALUE_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class Node;
|
class Node;
|
||||||
class NodeInput;
|
|
||||||
|
|
||||||
class NodeValue
|
class NodeValue
|
||||||
{
|
{
|
||||||
@@ -374,59 +374,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
int GetInternal(const QVector<NodeValue::Type> &type, const QString& tag) const;
|
int GetInternal(const QVector<NodeValue::Type> &type, const QString& tag) const;
|
||||||
|
|
||||||
QList<NodeValue> values_;
|
QVector<NodeValue> values_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeValueDatabase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NodeValueDatabase() = default;
|
|
||||||
|
|
||||||
NodeValueTable& operator[](const QString& input_id)
|
|
||||||
{
|
|
||||||
return tables_[input_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValueTable& operator[](const NodeInput* input);
|
|
||||||
|
|
||||||
void Insert(const QString& key, const NodeValueTable &value)
|
|
||||||
{
|
|
||||||
tables_.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Insert(const NodeInput* key, const NodeValueTable& value);
|
|
||||||
|
|
||||||
NodeValueTable Merge() const;
|
|
||||||
|
|
||||||
using const_iterator = QHash<QString, NodeValueTable>::const_iterator;
|
|
||||||
|
|
||||||
inline QHash<QString, NodeValueTable>::const_iterator begin() const
|
|
||||||
{
|
|
||||||
return tables_.cbegin();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QHash<QString, NodeValueTable>::const_iterator end() const
|
|
||||||
{
|
|
||||||
return tables_.cend();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool contains(const QString& s) const
|
|
||||||
{
|
|
||||||
return tables_.contains(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, NodeValueTable> tables_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
using NodeValueMap = QHash<QString, NodeValue>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(olive::NodeValue)
|
Q_DECLARE_METATYPE(olive::NodeValue)
|
||||||
Q_DECLARE_METATYPE(olive::NodeValueTable)
|
Q_DECLARE_METATYPE(olive::NodeValueTable)
|
||||||
Q_DECLARE_METATYPE(olive::NodeValueDatabase)
|
|
||||||
|
|
||||||
#endif // VALUE_H
|
#endif // NODEVALUE_H
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2020 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "valuedatabase.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
NodeValueTable NodeValueDatabase::Merge() const
|
||||||
|
{
|
||||||
|
QHash<QString, NodeValueTable> copy = tables_;
|
||||||
|
|
||||||
|
// Kinda hacky, but we don't need this table to slipstream
|
||||||
|
copy.remove(QStringLiteral("global"));
|
||||||
|
|
||||||
|
return NodeValueTable::Merge(copy.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2020 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef NODEVALUEDATABASE_H
|
||||||
|
#define NODEVALUEDATABASE_H
|
||||||
|
|
||||||
|
#include "param.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class NodeValueDatabase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeValueDatabase() = default;
|
||||||
|
|
||||||
|
NodeValueTable& operator[](const QString& input_id)
|
||||||
|
{
|
||||||
|
return tables_[input_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Insert(const QString& key, const NodeValueTable &value)
|
||||||
|
{
|
||||||
|
tables_.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeValueTable Merge() const;
|
||||||
|
|
||||||
|
using const_iterator = QHash<QString, NodeValueTable>::const_iterator;
|
||||||
|
|
||||||
|
inline QHash<QString, NodeValueTable>::const_iterator begin() const
|
||||||
|
{
|
||||||
|
return tables_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QHash<QString, NodeValueTable>::const_iterator end() const
|
||||||
|
{
|
||||||
|
return tables_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool contains(const QString& s) const
|
||||||
|
{
|
||||||
|
return tables_.contains(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, NodeValueTable> tables_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using NodeValueMap = QHash<QString, NodeValue>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(olive::NodeValueDatabase)
|
||||||
|
|
||||||
|
#endif // NODEVALUEDATABASE_H
|
||||||
@@ -93,12 +93,14 @@ public slots:
|
|||||||
|
|
||||||
void SelectBlocks(const QVector<Block*>& nodes)
|
void SelectBlocks(const QVector<Block*>& nodes)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(nodes)
|
||||||
qDebug() << "Stub";
|
qDebug() << "Stub";
|
||||||
//node_view_->SelectBlocks(nodes);
|
//node_view_->SelectBlocks(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeselectBlocks(const QVector<Block*>& nodes)
|
void DeselectBlocks(const QVector<Block*>& nodes)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(nodes)
|
||||||
qDebug() << "Stub";
|
qDebug() << "Stub";
|
||||||
//node_view_->DeselectBlocks(nodes);
|
//node_view_->DeselectBlocks(nodes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ Footage::~Footage()
|
|||||||
|
|
||||||
void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled)
|
void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(version)
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (cancelled && *cancelled) {
|
if (cancelled && *cancelled) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ Stream *Stream::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const
|
|||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("ptr")) {
|
if (reader->name() == QStringLiteral("ptr")) {
|
||||||
xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream);
|
//xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream);
|
||||||
} else if (reader->name() == QStringLiteral("index")) {
|
} else if (reader->name() == QStringLiteral("index")) {
|
||||||
stream->set_index(reader->readElementText().toInt());
|
stream->set_index(reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||||
|
|||||||
@@ -207,17 +207,8 @@ void Sequence::Save(QXmlStreamWriter *writer) const
|
|||||||
void Sequence::add_default_nodes()
|
void Sequence::add_default_nodes()
|
||||||
{
|
{
|
||||||
// Create tracks and connect them to the viewer
|
// Create tracks and connect them to the viewer
|
||||||
Track* video_track = new Track();
|
TimelineAddTrackCommand(viewer_output_->track_list(Track::kVideo)).redo();
|
||||||
video_track->setParent(this);
|
TimelineAddTrackCommand(viewer_output_->track_list(Track::kAudio)).redo();
|
||||||
viewer_output_->track_input(Track::kVideo)->ArrayAppend();
|
|
||||||
Node::ConnectEdge(video_track, viewer_output_->track_input(Track::kVideo), 0);
|
|
||||||
Node::ConnectEdge(video_track, viewer_output_->texture_input());
|
|
||||||
|
|
||||||
Track* audio_track = new Track();
|
|
||||||
audio_track->setParent(this);
|
|
||||||
viewer_output_->track_input(Track::kAudio)->ArrayAppend();
|
|
||||||
Node::ConnectEdge(audio_track, viewer_output_->track_input(Track::kAudio), 0);
|
|
||||||
Node::ConnectEdge(audio_track, viewer_output_->samples_input());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::Type Sequence::type() const
|
Item::Type Sequence::type() const
|
||||||
|
|||||||
@@ -86,12 +86,6 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) {
|
|
||||||
if (con.footage) {
|
|
||||||
con.input->SetStandardValue(Node::PtrToValue(xml_node_data.footage_ptrs.value(con.footage)), con.element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Project::Save(QXmlStreamWriter *writer) const
|
void Project::Save(QXmlStreamWriter *writer) const
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
|
render/job/acceleratedjob.cpp
|
||||||
render/job/acceleratedjob.h
|
render/job/acceleratedjob.h
|
||||||
render/job/generatejob.h
|
render/job/generatejob.h
|
||||||
render/job/samplejob.h
|
render/job/samplejob.h
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2020 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "acceleratedjob.h"
|
||||||
|
|
||||||
|
#include "node/node.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
void AcceleratedJob::InsertValue(const Node* node, const QString& input, NodeValueDatabase& value)
|
||||||
|
{
|
||||||
|
InsertValue(input, value[input].TakeWithMeta(node->GetInputDataType(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
#ifndef ACCELERATEDJOB_H
|
#ifndef ACCELERATEDJOB_H
|
||||||
#define ACCELERATEDJOB_H
|
#define ACCELERATEDJOB_H
|
||||||
|
|
||||||
#include "node/input.h"
|
#include "node/param.h"
|
||||||
#include "node/value.h"
|
#include "node/valuedatabase.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
@@ -30,31 +30,18 @@ class AcceleratedJob {
|
|||||||
public:
|
public:
|
||||||
AcceleratedJob() = default;
|
AcceleratedJob() = default;
|
||||||
|
|
||||||
NodeValue GetValue(NodeInput* input) const
|
|
||||||
{
|
|
||||||
return value_map_.value(input->id());
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeValue GetValue(const QString& input) const
|
NodeValue GetValue(const QString& input) const
|
||||||
{
|
{
|
||||||
return value_map_.value(input);
|
return value_map_.value(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertValue(NodeInput* input, NodeValueDatabase& value)
|
void InsertValue(const Node* node, const QString& input, NodeValueDatabase& value);
|
||||||
{
|
|
||||||
InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void InsertValue(const QString& input, const NodeValue& value)
|
void InsertValue(const QString& input, const NodeValue& value)
|
||||||
{
|
{
|
||||||
value_map_.insert(input, value);
|
value_map_.insert(input, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertValue(NodeInput* input, const NodeValue& value)
|
|
||||||
{
|
|
||||||
value_map_.insert(input->id(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NodeValueMap &GetValues() const
|
const NodeValueMap &GetValues() const
|
||||||
{
|
{
|
||||||
return value_map_;
|
return value_map_;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public:
|
|||||||
samples_ = value.data().value<SampleBufferPtr>();
|
samples_ = value.data().value<SampleBufferPtr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleJob(NodeInput* from, NodeValueDatabase& db)
|
SampleJob(const QString& from, NodeValueDatabase& db)
|
||||||
{
|
{
|
||||||
samples_ = db[from].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
samples_ = db[from].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ public:
|
|||||||
shader_id_ = id;
|
shader_id_ = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIterations(int iterations, NodeInput* iterative_input)
|
void SetIterations(int iterations, const NodeInput& iterative_input)
|
||||||
{
|
{
|
||||||
SetIterations(iterations, iterative_input->id());
|
SetIterations(iterations, iterative_input.input());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIterations(int iterations, const QString& iterative_input)
|
void SetIterations(int iterations, const QString& iterative_input)
|
||||||
@@ -72,9 +72,9 @@ public:
|
|||||||
return interpolation_.value(id, Texture::kDefaultInterpolation);
|
return interpolation_.value(id, Texture::kDefaultInterpolation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetInterpolation(NodeInput* input, Texture::Interpolation interp)
|
void SetInterpolation(const NodeInput& input, Texture::Interpolation interp)
|
||||||
{
|
{
|
||||||
interpolation_.insert(input->id(), interp);
|
interpolation_.insert(input.input(), interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetInterpolation(const QString& id, Texture::Interpolation interp)
|
void SetInterpolation(const QString& id, Texture::Interpolation interp)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cac
|
|||||||
|
|
||||||
foreach (const rational& time, times) {
|
foreach (const rational& time, times) {
|
||||||
// See if hash already exists in disk cache
|
// See if hash already exists in disk cache
|
||||||
QByteArray hash = RenderManager::Hash(viewer->texture_input()->GetConnectedNode(), viewer->video_params(), time);
|
QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), viewer->video_params(), time);
|
||||||
|
|
||||||
// Check memory list since disk checking is slow
|
// Check memory list since disk checking is slow
|
||||||
bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end());
|
bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end());
|
||||||
@@ -256,13 +256,13 @@ void PreviewAutoCacher::ProcessUpdateQueue()
|
|||||||
RemoveNode(job.node);
|
RemoveNode(job.node);
|
||||||
break;
|
break;
|
||||||
case QueuedJob::kEdgeAdded:
|
case QueuedJob::kEdgeAdded:
|
||||||
AddEdge(job.node, job.input, job.element);
|
AddEdge(job.output, job.input);
|
||||||
break;
|
break;
|
||||||
case QueuedJob::kEdgeRemoved:
|
case QueuedJob::kEdgeRemoved:
|
||||||
RemoveEdge(job.node, job.input, job.element);
|
RemoveEdge(job.output, job.input);
|
||||||
break;
|
break;
|
||||||
case QueuedJob::kValueChanged:
|
case QueuedJob::kValueChanged:
|
||||||
CopyValue(job.input, job.element);
|
CopyValue(job.input);
|
||||||
break;
|
break;
|
||||||
case QueuedJob::kVideoParamsChanged:
|
case QueuedJob::kVideoParamsChanged:
|
||||||
UpdateVideoParams();
|
UpdateVideoParams();
|
||||||
@@ -305,26 +305,26 @@ void PreviewAutoCacher::RemoveNode(Node *node)
|
|||||||
delete copy;
|
delete copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::AddEdge(Node *output, NodeInput *input, int element)
|
void PreviewAutoCacher::AddEdge(const NodeOutput &output, const NodeInput &input)
|
||||||
{
|
{
|
||||||
Node* our_output = copy_map_.value(output);
|
Node* our_output = copy_map_.value(output.node());
|
||||||
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
|
Node* our_input = copy_map_.value(input.node());
|
||||||
|
|
||||||
Node::ConnectEdge(our_output, our_input, element);
|
Node::ConnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::RemoveEdge(Node *output, NodeInput *input, int element)
|
void PreviewAutoCacher::RemoveEdge(const NodeOutput &output, const NodeInput &input)
|
||||||
{
|
{
|
||||||
Node* our_output = copy_map_.value(output);
|
Node* our_output = copy_map_.value(output.node());
|
||||||
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
|
Node* our_input = copy_map_.value(input.node());
|
||||||
|
|
||||||
Node::DisconnectEdge(our_output, our_input, element);
|
Node::DisconnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::CopyValue(NodeInput *input, int element)
|
void PreviewAutoCacher::CopyValue(const NodeInput &input)
|
||||||
{
|
{
|
||||||
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
|
Node* our_input = copy_map_.value(input.node());
|
||||||
NodeInput::CopyValuesOfElement(input, our_input, element);
|
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::UpdateVideoParams()
|
void PreviewAutoCacher::UpdateVideoParams()
|
||||||
@@ -423,27 +423,27 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait)
|
|||||||
|
|
||||||
void PreviewAutoCacher::NodeAdded(Node *node)
|
void PreviewAutoCacher::NodeAdded(Node *node)
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1});
|
graph_update_queue_.append({QueuedJob::kNodeAdded, node, NodeInput(), NodeOutput()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::NodeRemoved(Node *node)
|
void PreviewAutoCacher::NodeRemoved(Node *node)
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kNodeRemoved, node, nullptr, -1});
|
graph_update_queue_.append({QueuedJob::kNodeRemoved, node, NodeInput(), NodeOutput()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::EdgeAdded(Node *output, NodeInput *input, int element)
|
void PreviewAutoCacher::EdgeAdded(const NodeOutput &output, const NodeInput &input)
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kEdgeAdded, output, input, element});
|
graph_update_queue_.append({QueuedJob::kEdgeAdded, nullptr, input, output});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::EdgeRemoved(Node *output, NodeInput *input, int element)
|
void PreviewAutoCacher::EdgeRemoved(const NodeOutput &output, const NodeInput &input)
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, input, element});
|
graph_update_queue_.append({QueuedJob::kEdgeRemoved, nullptr, input, output});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::ValueChanged(NodeInput *input, int element)
|
void PreviewAutoCacher::ValueChanged(const NodeInput &input)
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, element});
|
graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, NodeOutput()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::VideoParamsChanged()
|
void PreviewAutoCacher::VideoParamsChanged()
|
||||||
@@ -451,14 +451,14 @@ void PreviewAutoCacher::VideoParamsChanged()
|
|||||||
// In case the user is pressing the mouse at this exact moment
|
// In case the user is pressing the mouse at this exact moment
|
||||||
IgnoreNextMouseButton();
|
IgnoreNextMouseButton();
|
||||||
|
|
||||||
graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, nullptr, -1});
|
graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, NodeInput(), NodeOutput()});
|
||||||
ClearVideoQueue();
|
ClearVideoQueue();
|
||||||
TryRender();
|
TryRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewAutoCacher::AudioParamsChanged()
|
void PreviewAutoCacher::AudioParamsChanged()
|
||||||
{
|
{
|
||||||
graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, nullptr, -1});
|
graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, NodeInput(), NodeOutput()});
|
||||||
ClearAudioQueue();
|
ClearAudioQueue();
|
||||||
TryRender();
|
TryRender();
|
||||||
}
|
}
|
||||||
@@ -677,10 +677,8 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
|
|||||||
|
|
||||||
// Add all connections
|
// Add all connections
|
||||||
foreach (Node* node, graph->nodes()) {
|
foreach (Node* node, graph->nodes()) {
|
||||||
foreach (NodeInput* input, node->inputs()) {
|
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||||
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
|
AddEdge(it->second, it->first);
|
||||||
AddEdge(it->second, input, it->first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ private:
|
|||||||
|
|
||||||
void AddNode(Node* node);
|
void AddNode(Node* node);
|
||||||
void RemoveNode(Node* node);
|
void RemoveNode(Node* node);
|
||||||
void AddEdge(Node* output, NodeInput* input, int element);
|
void AddEdge(const NodeOutput& output, const NodeInput& input);
|
||||||
void RemoveEdge(Node* output, NodeInput* input, int element);
|
void RemoveEdge(const NodeOutput& output, const NodeInput& input);
|
||||||
void CopyValue(NodeInput* input, int element);
|
void CopyValue(const NodeInput& input);
|
||||||
void UpdateVideoParams();
|
void UpdateVideoParams();
|
||||||
void UpdateAudioParams();
|
void UpdateAudioParams();
|
||||||
|
|
||||||
@@ -127,8 +127,8 @@ private:
|
|||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
Node* node;
|
Node* node;
|
||||||
NodeInput* input;
|
NodeInput input;
|
||||||
int element;
|
NodeOutput output;
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewerOutput* viewer_node_;
|
ViewerOutput* viewer_node_;
|
||||||
@@ -201,11 +201,11 @@ private slots:
|
|||||||
|
|
||||||
void NodeRemoved(Node* node);
|
void NodeRemoved(Node* node);
|
||||||
|
|
||||||
void EdgeAdded(Node* output, NodeInput* input, int element);
|
void EdgeAdded(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void EdgeRemoved(Node* output, NodeInput* input, int element);
|
void EdgeRemoved(const NodeOutput& output, const NodeInput& input);
|
||||||
|
|
||||||
void ValueChanged(NodeInput* input, int element);
|
void ValueChanged(const NodeInput& input);
|
||||||
|
|
||||||
void VideoParamsChanged();
|
void VideoParamsChanged();
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ void RenderProcessor::Run()
|
|||||||
const VideoParams& video_params = ticket_->property("vparam").value<VideoParams>();
|
const VideoParams& video_params = ticket_->property("vparam").value<VideoParams>();
|
||||||
rational time = ticket_->property("time").value<rational>();
|
rational time = ticket_->property("time").value<rational>();
|
||||||
|
|
||||||
NodeValueTable table = ProcessInput(viewer->texture_input(),
|
NodeValueTable table = ProcessInput(viewer, ViewerOutput::kTextureInput,
|
||||||
TimeRange(time, time + video_params.time_base()));
|
TimeRange(time, time + video_params.time_base()));
|
||||||
|
|
||||||
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
|
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
|
||||||
@@ -133,7 +133,7 @@ void RenderProcessor::Run()
|
|||||||
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
|
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
|
||||||
TimeRange time = ticket_->property("time").value<TimeRange>();
|
TimeRange time = ticket_->property("time").value<TimeRange>();
|
||||||
|
|
||||||
NodeValueTable table = ProcessInput(viewer->samples_input(), time);
|
NodeValueTable table = ProcessInput(viewer, ViewerOutput::kSamplesInput, time);
|
||||||
|
|
||||||
ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled());
|
ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled());
|
||||||
break;
|
break;
|
||||||
@@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Doesn't handle reversing
|
// FIXME: Doesn't handle reversing
|
||||||
if (b->speed_input()->IsKeyframing() || b->speed_input()->IsConnected()) {
|
if (b->IsInputKeyframing(Block::kSpeedInput) || b->IsInputConnected(Block::kSpeedInput)) {
|
||||||
// FIXME: We'll need to calculate the speed hoo boy
|
// FIXME: We'll need to calculate the speed hoo boy
|
||||||
} else {
|
} else {
|
||||||
double speed_value = b->speed_input()->GetStandardValue().toDouble();
|
double speed_value = b->GetStandardValue(Block::kSpeedInput).toDouble();
|
||||||
|
|
||||||
if (qIsNull(speed_value)) {
|
if (qIsNull(speed_value)) {
|
||||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||||
@@ -450,16 +450,8 @@ QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &rang
|
|||||||
rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second);
|
rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second);
|
||||||
|
|
||||||
// Update all non-sample and non-footage inputs
|
// Update all non-sample and non-footage inputs
|
||||||
NodeValueMap::const_iterator j;
|
for (auto j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) {
|
||||||
for (j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) {
|
NodeValueTable value = ProcessInput(node, j.key(), TimeRange(this_sample_time, this_sample_time));
|
||||||
NodeValueTable value;
|
|
||||||
NodeInput* corresponding_input = node->GetInputWithID(j.key());
|
|
||||||
|
|
||||||
if (corresponding_input) {
|
|
||||||
value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time));
|
|
||||||
} else {
|
|
||||||
value.Push(j.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
value_db.Insert(j.key(), value);
|
value_db.Insert(j.key(), value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ PreCacheTask::PreCacheTask(VideoStream *footage, Sequence* sequence) :
|
|||||||
video_node_ = new MediaInput();
|
video_node_ = new MediaInput();
|
||||||
video_node_->SetStream(footage);
|
video_node_->SetStream(footage);
|
||||||
|
|
||||||
Node::ConnectEdge(video_node_, viewer()->texture_input());
|
Node::ConnectEdge(video_node_, NodeInput(viewer(), ViewerOutput::kTextureInput));
|
||||||
|
|
||||||
SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(),
|
SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(),
|
||||||
QString::number(footage->index())));
|
QString::number(footage->index())));
|
||||||
|
|||||||
@@ -63,59 +63,51 @@ void CurveView::Clear()
|
|||||||
lines_.clear();
|
lines_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::ConnectInput(NodeInput *input, int element, int track)
|
void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
NodeInput::KeyframeTrackReference ref = {input, element, track};
|
|
||||||
|
|
||||||
if (connected_inputs_.contains(ref)) {
|
if (connected_inputs_.contains(ref)) {
|
||||||
// Input wasn't connected, do nothing
|
// Input wasn't connected, do nothing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add keyframes from track
|
// Add keyframes from track
|
||||||
AddKeyframesOfTrack(input, element, track);
|
AddKeyframesOfTrack(ref);
|
||||||
|
|
||||||
// Append to the list
|
// Append to the list
|
||||||
connected_inputs_.append(ref);
|
connected_inputs_.append(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::DisconnectInput(NodeInput *input, int element, int track)
|
void CurveView::DisconnectInput(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
NodeInput::KeyframeTrackReference ref = {input, element, track};
|
|
||||||
|
|
||||||
if (!connected_inputs_.contains(ref)) {
|
if (!connected_inputs_.contains(ref)) {
|
||||||
// Input wasn't connected, do nothing
|
// Input wasn't connected, do nothing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove keyframes belonging to this element and track
|
// Remove keyframes belonging to this element and track
|
||||||
RemoveKeyframesOfTrack(input, element, track);
|
RemoveKeyframesOfTrack(ref);
|
||||||
|
|
||||||
// Remove from the list
|
// Remove from the list
|
||||||
connected_inputs_.removeOne(ref);
|
connected_inputs_.removeOne(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::SelectKeyframesOfInput(NodeInput *input, int element, int track)
|
void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
DeselectAll();
|
DeselectAll();
|
||||||
|
|
||||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||||
if (it.key()->parent() == input
|
if (it.key()->key_track_ref() == ref) {
|
||||||
&& it.key()->element() == element
|
|
||||||
&& it.key()->track() == track) {
|
|
||||||
it.value()->setSelected(true);
|
it.value()->setSelected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::ZoomToFitInput(NodeInput *input, int element, int track)
|
void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
QList<NodeKeyframe*> keys;
|
QList<NodeKeyframe*> keys;
|
||||||
|
|
||||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||||
if (it.key()->parent() == input
|
if (it.key()->key_track_ref() == ref) {
|
||||||
&& it.key()->element() == element
|
|
||||||
&& it.key()->track() == track) {
|
|
||||||
keys.append(it.key());
|
keys.append(it.key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,14 +115,14 @@ void CurveView::ZoomToFitInput(NodeInput *input, int element, int track)
|
|||||||
ZoomToFitInternal(keys);
|
ZoomToFitInternal(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference &ref, const QColor &color)
|
void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color)
|
||||||
{
|
{
|
||||||
// Insert color into hashmap
|
// Insert color into hashmap
|
||||||
keyframe_colors_.insert(ref, color);
|
keyframe_colors_.insert(ref, color);
|
||||||
|
|
||||||
// Update all keyframes
|
// Update all keyframes
|
||||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||||
if (it.key()->parent() == ref.input && it.key()->element() == ref.element && it.key()->track() == ref.track) {
|
if (it.key()->key_track_ref() == ref) {
|
||||||
it.value()->SetOverrideBrush(color);
|
it.value()->SetOverrideBrush(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,13 +179,14 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
|||||||
painter->drawLines(lines);
|
painter->drawLines(lines);
|
||||||
|
|
||||||
// Draw keyframe lines
|
// Draw keyframe lines
|
||||||
foreach (const NodeInput::KeyframeTrackReference& ref, connected_inputs_) {
|
foreach (const NodeKeyframeTrackReference& ref, connected_inputs_) {
|
||||||
NodeInput* input = ref.input;
|
Node* node = ref.input().node();
|
||||||
|
const QString& input = ref.input().input();
|
||||||
|
|
||||||
if (input->IsKeyframing(ref.element)) {
|
if (node->IsInputKeyframing(input, ref.input().element())) {
|
||||||
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(ref.element);
|
const QVector<NodeKeyframeTrack>& tracks = node->GetKeyframeTracks(ref.input());
|
||||||
|
|
||||||
const NodeKeyframeTrack& track = tracks.at(ref.track);
|
const NodeKeyframeTrack& track = tracks.at(ref.track());
|
||||||
|
|
||||||
if (!track.isEmpty()) {
|
if (!track.isEmpty()) {
|
||||||
painter->setPen(QPen(keyframe_colors_.value(ref),
|
painter->setPen(QPen(keyframe_colors_.value(ref),
|
||||||
@@ -348,7 +341,7 @@ void CurveView::ZoomToFitInternal(const QList<NodeKeyframe *> &keys)
|
|||||||
double max_val = DBL_MIN;
|
double max_val = DBL_MIN;
|
||||||
|
|
||||||
foreach (NodeKeyframe* key, keys) {
|
foreach (NodeKeyframe* key, keys) {
|
||||||
rational transformed_time = GetAdjustedTime(key->parent()->parent(),
|
rational transformed_time = GetAdjustedTime(key->parent(),
|
||||||
GetTimeTarget(),
|
GetTimeTarget(),
|
||||||
key->time(),
|
key->time(),
|
||||||
false);
|
false);
|
||||||
@@ -480,7 +473,7 @@ KeyframeViewItem* CurveView::AddKeyframe(NodeKeyframe* key)
|
|||||||
{
|
{
|
||||||
KeyframeViewItem* item = super::AddKeyframe(key);
|
KeyframeViewItem* item = super::AddKeyframe(key);
|
||||||
SetItemYFromKeyframeValue(key, item);
|
SetItemYFromKeyframeValue(key, item);
|
||||||
item->SetOverrideBrush(keyframe_colors_.value({key->parent(), key->element(), key->track()}));
|
item->SetOverrideBrush(keyframe_colors_.value(key->key_track_ref()));
|
||||||
|
|
||||||
connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||||
connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
|
connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ public:
|
|||||||
|
|
||||||
virtual void Clear() override;
|
virtual void Clear() override;
|
||||||
|
|
||||||
void ConnectInput(NodeInput* input, int element, int track);
|
void ConnectInput(const NodeKeyframeTrackReference &ref);
|
||||||
|
|
||||||
void DisconnectInput(NodeInput* input, int element, int track);
|
void DisconnectInput(const NodeKeyframeTrackReference &ref);
|
||||||
|
|
||||||
void SelectKeyframesOfInput(NodeInput* input, int element, int track);
|
void SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref);
|
||||||
|
|
||||||
void ZoomToFitInput(NodeInput* input, int element, int track);
|
void ZoomToFitInput(const NodeKeyframeTrackReference &ref);
|
||||||
|
|
||||||
void SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference& ref, const QColor& color);
|
void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
||||||
@@ -84,7 +84,7 @@ private:
|
|||||||
|
|
||||||
void CreateBezierControlPoints(KeyframeViewItem *item);
|
void CreateBezierControlPoints(KeyframeViewItem *item);
|
||||||
|
|
||||||
QHash<NodeInput::KeyframeTrackReference, QColor> keyframe_colors_;
|
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
||||||
|
|
||||||
int text_padding_;
|
int text_padding_;
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ private:
|
|||||||
|
|
||||||
QVector<BezierControlPointItem*> bezier_control_points_;
|
QVector<BezierControlPointItem*> bezier_control_points_;
|
||||||
|
|
||||||
QVector<NodeInput::KeyframeTrackReference> connected_inputs_;
|
QVector<NodeKeyframeTrackReference> connected_inputs_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void KeyframeValueChanged();
|
void KeyframeValueChanged();
|
||||||
|
|||||||
@@ -219,24 +219,37 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
|||||||
|
|
||||||
void CurveWidget::ConnectNode(Node *node, bool connect)
|
void CurveWidget::ConnectNode(Node *node, bool connect)
|
||||||
{
|
{
|
||||||
foreach (NodeInput* input, node->parameters()) {
|
foreach (const QString& input, node->inputs()) {
|
||||||
ConnectInput(input, connect);
|
ConnectInput(node, input, connect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect add/remove signals
|
||||||
|
if (connect) {
|
||||||
|
QObject::connect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe);
|
||||||
|
QObject::connect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe);
|
||||||
|
} else {
|
||||||
|
QObject::disconnect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe);
|
||||||
|
QObject::disconnect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::ConnectInput(NodeInput *input, bool connect)
|
void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect)
|
||||||
{
|
{
|
||||||
int track_count = NodeValue::get_number_of_keyframe_tracks(input->GetDataType());
|
if (!node->IsInputKeyframable(input)) {
|
||||||
|
qWarning() << "Tried to connect input that isn't keyframable";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track_count = NodeValue::get_number_of_keyframe_tracks(node->GetInputDataType(input));
|
||||||
bool multiple_tracks = track_count > 1;
|
bool multiple_tracks = track_count > 1;
|
||||||
|
|
||||||
for (int i=-1; i<input->ArraySize(); i++) {
|
int arr_sz = node->InputArraySize(input);
|
||||||
if (!input->IsKeyframable()) {
|
for (int i=-1; i<arr_sz; i++) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a random color for this input
|
// Generate a random color for this input
|
||||||
for (int j=0; j<input->GetKeyframeTracks(i).size(); j++) {
|
const QVector<NodeKeyframeTrack>& tracks = node->GetKeyframeTracks(input, i);
|
||||||
NodeInput::KeyframeTrackReference ref = {input, i, j};
|
|
||||||
|
for (int j=0; j<tracks.size(); j++) {
|
||||||
|
NodeKeyframeTrackReference ref(NodeInput(node, input, i), j);
|
||||||
|
|
||||||
if (!keyframe_colors_.contains(ref)) {
|
if (!keyframe_colors_.contains(ref)) {
|
||||||
QColor c = QColor::fromHsv(std::rand()%360, std::rand()%255, 255);
|
QColor c = QColor::fromHsv(std::rand()%360, std::rand()%255, 255);
|
||||||
@@ -247,35 +260,28 @@ void CurveWidget::ConnectInput(NodeInput *input, bool connect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree_view_->IsInputEnabled(input, i, multiple_tracks ? -1 : 0)) {
|
if (tree_view_->IsInputEnabled(NodeKeyframeTrackReference(NodeInput(node, input, i), multiple_tracks ? -1 : 0))) {
|
||||||
if (multiple_tracks) {
|
if (multiple_tracks) {
|
||||||
for (int j=0; j<track_count; j++) {
|
for (int j=0; j<track_count; j++) {
|
||||||
if (tree_view_->IsInputEnabled(input, i, j)) {
|
NodeKeyframeTrackReference ref(NodeInput(node, input, i), j);
|
||||||
|
if (tree_view_->IsInputEnabled(ref)) {
|
||||||
if (connect) {
|
if (connect) {
|
||||||
view_->ConnectInput(input, i, j);
|
view_->ConnectInput(ref);
|
||||||
} else {
|
} else {
|
||||||
view_->DisconnectInput(input, i, j);
|
view_->DisconnectInput(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0);
|
||||||
if (connect) {
|
if (connect) {
|
||||||
view_->ConnectInput(input, i, 0);
|
view_->ConnectInput(ref);
|
||||||
} else {
|
} else {
|
||||||
view_->DisconnectInput(input, i, 0);
|
view_->DisconnectInput(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect add/remove signals
|
|
||||||
if (connect) {
|
|
||||||
QObject::connect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe);
|
|
||||||
QObject::connect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe);
|
|
||||||
} else {
|
|
||||||
QObject::disconnect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe);
|
|
||||||
QObject::disconnect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::SelectionChanged()
|
void CurveWidget::SelectionChanged()
|
||||||
@@ -357,12 +363,12 @@ void CurveWidget::NodeEnabledChanged(Node* n, bool e)
|
|||||||
ConnectNode(n, e);
|
ConnectNode(n, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::InputEnabledChanged(NodeInput *i, int element, int track, bool e)
|
void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, bool e)
|
||||||
{
|
{
|
||||||
if (e) {
|
if (e) {
|
||||||
view_->ConnectInput(i, element, track);
|
view_->ConnectInput(ref);
|
||||||
} else {
|
} else {
|
||||||
view_->DisconnectInput(i, element, track);
|
view_->DisconnectInput(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,18 +382,18 @@ void CurveWidget::RemoveKeyframe(NodeKeyframe *key)
|
|||||||
view_->RemoveKeyframe(key);
|
view_->RemoveKeyframe(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::InputSelectionChanged(NodeInput *input, int element, int track)
|
void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
key_control_->SetInput(input, element);
|
key_control_->SetInput(ref.input());
|
||||||
|
|
||||||
if (input) {
|
if (ref.IsValid()) {
|
||||||
view_->SelectKeyframesOfInput(input, element, track);
|
view_->SelectKeyframesOfInput(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::InputDoubleClicked(NodeInput *input, int element, int track)
|
void CurveWidget::InputDoubleClicked(const NodeKeyframeTrackReference& ref)
|
||||||
{
|
{
|
||||||
view_->ZoomToFitInput(input, element, track);
|
view_->ZoomToFitInput(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::KeyframeViewDragged(int x, int y)
|
void CurveWidget::KeyframeViewDragged(int x, int y)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "curveview.h"
|
#include "curveview.h"
|
||||||
#include "node/input.h"
|
|
||||||
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
||||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||||
#include "widget/nodetreeview/nodetreeview.h"
|
#include "widget/nodetreeview/nodetreeview.h"
|
||||||
@@ -81,9 +80,9 @@ private:
|
|||||||
|
|
||||||
void ConnectNode(Node* node, bool connect);
|
void ConnectNode(Node* node, bool connect);
|
||||||
|
|
||||||
void ConnectInput(NodeInput* input, bool connect);
|
void ConnectInput(Node* node, const QString& input, bool connect);
|
||||||
|
|
||||||
QHash<NodeInput::KeyframeTrackReference, QColor> keyframe_colors_;
|
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
||||||
|
|
||||||
NodeTreeView* tree_view_;
|
NodeTreeView* tree_view_;
|
||||||
|
|
||||||
@@ -108,15 +107,15 @@ private slots:
|
|||||||
|
|
||||||
void NodeEnabledChanged(Node* n, bool e);
|
void NodeEnabledChanged(Node* n, bool e);
|
||||||
|
|
||||||
void InputEnabledChanged(NodeInput* i, int element, int track, bool e);
|
void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e);
|
||||||
|
|
||||||
void AddKeyframe(NodeKeyframe* key);
|
void AddKeyframe(NodeKeyframe* key);
|
||||||
|
|
||||||
void RemoveKeyframe(NodeKeyframe* key);
|
void RemoveKeyframe(NodeKeyframe* key);
|
||||||
|
|
||||||
void InputSelectionChanged(NodeInput* input, int element, int track);
|
void InputSelectionChanged(const NodeKeyframeTrackReference& ref);
|
||||||
|
|
||||||
void InputDoubleClicked(NodeInput* input, int element, int track);
|
void InputDoubleClicked(const NodeKeyframeTrackReference& ref);
|
||||||
|
|
||||||
void KeyframeViewDragged(int x, int y);
|
void KeyframeViewDragged(int x, int y);
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ KeyframeView::KeyframeView(QWidget *parent) :
|
|||||||
setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeView::SetElementY(const NodeConnectable::InputConnection &c, int y)
|
void KeyframeView::SetElementY(const NodeInput &c, int y)
|
||||||
{
|
{
|
||||||
qreal scene_y = mapToScene(mapFromGlobal(QPoint(0, y))).y();
|
qreal scene_y = mapToScene(mapFromGlobal(QPoint(0, y))).y();
|
||||||
|
|
||||||
element_y_.insert(c, scene_y);
|
element_y_.insert(c, scene_y);
|
||||||
|
|
||||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||||
if (it.key()->parent() == c.input && it.key()->element() == c.element) {
|
if (it.key()->key_track_ref().input() == c) {
|
||||||
it.value()->SetOverrideY(scene_y);
|
it.value()->SetOverrideY(scene_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
|
|||||||
KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* key)
|
KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* key)
|
||||||
{
|
{
|
||||||
KeyframeViewItem* item = super::AddKeyframe(key);
|
KeyframeViewItem* item = super::AddKeyframe(key);
|
||||||
item->SetOverrideY(element_y_.value({key->parent(), key->element()}));
|
item->SetOverrideY(element_y_.value(key->key_track_ref().input()));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
max_scroll_ = i;
|
max_scroll_ = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetElementY(const Node::InputConnection& c, int y);
|
void SetElementY(const NodeInput& c, int y);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void wheelEvent(QWheelEvent* event) override;
|
virtual void wheelEvent(QWheelEvent* event) override;
|
||||||
@@ -47,7 +47,7 @@ public slots:
|
|||||||
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<Node::InputConnection, qreal> element_y_;
|
QHash<NodeInput, qreal> element_y_;
|
||||||
|
|
||||||
int max_scroll_;
|
int max_scroll_;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user