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
|
||||
{
|
||||
Q_UNUSED(cancelled)
|
||||
|
||||
// Variable for receiving errors from FFmpeg
|
||||
int error_code;
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ namespace olive {
|
||||
void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command)
|
||||
{
|
||||
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) {
|
||||
command->add_child(new NodeEdgeAddCommand(out, con.input, con.element));
|
||||
command->add_child(new NodeEdgeAddCommand(out, con.input));
|
||||
} else {
|
||||
Node::ConnectEdge(out, con.input, con.element);
|
||||
Node::ConnectEdge(out, con.input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-11
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "node/param.h"
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
@@ -38,15 +39,9 @@ class NodeInput;
|
||||
|
||||
struct XMLNodeData {
|
||||
struct SerializedConnection {
|
||||
NodeInput* input;
|
||||
int element;
|
||||
quintptr output;
|
||||
};
|
||||
|
||||
struct FootageConnection {
|
||||
NodeInput* input;
|
||||
int element;
|
||||
quintptr footage;
|
||||
NodeInput input;
|
||||
quintptr output_node;
|
||||
QString output;
|
||||
};
|
||||
|
||||
struct BlockLink {
|
||||
@@ -56,8 +51,6 @@ struct XMLNodeData {
|
||||
|
||||
QHash<quintptr, Node*> node_ptrs;
|
||||
QList<SerializedConnection> desired_connections;
|
||||
QHash<quintptr, Stream*> footage_ptrs;
|
||||
QList<FootageConnection> footage_connections;
|
||||
QList<BlockLink> block_links;
|
||||
QHash<quintptr, Item*> item_ptrs;
|
||||
|
||||
|
||||
@@ -892,6 +892,8 @@ QString Core::GetProjectFilter(bool include_any_filter)
|
||||
if (include_any_filter) {
|
||||
filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;"));
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(include_any_filter)
|
||||
#endif
|
||||
|
||||
// Append standard filter
|
||||
|
||||
@@ -33,7 +33,7 @@ class CodecSection : public QWidget
|
||||
public:
|
||||
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 "config/config.h"
|
||||
#include "node/input.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
+13
-13
@@ -25,28 +25,28 @@ add_subdirectory(output)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/connectable.cpp
|
||||
node/connectable.h
|
||||
node/factory.h
|
||||
node/factory.cpp
|
||||
node/graph.h
|
||||
node/factory.h
|
||||
node/graph.cpp
|
||||
node/input.h
|
||||
node/input.cpp
|
||||
node/inputdragger.h
|
||||
node/graph.h
|
||||
node/inputdragger.cpp
|
||||
node/inputimmediate.h
|
||||
node/inputdragger.h
|
||||
node/inputimmediate.cpp
|
||||
node/keyframe.h
|
||||
node/inputimmediate.h
|
||||
node/keyframe.cpp
|
||||
node/node.h
|
||||
node/keyframe.h
|
||||
node/node.cpp
|
||||
node/nodecopypaste.h
|
||||
node/node.h
|
||||
node/nodecopypaste.cpp
|
||||
node/nodecopypaste.h
|
||||
node/param.cpp
|
||||
node/param.h
|
||||
node/splitvalue.h
|
||||
node/traverser.h
|
||||
node/traverser.cpp
|
||||
node/value.h
|
||||
node/traverser.h
|
||||
node/value.cpp
|
||||
node/value.h
|
||||
node/valuedatabase.cpp
|
||||
node/valuedatabase.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
+18
-13
@@ -24,14 +24,17 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString PanNode::kSamplesInput = QStringLiteral("samples_in");
|
||||
const QString PanNode::kPanningInput = QStringLiteral("panning_in");
|
||||
|
||||
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);
|
||||
panning_input_->setProperty("min", -1.0);
|
||||
panning_input_->setProperty("max", 1.0);
|
||||
panning_input_->setProperty("view", FloatSlider::kPercentage);
|
||||
AddInput(kPanningInput, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0);
|
||||
SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0);
|
||||
SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
}
|
||||
|
||||
Node *PanNode::copy() const
|
||||
@@ -59,18 +62,20 @@ QString PanNode::Description() const
|
||||
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
|
||||
SampleJob job(samples_input_, value);
|
||||
job.InsertValue(panning_input_, value);
|
||||
SampleJob job(kSamplesInput, value);
|
||||
job.InsertValue(this, kPanningInput, value);
|
||||
|
||||
// Push it to our table
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (job.HasSamples()) {
|
||||
float pan_volume = job.GetValue(panning_input_).data().toFloat();
|
||||
if (panning_input_->IsStatic()) {
|
||||
float pan_volume = job.GetValue(kPanningInput).data().toFloat();
|
||||
if (IsInputStatic(kPanningInput)) {
|
||||
if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) {
|
||||
if (pan_volume > 0) {
|
||||
job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume);
|
||||
@@ -95,7 +100,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in
|
||||
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++) {
|
||||
output->data()[i][index] = input->data()[i][index];
|
||||
@@ -110,8 +115,8 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in
|
||||
|
||||
void PanNode::Retranslate()
|
||||
{
|
||||
samples_input_->set_name(tr("Samples"));
|
||||
panning_input_->set_name(tr("Pan"));
|
||||
SetInputName(kSamplesInput, tr("Samples"));
|
||||
SetInputName(kPanningInput, tr("Pan"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,12 +38,15 @@ public:
|
||||
virtual QVector<CategoryID> Category() 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 Retranslate() override;
|
||||
|
||||
static const QString kSamplesInput;
|
||||
static const QString kPanningInput;
|
||||
|
||||
private:
|
||||
NodeInput* samples_input_;
|
||||
NodeInput* panning_input_;
|
||||
|
||||
@@ -24,14 +24,16 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in");
|
||||
const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in");
|
||||
|
||||
VolumeNode::VolumeNode()
|
||||
{
|
||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
||||
samples_input_->SetKeyframable(false);
|
||||
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0);
|
||||
volume_input_->setProperty("min", 0.0);
|
||||
volume_input_->setProperty("view", FloatSlider::kDecibel);
|
||||
AddInput(kVolumeInput, NodeValue::kFloat, 1.0);
|
||||
SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel);
|
||||
}
|
||||
|
||||
Node *VolumeNode::copy() const
|
||||
@@ -59,26 +61,28 @@ QString VolumeNode::Description() const
|
||||
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,
|
||||
kOpMultiply,
|
||||
kPairSampleNumber,
|
||||
samples_input_,
|
||||
value[samples_input_].TakeWithMeta(NodeValue::kSamples),
|
||||
volume_input_,
|
||||
value[volume_input_].TakeWithMeta(NodeValue::kFloat));
|
||||
kSamplesInput,
|
||||
value[kSamplesInput].TakeWithMeta(NodeValue::kSamples),
|
||||
kVolumeInput,
|
||||
value[kVolumeInput].TakeWithMeta(NodeValue::kFloat));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
samples_input_->set_name(tr("Samples"));
|
||||
volume_input_->set_name(tr("Volume"));
|
||||
SetInputName(kSamplesInput, tr("Samples"));
|
||||
SetInputName(kVolumeInput, tr("Volume"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,20 +38,14 @@ public:
|
||||
virtual QVector<CategoryID> Category() 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 Retranslate() override;
|
||||
|
||||
NodeInput* samples_input() const
|
||||
{
|
||||
return samples_input_;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeInput* samples_input_;
|
||||
NodeInput* volume_input_;
|
||||
static const QString kSamplesInput;
|
||||
static const QString kVolumeInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+37
-39
@@ -28,6 +28,11 @@
|
||||
|
||||
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() :
|
||||
previous_(nullptr),
|
||||
next_(nullptr),
|
||||
@@ -36,24 +41,18 @@ Block::Block() :
|
||||
in_transition_(nullptr),
|
||||
out_transition_(nullptr)
|
||||
{
|
||||
length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational);
|
||||
length_input_->SetConnectable(false);
|
||||
length_input_->SetKeyframable(false);
|
||||
IgnoreInvalidationsFrom(length_input_);
|
||||
connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthChanged);
|
||||
AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
IgnoreInvalidationsFrom(kLengthInput);
|
||||
IgnoreHashingFrom(kLengthInput);
|
||||
|
||||
media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational);
|
||||
media_in_input_->SetConnectable(false);
|
||||
media_in_input_->SetKeyframable(false);
|
||||
AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
IgnoreHashingFrom(kMediaInInput);
|
||||
|
||||
enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean);
|
||||
enabled_input_->SetConnectable(false);
|
||||
enabled_input_->SetKeyframable(false);
|
||||
enabled_input_->SetStandardValue(true);
|
||||
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat);
|
||||
speed_input_->SetStandardValue(1.0);
|
||||
speed_input_->setProperty("view", FloatSlider::kPercentage);
|
||||
AddInput(kSpeedInput, NodeValue::kFloat, 1.0);
|
||||
SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
IgnoreHashingFrom(kSpeedInput);
|
||||
|
||||
// A block's length must be greater than 0
|
||||
set_length_and_media_out(1);
|
||||
@@ -66,7 +65,7 @@ QVector<Node::CategoryID> Block::Category() 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)
|
||||
@@ -97,22 +96,22 @@ void Block::set_length_and_media_in(const rational &length)
|
||||
|
||||
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)
|
||||
{
|
||||
media_in_input_->SetStandardValue(QVariant::fromValue(media_in));
|
||||
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
|
||||
}
|
||||
|
||||
bool Block::is_enabled() const
|
||||
{
|
||||
return enabled_input_->GetStandardValue().toBool();
|
||||
return GetStandardValue(kEnabledInput).toBool();
|
||||
}
|
||||
|
||||
void Block::set_enabled(bool e)
|
||||
{
|
||||
enabled_input_->SetStandardValue(e);
|
||||
SetStandardValue(kEnabledInput, e);
|
||||
|
||||
emit EnabledChanged();
|
||||
}
|
||||
@@ -127,10 +126,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
|
||||
rational local_time = sequence_time;
|
||||
|
||||
// FIXME: Doesn't handle reversing
|
||||
if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) {
|
||||
// FIXME: We'll need to calculate the speed hoo boy
|
||||
} else {
|
||||
double speed_value = speed_input_->GetStandardValue().toDouble();
|
||||
if (IsInputStatic(kSpeedInput)) {
|
||||
double speed_value = GetStandardValue(kSpeedInput).toDouble();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Effectively holds the frame at the in point
|
||||
@@ -139,6 +136,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
|
||||
// Multiply time
|
||||
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();
|
||||
@@ -154,10 +153,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
|
||||
rational sequence_time = media_time - media_in();
|
||||
|
||||
// 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
|
||||
} else {
|
||||
double speed_value = speed_input_->GetStandardValue().toDouble();
|
||||
double speed_value = GetStandardValue(kSpeedInput).toDouble();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
QVector<NodeInput *> Block::GetInputsToHash() const
|
||||
void Block::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
QVector<NodeInput*> inputs = Node::GetInputsToHash();
|
||||
Q_UNUSED(element)
|
||||
|
||||
// Ignore these inputs
|
||||
inputs.removeOne(media_in_input_);
|
||||
inputs.removeOne(speed_input_);
|
||||
inputs.removeOne(length_input_);
|
||||
|
||||
return inputs;
|
||||
if (input == kLengthInput) {
|
||||
emit LengthChanged();
|
||||
} else if (input == kEnabledInput) {
|
||||
emit EnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Block::LinkChangeEvent()
|
||||
@@ -198,17 +196,17 @@ void Block::LinkChangeEvent()
|
||||
|
||||
void Block::set_length_internal(const rational &length)
|
||||
{
|
||||
length_input_->SetStandardValue(QVariant::fromValue(length));
|
||||
SetStandardValue(kLengthInput, QVariant::fromValue(length));
|
||||
}
|
||||
|
||||
void Block::Retranslate()
|
||||
{
|
||||
Node::Retranslate();
|
||||
|
||||
length_input_->set_name(tr("Length"));
|
||||
media_in_input_->set_name(tr("Media In"));
|
||||
enabled_input_->set_name(tr("Enabled"));
|
||||
speed_input_->set_name(tr("Speed"));
|
||||
SetInputName(kLengthInput, tr("Length"));
|
||||
SetInputName(kMediaInInput, tr("Media In"));
|
||||
SetInputName(kEnabledInput, tr("Enabled"));
|
||||
SetInputName(kSpeedInput, tr("Speed"));
|
||||
}
|
||||
|
||||
void Block::Hash(QCryptographicHash &, const rational &) const
|
||||
|
||||
+6
-21
@@ -114,21 +114,6 @@ public:
|
||||
|
||||
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()
|
||||
{
|
||||
return in_transition_;
|
||||
@@ -166,6 +151,11 @@ public:
|
||||
|
||||
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:
|
||||
|
||||
signals:
|
||||
@@ -178,7 +168,7 @@ protected:
|
||||
|
||||
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;
|
||||
|
||||
@@ -188,11 +178,6 @@ protected:
|
||||
private:
|
||||
void set_length_internal(const rational &length);
|
||||
|
||||
NodeInput* length_input_;
|
||||
NodeInput* media_in_input_;
|
||||
NodeInput* speed_input_;
|
||||
NodeInput* enabled_input_;
|
||||
|
||||
rational in_point_;
|
||||
rational out_point_;
|
||||
Track* track_;
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in");
|
||||
|
||||
ClipBlock::ClipBlock()
|
||||
{
|
||||
texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone);
|
||||
texture_input_->SetKeyframable(false);
|
||||
AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||
}
|
||||
|
||||
Node *ClipBlock::copy() const
|
||||
@@ -53,15 +54,12 @@ QString ClipBlock::Description() const
|
||||
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 (from.input == texture_input_) {
|
||||
if (from == kBufferIn) {
|
||||
// Adjust range from media time to sequence time
|
||||
rational start = MediaToSequenceTime(range.in());
|
||||
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)
|
||||
|
||||
if (input == texture_input_) {
|
||||
if (input == kBufferIn) {
|
||||
return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out()));
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if (input == texture_input_) {
|
||||
if (input == kBufferIn) {
|
||||
return TimeRange(MediaToSequenceTime(input_time.in()), MediaToSequenceTime(input_time.out()));
|
||||
}
|
||||
|
||||
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
|
||||
NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer);
|
||||
NodeValue data = value[kBufferIn].GetWithMeta(NodeValue::kBuffer);
|
||||
|
||||
NodeValueTable table;
|
||||
if (data.type() != NodeValue::kNone) {
|
||||
@@ -111,15 +111,15 @@ void ClipBlock::Retranslate()
|
||||
{
|
||||
Block::Retranslate();
|
||||
|
||||
texture_input_->set_name(tr("Buffer"));
|
||||
SetInputName(kBufferIn, tr("Buffer"));
|
||||
}
|
||||
|
||||
void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
{
|
||||
if (texture_input_->IsConnected()) {
|
||||
rational t = InputTimeAdjustment(texture_input_, -1, TimeRange(time, time)).in();
|
||||
if (IsInputConnected(kBufferIn)) {
|
||||
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 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(NodeValueDatabase& value) const override;
|
||||
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
||||
|
||||
private:
|
||||
NodeInput* texture_input_;
|
||||
static const QString kBufferIn;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ public:
|
||||
virtual QString id() 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
|
||||
{
|
||||
Q_UNUSED(value)
|
||||
|
||||
job.SetAlphaChannelRequired(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString DipToColorTransition::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
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
|
||||
@@ -61,7 +63,7 @@ ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) 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;
|
||||
|
||||
static const QString kColorInput;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const override;
|
||||
|
||||
private:
|
||||
NodeInput* color_input_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,23 +24,19 @@
|
||||
|
||||
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() :
|
||||
connected_out_block_(nullptr),
|
||||
connected_in_block_(nullptr)
|
||||
{
|
||||
out_block_input_ = new NodeInput(this, QStringLiteral("out_block_in"), NodeValue::kNone);
|
||||
out_block_input_->SetKeyframable(false);
|
||||
connect(out_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::InBlockConnected);
|
||||
connect(out_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::InBlockDisconnected);
|
||||
AddInput(kOutBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
in_block_input_ = new NodeInput(this, QStringLiteral("in_block_in"), NodeValue::kNone);
|
||||
in_block_input_->SetKeyframable(false);
|
||||
connect(in_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::OutBlockConnected);
|
||||
connect(in_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::OutBlockDisconnected);
|
||||
AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo);
|
||||
curve_input_->SetKeyframable(false);
|
||||
curve_input_->SetConnectable(false);
|
||||
AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
}
|
||||
|
||||
Block::Type TransitionBlock::type() const
|
||||
@@ -48,26 +44,16 @@ Block::Type TransitionBlock::type() const
|
||||
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()
|
||||
{
|
||||
Block::Retranslate();
|
||||
|
||||
out_block_input_->set_name(tr("From"));
|
||||
in_block_input_->set_name(tr("To"));
|
||||
curve_input_->set_name(tr("Curve"));
|
||||
SetInputName(kOutBlockInput, tr("From"));
|
||||
SetInputName(kInBlockInput, tr("To"));
|
||||
SetInputName(kCurveInput, tr("Curve"));
|
||||
|
||||
// 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
|
||||
@@ -169,56 +155,16 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t
|
||||
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
|
||||
if ((connected_out_block_ = dynamic_cast<Block*>(node))) {
|
||||
Q_UNUSED(output)
|
||||
|
||||
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;
|
||||
|
||||
if (out_block_input()->IsConnected()) {
|
||||
data_type = value[out_block_input()].GetWithMeta(NodeValue::kBuffer).type();
|
||||
} else if (in_block_input()->IsConnected()) {
|
||||
data_type = value[in_block_input()].GetWithMeta(NodeValue::kBuffer).type();
|
||||
if (IsInputConnected(kOutBlockInput)) {
|
||||
data_type = value[kOutBlockInput].GetWithMeta(NodeValue::kBuffer).type();
|
||||
} else if (IsInputConnected(kInBlockInput)) {
|
||||
data_type = value[kInBlockInput].GetWithMeta(NodeValue::kBuffer).type();
|
||||
} else {
|
||||
data_type = NodeValue::kNone;
|
||||
}
|
||||
@@ -230,9 +176,9 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
|
||||
// This must be a visual transition
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(out_block_input(), value);
|
||||
job.InsertValue(in_block_input(), value);
|
||||
job.InsertValue(curve_input_, value);
|
||||
job.InsertValue(this, kOutBlockInput, value);
|
||||
job.InsertValue(this, kInBlockInput, value);
|
||||
job.InsertValue(this, kCurveInput, value);
|
||||
|
||||
double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
|
||||
InsertTransitionTimes(&job, time);
|
||||
@@ -243,8 +189,8 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
|
||||
push_job = QVariant::fromValue(job);
|
||||
} else if (data_type == NodeValue::kSamples) {
|
||||
// This must be an audio transition
|
||||
SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
SampleBufferPtr from_samples = value[kOutBlockInput].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
SampleBufferPtr to_samples = value[kInBlockInput].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
|
||||
if (from_samples || to_samples) {
|
||||
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
|
||||
{
|
||||
switch (static_cast<CurveType>(curve_input_->GetStandardValue().toInt())) {
|
||||
switch (static_cast<CurveType>(GetStandardValue(kCurveInput).toInt())) {
|
||||
case kLinear:
|
||||
break;
|
||||
case kExponential:
|
||||
@@ -301,4 +247,49 @@ double TransitionBlock::TransformCurve(double linear) const
|
||||
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;
|
||||
|
||||
NodeInput* out_block_input() const;
|
||||
NodeInput* in_block_input() const;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
rational in_offset() const;
|
||||
@@ -50,7 +47,11 @@ public:
|
||||
|
||||
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:
|
||||
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const;
|
||||
@@ -59,6 +60,10 @@ protected:
|
||||
|
||||
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:
|
||||
enum CurveType {
|
||||
kLinear,
|
||||
@@ -70,25 +75,10 @@ private:
|
||||
|
||||
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_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 {
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
left_input_->setProperty("min", 0.0);
|
||||
left_input_->setProperty("max", 1.0);
|
||||
left_input_->setProperty("view", FloatSlider::kPercentage);
|
||||
CreateCropSideInput(kLeftInput);
|
||||
CreateCropSideInput(kTopInput);
|
||||
CreateCropSideInput(kRightInput);
|
||||
CreateCropSideInput(kBottomInput);
|
||||
|
||||
top_input_ = new NodeInput(this, QStringLiteral("top_in"), NodeValue::kFloat, 0.0);
|
||||
top_input_->setProperty("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);
|
||||
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0);
|
||||
}
|
||||
|
||||
void CropDistortNode::Retranslate()
|
||||
{
|
||||
texture_input_->set_name(tr("Texture"));
|
||||
left_input_->set_name(tr("Left"));
|
||||
top_input_->set_name(tr("Top"));
|
||||
right_input_->set_name(tr("Right"));
|
||||
bottom_input_->set_name(tr("Bottom"));
|
||||
feather_input_->set_name(tr("Feather"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kLeftInput, tr("Left"));
|
||||
SetInputName(kTopInput, tr("Top"));
|
||||
SetInputName(kRightInput, tr("Right"));
|
||||
SetInputName(kBottomInput, tr("Bottom"));
|
||||
SetInputName(kFeatherInput, tr("Feather"));
|
||||
}
|
||||
|
||||
NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
job.InsertValue(texture_input_, value);
|
||||
job.InsertValue(left_input_, value);
|
||||
job.InsertValue(top_input_, value);
|
||||
job.InsertValue(right_input_, value);
|
||||
job.InsertValue(bottom_input_, value);
|
||||
job.InsertValue(feather_input_, value);
|
||||
job.InsertValue(this, kTextureInput, value);
|
||||
job.InsertValue(this, kLeftInput, value);
|
||||
job.InsertValue(this, kTopInput, value);
|
||||
job.InsertValue(this, kRightInput, value);
|
||||
job.InsertValue(this, kBottomInput, value);
|
||||
job.InsertValue(this, kFeatherInput, value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||
job.SetAlphaChannelRequired(true);
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (!job.GetValue(texture_input_).data().isNull()) {
|
||||
if (!qIsNull(job.GetValue(left_input_).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(right_input_).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(top_input_).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(bottom_input_).data().toDouble())) {
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
if (!qIsNull(job.GetValue(kLeftInput).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(kRightInput).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(kTopInput).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(kBottomInput).data().toDouble())) {
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
} 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));
|
||||
|
||||
double left_pt = resolution.x() * db[left_input_].Get(NodeValue::kFloat).toDouble();
|
||||
double top_pt = resolution.y() * db[top_input_].Get(NodeValue::kFloat).toDouble();
|
||||
double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeValue::kFloat).toDouble());
|
||||
double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeValue::kFloat).toDouble());
|
||||
double left_pt = resolution.x() * db[kLeftInput].Get(NodeValue::kFloat).toDouble();
|
||||
double top_pt = resolution.y() * db[kTopInput].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[kBottomInput].Get(NodeValue::kFloat).toDouble());
|
||||
double center_x_pt = lerp(left_pt, right_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) {
|
||||
gizmo_drag_ |= kGizmoLeft;
|
||||
|
||||
gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat));
|
||||
gizmo_start_.append(db[kLeftInput].Get(NodeValue::kFloat));
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleTopLeft]
|
||||
@@ -165,7 +159,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoTop;
|
||||
|
||||
gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat));
|
||||
gizmo_start_.append(db[kTopInput].Get(NodeValue::kFloat));
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleTopRight]
|
||||
@@ -174,7 +168,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoRight;
|
||||
|
||||
gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat));
|
||||
gizmo_start_.append(db[kRightInput].Get(NodeValue::kFloat));
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleBottomLeft]
|
||||
@@ -183,7 +177,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoBottom;
|
||||
|
||||
gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat));
|
||||
gizmo_start_.append(db[kBottomInput].Get(NodeValue::kFloat));
|
||||
}
|
||||
|
||||
if (gizmo_drag_ > kGizmoNone) {
|
||||
@@ -204,22 +198,22 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
||||
int counter = 0;
|
||||
|
||||
if (gizmo_drag_ & kGizmoLeft) {
|
||||
gizmo_dragger_[counter].Start(left_input_, time, 0);
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kLeftInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoTop) {
|
||||
gizmo_dragger_[counter].Start(top_input_, time, 0);
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kTopInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoRight) {
|
||||
gizmo_dragger_[counter].Start(right_input_, time, 0);
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kRightInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoBottom) {
|
||||
gizmo_dragger_[counter].Start(bottom_input_, time, 0);
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kBottomInput), time);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
@@ -260,4 +254,12 @@ void CropDistortNode::GizmoRelease()
|
||||
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 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;
|
||||
|
||||
@@ -75,15 +75,15 @@ public:
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time) 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:
|
||||
NodeInput* texture_input_;
|
||||
|
||||
NodeInput* left_input_;
|
||||
NodeInput* top_input_;
|
||||
NodeInput* right_input_;
|
||||
NodeInput* bottom_input_;
|
||||
|
||||
NodeInput* feather_input_;
|
||||
void CreateCropSideInput(const QString& id);
|
||||
|
||||
// Gizmo variables
|
||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
||||
|
||||
@@ -26,35 +26,40 @@
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
texture_input_->SetKeyframable(false);
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
}
|
||||
|
||||
void TransformDistortNode::Retranslate()
|
||||
{
|
||||
MatrixGenerator::Retranslate();
|
||||
|
||||
autoscale_input_->set_name(tr("Auto-Scale"));
|
||||
texture_input_->set_name(tr("Texture"));
|
||||
interpolation_input_->set_name(tr("Interpolation"));
|
||||
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kInterpolationInput, tr("Interpolation"));
|
||||
|
||||
autoscale_input_->set_combobox_strings({tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")});
|
||||
interpolation_input_->set_combobox_strings({tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")});
|
||||
SetComboBoxStrings(kAutoscaleInput, {tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")});
|
||||
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
|
||||
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
|
||||
|
||||
// Pop texture
|
||||
TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value<TexturePtr>();
|
||||
TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value<TexturePtr>();
|
||||
|
||||
// Merge table
|
||||
NodeValueTable table = value.Merge();
|
||||
@@ -64,7 +69,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
|
||||
// Adjust our matrix by the resolutions involved
|
||||
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());
|
||||
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,
|
||||
sequence_res,
|
||||
@@ -79,7 +84,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), 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
|
||||
// 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) {
|
||||
|
||||
// Dragging scale handle
|
||||
gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)};
|
||||
gizmo_drag_ = scale_input();
|
||||
gizmo_start_ = {db[kScaleInput].Get(NodeValue::kVec2)};
|
||||
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]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) {
|
||||
@@ -136,8 +141,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||
}
|
||||
|
||||
// Store texture size
|
||||
QVector2D texture_sz = db[texture_input()].Get(NodeValue::kTexture).value<QVector2D>();
|
||||
gizmo_scale_anchor_ = db[anchor_input()].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
|
||||
QVector2D texture_sz = db[kTextureInput].Get(NodeValue::kTexture).value<QVector2D>();
|
||||
gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
|
||||
|
||||
if (gizmo_scale_active[kGizmoScaleTopRight]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomRight]
|
||||
@@ -161,9 +166,9 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||
} else if (gizmo_anchor_pt_.contains(p)) {
|
||||
|
||||
// Dragging the anchor point specifically
|
||||
gizmo_start_ = {db[anchor_input()].Get(NodeValue::kVec2),
|
||||
db[position_input()].Get(NodeValue::kVec2)};
|
||||
gizmo_drag_ = anchor_input();
|
||||
gizmo_start_ = {db[kAnchorInput].Get(NodeValue::kVec2),
|
||||
db[kPositionInput].Get(NodeValue::kVec2)};
|
||||
gizmo_drag_ = kAnchorInput;
|
||||
|
||||
// Store current matrix
|
||||
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)) {
|
||||
|
||||
// Dragging the main rectangle
|
||||
gizmo_start_ = {db[position_input()].Get(NodeValue::kVec2)};
|
||||
gizmo_drag_ = position_input();
|
||||
gizmo_start_ = {db[kPositionInput].Get(NodeValue::kVec2)};
|
||||
gizmo_drag_ = kPositionInput;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// Dragging rotation
|
||||
gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)};
|
||||
gizmo_drag_ = rotation_input();
|
||||
gizmo_start_ = {db[kRotationInput].Get(NodeValue::kFloat)};
|
||||
gizmo_drag_ = kRotationInput;
|
||||
gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(),
|
||||
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_);
|
||||
QVector2D vec_movement(movement);
|
||||
|
||||
if (gizmo_drag_ == anchor_input()) {
|
||||
if (gizmo_drag_ == kAnchorInput) {
|
||||
|
||||
// Dragging the anchor point around
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(4);
|
||||
gizmo_dragger_[0].Start(anchor_input(), time, 0);
|
||||
gizmo_dragger_[1].Start(anchor_input(), time, 1);
|
||||
gizmo_dragger_[2].Start(position_input(), time, 0);
|
||||
gizmo_dragger_[3].Start(position_input(), time, 1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1), time);
|
||||
gizmo_dragger_[2].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||
gizmo_dragger_[3].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||
}
|
||||
|
||||
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_[3].Drag(position_cont.y());
|
||||
|
||||
} else if (gizmo_drag_ == position_input()) {
|
||||
} else if (gizmo_drag_ == kPositionInput) {
|
||||
|
||||
// Dragging the main rectangle around
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(2);
|
||||
gizmo_dragger_[0].Start(position_input(), time, 0);
|
||||
gizmo_dragger_[1].Start(position_input(), time, 1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||
}
|
||||
|
||||
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_[1].Drag(position_cont.y());
|
||||
|
||||
} else if (gizmo_drag_ == scale_input()) {
|
||||
} else if (gizmo_drag_ == kScaleInput) {
|
||||
|
||||
// Dragging a resize handle
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) {
|
||||
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) {
|
||||
gizmo_dragger_.resize(1);
|
||||
gizmo_dragger_[0].Start(scale_input(), time, 1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||
} else {
|
||||
gizmo_dragger_.resize(2);
|
||||
gizmo_dragger_[0].Start(scale_input(), time, 0);
|
||||
gizmo_dragger_[1].Start(scale_input(), time, 1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,12 +279,12 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time)
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (gizmo_drag_ == rotation_input()) {
|
||||
} else if (gizmo_drag_ == kRotationInput) {
|
||||
|
||||
// Dragging outside the rectangle to rotate
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
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(),
|
||||
@@ -367,10 +372,10 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
||||
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
// really anchor around the center
|
||||
position_input()->setProperty("offset", sequence_res * 0.5);
|
||||
anchor_input()->setProperty("offset", tex_sz * 0.5);
|
||||
SetInputProperty(kPositionInput, QStringLiteral("offset"), sequence_res * 0.5);
|
||||
SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
@@ -78,11 +78,6 @@ public:
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time) override;
|
||||
virtual void GizmoRelease() override;
|
||||
|
||||
NodeInput* texture_input() const
|
||||
{
|
||||
return texture_input_;
|
||||
}
|
||||
|
||||
enum AutoScaleType {
|
||||
kAutoScaleNone,
|
||||
kAutoScaleFit,
|
||||
@@ -95,15 +90,15 @@ public:
|
||||
const QVector2D& texture_res,
|
||||
AutoScaleType autoscale_type = kAutoScaleNone);
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kAutoscaleInput;
|
||||
static const QString kInterpolationInput;
|
||||
|
||||
private:
|
||||
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
|
||||
NodeInput* gizmo_drag_;
|
||||
QString gizmo_drag_;
|
||||
QVector<QVariant> gizmo_start_;
|
||||
QVector<NodeInputDragger> gizmo_dragger_;
|
||||
QPointF gizmo_drag_pos_;
|
||||
|
||||
+11
-6
@@ -52,13 +52,18 @@ void NodeFactory::Initialize()
|
||||
|
||||
// Add internal types
|
||||
for (int i=0;i<kInternalNodeCount;i++) {
|
||||
library_.append(CreateFromFactoryIndex(static_cast<InternalID>(i)));
|
||||
}
|
||||
Node* created_node = CreateFromFactoryIndex(static_cast<InternalID>(i));
|
||||
|
||||
/*
|
||||
library_.append(new ExternalTransition(":/shaders/crossdissolve.xml"));
|
||||
library_.append(new ExternalTransition(":/shaders/diptoblack.xml"));
|
||||
*/
|
||||
library_.append(created_node);
|
||||
|
||||
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()
|
||||
|
||||
@@ -22,20 +22,27 @@
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
radius_input_->setProperty("min", 0.0f);
|
||||
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
|
||||
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
|
||||
@@ -65,13 +72,13 @@ QString BlurFilterNode::Description() const
|
||||
|
||||
void BlurFilterNode::Retranslate()
|
||||
{
|
||||
texture_input_->set_name(tr("Input"));
|
||||
method_input_->set_name(tr("Method"));
|
||||
method_input_->set_combobox_strings({ tr("Box"), tr("Gaussian") });
|
||||
radius_input_->set_name(tr("Radius"));
|
||||
horiz_input_->set_name(tr("Horizontal"));
|
||||
vert_input_->set_name(tr("Vertical"));
|
||||
repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels"));
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kMethodInput, tr("Method"));
|
||||
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") });
|
||||
SetInputName(kRadiusInput, tr("Radius"));
|
||||
SetInputName(kHorizInput, tr("Horizontal"));
|
||||
SetInputName(kVertInput, tr("Vertical"));
|
||||
SetInputName(kRepeatEdgePixelsInput, tr("Repeat Edge Pixels"));
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(texture_input_, value);
|
||||
job.InsertValue(method_input_, value);
|
||||
job.InsertValue(radius_input_, value);
|
||||
job.InsertValue(horiz_input_, value);
|
||||
job.InsertValue(vert_input_, value);
|
||||
job.InsertValue(repeat_edge_pixels_input_, value);
|
||||
job.InsertValue(this, kTextureInput, value);
|
||||
job.InsertValue(this, kMethodInput, value);
|
||||
job.InsertValue(this, kRadiusInput, value);
|
||||
job.InsertValue(this, kHorizInput, value);
|
||||
job.InsertValue(this, kVertInput, value);
|
||||
job.InsertValue(this, kRepeatEdgePixelsInput, value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
// 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
|
||||
if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool())
|
||||
&& job.GetValue(radius_input_).data().toDouble() > 0.0) {
|
||||
if ((job.GetValue(kHorizInput).data().toBool() || job.GetValue(kVertInput).data().toBool())
|
||||
&& job.GetValue(kRadiusInput).data().toDouble() > 0.0) {
|
||||
|
||||
// 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()) {
|
||||
job.SetIterations(2, texture_input_);
|
||||
if (job.GetValue(kHorizInput).data().toBool() && job.GetValue(kVertInput).data().toBool()) {
|
||||
job.SetIterations(2, kTextureInput);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -116,7 +125,7 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
||||
|
||||
} else {
|
||||
// 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 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:
|
||||
NodeInput* texture_input_;
|
||||
|
||||
NodeInput* method_input_;
|
||||
|
||||
NodeInput* radius_input_;
|
||||
|
||||
NodeInput* horiz_input_;
|
||||
|
||||
NodeInput* vert_input_;
|
||||
|
||||
NodeInput* repeat_edge_pixels_input_;
|
||||
static const QString kTextureInput;
|
||||
static const QString kMethodInput;
|
||||
static const QString kRadiusInput;
|
||||
static const QString kHorizInput;
|
||||
static const QString kVertInput;
|
||||
static const QString kRepeatEdgePixelsInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -22,46 +22,52 @@
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
horiz_input_->setProperty("min", 1.0);
|
||||
AddInput(kHorizInput, NodeValue::kFloat, 32.0);
|
||||
SetInputProperty(kHorizInput, QStringLiteral("min"), 1.0);
|
||||
|
||||
vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0);
|
||||
vert_input_->setProperty("min", 1.0);
|
||||
AddInput(kVertInput, NodeValue::kFloat, 18.0);
|
||||
SetInputProperty(kVertInput, QStringLiteral("min"), 1.0);
|
||||
}
|
||||
|
||||
void MosaicFilterNode::Retranslate()
|
||||
{
|
||||
tex_input_->set_name(tr("Texture"));
|
||||
horiz_input_->set_name(tr("Horizontal"));
|
||||
vert_input_->set_name(tr("Vertical"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kHorizInput, tr("Horizontal"));
|
||||
SetInputName(kVertInput, tr("Vertical"));
|
||||
}
|
||||
|
||||
NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable MosaicFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(tex_input_, value);
|
||||
job.InsertValue(horiz_input_, value);
|
||||
job.InsertValue(vert_input_, value);
|
||||
job.InsertValue(this, kTextureInput, value);
|
||||
job.InsertValue(this, kHorizInput, value);
|
||||
job.InsertValue(this, kVertInput, value);
|
||||
|
||||
// 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();
|
||||
|
||||
if (!job.GetValue(tex_input_).data().isNull()) {
|
||||
TexturePtr texture = job.GetValue(tex_input_).data().value<TexturePtr>();
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
TexturePtr texture = job.GetValue(kTextureInput).data().value<TexturePtr>();
|
||||
|
||||
if (texture
|
||||
&& job.GetValue(horiz_input_).data().toInt() != texture->width()
|
||||
&& job.GetValue(vert_input_).data().toInt() != texture->height()) {
|
||||
&& job.GetValue(kHorizInput).data().toInt() != texture->width()
|
||||
&& job.GetValue(kVertInput).data().toInt() != texture->height()) {
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table.Push(job.GetValue(tex_input_));
|
||||
table.Push(job.GetValue(kTextureInput));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,15 +58,12 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NodeInput* tex_input_;
|
||||
|
||||
NodeInput* horiz_input_;
|
||||
|
||||
NodeInput* vert_input_;
|
||||
static const QString kTextureInput;
|
||||
static const QString kHorizInput;
|
||||
static const QString kVertInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -25,24 +25,27 @@
|
||||
|
||||
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()
|
||||
{
|
||||
tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
color_input_ = new NodeInput(this,
|
||||
QStringLiteral("color_in"),
|
||||
NodeValue::kColor,
|
||||
QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f)));
|
||||
AddInput(kColorInput, 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);
|
||||
radius_input_->setProperty("min", 0.0f);
|
||||
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
|
||||
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
|
||||
|
||||
opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f);
|
||||
opacity_input_->setProperty("view", FloatSlider::kPercentage);
|
||||
opacity_input_->setProperty("min", 0.0f);
|
||||
opacity_input_->setProperty("max", 1.0f);
|
||||
AddInput(kOpacityInput, NodeValue::kFloat, 1.0f);
|
||||
SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.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
|
||||
@@ -72,33 +75,35 @@ QString StrokeFilterNode::Description() const
|
||||
|
||||
void StrokeFilterNode::Retranslate()
|
||||
{
|
||||
tex_input_->set_name(tr("Input"));
|
||||
color_input_->set_name(tr("Color"));
|
||||
radius_input_->set_name(tr("Radius"));
|
||||
opacity_input_->set_name(tr("Opacity"));
|
||||
inner_input_->set_name(tr("Inner"));
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
SetInputName(kRadiusInput, tr("Radius"));
|
||||
SetInputName(kOpacityInput, tr("Opacity"));
|
||||
SetInputName(kInnerInput, tr("Inner"));
|
||||
}
|
||||
|
||||
NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable StrokeFilterNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(tex_input_, value);
|
||||
job.InsertValue(color_input_, value);
|
||||
job.InsertValue(radius_input_, value);
|
||||
job.InsertValue(opacity_input_, value);
|
||||
job.InsertValue(inner_input_, value);
|
||||
job.InsertValue(this, kTextureInput, value);
|
||||
job.InsertValue(this, kColorInput, value);
|
||||
job.InsertValue(this, kRadiusInput, value);
|
||||
job.InsertValue(this, kOpacityInput, value);
|
||||
job.InsertValue(this, kInnerInput, value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (!job.GetValue(tex_input_).data().isNull()) {
|
||||
if (job.GetValue(radius_input_).data().toDouble() > 0.0
|
||||
&& job.GetValue(opacity_input_).data().toDouble() > 0.0) {
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
if (job.GetValue(kRadiusInput).data().toDouble() > 0.0
|
||||
&& job.GetValue(kOpacityInput).data().toDouble() > 0.0) {
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table.Push(job.GetValue(tex_input_));
|
||||
table.Push(job.GetValue(kTextureInput));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,19 +40,14 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NodeInput* tex_input_;
|
||||
|
||||
NodeInput* color_input_;
|
||||
|
||||
NodeInput* radius_input_;
|
||||
|
||||
NodeInput* opacity_input_;
|
||||
|
||||
NodeInput* inner_input_;
|
||||
static const QString kTextureInput;
|
||||
static const QString kColorInput;
|
||||
static const QString kRadiusInput;
|
||||
static const QString kOpacityInput;
|
||||
static const QString kInnerInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -27,23 +27,26 @@
|
||||
|
||||
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()
|
||||
{
|
||||
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));
|
||||
scale_input_->setProperty("min", QVector2D(0, 0));
|
||||
scale_input_->setProperty("view", FloatSlider::kPercentage);
|
||||
scale_input_->setProperty("disabley", true);
|
||||
AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f));
|
||||
SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0));
|
||||
SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
SetInputProperty(kScaleInput, QStringLiteral("disabley"), true);
|
||||
|
||||
uniform_scale_input_ = new NodeInput(this, QStringLiteral("uniform_scale_in"), NodeValue::kBoolean, true);
|
||||
uniform_scale_input_->SetKeyframable(false);
|
||||
uniform_scale_input_->SetConnectable(false);
|
||||
connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged);
|
||||
AddInput(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D());
|
||||
AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||
}
|
||||
|
||||
Node *MatrixGenerator::copy() const
|
||||
@@ -78,20 +81,22 @@ QString MatrixGenerator::Description() const
|
||||
|
||||
void MatrixGenerator::Retranslate()
|
||||
{
|
||||
position_input_->set_name(tr("Position"));
|
||||
rotation_input_->set_name(tr("Rotation"));
|
||||
scale_input_->set_name(tr("Scale"));
|
||||
uniform_scale_input_->set_name(tr("Uniform Scale"));
|
||||
anchor_input_->set_name(tr("Anchor Point"));
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
SetInputName(kRotationInput, tr("Rotation"));
|
||||
SetInputName(kScaleInput, tr("Scale"));
|
||||
SetInputName(kUniformScaleInput, tr("Uniform Scale"));
|
||||
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
|
||||
QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false);
|
||||
NodeValueTable output = value.Merge();
|
||||
output.Push(NodeValue::kMatrix, mat, this);
|
||||
return output;
|
||||
NodeValueTable out = value.Merge();
|
||||
out.Push(NodeValue::kMatrix, mat, this);
|
||||
return out;
|
||||
}
|
||||
|
||||
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 (take) {
|
||||
// Take and store
|
||||
anchor = value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
anchor = value[kAnchorInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
} else {
|
||||
// Get and store
|
||||
anchor = value[anchor_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
anchor = value[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
} else if (take) {
|
||||
// Just take
|
||||
value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
value[kAnchorInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
|
||||
if (!ignore_scale) {
|
||||
if (take) {
|
||||
scale = value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
scale = value[kScaleInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
} else {
|
||||
scale = value[scale_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
scale = value[kScaleInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
} else if (take) {
|
||||
value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
value[kScaleInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
|
||||
if (!ignore_position) {
|
||||
if (take) {
|
||||
position = value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
position = value[kPositionInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
} else {
|
||||
position = value[position_input_].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
position = value[kPositionInput].Get(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
} else if (take) {
|
||||
value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
value[kPositionInput].Take(NodeValue::kVec2).value<QVector2D>();
|
||||
}
|
||||
|
||||
if (take) {
|
||||
return GenerateMatrix(position,
|
||||
value[rotation_input_].Take(NodeValue::kFloat).toFloat(),
|
||||
value[kRotationInput].Take(NodeValue::kFloat).toFloat(),
|
||||
scale,
|
||||
value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(),
|
||||
value[kUniformScaleInput].Take(NodeValue::kBoolean).toBool(),
|
||||
anchor);
|
||||
} else {
|
||||
return GenerateMatrix(position,
|
||||
value[rotation_input_].Get(NodeValue::kFloat).toFloat(),
|
||||
value[kRotationInput].Get(NodeValue::kFloat).toFloat(),
|
||||
scale,
|
||||
value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(),
|
||||
value[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(),
|
||||
anchor);
|
||||
|
||||
}
|
||||
@@ -178,9 +183,13 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
|
||||
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 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:
|
||||
QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const;
|
||||
@@ -54,44 +60,7 @@ protected:
|
||||
bool uniform_scale,
|
||||
const QVector2D &anchor);
|
||||
|
||||
NodeInput* position_input() const
|
||||
{
|
||||
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();
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -25,25 +25,27 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in");
|
||||
const QString PolygonGenerator::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
PolygonGenerator::PolygonGenerator()
|
||||
{
|
||||
points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0));
|
||||
points_input_->SetIsArray(true);
|
||||
AddInput(kPointsInput, NodeValue::kVec2, QVector2D(0, 0), InputFlags(kInputFlagArray));
|
||||
|
||||
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)
|
||||
points_input_->ArrayResize(5);
|
||||
points_input_->SetStandardValueOnTrack(960, 0, 0);
|
||||
points_input_->SetStandardValueOnTrack(240, 1, 0);
|
||||
points_input_->SetStandardValueOnTrack(640, 0, 1);
|
||||
points_input_->SetStandardValueOnTrack(480, 1, 1);
|
||||
points_input_->SetStandardValueOnTrack(760, 0, 2);
|
||||
points_input_->SetStandardValueOnTrack(800, 1, 2);
|
||||
points_input_->SetStandardValueOnTrack(1100, 0, 3);
|
||||
points_input_->SetStandardValueOnTrack(800, 1, 3);
|
||||
points_input_->SetStandardValueOnTrack(1280, 0, 4);
|
||||
points_input_->SetStandardValueOnTrack(480, 1, 4);
|
||||
InputArrayResize(kPointsInput, 5);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, 960, 0);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, 240, 0);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, 640, 1);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 1);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, 760, 2);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 2);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, 1100, 3);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 3);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, 1200, 4);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 4);
|
||||
}
|
||||
|
||||
Node *PolygonGenerator::copy() const
|
||||
@@ -73,8 +75,8 @@ QString PolygonGenerator::Description() const
|
||||
|
||||
void PolygonGenerator::Retranslate()
|
||||
{
|
||||
points_input_->set_name(tr("Points"));
|
||||
color_input_->set_name(tr("Color"));
|
||||
SetInputName(kPointsInput, tr("Points"));
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
}
|
||||
|
||||
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")));
|
||||
}
|
||||
|
||||
NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(points_input_, value);
|
||||
job.InsertValue(color_input_, value);
|
||||
job.InsertValue(this, kPointsInput, value);
|
||||
job.InsertValue(this, kColorInput, value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
||||
job.SetAlphaChannelRequired(true);
|
||||
|
||||
@@ -168,10 +172,10 @@ void PolygonGenerator::GizmoRelease()
|
||||
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
|
||||
{
|
||||
// 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());
|
||||
|
||||
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>();
|
||||
|
||||
v *= scale;
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
virtual void Retranslate() 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 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 GizmoRelease() override;
|
||||
|
||||
static const QString kPointsInput;
|
||||
static const QString kColorInput;
|
||||
|
||||
private:
|
||||
QVector<QPointF> GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const;
|
||||
|
||||
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
|
||||
|
||||
NodeInput* points_input_;
|
||||
|
||||
NodeInput* color_input_;
|
||||
|
||||
NodeInput* gizmo_drag_;
|
||||
QPointF gizmo_drag_start_;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString SolidGenerator::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
SolidGenerator::SolidGenerator()
|
||||
{
|
||||
// Default to a color that isn't black
|
||||
color_input_ = new NodeInput(this,
|
||||
QStringLiteral("color_in"),
|
||||
NodeValue::kColor,
|
||||
QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f)));
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f)));
|
||||
}
|
||||
|
||||
Node *SolidGenerator::copy() const
|
||||
@@ -60,13 +59,15 @@ QString SolidGenerator::Description() const
|
||||
|
||||
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;
|
||||
job.InsertValue(color_input_, value);
|
||||
job.InsertValue(this, kColorInput, value);
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
|
||||
@@ -40,11 +40,10 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NodeInput* color_input_;
|
||||
static const QString kColorInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -30,31 +30,23 @@ enum TextVerticalAlign {
|
||||
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()
|
||||
{
|
||||
text_input_ = new NodeInput(this,
|
||||
QStringLiteral("text_in"),
|
||||
NodeValue::kText,
|
||||
tr("Sample Text"));
|
||||
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||
|
||||
color_input_ = new NodeInput(this,
|
||||
QStringLiteral("color_in"),
|
||||
NodeValue::kColor,
|
||||
QVariant::fromValue(Color(1.0f, 1.0f, 1.0)));
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0)));
|
||||
|
||||
valign_input_ = new NodeInput(this,
|
||||
QStringLiteral("valign_in"),
|
||||
NodeValue::kCombo,
|
||||
1);
|
||||
AddInput(kVAlignInput, NodeValue::kCombo, 1);
|
||||
|
||||
font_input_ = new NodeInput(this,
|
||||
QStringLiteral("font_in"),
|
||||
NodeValue::kFont);
|
||||
AddInput(kFontInput, NodeValue::kFont);
|
||||
|
||||
font_size_input_ = new NodeInput(this,
|
||||
QStringLiteral("font_size_in"),
|
||||
NodeValue::kFloat,
|
||||
72.0f);
|
||||
AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f);
|
||||
}
|
||||
|
||||
Node *TextGenerator::copy() const
|
||||
@@ -84,27 +76,29 @@ QString TextGenerator::Description() const
|
||||
|
||||
void TextGenerator::Retranslate()
|
||||
{
|
||||
text_input_->set_name(tr("Text"));
|
||||
font_input_->set_name(tr("Font"));
|
||||
font_size_input_->set_name(tr("Font Size"));
|
||||
color_input_->set_name(tr("Color"));
|
||||
valign_input_->set_name(tr("Vertical Align"));
|
||||
valign_input_->set_combobox_strings({tr("Top"), tr("Center"), tr("Bottom")});
|
||||
SetInputName(kTextInput, tr("Text"));
|
||||
SetInputName(kFontInput, tr("Font"));
|
||||
SetInputName(kFontSizeInput, tr("Font Size"));
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
SetInputName(kVAlignInput, tr("Vertical Align"));
|
||||
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;
|
||||
job.InsertValue(text_input_, value);
|
||||
job.InsertValue(color_input_, value);
|
||||
job.InsertValue(valign_input_, value);
|
||||
job.InsertValue(font_input_, value);
|
||||
job.InsertValue(font_size_input_, value);
|
||||
job.InsertValue(this, kTextInput, value);
|
||||
job.InsertValue(this, kColorInput, value);
|
||||
job.InsertValue(this, kVAlignInput, value);
|
||||
job.InsertValue(this, kFontInput, value);
|
||||
job.InsertValue(this, kFontSizeInput, value);
|
||||
job.SetAlphaChannelRequired(true);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -124,14 +118,14 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
|
||||
// Set default font
|
||||
QFont default_font;
|
||||
default_font.setFamily(job.GetValue(font_input_).data().toString());
|
||||
default_font.setPointSizeF(job.GetValue(font_size_input_).data().toFloat());
|
||||
default_font.setFamily(job.GetValue(kFontInput).data().toString());
|
||||
default_font.setPointSizeF(job.GetValue(kFontSizeInput).data().toFloat());
|
||||
text_doc.setDefaultFont(default_font);
|
||||
|
||||
// Center by default
|
||||
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
|
||||
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
|
||||
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();
|
||||
|
||||
switch (valign) {
|
||||
@@ -165,7 +159,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
text_doc.drawContents(&p);
|
||||
|
||||
// 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 y=0; y<frame->height(); y++) {
|
||||
uchar src_alpha = img.bits()[img.bytesPerLine() * y + x];
|
||||
|
||||
@@ -40,20 +40,15 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NodeInput* text_input_;
|
||||
|
||||
NodeInput* color_input_;
|
||||
|
||||
NodeInput* valign_input_;
|
||||
|
||||
NodeInput* font_input_;
|
||||
|
||||
NodeInput* font_size_input_;
|
||||
static const QString kTextInput;
|
||||
static const QString kColorInput;
|
||||
static const QString kVAlignInput;
|
||||
static const QString kFontInput;
|
||||
static const QString kFontSizeInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -65,11 +65,11 @@ signals:
|
||||
*/
|
||||
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:
|
||||
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 {
|
||||
|
||||
const QString MediaInput::kFootageInput = QStringLiteral("footage_in");
|
||||
|
||||
MediaInput::MediaInput() :
|
||||
connected_footage_(nullptr)
|
||||
{
|
||||
footage_input_ = new NodeInput(this, QStringLiteral("footage_in"), NodeValue::kFootage);
|
||||
footage_input_->SetConnectable(false);
|
||||
footage_input_->SetKeyframable(false);
|
||||
connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged);
|
||||
AddInput(kFootageInput, NodeValue::kFootage, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> MediaInput::Category() const
|
||||
@@ -41,21 +40,23 @@ QVector<Node::CategoryID> MediaInput::Category() const
|
||||
|
||||
Stream *MediaInput::stream() const
|
||||
{
|
||||
return Node::ValueToPtr<Stream>(footage_input_->GetStandardValue());
|
||||
return Node::ValueToPtr<Stream>(GetStandardValue(kFootageInput));
|
||||
}
|
||||
|
||||
void MediaInput::SetStream(Stream* s)
|
||||
{
|
||||
footage_input_->SetStandardValue(Node::PtrToValue(s));
|
||||
SetStandardValue(kFootageInput, Node::PtrToValue(s));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (connected_footage_) {
|
||||
@@ -68,28 +69,32 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
|
||||
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_) {
|
||||
return;
|
||||
}
|
||||
if (input == kFootageInput) {
|
||||
Stream* new_footage = stream();
|
||||
|
||||
if (connected_footage_) {
|
||||
disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
||||
}
|
||||
if (new_footage == connected_footage_) {
|
||||
return;
|
||||
}
|
||||
|
||||
connected_footage_ = new_footage;
|
||||
if (connected_footage_) {
|
||||
disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
||||
}
|
||||
|
||||
if (connected_footage_) {
|
||||
connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
|
||||
connected_footage_ = new_footage;
|
||||
|
||||
if (connected_footage_) {
|
||||
connect(connected_footage_, &Stream::ParametersChanged, this, &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 NodeValueTable Value(NodeValueDatabase& value) const override;
|
||||
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||
|
||||
static const QString kFootageInput;
|
||||
|
||||
protected:
|
||||
NodeInput* footage_input_;
|
||||
virtual void InputValueChangedEvent(const QString& input, int element);
|
||||
|
||||
Stream* connected_footage_;
|
||||
|
||||
private slots:
|
||||
void FootageChanged();
|
||||
|
||||
void FootageParametersChanged();
|
||||
|
||||
};
|
||||
|
||||
@@ -51,8 +51,10 @@ QString TimeInput::Description() const
|
||||
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();
|
||||
|
||||
table.Push(NodeValue::kFloat,
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
virtual QVector<CategoryID> Category() 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;
|
||||
|
||||
|
||||
+28
-25
@@ -26,61 +26,64 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeInputDragger::NodeInputDragger() :
|
||||
input_(nullptr)
|
||||
NodeInputDragger::NodeInputDragger()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
input_ = input;
|
||||
time_ = time;
|
||||
track_ = track;
|
||||
element_ = element;
|
||||
|
||||
Node* node = input_.input().node();
|
||||
|
||||
// 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
|
||||
if (input_->IsKeyframing(element_)) {
|
||||
dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_);
|
||||
if (input_.input().IsKeyframing()) {
|
||||
dragging_key_ = node->GetKeyframeAtTimeOnTrack(input_, time);
|
||||
drag_created_key_ = !dragging_key_;
|
||||
|
||||
if (drag_created_key_) {
|
||||
dragging_key_ = new NodeKeyframe(time,
|
||||
start_value_,
|
||||
input_->GetBestKeyframeTypeForTime(time, track, element_),
|
||||
track,
|
||||
element_,
|
||||
input_);
|
||||
node->GetBestKeyframeTypeForTimeOnTrack(input_, time),
|
||||
input_.track(),
|
||||
input_.input().element(),
|
||||
input_.input().input(),
|
||||
node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
double min = input_->property("min").toDouble();
|
||||
double min = node->GetInputProperty(input, QStringLiteral("min")).toDouble();
|
||||
double v = value.toDouble();
|
||||
if (v < min) {
|
||||
value = min;
|
||||
}
|
||||
}
|
||||
|
||||
if (input_->property("max").isValid()) {
|
||||
double max = input_->property("max").toDouble();
|
||||
if (node->HasInputProperty(input, QStringLiteral("max"))) {
|
||||
double max = node->GetInputProperty(input, QStringLiteral("max")).toDouble();
|
||||
double v = value.toDouble();
|
||||
if (v > max) {
|
||||
value = max;
|
||||
@@ -91,10 +94,10 @@ void NodeInputDragger::Drag(QVariant value)
|
||||
|
||||
//input_->blockSignals(true);
|
||||
|
||||
if (input_->IsKeyframing(element_)) {
|
||||
if (input_.input().IsKeyframing()) {
|
||||
dragging_key_->set_value(value);
|
||||
} else {
|
||||
input_->SetStandardValueOnTrack(value, track_, element_);
|
||||
node->SetSplitStandardValueOnTrack(input_, value);
|
||||
}
|
||||
|
||||
//input_->blockSignals(false);
|
||||
@@ -108,10 +111,10 @@ void NodeInputDragger::End()
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
if (input_->IsKeyframing(element_)) {
|
||||
if (input_.input().node()->IsInputKeyframing(input_.input())) {
|
||||
if (drag_created_key_) {
|
||||
// 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
|
||||
@@ -120,12 +123,12 @@ void NodeInputDragger::End()
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_));
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
input_ = nullptr;
|
||||
input_.Reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#ifndef NODEINPUTDRAGGER_H
|
||||
#define NODEINPUTDRAGGER_H
|
||||
|
||||
#include "node/input.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "node/param.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -32,21 +34,17 @@ public:
|
||||
|
||||
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 End();
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
|
||||
int track_;
|
||||
NodeKeyframeTrackReference input_;
|
||||
|
||||
rational time_;
|
||||
|
||||
int element_;
|
||||
|
||||
QVariant start_value_;
|
||||
|
||||
QVariant end_value_;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "common/bezier.h"
|
||||
#include "common/lerp.h"
|
||||
#include "common/tohex.h"
|
||||
#include "input.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -53,7 +53,12 @@ public:
|
||||
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);
|
||||
|
||||
|
||||
@@ -20,18 +20,19 @@
|
||||
|
||||
#include "keyframe.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
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),
|
||||
value_(value),
|
||||
type_(type),
|
||||
bezier_control_in_(QPointF(0.0, 0.0)),
|
||||
bezier_control_out_(QPointF(0.0, 0.0)),
|
||||
input_(input),
|
||||
track_(track),
|
||||
element_(element),
|
||||
previous_(nullptr),
|
||||
@@ -47,7 +48,7 @@ NodeKeyframe::~NodeKeyframe()
|
||||
|
||||
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_out_ = bezier_control_out_;
|
||||
return copy;
|
||||
@@ -58,9 +59,9 @@ NodeKeyframe *NodeKeyframe::copy(QObject* parent) const
|
||||
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
|
||||
|
||||
+15
-4
@@ -26,11 +26,11 @@
|
||||
#include <QVariant>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "node/param.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeInput;
|
||||
class NodeInputImmediate;
|
||||
class Node;
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
|
||||
NodeKeyframe* copy(int element, 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
|
||||
@@ -194,6 +203,8 @@ private:
|
||||
|
||||
QPointF bezier_control_out_;
|
||||
|
||||
QString input_;
|
||||
|
||||
int track_;
|
||||
|
||||
int element_;
|
||||
|
||||
+26
-21
@@ -22,19 +22,22 @@
|
||||
|
||||
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()
|
||||
{
|
||||
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
|
||||
method_in_->SetConnectable(false);
|
||||
method_in_->SetKeyframable(false);
|
||||
AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
param_a_in_ = new NodeInput(this, QStringLiteral("param_a_in"), NodeValue::kFloat, 0.0);
|
||||
param_a_in_->setProperty("decimalplaces", 8);
|
||||
param_a_in_->setProperty("autotrim", true);
|
||||
AddInput(kParamAIn, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8);
|
||||
SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true);
|
||||
|
||||
param_b_in_ = new NodeInput(this, QStringLiteral("param_b_in"), NodeValue::kFloat, 0.0);
|
||||
param_b_in_->setProperty("decimalplaces", 8);
|
||||
param_b_in_->setProperty("autotrim", true);
|
||||
AddInput(kParamBIn, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8);
|
||||
SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true);
|
||||
}
|
||||
|
||||
Node *MathNode::copy() const
|
||||
@@ -66,9 +69,9 @@ void MathNode::Retranslate()
|
||||
{
|
||||
Node::Retranslate();
|
||||
|
||||
method_in_->set_name(tr("Method"));
|
||||
param_a_in_->set_name(tr("Value"));
|
||||
param_b_in_->set_name(tr("Value"));
|
||||
SetInputName(kMethodIn, tr("Method"));
|
||||
SetInputName(kParamAIn, tr("Value"));
|
||||
SetInputName(kParamBIn, tr("Value"));
|
||||
|
||||
QStringList operations = {tr("Add"),
|
||||
tr("Subtract"),
|
||||
@@ -77,19 +80,21 @@ void MathNode::Retranslate()
|
||||
QString(),
|
||||
tr("Power")};
|
||||
|
||||
method_in_->set_combobox_strings(operations);
|
||||
SetComboBoxStrings(kMethodIn, operations);
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
if (!calc.FoundMostLikelyPairing()) {
|
||||
@@ -97,23 +102,23 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const
|
||||
}
|
||||
|
||||
NodeValue val_a = calc.GetMostLikelyValueA();
|
||||
value[param_a_in_].Remove(val_a);
|
||||
value[kParamAIn].Remove(val_a);
|
||||
|
||||
NodeValue val_b = calc.GetMostLikelyValueB();
|
||||
value[param_b_in_].Remove(val_b);
|
||||
value[kParamBIn].Remove(val_b);
|
||||
|
||||
return ValueInternal(value,
|
||||
GetOperation(),
|
||||
calc.GetMostLikelyPairing(),
|
||||
param_a_in_,
|
||||
kParamAIn,
|
||||
val_a,
|
||||
param_b_in_,
|
||||
kParamBIn,
|
||||
val_b);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return static_cast<Operation>(method_in_->GetStandardValue().toInt());
|
||||
return static_cast<Operation>(GetStandardValue(kMethodIn).toInt());
|
||||
}
|
||||
|
||||
void SetOperation(Operation o)
|
||||
{
|
||||
method_in_->SetStandardValue(o);
|
||||
SetStandardValue(kMethodIn, o);
|
||||
}
|
||||
|
||||
NodeInput* param_a_in() const
|
||||
{
|
||||
return param_a_in_;
|
||||
}
|
||||
|
||||
NodeInput* param_b_in() const
|
||||
{
|
||||
return param_b_in_;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NodeInput* method_in_;
|
||||
|
||||
NodeInput* param_a_in_;
|
||||
|
||||
NodeInput* param_b_in_;
|
||||
static const QString kMethodIn;
|
||||
static const QString kParamAIn;
|
||||
static const QString kParamBIn;
|
||||
static const QString kParamCIn;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
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('.');
|
||||
|
||||
@@ -43,11 +43,11 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
||||
if (pairing == kPairTextureMatrix && op == kOpMultiply) {
|
||||
|
||||
// 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;
|
||||
NodeInput* mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
|
||||
const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in;
|
||||
const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
|
||||
|
||||
// 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"
|
||||
"\n"
|
||||
@@ -59,7 +59,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
||||
"void main() {\n"
|
||||
" gl_Position = %1 * a_position;\n"
|
||||
" ove_texcoord = a_texcoord;\n"
|
||||
"}\n").arg(mat_in->id());
|
||||
"}\n").arg(mat_in);
|
||||
|
||||
} else {
|
||||
switch (op) {
|
||||
@@ -89,8 +89,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
||||
break;
|
||||
}
|
||||
|
||||
operation = operation.arg(GetShaderVariableCall(param_a_in->id(), type_a),
|
||||
GetShaderVariableCall(param_b_in->id(), type_b));
|
||||
operation = operation.arg(GetShaderVariableCall(param_a_in, type_a),
|
||||
GetShaderVariableCall(param_b_in, type_b));
|
||||
}
|
||||
|
||||
frag = QStringLiteral("uniform %1 %3;\n"
|
||||
@@ -104,8 +104,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
||||
" fragColor = %5;\n"
|
||||
"}\n").arg(GetShaderUniformType(type_a),
|
||||
GetShaderUniformType(type_b),
|
||||
param_a_in->id(),
|
||||
param_b_in->id(),
|
||||
param_a_in,
|
||||
param_b_in,
|
||||
operation);
|
||||
|
||||
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();
|
||||
|
||||
@@ -342,7 +342,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
||||
{
|
||||
// Queue a sample job
|
||||
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);
|
||||
|
||||
@@ -350,7 +350,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
||||
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
|
||||
|
||||
if (job.HasSamples()) {
|
||||
if (number_param->IsStatic()) {
|
||||
if (IsInputStatic(number_param)) {
|
||||
if (!NumberIsNoOp(operation, number)) {
|
||||
for (int i=0;i<job.samples()->audio_params().channel_count();i++) {
|
||||
for (int j=0;j<job.samples()->sample_count();j++) {
|
||||
@@ -375,7 +375,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
||||
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
|
||||
NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber);
|
||||
|
||||
@@ -109,13 +109,13 @@ protected:
|
||||
|
||||
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;
|
||||
|
||||
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 {
|
||||
|
||||
const QString MergeNode::kBaseIn = QStringLiteral("base_in");
|
||||
const QString MergeNode::kBlendIn = QStringLiteral("blend_in");
|
||||
|
||||
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
|
||||
@@ -56,8 +59,9 @@ QString MergeNode::Description() const
|
||||
|
||||
void MergeNode::Retranslate()
|
||||
{
|
||||
base_in_->set_name(tr("Base"));
|
||||
blend_in_->set_name(tr("Blend"));
|
||||
SetInputName(kBaseIn, tr("Base"));
|
||||
|
||||
SetInputName(kBlendIn, tr("Blend"));
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
job.InsertValue(base_in_, value);
|
||||
job.InsertValue(blend_in_, value);
|
||||
job.InsertValue(this, kBaseIn, value);
|
||||
job.InsertValue(this, kBlendIn, value);
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
TexturePtr base_tex = job.GetValue(base_in_).data().value<TexturePtr>();
|
||||
TexturePtr blend_tex = job.GetValue(blend_in_).data().value<TexturePtr>();
|
||||
TexturePtr base_tex = job.GetValue(kBaseIn).data().value<TexturePtr>();
|
||||
TexturePtr blend_tex = job.GetValue(kBlendIn).data().value<TexturePtr>();
|
||||
|
||||
if (base_tex || blend_tex) {
|
||||
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
|
||||
table.Push(job.GetValue(blend_in_));
|
||||
table.Push(job.GetValue(kBlendIn));
|
||||
} else if (!blend_tex) {
|
||||
// We only have a base texture, no need to alpha over
|
||||
table.Push(job.GetValue(base_in_));
|
||||
table.Push(job.GetValue(kBaseIn));
|
||||
} else {
|
||||
// We have both textures, push the job
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
@@ -94,16 +100,6 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
||||
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
|
||||
{
|
||||
// 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 blend_changed_hash = false;
|
||||
|
||||
if (base_in_->IsConnected()) {
|
||||
base_in_->GetConnectedNode()->Hash(hash, time);
|
||||
if (IsInputConnected(kBaseIn)) {
|
||||
GetConnectedNode(kBaseIn)->Hash(hash, time);
|
||||
|
||||
QByteArray post_base_hash = hash.result();
|
||||
base_changed_hash = (post_base_hash != current_result);
|
||||
current_result = post_base_hash;
|
||||
}
|
||||
|
||||
if (blend_in_->IsConnected()) {
|
||||
blend_in_->GetConnectedNode()->Hash(hash, time);
|
||||
if(IsInputConnected(kBlendIn)) {
|
||||
GetConnectedNode(kBlendIn)->Hash(hash, time);
|
||||
|
||||
blend_changed_hash = (hash.result() != current_result);
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ public:
|
||||
virtual void Retranslate() 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;
|
||||
NodeInput* blend_in() const;
|
||||
static const QString kBaseIn;
|
||||
static const QString kBlendIn;
|
||||
|
||||
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
|
||||
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString TrigonometryNode::kMethodIn = QStringLiteral("method_in");
|
||||
const QString TrigonometryNode::kXIn = QStringLiteral("x_in");
|
||||
|
||||
TrigonometryNode::TrigonometryNode()
|
||||
{
|
||||
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
|
||||
method_in_->SetConnectable(false);
|
||||
method_in_->SetKeyframable(false);
|
||||
AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0);
|
||||
AddInput(kXIn, NodeValue::kFloat, 0.0);
|
||||
}
|
||||
|
||||
olive::Node *olive::TrigonometryNode::copy() const
|
||||
@@ -70,18 +71,20 @@ void TrigonometryNode::Retranslate()
|
||||
tr("Hyperbolic Cosine"),
|
||||
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();
|
||||
|
||||
switch (static_cast<Operation>(method_in_->GetStandardValue().toInt())) {
|
||||
switch (static_cast<Operation>(GetStandardValue(kMethodIn).toInt())) {
|
||||
case kOpSine:
|
||||
x = qSin(x);
|
||||
break;
|
||||
|
||||
@@ -40,7 +40,10 @@ public:
|
||||
|
||||
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:
|
||||
enum Operation {
|
||||
@@ -55,10 +58,6 @@ private:
|
||||
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
|
||||
#define NODE_H
|
||||
|
||||
#include <map>
|
||||
#include <QCryptographicHash>
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
@@ -30,15 +31,17 @@
|
||||
#include "codec/frame.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "common/rational.h"
|
||||
#include "common/timerange.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "node/connectable.h"
|
||||
#include "node/input.h"
|
||||
#include "node/value.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "node/inputimmediate.h"
|
||||
#include "node/param.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/job/generatejob.h"
|
||||
#include "render/job/samplejob.h"
|
||||
#include "render/job/shaderjob.h"
|
||||
#include "render/shadercode.h"
|
||||
#include "splitvalue.h"
|
||||
|
||||
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.
|
||||
* 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
|
||||
public:
|
||||
@@ -80,7 +83,7 @@ public:
|
||||
kCategoryCount
|
||||
};
|
||||
|
||||
Node();
|
||||
Node(bool create_default_output = true);
|
||||
|
||||
virtual ~Node() override;
|
||||
|
||||
@@ -154,6 +157,31 @@ public:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -180,26 +208,266 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a list of NodeParams
|
||||
*/
|
||||
const QVector<NodeInput*>& parameters() const
|
||||
static void ConnectEdge(const NodeOutput& output, const NodeInput& input);
|
||||
|
||||
static void DisconnectEdge(const NodeOutput& output, const NodeInput& input);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the index of a parameter
|
||||
* @return Parameter index or -1 if this parameter is not part of this Node
|
||||
*/
|
||||
int IndexOfParameter(NodeInput* param) const
|
||||
bool IsInputConnected(const QString& input, int element = -1) const;
|
||||
bool IsInputConnected(const NodeInput& input) 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;
|
||||
|
||||
/**
|
||||
* @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`
|
||||
*
|
||||
@@ -257,7 +520,6 @@ public:
|
||||
* (FALSE).
|
||||
*/
|
||||
bool OutputsTo(Node* n, bool recursively) const;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
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
|
||||
@@ -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
|
||||
* 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
|
||||
@@ -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
|
||||
* 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()
|
||||
*/
|
||||
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
|
||||
@@ -365,6 +631,14 @@ public:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -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
|
||||
* of the NodeParam objects will handle everything related to it automatically.
|
||||
*/
|
||||
virtual NodeValueTable Value(NodeValueDatabase& value) const;
|
||||
|
||||
/**
|
||||
* @brief Return whether a parameter with ID `id` has already been added to this Node
|
||||
*/
|
||||
bool HasParamWithID(const QString& id) const;
|
||||
virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const;
|
||||
|
||||
const QPointF& GetPosition() const;
|
||||
|
||||
@@ -418,12 +687,7 @@ public:
|
||||
|
||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const;
|
||||
|
||||
const std::vector<InputConnection>& edges() const
|
||||
{
|
||||
return output_connections();
|
||||
}
|
||||
|
||||
void InvalidateAll(NodeInput *input, int element);
|
||||
void InvalidateAll(const QString& input, int element = -1);
|
||||
|
||||
bool HasLinks() const
|
||||
{
|
||||
@@ -439,7 +703,58 @@ public:
|
||||
static bool Unlink(Node* a, Node* b);
|
||||
static bool AreLinked(Node* a, Node* b);
|
||||
|
||||
static const QString kDefaultOutput;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
@@ -449,14 +764,14 @@ protected:
|
||||
* 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
|
||||
*/
|
||||
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 SaveInternal(QXmlStreamWriter* writer) const;
|
||||
|
||||
virtual QVector<NodeInput*> GetInputsToHash() const;
|
||||
|
||||
enum GizmoScaleHandles {
|
||||
kGizmoScaleTopLeft,
|
||||
kGizmoScaleTopCenter,
|
||||
@@ -475,10 +790,16 @@ protected:
|
||||
|
||||
static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count);
|
||||
|
||||
virtual void childEvent(QChildEvent* event) override;
|
||||
|
||||
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:
|
||||
/**
|
||||
* @brief Signal emitted whenever the position is set through SetPosition()
|
||||
@@ -492,19 +813,231 @@ signals:
|
||||
|
||||
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 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:
|
||||
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>
|
||||
static void FindInputNodeInternal(const Node* n, QVector<T *>& list);
|
||||
|
||||
@@ -513,11 +1046,29 @@ private:
|
||||
|
||||
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
|
||||
@@ -544,29 +1095,59 @@ private:
|
||||
*/
|
||||
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:
|
||||
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>
|
||||
void Node::FindInputNodeInternal(const Node* n, QVector<T *> &list)
|
||||
{
|
||||
foreach (NodeInput* input, n->inputs_) {
|
||||
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
|
||||
Node* edge = it->second;
|
||||
T* cast_test = dynamic_cast<T*>(edge);
|
||||
for (auto it=n->input_connections_.cbegin(); it!=n->input_connections_.cend(); it++) {
|
||||
Node* edge = it->second.node();
|
||||
T* cast_test = dynamic_cast<T*>(edge);
|
||||
|
||||
if (cast_test) {
|
||||
list.append(cast_test);
|
||||
}
|
||||
|
||||
FindInputNodeInternal<T>(edge, list);
|
||||
if (cast_test) {
|
||||
list.append(cast_test);
|
||||
}
|
||||
|
||||
FindInputNodeInternal<T>(edge, list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,15 +1170,17 @@ T* Node::ValueToPtr(const QVariant &ptr)
|
||||
template<class T>
|
||||
void Node::FindOutputNodeInternal(const Node* n, QVector<T *>& list)
|
||||
{
|
||||
foreach (const InputConnection& edge, n->edges()) {
|
||||
Node* connected = static_cast<Node*>(edge.input->parent());
|
||||
T* cast_test = dynamic_cast<T*>(connected);
|
||||
foreach (const std::vector<NodeInput>& outputs, n->output_connections_) {
|
||||
foreach (const NodeInput& output, outputs) {
|
||||
Node* connected = output.node();
|
||||
T* cast_test = dynamic_cast<T*>(connected);
|
||||
|
||||
if (cast_test) {
|
||||
list.append(cast_test);
|
||||
if (cast_test) {
|
||||
list.append(cast_test);
|
||||
}
|
||||
|
||||
FindOutputNodeInternal<T>(connected);
|
||||
}
|
||||
|
||||
FindOutputNodeInternal<T>(connected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,37 +132,6 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
// Link blocks
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -172,6 +141,7 @@ void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, void*
|
||||
|
||||
void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
|
||||
{
|
||||
Q_UNUSED(xml_node_data)
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
|
||||
|
||||
+169
-169
@@ -33,24 +33,21 @@ const double Track::kTrackHeightDefault = 3.0;
|
||||
const double Track::kTrackHeightMinimum = 1.5;
|
||||
const double Track::kTrackHeightInterval = 0.5;
|
||||
|
||||
const QString Track::kBlockInput = QStringLiteral("block_in");
|
||||
const QString Track::kMutedInput = QStringLiteral("muted_in");
|
||||
|
||||
Track::Track() :
|
||||
track_type_(Track::kNone),
|
||||
index_(-1),
|
||||
locked_(false)
|
||||
{
|
||||
block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone);
|
||||
block_input_->SetKeyframable(false);
|
||||
block_input_->SetIsArray(true);
|
||||
connect(block_input_, &NodeInput::InputConnected, this, &Track::BlockConnected);
|
||||
connect(block_input_, &NodeInput::InputDisconnected, this, &Track::BlockDisconnected);
|
||||
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
|
||||
|
||||
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
||||
// on our end
|
||||
IgnoreInvalidationsFrom(block_input_);
|
||||
IgnoreInvalidationsFrom(kBlockInput);
|
||||
|
||||
muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean);
|
||||
muted_input_->SetKeyframable(false);
|
||||
connect(muted_input_, &NodeInput::ValueChanged, this, &Track::MutedInputValueChanged);
|
||||
AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
// Set default height
|
||||
track_height_ = kTrackHeightDefault;
|
||||
@@ -97,9 +94,9 @@ QString Track::Description() const
|
||||
"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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Node::Retranslate();
|
||||
|
||||
block_input_->set_name(tr("Blocks"));
|
||||
muted_input_->set_name(tr("Muted"));
|
||||
SetInputName(kBlockInput, tr("Blocks"));
|
||||
SetInputName(kMutedInput, tr("Muted"));
|
||||
}
|
||||
|
||||
void Track::SetIndex(const int &index)
|
||||
@@ -276,15 +414,15 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
||||
return list;
|
||||
}
|
||||
|
||||
void Track::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||
{
|
||||
TimeRange limited;
|
||||
|
||||
Block* b;
|
||||
const Block* b;
|
||||
|
||||
if (from.input == block_input_
|
||||
&& from.element >= 0
|
||||
&& (b = dynamic_cast<Block*>(from.input->GetConnectedNode(from.element)))) {
|
||||
if (from == kBlockInput
|
||||
&& element >= 0
|
||||
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))) {
|
||||
// Limit the range signal to the corresponding block
|
||||
if (range.out() <= b->in() || range.in() >= b->out()) {
|
||||
return;
|
||||
@@ -329,8 +467,8 @@ void Track::PrependBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->ArrayPrepend();
|
||||
Node::ConnectEdge(block, block_input_, 0);
|
||||
InputArrayPrepend(kBlockInput);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, 0));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -343,8 +481,8 @@ void Track::InsertBlockAtIndex(Block *block, int index)
|
||||
BeginOperation();
|
||||
|
||||
int insert_index = GetArrayIndexFromCacheIndex(index);
|
||||
block_input_->ArrayInsert(insert_index);
|
||||
Node::ConnectEdge(block, block_input_, insert_index);
|
||||
InputArrayInsert(kBlockInput, insert_index);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, insert_index));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -355,8 +493,8 @@ void Track::AppendBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->ArrayAppend();
|
||||
Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1);
|
||||
InputArrayAppend(kBlockInput);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, InputArraySize(kBlockInput) - 1));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -371,7 +509,7 @@ void Track::RippleRemoveBlock(Block *block)
|
||||
rational remove_in = block->in();
|
||||
rational remove_out = block->out();
|
||||
|
||||
block_input_->ArrayRemove(GetArrayIndexFromBlock(block));
|
||||
InputArrayRemove(kBlockInput, GetArrayIndexFromBlock(block));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -384,9 +522,9 @@ void Track::ReplaceBlock(Block *old, Block *replace)
|
||||
|
||||
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();
|
||||
|
||||
@@ -421,7 +559,7 @@ QString Track::GetDefaultTrackName(Track::Type type, int index)
|
||||
|
||||
bool Track::IsMuted() const
|
||||
{
|
||||
return muted_input_->GetStandardValue().toBool();
|
||||
return GetStandardValue(kMutedInput).toBool();
|
||||
}
|
||||
|
||||
bool Track::IsLocked() const
|
||||
@@ -429,11 +567,6 @@ bool Track::IsLocked() const
|
||||
return locked_;
|
||||
}
|
||||
|
||||
NodeInput *Track::block_input() const
|
||||
{
|
||||
return block_input_;
|
||||
}
|
||||
|
||||
void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
{
|
||||
Block* b = BlockAtTime(time);
|
||||
@@ -446,8 +579,7 @@ void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
|
||||
void Track::SetMuted(bool e)
|
||||
{
|
||||
muted_input_->SetStandardValue(e);
|
||||
InvalidateCache(TimeRange(0, track_length()));
|
||||
SetStandardValue(kMutedInput, 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()
|
||||
{
|
||||
// Assumes sender is a Block
|
||||
@@ -654,11 +659,6 @@ void Track::BlockLengthChanged()
|
||||
InvalidateCache(invalidate_region);
|
||||
}
|
||||
|
||||
void Track::MutedInputValueChanged()
|
||||
{
|
||||
emit MutedChanged(IsMuted());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -56,9 +56,9 @@ public:
|
||||
virtual QVector<CategoryID> Category() 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);
|
||||
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
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
|
||||
@@ -274,8 +274,6 @@ public:
|
||||
|
||||
bool IsLocked() const;
|
||||
|
||||
NodeInput* block_input() const;
|
||||
|
||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
||||
|
||||
AudioVisualWaveform& waveform()
|
||||
@@ -287,6 +285,9 @@ public:
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
|
||||
static const QString kBlockInput;
|
||||
static const QString kMutedInput;
|
||||
|
||||
public slots:
|
||||
void SetMuted(bool e);
|
||||
|
||||
@@ -338,6 +339,12 @@ protected:
|
||||
|
||||
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:
|
||||
void UpdateInOutFrom(int index);
|
||||
|
||||
@@ -352,10 +359,6 @@ private:
|
||||
QVector<Block*> blocks_;
|
||||
QVector<int> block_array_indexes_;
|
||||
|
||||
NodeInput* block_input_;
|
||||
|
||||
NodeInput* muted_input_;
|
||||
|
||||
Track::Type track_type_;
|
||||
|
||||
rational track_length_;
|
||||
@@ -371,14 +374,8 @@ private:
|
||||
AudioVisualWaveform waveform_;
|
||||
|
||||
private slots:
|
||||
void BlockConnected(Node* node, int element);
|
||||
|
||||
void BlockDisconnected(Node* node, int element);
|
||||
|
||||
void BlockLengthChanged();
|
||||
|
||||
void MutedInputValueChanged();
|
||||
|
||||
};
|
||||
|
||||
uint qHash(const Track::Reference& r, uint seed = 0);
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
|
||||
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),
|
||||
track_input_(track_input),
|
||||
type_(type)
|
||||
{
|
||||
connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected);
|
||||
connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected);
|
||||
}
|
||||
|
||||
Track *TrackList::GetTrackAt(int index) const
|
||||
@@ -48,7 +46,7 @@ Track *TrackList::GetTrackAt(int index) const
|
||||
void TrackList::TrackConnected(Node *node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
parent()->InvalidateAll(track_input_, element);
|
||||
parent()->InvalidateAll(track_input(), element);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ void TrackList::TrackConnected(Node *node, int element)
|
||||
|
||||
// Determine where in the cache this block will be
|
||||
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
|
||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||
|
||||
@@ -97,7 +95,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
parent()->InvalidateAll(track_input_, element);
|
||||
parent()->InvalidateAll(track_input(), element);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,11 +139,36 @@ NodeGraph *TrackList::GetParentGraph() const
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
total_length_ = 0;
|
||||
|
||||
@@ -35,7 +35,7 @@ class TrackList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
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
|
||||
{
|
||||
@@ -61,13 +61,27 @@ public:
|
||||
|
||||
NodeGraph* GetParentGraph() const;
|
||||
|
||||
NodeInput* track_input() const
|
||||
{
|
||||
return track_input_;
|
||||
}
|
||||
const QString &track_input() const;
|
||||
NodeInput track_input(int element) 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:
|
||||
void TrackListChanged();
|
||||
|
||||
@@ -96,23 +110,13 @@ private:
|
||||
return track_array_indexes_.indexOf(index);
|
||||
}
|
||||
|
||||
NodeInput* track_input_;
|
||||
QString track_input_;
|
||||
|
||||
rational total_length_;
|
||||
|
||||
enum Track::Type type_;
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -24,31 +24,31 @@
|
||||
|
||||
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() :
|
||||
video_frame_cache_(this),
|
||||
audio_playback_cache_(this),
|
||||
operation_stack_(0)
|
||||
{
|
||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
||||
texture_input_->SetKeyframable(false);
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
||||
samples_input_->SetKeyframable(false);
|
||||
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
// Create TrackList instances
|
||||
track_inputs_.resize(Track::kCount);
|
||||
track_lists_.resize(Track::kCount);
|
||||
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
// Create track input
|
||||
NodeInput* track_input = new NodeInput(this, QStringLiteral("track_in_%1").arg(i), NodeValue::kNone);
|
||||
track_input->SetIsArray(true);
|
||||
track_input->SetKeyframable(false);
|
||||
QString track_input_id = kTrackInputFormat.arg(i);
|
||||
|
||||
IgnoreInvalidationsFrom(track_input);
|
||||
track_inputs_.replace(i, track_input);
|
||||
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
|
||||
|
||||
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);
|
||||
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
|
||||
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
|
||||
@@ -110,15 +110,17 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &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 (from.input == texture_input_ || from.input == samples_input_) {
|
||||
if (from == kTextureInput || from == kSamplesInput) {
|
||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||
qMin(GetLength(), range.out()));
|
||||
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from.input == texture_input_) {
|
||||
if (from == kTextureInput) {
|
||||
video_frame_cache_.Invalidate(invalidated_range);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range);
|
||||
@@ -216,8 +218,8 @@ void ViewerOutput::VerifyLength()
|
||||
{
|
||||
video_length = track_lists_.at(Track::kVideo)->GetTotalLength();
|
||||
|
||||
if (video_length.isNull() && texture_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0);
|
||||
if (video_length.isNull() && IsInputConnected(kTextureInput)) {
|
||||
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0));
|
||||
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();
|
||||
|
||||
if (audio_length.isNull() && samples_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0);
|
||||
if (audio_length.isNull() && IsInputConnected(kSamplesInput)) {
|
||||
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0));
|
||||
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
||||
}
|
||||
|
||||
@@ -251,11 +253,11 @@ void ViewerOutput::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;
|
||||
|
||||
switch (static_cast<Track::Type>(i)) {
|
||||
@@ -274,7 +276,7 @@ void ViewerOutput::Retranslate()
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 ShiftCache(const rational& from, const rational& to);
|
||||
|
||||
NodeInput* texture_input() const
|
||||
{
|
||||
return texture_input_;
|
||||
}
|
||||
|
||||
NodeInput* samples_input() const
|
||||
{
|
||||
return samples_input_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
|
||||
|
||||
const VideoParams& video_params() const
|
||||
{
|
||||
@@ -106,11 +96,6 @@ public:
|
||||
*/
|
||||
QVector<Track *> GetUnlockedTracks() const;
|
||||
|
||||
NodeInput* track_input(Track::Type type) const
|
||||
{
|
||||
return track_inputs_.at(type);
|
||||
}
|
||||
|
||||
TrackList* track_list(Track::Type type) const
|
||||
{
|
||||
return track_lists_.at(type);
|
||||
@@ -132,6 +117,10 @@ public:
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kSamplesInput;
|
||||
static const QString kTrackInputFormat;
|
||||
|
||||
signals:
|
||||
void TimebaseChanged(const rational&);
|
||||
|
||||
@@ -149,19 +138,20 @@ signals:
|
||||
void TrackAdded(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:
|
||||
QUuid uuid_;
|
||||
|
||||
NodeInput* texture_input_;
|
||||
|
||||
NodeInput* samples_input_;
|
||||
|
||||
VideoParams video_params_;
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
QVector<NodeInput*> track_inputs_;
|
||||
|
||||
QVector<TrackList*> track_lists_;
|
||||
|
||||
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;
|
||||
|
||||
// We need to insert tables into the database for each input
|
||||
foreach (NodeInput* input, node->parameters()) {
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
if (IsCancelled()) {
|
||||
return NodeValueDatabase();
|
||||
}
|
||||
|
||||
database.Insert(input, ProcessInput(input, range));
|
||||
database.Insert(input, ProcessInput(node, input, range));
|
||||
}
|
||||
|
||||
AddGlobalsToDatabase(database, range);
|
||||
@@ -42,37 +42,35 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
|
||||
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
|
||||
Node* node = input->parent();
|
||||
|
||||
if (input->IsConnected()) {
|
||||
if (node->IsInputConnected(input)) {
|
||||
|
||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range);
|
||||
|
||||
// Value will equal something from the connected node, follow it
|
||||
return GenerateTable(input->GetConnectedNode(), adjusted_range);
|
||||
return GenerateTable(node->GetConnectedOutput(input), adjusted_range);
|
||||
|
||||
} else {
|
||||
|
||||
// Store node
|
||||
QVariant return_val;
|
||||
|
||||
if (input->IsArray()) {
|
||||
if (node->InputIsArray(input)) {
|
||||
|
||||
// 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++) {
|
||||
NodeValueTable& sub_tbl = array_tbl[i];
|
||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, i, range);
|
||||
|
||||
if (input->IsConnected(i)) {
|
||||
sub_tbl = GenerateTable(input->GetConnectedNode(i), adjusted_range);
|
||||
if (node->IsInputConnected(input, i)) {
|
||||
sub_tbl = GenerateTable(node->GetConnectedOutput(input, i), adjusted_range);
|
||||
} else {
|
||||
QVariant input_value = input->GetValueAtTime(adjusted_range.in(), i);
|
||||
sub_tbl.Push(input->GetDataType(), input_value, node);
|
||||
QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), i);
|
||||
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
|
||||
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;
|
||||
return_table.Push(input->GetDataType(), return_val, node, true);
|
||||
return_table.Push(node->GetInputDataType(input), return_val, node, true);
|
||||
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);
|
||||
if (track) {
|
||||
@@ -108,18 +106,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
NodeValueDatabase database = GenerateDatabase(n, range);
|
||||
|
||||
// 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);
|
||||
|
||||
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)
|
||||
{
|
||||
// By default, just follow the in point
|
||||
|
||||
@@ -36,13 +36,16 @@ class NodeTraverser : public CancelableObject
|
||||
public:
|
||||
NodeTraverser() = default;
|
||||
|
||||
NodeValueTable GenerateTable(const Node *n, const TimeRange &range);
|
||||
NodeValueTable GenerateTable(const Node *n, const rational &in, const rational& out);
|
||||
NodeValueTable GenerateTable(const Node *n, const QString &output, const TimeRange &range);
|
||||
NodeValueTable GenerateTable(const NodeOutput& output, const TimeRange &range)
|
||||
{
|
||||
return GenerateTable(output.node(), output.output(), range);
|
||||
}
|
||||
|
||||
NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
+1
-21
@@ -20,13 +20,13 @@
|
||||
|
||||
#include "value.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "common/tohex.h"
|
||||
#include "node/input.h"
|
||||
#include "render/color.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -311,26 +311,6 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
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
|
||||
{
|
||||
int value_index = GetInternal(type, tag);
|
||||
|
||||
+5
-51
@@ -18,16 +18,16 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VALUE_H
|
||||
#define VALUE_H
|
||||
#ifndef NODEVALUE_H
|
||||
#define NODEVALUE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Node;
|
||||
class NodeInput;
|
||||
|
||||
class NodeValue
|
||||
{
|
||||
@@ -374,59 +374,13 @@ public:
|
||||
private:
|
||||
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::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)
|
||||
{
|
||||
Q_UNUSED(nodes)
|
||||
qDebug() << "Stub";
|
||||
//node_view_->SelectBlocks(nodes);
|
||||
}
|
||||
|
||||
void DeselectBlocks(const QVector<Block*>& nodes)
|
||||
{
|
||||
Q_UNUSED(nodes)
|
||||
qDebug() << "Stub";
|
||||
//node_view_->DeselectBlocks(nodes);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ Footage::~Footage()
|
||||
|
||||
void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled)
|
||||
{
|
||||
Q_UNUSED(version)
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
return;
|
||||
|
||||
@@ -67,7 +67,7 @@ Stream *Stream::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
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")) {
|
||||
stream->set_index(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||
|
||||
@@ -207,17 +207,8 @@ void Sequence::Save(QXmlStreamWriter *writer) const
|
||||
void Sequence::add_default_nodes()
|
||||
{
|
||||
// Create tracks and connect them to the viewer
|
||||
Track* video_track = new Track();
|
||||
video_track->setParent(this);
|
||||
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());
|
||||
TimelineAddTrackCommand(viewer_output_->track_list(Track::kVideo)).redo();
|
||||
TimelineAddTrackCommand(viewer_output_->track_list(Track::kAudio)).redo();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
render/job/acceleratedjob.cpp
|
||||
render/job/acceleratedjob.h
|
||||
render/job/generatejob.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
|
||||
#define ACCELERATEDJOB_H
|
||||
|
||||
#include "node/input.h"
|
||||
#include "node/value.h"
|
||||
#include "node/param.h"
|
||||
#include "node/valuedatabase.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -30,31 +30,18 @@ class AcceleratedJob {
|
||||
public:
|
||||
AcceleratedJob() = default;
|
||||
|
||||
NodeValue GetValue(NodeInput* input) const
|
||||
{
|
||||
return value_map_.value(input->id());
|
||||
}
|
||||
|
||||
NodeValue GetValue(const QString& input) const
|
||||
{
|
||||
return value_map_.value(input);
|
||||
}
|
||||
|
||||
void InsertValue(NodeInput* input, NodeValueDatabase& value)
|
||||
{
|
||||
InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType()));
|
||||
}
|
||||
void InsertValue(const Node* node, const QString& input, NodeValueDatabase& value);
|
||||
|
||||
void InsertValue(const QString& input, const NodeValue& value)
|
||||
{
|
||||
value_map_.insert(input, value);
|
||||
}
|
||||
|
||||
void InsertValue(NodeInput* input, const NodeValue& value)
|
||||
{
|
||||
value_map_.insert(input->id(), value);
|
||||
}
|
||||
|
||||
const NodeValueMap &GetValues() const
|
||||
{
|
||||
return value_map_;
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
samples_ = value.data().value<SampleBufferPtr>();
|
||||
}
|
||||
|
||||
SampleJob(NodeInput* from, NodeValueDatabase& db)
|
||||
SampleJob(const QString& from, NodeValueDatabase& db)
|
||||
{
|
||||
samples_ = db[from].Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ public:
|
||||
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)
|
||||
@@ -72,9 +72,9 @@ public:
|
||||
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)
|
||||
|
||||
@@ -65,7 +65,7 @@ void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cac
|
||||
|
||||
foreach (const rational& time, times) {
|
||||
// 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
|
||||
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);
|
||||
break;
|
||||
case QueuedJob::kEdgeAdded:
|
||||
AddEdge(job.node, job.input, job.element);
|
||||
AddEdge(job.output, job.input);
|
||||
break;
|
||||
case QueuedJob::kEdgeRemoved:
|
||||
RemoveEdge(job.node, job.input, job.element);
|
||||
RemoveEdge(job.output, job.input);
|
||||
break;
|
||||
case QueuedJob::kValueChanged:
|
||||
CopyValue(job.input, job.element);
|
||||
CopyValue(job.input);
|
||||
break;
|
||||
case QueuedJob::kVideoParamsChanged:
|
||||
UpdateVideoParams();
|
||||
@@ -305,26 +305,26 @@ void PreviewAutoCacher::RemoveNode(Node *node)
|
||||
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);
|
||||
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
|
||||
Node* our_output = copy_map_.value(output.node());
|
||||
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);
|
||||
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
|
||||
Node* our_output = copy_map_.value(output.node());
|
||||
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());
|
||||
NodeInput::CopyValuesOfElement(input, our_input, element);
|
||||
Node* our_input = copy_map_.value(input.node());
|
||||
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::UpdateVideoParams()
|
||||
@@ -423,27 +423,27 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait)
|
||||
|
||||
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)
|
||||
{
|
||||
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()
|
||||
@@ -451,14 +451,14 @@ void PreviewAutoCacher::VideoParamsChanged()
|
||||
// In case the user is pressing the mouse at this exact moment
|
||||
IgnoreNextMouseButton();
|
||||
|
||||
graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, nullptr, -1});
|
||||
graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, NodeInput(), NodeOutput()});
|
||||
ClearVideoQueue();
|
||||
TryRender();
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::AudioParamsChanged()
|
||||
{
|
||||
graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, nullptr, -1});
|
||||
graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, NodeInput(), NodeOutput()});
|
||||
ClearAudioQueue();
|
||||
TryRender();
|
||||
}
|
||||
@@ -677,10 +677,8 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
|
||||
|
||||
// Add all connections
|
||||
foreach (Node* node, graph->nodes()) {
|
||||
foreach (NodeInput* input, node->inputs()) {
|
||||
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
|
||||
AddEdge(it->second, input, it->first);
|
||||
}
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
AddEdge(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,9 +107,9 @@ private:
|
||||
|
||||
void AddNode(Node* node);
|
||||
void RemoveNode(Node* node);
|
||||
void AddEdge(Node* output, NodeInput* input, int element);
|
||||
void RemoveEdge(Node* output, NodeInput* input, int element);
|
||||
void CopyValue(NodeInput* input, int element);
|
||||
void AddEdge(const NodeOutput& output, const NodeInput& input);
|
||||
void RemoveEdge(const NodeOutput& output, const NodeInput& input);
|
||||
void CopyValue(const NodeInput& input);
|
||||
void UpdateVideoParams();
|
||||
void UpdateAudioParams();
|
||||
|
||||
@@ -127,8 +127,8 @@ private:
|
||||
|
||||
Type type;
|
||||
Node* node;
|
||||
NodeInput* input;
|
||||
int element;
|
||||
NodeInput input;
|
||||
NodeOutput output;
|
||||
};
|
||||
|
||||
ViewerOutput* viewer_node_;
|
||||
@@ -201,11 +201,11 @@ private slots:
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ void RenderProcessor::Run()
|
||||
const VideoParams& video_params = ticket_->property("vparam").value<VideoParams>();
|
||||
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()));
|
||||
|
||||
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
|
||||
@@ -133,7 +133,7 @@ void RenderProcessor::Run()
|
||||
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
|
||||
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());
|
||||
break;
|
||||
@@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
}
|
||||
|
||||
// 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
|
||||
} else {
|
||||
double speed_value = b->speed_input()->GetStandardValue().toDouble();
|
||||
double speed_value = b->GetStandardValue(Block::kSpeedInput).toDouble();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// 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);
|
||||
|
||||
// Update all non-sample and non-footage inputs
|
||||
NodeValueMap::const_iterator j;
|
||||
for (j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) {
|
||||
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());
|
||||
}
|
||||
for (auto j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) {
|
||||
NodeValueTable value = ProcessInput(node, j.key(), TimeRange(this_sample_time, this_sample_time));
|
||||
|
||||
value_db.Insert(j.key(), value);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ PreCacheTask::PreCacheTask(VideoStream *footage, Sequence* sequence) :
|
||||
video_node_ = new MediaInput();
|
||||
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(),
|
||||
QString::number(footage->index())));
|
||||
|
||||
@@ -63,59 +63,51 @@ void CurveView::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)) {
|
||||
// Input wasn't connected, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// Add keyframes from track
|
||||
AddKeyframesOfTrack(input, element, track);
|
||||
AddKeyframesOfTrack(ref);
|
||||
|
||||
// Append to the list
|
||||
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)) {
|
||||
// Input wasn't connected, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove keyframes belonging to this element and track
|
||||
RemoveKeyframesOfTrack(input, element, track);
|
||||
RemoveKeyframesOfTrack(ref);
|
||||
|
||||
// Remove from the list
|
||||
connected_inputs_.removeOne(ref);
|
||||
}
|
||||
|
||||
void CurveView::SelectKeyframesOfInput(NodeInput *input, int element, int track)
|
||||
void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref)
|
||||
{
|
||||
DeselectAll();
|
||||
|
||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||
if (it.key()->parent() == input
|
||||
&& it.key()->element() == element
|
||||
&& it.key()->track() == track) {
|
||||
if (it.key()->key_track_ref() == ref) {
|
||||
it.value()->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::ZoomToFitInput(NodeInput *input, int element, int track)
|
||||
void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref)
|
||||
{
|
||||
QList<NodeKeyframe*> keys;
|
||||
|
||||
for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) {
|
||||
if (it.key()->parent() == input
|
||||
&& it.key()->element() == element
|
||||
&& it.key()->track() == track) {
|
||||
if (it.key()->key_track_ref() == ref) {
|
||||
keys.append(it.key());
|
||||
}
|
||||
}
|
||||
@@ -123,14 +115,14 @@ void CurveView::ZoomToFitInput(NodeInput *input, int element, int track)
|
||||
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
|
||||
keyframe_colors_.insert(ref, color);
|
||||
|
||||
// Update all keyframes
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -187,13 +179,14 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
painter->drawLines(lines);
|
||||
|
||||
// Draw keyframe lines
|
||||
foreach (const NodeInput::KeyframeTrackReference& ref, connected_inputs_) {
|
||||
NodeInput* input = ref.input;
|
||||
foreach (const NodeKeyframeTrackReference& ref, connected_inputs_) {
|
||||
Node* node = ref.input().node();
|
||||
const QString& input = ref.input().input();
|
||||
|
||||
if (input->IsKeyframing(ref.element)) {
|
||||
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(ref.element);
|
||||
if (node->IsInputKeyframing(input, ref.input().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()) {
|
||||
painter->setPen(QPen(keyframe_colors_.value(ref),
|
||||
@@ -348,7 +341,7 @@ void CurveView::ZoomToFitInternal(const QList<NodeKeyframe *> &keys)
|
||||
double max_val = DBL_MIN;
|
||||
|
||||
foreach (NodeKeyframe* key, keys) {
|
||||
rational transformed_time = GetAdjustedTime(key->parent()->parent(),
|
||||
rational transformed_time = GetAdjustedTime(key->parent(),
|
||||
GetTimeTarget(),
|
||||
key->time(),
|
||||
false);
|
||||
@@ -480,7 +473,7 @@ KeyframeViewItem* CurveView::AddKeyframe(NodeKeyframe* key)
|
||||
{
|
||||
KeyframeViewItem* item = super::AddKeyframe(key);
|
||||
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::TypeChanged, this, &CurveView::KeyframeTypeChanged);
|
||||
|
||||
@@ -38,15 +38,15 @@ public:
|
||||
|
||||
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:
|
||||
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
|
||||
void CreateBezierControlPoints(KeyframeViewItem *item);
|
||||
|
||||
QHash<NodeInput::KeyframeTrackReference, QColor> keyframe_colors_;
|
||||
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
||||
|
||||
int text_padding_;
|
||||
|
||||
@@ -94,7 +94,7 @@ private:
|
||||
|
||||
QVector<BezierControlPointItem*> bezier_control_points_;
|
||||
|
||||
QVector<NodeInput::KeyframeTrackReference> connected_inputs_;
|
||||
QVector<NodeKeyframeTrackReference> connected_inputs_;
|
||||
|
||||
private slots:
|
||||
void KeyframeValueChanged();
|
||||
|
||||
@@ -219,24 +219,37 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
||||
|
||||
void CurveWidget::ConnectNode(Node *node, bool connect)
|
||||
{
|
||||
foreach (NodeInput* input, node->parameters()) {
|
||||
ConnectInput(input, connect);
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
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;
|
||||
|
||||
for (int i=-1; i<input->ArraySize(); i++) {
|
||||
if (!input->IsKeyframable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int arr_sz = node->InputArraySize(input);
|
||||
for (int i=-1; i<arr_sz; i++) {
|
||||
// Generate a random color for this input
|
||||
for (int j=0; j<input->GetKeyframeTracks(i).size(); j++) {
|
||||
NodeInput::KeyframeTrackReference ref = {input, i, j};
|
||||
const QVector<NodeKeyframeTrack>& tracks = node->GetKeyframeTracks(input, i);
|
||||
|
||||
for (int j=0; j<tracks.size(); j++) {
|
||||
NodeKeyframeTrackReference ref(NodeInput(node, input, i), j);
|
||||
|
||||
if (!keyframe_colors_.contains(ref)) {
|
||||
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) {
|
||||
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) {
|
||||
view_->ConnectInput(input, i, j);
|
||||
view_->ConnectInput(ref);
|
||||
} else {
|
||||
view_->DisconnectInput(input, i, j);
|
||||
view_->DisconnectInput(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0);
|
||||
if (connect) {
|
||||
view_->ConnectInput(input, i, 0);
|
||||
view_->ConnectInput(ref);
|
||||
} 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()
|
||||
@@ -357,12 +363,12 @@ void CurveWidget::NodeEnabledChanged(Node* n, bool 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) {
|
||||
view_->ConnectInput(i, element, track);
|
||||
view_->ConnectInput(ref);
|
||||
} else {
|
||||
view_->DisconnectInput(i, element, track);
|
||||
view_->DisconnectInput(ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,18 +382,18 @@ void CurveWidget::RemoveKeyframe(NodeKeyframe *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) {
|
||||
view_->SelectKeyframesOfInput(input, element, track);
|
||||
if (ref.IsValid()) {
|
||||
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)
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "curveview.h"
|
||||
#include "node/input.h"
|
||||
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||
#include "widget/nodetreeview/nodetreeview.h"
|
||||
@@ -81,9 +80,9 @@ private:
|
||||
|
||||
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_;
|
||||
|
||||
@@ -108,15 +107,15 @@ private slots:
|
||||
|
||||
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 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);
|
||||
|
||||
|
||||
@@ -31,14 +31,14 @@ KeyframeView::KeyframeView(QWidget *parent) :
|
||||
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();
|
||||
|
||||
element_y_.insert(c, scene_y);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
|
||||
KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
max_scroll_ = i;
|
||||
}
|
||||
|
||||
void SetElementY(const Node::InputConnection& c, int y);
|
||||
void SetElementY(const NodeInput& c, int y);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent* event) override;
|
||||
@@ -47,7 +47,7 @@ public slots:
|
||||
virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override;
|
||||
|
||||
private:
|
||||
QHash<Node::InputConnection, qreal> element_y_;
|
||||
QHash<NodeInput, qreal> element_y_;
|
||||
|
||||
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