began new infrastructure

Yep, this is another one of my "famous" sweeping rewrites. Expect things to break.

Goals for this are:
- Greatly simplify node connections (particularly with arrays) so the code requires less maintenance/is more stable
- Redesign node structure to address issues where UI would stall for lengthy periods of time
- Less reliance on shared ptrs/greater reliance on QObject system for inheritance/memory management
- General code cleanup and improvements
This commit is contained in:
itsmattkc
2021-01-05 11:20:38 +11:00
parent 19eabf2830
commit 181235f6ce
152 changed files with 3932 additions and 8166 deletions
+1 -1
View File
@@ -138,7 +138,7 @@ private:
#define RATIONAL_MIN rational(INT64_MIN, 1)
#define RATIONAL_MAX rational(INT64_MAX, 1)
uint qHash(const rational& r, uint seed);
uint qHash(const rational& r, uint seed = 0);
}
+1 -1
View File
@@ -137,7 +137,7 @@ private:
};
uint qHash(const TimeRange& r, uint seed);
uint qHash(const TimeRange& r, uint seed = 0);
}
+3 -3
View File
@@ -29,13 +29,13 @@ namespace olive {
void XMLConnectNodes(const XMLNodeData &xml_node_data, QUndoCommand *command)
{
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
NodeOutput* out = xml_node_data.output_ptrs.value(con.output);
Node* out = xml_node_data.node_ptrs.value(con.output);
if (out) {
if (command) {
new NodeEdgeAddCommand(out, con.input, command);
new NodeEdgeAddCommand(out, con.input, con.element, command);
} else {
NodeParam::ConnectEdge(out, con.input);
Node::ConnectEdge(out, con.input, con.element);
}
}
}
+5 -7
View File
@@ -29,24 +29,23 @@
namespace olive {
class Block;
class Node;
class NodeParam;
class NodeInput;
class NodeOutput;
class Item;
class Node;
class NodeInput;
#define XMLAttributeLoop(reader, item) \
QXmlStreamAttributes __attributes = reader->attributes(); \
foreach (const QXmlStreamAttribute& item, __attributes)
foreach (const QXmlStreamAttribute& item, reader->attributes())
struct XMLNodeData {
struct SerializedConnection {
NodeInput* input;
int element;
quintptr output;
};
struct FootageConnection {
NodeInput* input;
int element;
quintptr footage;
};
@@ -56,7 +55,6 @@ struct XMLNodeData {
};
QHash<quintptr, Node*> node_ptrs;
QHash<quintptr, NodeOutput*> output_ptrs;
QList<SerializedConnection> desired_connections;
QHash<quintptr, Stream*> footage_ptrs;
QList<FootageConnection> footage_connections;
+59 -59
View File
@@ -43,7 +43,7 @@ Config::Config()
SetDefaults();
}
void Config::SetEntryInternal(const QString &key, NodeParam::DataType type, const QVariant &data)
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data)
{
config_map_[key] = {type, data};
}
@@ -61,67 +61,67 @@ Config &Config::Current()
void Config::SetDefaults()
{
config_map_.clear();
SetEntryInternal(QStringLiteral("Style"), NodeParam::kString, StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeParam::kInt, Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeParam::kRational, QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("HoverFocus"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeParam::kInt, 1);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeParam::kInt, 10000);
SetEntryInternal(QStringLiteral("Language"), NodeParam::kString, QString());
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeParam::kInt, AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeParam::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeParam::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeParam::kBoolean, true);
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000);
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeParam::kInt, 1000);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("NodeCatColor0"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor1"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor2"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor3"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor4"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor5"), NodeParam::kColor, QVariant::fromValue(Color(0.50, 0.50, 0.50)));
SetEntryInternal(QStringLiteral("NodeCatColor6"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor7"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor8"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor9"), NodeParam::kColor, QVariant::fromValue(Color(0.55, 0.55, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor10"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.55, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor0"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor1"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor2"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor3"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor4"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor5"), NodeValue::kColor, QVariant::fromValue(Color(0.50, 0.50, 0.50)));
SetEntryInternal(QStringLiteral("NodeCatColor6"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor7"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor8"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.25)));
SetEntryInternal(QStringLiteral("NodeCatColor9"), NodeValue::kColor, QVariant::fromValue(Color(0.55, 0.55, 0.75)));
SetEntryInternal(QStringLiteral("NodeCatColor10"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.55, 0.25)));
SetEntryInternal(QStringLiteral("AudioOutput"), NodeParam::kString, QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeParam::kString, QString());
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeParam::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeParam::kRational, QVariant::fromValue(rational(5)));
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(5)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeParam::kInt, 1920);
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeParam::kInt, 1080);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeParam::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeParam::kRational, QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeParam::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeParam::kInt, 48000);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeParam::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920);
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
// Online/offline settings
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat16);
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, VideoParams::kFormatFloat32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, VideoParams::kFormatFloat16);
}
void Config::Load()
@@ -181,7 +181,7 @@ void Config::Load()
current_config_[key] = QVariant::fromValue(match.flipped());
} else {
current_config_[key] = NodeInput::StringToValue(current_config_.GetConfigEntryType(key), value, false);
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false);
}
}
@@ -233,9 +233,9 @@ void Config::Save()
while (iterator.hasNext()) {
iterator.next();
QString value = NodeInput::ValueToString(iterator.value().type, iterator.value().data, false);
QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false);
if (iterator.value().type == NodeParam::kNone) {
if (iterator.value().type == NodeValue::kNone) {
qWarning() << "Config key" << iterator.key() << "had null type and was discarded";
} else {
writer.writeTextElement(iterator.key(), value);
@@ -264,7 +264,7 @@ QVariant &Config::operator[](const QString &key)
return config_map_[key].data;
}
NodeParam::DataType Config::GetConfigEntryType(const QString &key) const
NodeValue::Type Config::GetConfigEntryType(const QString &key) const
{
return config_map_[key].type;
}
+4 -4
View File
@@ -26,7 +26,7 @@
#include <QVariant>
#include "common/timecodefunctions.h"
#include "node/param.h"
#include "node/value.h"
namespace olive {
@@ -44,17 +44,17 @@ public:
QVariant& operator[](const QString&);
NodeParam::DataType GetConfigEntryType(const QString& key) const;
NodeValue::Type GetConfigEntryType(const QString& key) const;
private:
Config();
struct ConfigEntry {
NodeParam::DataType type;
NodeValue::Type type;
QVariant data;
};
void SetEntryInternal(const QString& key, NodeParam::DataType type, const QVariant& data);
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data);
QMap<QString, ConfigEntry> config_map_;
@@ -30,7 +30,7 @@
namespace olive {
KeyframePropertiesDialog::KeyframePropertiesDialog(const QList<NodeKeyframePtr> &keys, const rational &timebase, QWidget *parent) :
KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector<NodeKeyframe*> &keys, const rational &timebase, QWidget *parent) :
QDialog(parent),
keys_(keys),
timebase_(timebase)
@@ -92,8 +92,8 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QList<NodeKeyframePtr>
for (int i=0;i<keys_.size();i++) {
if (i > 0) {
NodeKeyframePtr prev_key = keys_.at(i-1);
NodeKeyframePtr this_key = keys_.at(i);
NodeKeyframe* prev_key = keys_.at(i-1);
NodeKeyframe* this_key = keys_.at(i);
// Determine if the keyframes are all the same time or not
if (all_same_time) {
@@ -199,7 +199,7 @@ void KeyframePropertiesDialog::accept()
rational new_time = Timecode::timestamp_to_time(time_slider_->GetValue(), timebase_);
int new_type = type_select_->currentData().toInt();
foreach (NodeKeyframePtr key, keys_) {
foreach (NodeKeyframe* key, keys_) {
if (time_slider_->isEnabled() && !time_slider_->IsTristate()) {
new NodeParamSetKeyframeTimeCommand(key, new_time, command);
}
@@ -35,7 +35,7 @@ class KeyframePropertiesDialog : public QDialog
{
Q_OBJECT
public:
KeyframePropertiesDialog(const QList<NodeKeyframePtr>& keys, const rational& timebase, QWidget* parent = nullptr);
KeyframePropertiesDialog(const QVector<NodeKeyframe*>& keys, const rational& timebase, QWidget* parent = nullptr);
public slots:
virtual void accept() override;
@@ -43,7 +43,7 @@ public slots:
private:
void SetUpBezierSlider(FloatSlider *slider, bool all_same, double value);
const QList<NodeKeyframePtr>& keys_;
const QVector<NodeKeyframe*>& keys_;
rational timebase_;
+4 -8
View File
@@ -25,28 +25,24 @@ add_subdirectory(output)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/edge.h
node/edge.cpp
node/connectable.cpp
node/connectable.h
node/factory.h
node/factory.cpp
node/graph.h
node/graph.cpp
node/input.h
node/input.cpp
node/inputarray.h
node/inputarray.cpp
node/inputdragger.h
node/inputdragger.cpp
node/inputimmediate.h
node/inputimmediate.cpp
node/keyframe.h
node/keyframe.cpp
node/node.h
node/node.cpp
node/nodecopypaste.h
node/nodecopypaste.cpp
node/output.h
node/output.cpp
node/param.h
node/param.cpp
node/traverser.h
node/traverser.cpp
node/value.h
+7 -9
View File
@@ -24,14 +24,12 @@ namespace olive {
PanNode::PanNode()
{
samples_input_ = new NodeInput("samples_in", NodeParam::kSamples);
AddInput(samples_input_);
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
panning_input_ = new NodeInput("panning_in", NodeParam::kFloat, 0.0);
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", QStringLiteral("percent"));
AddInput(panning_input_);
}
Node *PanNode::copy() const
@@ -69,8 +67,8 @@ NodeValueTable PanNode::Value(NodeValueDatabase &value) const
NodeValueTable table = value.Merge();
if (job.HasSamples()) {
float pan_volume = job.GetValue(panning_input_).data.toFloat();
if (panning_input_->is_static()) {
float pan_volume = job.GetValue(panning_input_).data().toFloat();
if (panning_input_->IsStatic()) {
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);
@@ -79,9 +77,9 @@ NodeValueTable PanNode::Value(NodeValueDatabase &value) const
}
}
table.Push(NodeParam::kSamples, QVariant::fromValue(job.samples()), this);
table.Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this);
} else {
table.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
}
}
@@ -95,7 +93,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in
return;
}
float pan_val = values[panning_input_].Get(NodeParam::kFloat).toFloat();
float pan_val = values[panning_input_].Get(NodeValue::kFloat).toFloat();
for (int i=0;i<input->audio_params().channel_count();i++) {
output->data()[i][index] = input->data()[i][index];
+5 -7
View File
@@ -24,14 +24,12 @@ namespace olive {
VolumeNode::VolumeNode()
{
samples_input_ = new NodeInput(QStringLiteral("samples_in"), NodeParam::kSamples);
samples_input_->set_is_keyframable(false);
AddInput(samples_input_);
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
samples_input_->SetKeyframable(false);
volume_input_ = new NodeInput(QStringLiteral("volume_in"), NodeParam::kFloat, 1.0);
volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0);
volume_input_->setProperty("min", 0.0);
volume_input_->setProperty("view", QStringLiteral("db"));
AddInput(volume_input_);
}
Node *VolumeNode::copy() const
@@ -65,9 +63,9 @@ NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const
kOpMultiply,
kPairSampleNumber,
samples_input_,
value[samples_input_].TakeWithMeta(NodeParam::kSamples),
value[samples_input_].TakeWithMeta(NodeValue::kSamples),
volume_input_,
value[volume_input_].TakeWithMeta(NodeParam::kFloat));
value[volume_input_].TakeWithMeta(NodeValue::kFloat));
}
void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
+22 -31
View File
@@ -31,28 +31,24 @@ Block::Block() :
previous_(nullptr),
next_(nullptr)
{
length_input_ = new NodeInput("length_in", NodeParam::kRational);
length_input_->set_connectable(false);
length_input_->set_is_keyframable(false);
AddInput(length_input_);
length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational);
length_input_->SetConnectable(false);
length_input_->SetKeyframable(false);
disconnect(length_input_, &NodeInput::ValueChanged, this, &Block::InputChanged);
connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthInputChanged);
media_in_input_ = new NodeInput("media_in_in", NodeParam::kRational);
media_in_input_->set_connectable(false);
media_in_input_->set_is_keyframable(false);
AddInput(media_in_input_);
media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational);
media_in_input_->SetConnectable(false);
media_in_input_->SetKeyframable(false);
enabled_input_ = new NodeInput("enabled_in", NodeParam::kBoolean);
enabled_input_->set_connectable(false);
enabled_input_->set_is_keyframable(false);
enabled_input_->set_standard_value(true);
AddInput(enabled_input_);
enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean);
enabled_input_->SetConnectable(false);
enabled_input_->SetKeyframable(false);
enabled_input_->SetStandardValue(true);
speed_input_ = new NodeInput("speed_in", NodeParam::kFloat);
speed_input_->set_standard_value(1.0);
speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat);
speed_input_->SetStandardValue(1.0);
speed_input_->setProperty("view", QStringLiteral("percent"));
AddInput(speed_input_);
// A block's length must be greater than 0
set_length_and_media_out(1);
@@ -85,7 +81,7 @@ void Block::set_out(const rational &out)
rational Block::length() const
{
return length_input_->get_standard_value().value<rational>();
return length_input_->GetStandardValue().value<rational>();
}
void Block::set_length_and_media_out(const rational &length)
@@ -149,22 +145,22 @@ void Block::set_next(Block *next)
rational Block::media_in() const
{
return media_in_input_->get_standard_value().value<rational>();
return media_in_input_->GetStandardValue().value<rational>();
}
void Block::set_media_in(const rational &media_in)
{
media_in_input_->set_standard_value(QVariant::fromValue(media_in));
media_in_input_->SetStandardValue(QVariant::fromValue(media_in));
}
bool Block::is_enabled() const
{
return enabled_input_->get_standard_value().toBool();
return enabled_input_->GetStandardValue().toBool();
}
void Block::set_enabled(bool e)
{
enabled_input_->set_standard_value(e);
enabled_input_->SetStandardValue(e);
emit EnabledChanged();
}
@@ -179,10 +175,10 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
rational local_time = sequence_time - in();
// FIXME: Doesn't handle reversing
if (speed_input_->is_keyframing() || speed_input_->is_connected()) {
if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) {
// FIXME: We'll need to calculate the speed hoo boy
} else {
double speed_value = speed_input_->get_standard_value().toDouble();
double speed_value = speed_input_->GetStandardValue().toDouble();
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point
@@ -206,10 +202,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
rational sequence_time = media_time - media_in();
// FIXME: Doesn't handle reversing
if (speed_input_->is_keyframing() || speed_input_->is_connected()) {
if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) {
// FIXME: We'll need to calculate the speed hoo boy
} else {
double speed_value = speed_input_->get_standard_value().toDouble();
double speed_value = speed_input_->GetStandardValue().toDouble();
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point, also prevents divide by zero
@@ -259,7 +255,7 @@ void Block::LengthChangedEvent(const rational &, const rational &, const Timelin
void Block::set_length_internal(const rational &length)
{
length_input_->set_standard_value(QVariant::fromValue(length));
length_input_->SetStandardValue (QVariant::fromValue(length));
}
void Block::LengthInputChanged()
@@ -340,11 +336,6 @@ bool Block::HasLinks()
return !linked_clips_.isEmpty();
}
bool Block::IsBlock() const
{
return true;
}
void Block::Retranslate()
{
Node::Retranslate();
-2
View File
@@ -75,8 +75,6 @@ public:
const QVector<Block*>& linked_clips();
bool HasLinks();
virtual bool IsBlock() const override;
virtual void Retranslate() override;
NodeInput* length_input() const;
+10 -11
View File
@@ -24,9 +24,8 @@ namespace olive {
ClipBlock::ClipBlock()
{
texture_input_ = new NodeInput("buffer_in", NodeInput::kBuffer);
texture_input_->set_is_keyframable(false);
AddInput(texture_input_);
texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone);
texture_input_->SetKeyframable(false);
}
Node *ClipBlock::copy() const
@@ -59,18 +58,18 @@ NodeInput *ClipBlock::texture_input() const
return texture_input_;
}
void ClipBlock::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
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 == texture_input_) {
if (from.input == texture_input_) {
// Adjust range from media time to sequence time
rational start = MediaToSequenceTime(range.in());
rational end = MediaToSequenceTime(range.out());
Block::InvalidateCache(TimeRange(start, end), from, source);
Block::InvalidateCache(TimeRange(start, end), from);
} else {
// Otherwise, pass signal along normally
Block::InvalidateCache(range, from, source);
Block::InvalidateCache(range, from);
}
}
@@ -95,10 +94,10 @@ TimeRange ClipBlock::OutputTimeAdjustment(NodeInput *input, const TimeRange &inp
NodeValueTable ClipBlock::Value(NodeValueDatabase &value) const
{
// We discard most values here except for the buffer we received
NodeValue data = value[texture_input()].GetWithMeta(NodeParam::kBuffer);
NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer);
NodeValueTable table;
if (data.type() != NodeParam::kNone) {
if (data.type() != NodeValue::kNone) {
table.Push(data);
}
return table;
@@ -113,10 +112,10 @@ void ClipBlock::Retranslate()
void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const
{
if (texture_input_->is_connected()) {
if (texture_input_->IsConnected()) {
rational t = InputTimeAdjustment(texture_input_, TimeRange(time, time)).in();
texture_input_->get_connected_node()->Hash(hash, t);
texture_input_->GetConnectedNode()->Hash(hash, t);
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ public:
NodeInput* texture_input() const;
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) const override;
@@ -24,8 +24,7 @@ namespace olive {
DipToColorTransition::DipToColorTransition()
{
color_input_ = new NodeInput(QStringLiteral("color_in"), NodeParam::kColor, QVariant::fromValue(Color(0, 0, 0)));
AddInput(color_input_);
color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0)));
}
Node *DipToColorTransition::copy() const
+51 -62
View File
@@ -28,22 +28,19 @@ TransitionBlock::TransitionBlock() :
connected_out_block_(nullptr),
connected_in_block_(nullptr)
{
out_block_input_ = new NodeInput(QStringLiteral("out_block_in"), NodeParam::kBuffer);
out_block_input_->set_is_keyframable(false);
connect(out_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected);
connect(out_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected);
AddInput(out_block_input_);
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);
in_block_input_ = new NodeInput(QStringLiteral("in_block_in"), NodeParam::kBuffer);
in_block_input_->set_is_keyframable(false);
connect(in_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected);
connect(in_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected);
AddInput(in_block_input_);
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);
curve_input_ = new NodeInput(QStringLiteral("curve_in"), NodeParam::kCombo);
curve_input_->set_is_keyframable(false);
curve_input_->set_connectable(false);
AddInput(curve_input_);
curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo);
curve_input_->SetKeyframable(false);
curve_input_->SetConnectable(false);
}
Block::Type TransitionBlock::type() const
@@ -161,57 +158,55 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t
{
// Provides total transition progress from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_all"),
ShaderValue(GetTotalProgress(time), NodeParam::kFloat));
NodeValue(NodeValue::kFloat, GetTotalProgress(time), this));
// Provides progress of out section from 1.0 (start) - 0.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_out"),
ShaderValue(GetOutProgress(time), NodeParam::kFloat));
NodeValue(NodeValue::kFloat, GetOutProgress(time), this));
// Provides progress of in section from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_in"),
ShaderValue(GetInProgress(time), NodeParam::kFloat));
NodeValue(NodeValue::kFloat, GetInProgress(time), this));
}
void TransitionBlock::BlockConnected(NodeEdgePtr edge)
void TransitionBlock::OutBlockConnected(Node *node)
{
if (!edge->output()->parentNode()->IsBlock()) {
return;
}
Block* block = static_cast<Block*>(edge->output()->parentNode());
if (edge->input() == out_block_input_) {
connected_out_block_ = block;
} else {
connected_in_block_ = block;
}
// If node is not a block, this will just be null
connected_out_block_ = dynamic_cast<Block*>(node);
}
void TransitionBlock::BlockDisconnected(NodeEdgePtr edge)
void TransitionBlock::OutBlockDisconnected()
{
if (edge->input() == out_block_input_) {
connected_out_block_ = nullptr;
} else {
}
void TransitionBlock::InBlockConnected(Node *node)
{
// If node is not a block, this will just be null
connected_in_block_ = dynamic_cast<Block*>(node);
}
void TransitionBlock::InBlockDisconnected()
{
connected_in_block_ = nullptr;
}
}
NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
{
NodeParam::DataType data_type;
NodeValue::Type data_type;
if (out_block_input()->is_connected()) {
data_type = value[out_block_input()].GetWithMeta(NodeParam::kBuffer).type();
} else if (in_block_input()->is_connected()) {
data_type = value[in_block_input()].GetWithMeta(NodeParam::kBuffer).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();
} else {
data_type = NodeParam::kNone;
data_type = NodeValue::kNone;
}
NodeParam::DataType job_type;
NodeValue::Type job_type;
QVariant push_job;
if (data_type == NodeParam::kTexture) {
if (data_type == NodeValue::kTexture) {
// This must be a visual transition
ShaderJob job;
@@ -219,21 +214,21 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
job.InsertValue(in_block_input(), value);
job.InsertValue(curve_input_, value);
double time = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble();
double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
InsertTransitionTimes(&job, time);
ShaderJobEvent(value, job);
job_type = NodeParam::kShaderJob;
job_type = NodeValue::kShaderJob;
push_job = QVariant::fromValue(job);
} else if (data_type == NodeParam::kSamples) {
} else if (data_type == NodeValue::kSamples) {
// This must be an audio transition
SampleBufferPtr from_samples = value[out_block_input()].Take(NodeParam::kBuffer).value<SampleBufferPtr>();
SampleBufferPtr to_samples = value[in_block_input()].Take(NodeParam::kBuffer).value<SampleBufferPtr>();
SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value<SampleBufferPtr>();
if (from_samples || to_samples) {
double time_in = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble();
double time_out = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_out")).toDouble();
double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble();
double time_out = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_out")).toDouble();
const AudioParams& params = (from_samples) ? from_samples->audio_params() : to_samples->audio_params();
@@ -242,7 +237,7 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
SampleBufferPtr out_samples = SampleBuffer::CreateAllocated(params, nb_samples);
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
job_type = NodeParam::kSamples;
job_type = NodeValue::kSamples;
push_job = QVariant::fromValue(out_samples);
}
}
@@ -259,19 +254,13 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const
TransitionBlock *GetBlockTransitionInternal(Block *block, Timeline::MovementMode mode)
{
// See if this block outputs to a transition
foreach (NodeEdgePtr edge, block->output()->edges()) {
Node* connected_node = edge->input()->parentNode();
foreach (const NodeConnectable::InputConnection& conn, block->edges()) {
TransitionBlock* transition = dynamic_cast<TransitionBlock*>(conn.input->parent());
if (connected_node->IsBlock()) {
Block* connected_block = static_cast<Block*>(connected_node);
if (connected_block->type() == Block::kTransition) {
TransitionBlock* connected_transition = static_cast<TransitionBlock*>(connected_block);
if ((mode == Timeline::kTrimIn && edge->input() == connected_transition->in_block_input())
|| (mode == Timeline::kTrimOut && edge->input() == connected_transition->out_block_input())) {
return connected_transition;
}
if (transition) {
if ((mode == Timeline::kTrimIn && conn.input == transition->in_block_input())
|| (mode == Timeline::kTrimOut && conn.input == transition->out_block_input())) {
return transition;
}
}
}
@@ -305,7 +294,7 @@ void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferP
double TransitionBlock::TransformCurve(double linear) const
{
switch (static_cast<CurveType>(curve_input_->get_standard_value().toInt())) {
switch (static_cast<CurveType>(curve_input_->GetStandardValue().toInt())) {
case kLinear:
break;
case kExponential:
+6 -2
View File
@@ -85,9 +85,13 @@ private:
Block* connected_in_block_;
private slots:
void BlockConnected(NodeEdgePtr edge);
void OutBlockConnected(Node* node);
void BlockDisconnected(NodeEdgePtr edge);
void OutBlockDisconnected();
void InBlockConnected(Node* node);
void InBlockDisconnected();
};
+77
View File
@@ -0,0 +1,77 @@
/***
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 (output->output_connections_.contains(conn_to_in)) {
qDebug() << "Ignored connect that already exists";
return;
}
// Ensure a connection isn't getting overwritten
Q_ASSERT(!input->input_connections_.contains(element));
// Insert connections in both sides
output->output_connections_.append(conn_to_in);
input->input_connections_.insert(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 (!output->output_connections_.contains(conn_to_in)) {
qDebug() << "Ignored disconnect that doesn't exist";
return;
}
// Assertions to ensure connection exists
Q_ASSERT(input->input_connections_.value(element) == output);
// Remove connections from both sides
output->output_connections_.removeOne(conn_to_in);
input->input_connections_.remove(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);
}
}
+96
View File
@@ -0,0 +1,96 @@
/***
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 <QHash>
#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;
};
signals:
void OutputConnected(NodeInput* destination, int element);
void OutputDisconnected(NodeInput* destination, int element);
void InputConnected(Node* source, int element);
void InputDisconnected(Node* source, int element);
protected:
const QVector<InputConnection>& output_connections() const
{
return output_connections_;
}
const QHash<int, Node*>& input_connections() const
{
return input_connections_;
}
private:
QVector<InputConnection> output_connections_;
QHash<int, Node*> input_connections_;
};
uint qHash(const NodeConnectable::InputConnection& r, uint seed = 0);
}
#endif // CONNECTABLE_H
+24 -30
View File
@@ -26,36 +26,30 @@ namespace olive {
CropDistortNode::CropDistortNode()
{
texture_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture);
AddInput(texture_input_);
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
left_input_ = new NodeInput(QStringLiteral("left_in"), NodeParam::kFloat, 0.0);
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", QStringLiteral("percent"));
AddInput(left_input_);
top_input_ = new NodeInput(QStringLiteral("top_in"), NodeParam::kFloat, 0.0);
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", QStringLiteral("percent"));
AddInput(top_input_);
right_input_ = new NodeInput(QStringLiteral("right_in"), NodeParam::kFloat, 0.0);
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", QStringLiteral("percent"));
AddInput(right_input_);
bottom_input_ = new NodeInput(QStringLiteral("bottom_in"), NodeParam::kFloat, 0.0);
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", QStringLiteral("percent"));
AddInput(bottom_input_);
feather_input_ = new NodeInput(QStringLiteral("feather_in"), NodeParam::kFloat, 0.0);
feather_input_ = new NodeInput(this, QStringLiteral("feather_in"), NodeValue::kFloat, 0.0);
feather_input_->setProperty("min", 0.0);
AddInput(feather_input_);
}
void CropDistortNode::Retranslate()
@@ -78,19 +72,19 @@ NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const
job.InsertValue(bottom_input_, value);
job.InsertValue(feather_input_, value);
job.InsertValue(QStringLiteral("resolution_in"),
ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2));
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())) {
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
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())) {
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
} else {
table.Push(NodeParam::kTexture, job.GetValue(texture_input_).data, this);
table.Push(NodeValue::kTexture, job.GetValue(texture_input_).data(), this);
}
}
@@ -105,16 +99,16 @@ ShaderCode CropDistortNode::GetShaderCode(const QString &shader_id) const
void CropDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
{
QVector2D resolution = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D resolution = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
const double handle_radius = GetGizmoHandleRadius(p->transform());
p->setPen(QPen(Qt::white, 0));
double left_pt = resolution.x() * db[left_input_].Get(NodeParam::kFloat).toDouble();
double top_pt = resolution.y() * db[top_input_].Get(NodeParam::kFloat).toDouble();
double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeParam::kFloat).toDouble());
double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeParam::kFloat).toDouble());
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 center_x_pt = lerp(left_pt, right_pt, 0.5);
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
@@ -161,7 +155,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|| in_rect) {
gizmo_drag_ |= kGizmoLeft;
gizmo_start_.append(db[left_input_].Get(NodeParam::kFloat));
gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat));
}
if (gizmo_active[kGizmoScaleTopLeft]
@@ -170,7 +164,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|| in_rect) {
gizmo_drag_ |= kGizmoTop;
gizmo_start_.append(db[top_input_].Get(NodeParam::kFloat));
gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat));
}
if (gizmo_active[kGizmoScaleTopRight]
@@ -179,7 +173,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|| in_rect) {
gizmo_drag_ |= kGizmoRight;
gizmo_start_.append(db[right_input_].Get(NodeParam::kFloat));
gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat));
}
if (gizmo_active[kGizmoScaleBottomLeft]
@@ -188,11 +182,11 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|| in_rect) {
gizmo_drag_ |= kGizmoBottom;
gizmo_start_.append(db[bottom_input_].Get(NodeParam::kFloat));
gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat));
}
if (gizmo_drag_ > kGizmoNone) {
gizmo_res_ = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value<QVector2D>();
gizmo_res_ = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
gizmo_drag_start_ = p;
return true;
@@ -28,14 +28,11 @@ namespace olive {
TransformDistortNode::TransformDistortNode()
{
autoscale_input_ = new NodeInput(QStringLiteral("autoscale_in"), NodeParam::kCombo, 0);
AddInput(autoscale_input_);
autoscale_input_ = new NodeInput(this, QStringLiteral("autoscale_in"), NodeValue::kCombo, 0);
interpolation_input_ = new NodeInput(QStringLiteral("interpolation_in"), NodeParam::kCombo, 2);
AddInput(interpolation_input_);
interpolation_input_ = new NodeInput(this, QStringLiteral("interpolation_in"), NodeValue::kCombo, 2);
texture_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture);
AddInput(texture_input_);
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
}
void TransformDistortNode::Retranslate()
@@ -56,7 +53,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
// Pop texture
TexturePtr texture = value[texture_input_].Take(NodeParam::kTexture).value<TexturePtr>();
TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value<TexturePtr>();
// Merge table
NodeValueTable table = value.Merge();
@@ -64,9 +61,9 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
// If we have a texture, generate a matrix and make it happen
if (texture) {
// Adjust our matrix by the resolutions involved
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height());
AutoScaleType autoscale = static_cast<AutoScaleType>(value[autoscale_input_].Get(NodeParam::kCombo).toInt());
AutoScaleType autoscale = static_cast<AutoScaleType>(value[autoscale_input_].Get(NodeValue::kCombo).toInt());
QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix,
sequence_res,
@@ -75,19 +72,19 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const
if (real_matrix.isIdentity()) {
// We don't expect any changes, just push as normal
table.Push(NodeParam::kTexture, QVariant::fromValue(texture), this);
table.Push(NodeValue::kTexture, QVariant::fromValue(texture), this);
} else {
// The matrix will transform things
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture), NodeParam::kTexture));
job.InsertValue(QStringLiteral("ove_mvpmat"), ShaderValue(real_matrix, NodeParam::kMatrix));
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[interpolation_input_].Get(NodeParam::kCombo).toInt()));
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()));
// 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.
job.SetAlphaChannelRequired(true);
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
}
}
@@ -123,10 +120,10 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
if (scaling) {
// Dragging scale handle
gizmo_start_ = {db[scale_input()].Get(NodeParam::kVec2)};
gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)};
gizmo_drag_ = scale_input();
gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeParam::kBoolean).toBool();
gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeValue::kBoolean).toBool();
if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight]
|| gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) {
@@ -138,8 +135,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
}
// Store texture size
QVector2D texture_sz = db[texture_input()].Get(NodeParam::kTexture).value<QVector2D>();
gizmo_scale_anchor_ = db[anchor_input()].Get(NodeParam::kVec2).value<QVector2D>() + texture_sz/2;
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;
if (gizmo_scale_active[kGizmoScaleTopRight]
|| gizmo_scale_active[kGizmoScaleBottomRight]
@@ -163,8 +160,8 @@ 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(NodeParam::kVec2),
db[position_input()].Get(NodeParam::kVec2)};
gizmo_start_ = {db[anchor_input()].Get(NodeValue::kVec2),
db[position_input()].Get(NodeValue::kVec2)};
gizmo_drag_ = anchor_input();
// Store current matrix
@@ -175,7 +172,7 @@ 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(NodeParam::kVec2)};
gizmo_start_ = {db[position_input()].Get(NodeValue::kVec2)};
gizmo_drag_ = position_input();
return true;
@@ -183,7 +180,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
} else {
// Dragging rotation
gizmo_start_ = {db[rotation_input()].Get(NodeParam::kFloat)};
gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)};
gizmo_drag_ = rotation_input();
gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(),
gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x());
@@ -364,15 +361,15 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
p->setPen(QPen(Qt::white, 0));
// Get the sequence resolution
QVector2D sequence_res = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D sequence_res = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D sequence_half_res = sequence_res/2;
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(NodeParam::kTexture).value<QVector2D>();
QVector2D tex_sz = db[texture_input_].Get(NodeValue::kTexture).value<QVector2D>();
// Retrieve autoscale value
AutoScaleType autoscale = static_cast<AutoScaleType>(db[autoscale_input_].Get(NodeParam::kCombo).toInt());
AutoScaleType autoscale = static_cast<AutoScaleType>(db[autoscale_input_].Get(NodeValue::kCombo).toInt());
// Fold values into a matrix for the rectangle
QMatrix4x4 rectangle_matrix;
-50
View File
@@ -1,50 +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 "edge.h"
#include "input.h"
#include "node.h"
#include "output.h"
namespace olive {
NodeEdge::NodeEdge(NodeOutput *output, NodeInput *input)
{
output_ = ParamToConnection(output);
input_ = ParamToConnection(input);
}
NodeOutput *NodeEdge::output() const
{
return output_.node->GetOutputWithID(output_.id);
}
NodeInput *NodeEdge::input() const
{
return input_.node->GetInputWithID(input_.id);
}
NodeEdge::Connection NodeEdge::ParamToConnection(NodeParam *param)
{
return {param->parentNode(), param->id()};
}
}
-87
View File
@@ -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 EDGE_H
#define EDGE_H
#include <memory>
#include <QString>
#include "common/define.h"
namespace olive {
class Node;
class NodeInput;
class NodeOutput;
class NodeParam;
/**
* @brief A connection between two node parameters (a NodeOutput and a NodeInput)
*
* To simplify memory management, it's recommended to use NodeEdgePtr instead of raw pointers when working with
* NodeEdge.
*/
class NodeEdge
{
public:
/**
* @brief Create a node edge connecting an output to an input
*/
NodeEdge(NodeOutput* output, NodeInput* input);
Node* output_node() const
{
return output_.node;
}
Node* input_node() const
{
return input_.node;
}
/**
* @brief Return the output parameter this edge is connected to
*/
NodeOutput* output() const;
/**
* @brief Return the input parameter this edge is connected to
*/
NodeInput* input() const;
private:
struct Connection {
Node* node;
QString id;
};
static Connection ParamToConnection(NodeParam* param);
Connection output_;
Connection input_;
};
using NodeEdgePtr = std::shared_ptr<NodeEdge>;
}
#endif // EDGE_H
+14 -20
View File
@@ -24,24 +24,18 @@ namespace olive {
BlurFilterNode::BlurFilterNode()
{
texture_input_ = new NodeInput("tex_in", NodeParam::kTexture);
AddInput(texture_input_);
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
method_input_ = new NodeInput("method_in", NodeParam::kCombo, 0);
AddInput(method_input_);
method_input_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo, 0);
radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f);
radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f);
radius_input_->setProperty("min", 0.0f);
AddInput(radius_input_);
horiz_input_ = new NodeInput("horiz_in", NodeParam::kBoolean, true);
AddInput(horiz_input_);
horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kBoolean, true);
vert_input_ = new NodeInput("vert_in", NodeParam::kBoolean, true);
AddInput(vert_input_);
vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kBoolean, true);
repeat_edge_pixels_input_ = new NodeInput("repeat_edge_pixels_in", NodeParam::kBoolean, false);
AddInput(repeat_edge_pixels_input_);
repeat_edge_pixels_input_ = new NodeInput(this, QStringLiteral("repeat_edge_pixels_in"), NodeValue::kBoolean, false);
}
Node *BlurFilterNode::copy() const
@@ -97,32 +91,32 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
job.InsertValue(vert_input_, value);
job.InsertValue(repeat_edge_pixels_input_, value);
job.InsertValue(QStringLiteral("resolution_in"),
ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2));
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(texture_input_).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(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool())
&& job.GetValue(radius_input_).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()) {
if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) {
job.SetIterations(2, texture_input_);
}
// If we're not repeating pixels, expect an alpha channel to appear
if (!job.GetValue(repeat_edge_pixels_input_).data.toBool()) {
if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) {
job.SetAlphaChannelRequired(true);
}
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
} else {
// If we're not performing the blur job, just push the texture
table.Push(job.GetValue(texture_input_), this);
table.Push(job.GetValue(texture_input_));
}
}
+9 -12
View File
@@ -24,16 +24,13 @@ namespace olive {
MosaicFilterNode::MosaicFilterNode()
{
tex_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture);
AddInput(tex_input_);
tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
horiz_input_ = new NodeInput(QStringLiteral("horiz_in"), NodeParam::kFloat, 32.0);
horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kFloat, 32.0);
horiz_input_->setProperty("min", 1.0);
AddInput(horiz_input_);
vert_input_ = new NodeInput(QStringLiteral("vert_in"), NodeParam::kFloat, 18.0);
vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0);
vert_input_->setProperty("min", 1.0);
AddInput(vert_input_);
}
void MosaicFilterNode::Retranslate()
@@ -56,15 +53,15 @@ NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const
NodeValueTable table = value.Merge();
if (!job.GetValue(tex_input_).data.isNull()) {
TexturePtr texture = job.GetValue(tex_input_).data.value<TexturePtr>();
if (!job.GetValue(tex_input_).data().isNull()) {
TexturePtr texture = job.GetValue(tex_input_).data().value<TexturePtr>();
if (texture
&& job.GetValue(horiz_input_).data.toInt() != texture->width()
&& job.GetValue(vert_input_).data.toInt() != texture->height()) {
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
&& job.GetValue(horiz_input_).data().toInt() != texture->width()
&& job.GetValue(vert_input_).data().toInt() != texture->height()) {
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
} else {
table.Push(job.GetValue(tex_input_), this);
table.Push(job.GetValue(tex_input_));
}
}
+13 -17
View File
@@ -26,26 +26,22 @@ namespace olive {
StrokeFilterNode::StrokeFilterNode()
{
tex_input_ = new NodeInput("tex_in", NodeParam::kTexture);
AddInput(tex_input_);
tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
color_input_ = new NodeInput("color_in",
NodeParam::kColor,
color_input_ = new NodeInput(this,
QStringLiteral("color_in"),
NodeValue::kColor,
QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f)));
AddInput(color_input_);
radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f);
radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f);
radius_input_->setProperty("min", 0.0f);
AddInput(radius_input_);
opacity_input_ = new NodeInput("opacity_in", NodeParam::kFloat, 1.0f);
opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f);
opacity_input_->setProperty("view", QStringLiteral("percent"));
opacity_input_->setProperty("min", 0.0f);
opacity_input_->setProperty("max", 1.0f);
AddInput(opacity_input_);
inner_input_ = new NodeInput("inner_in", NodeParam::kBoolean, false);
AddInput(inner_input_);
inner_input_ = new NodeInput(this, QStringLiteral("inner_in"), NodeValue::kBoolean, false);
}
Node *StrokeFilterNode::copy() const
@@ -92,16 +88,16 @@ NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const
job.InsertValue(opacity_input_, value);
job.InsertValue(inner_input_, value);
job.InsertValue(QStringLiteral("resolution_in"),
ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2));
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) {
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
if (!job.GetValue(tex_input_).data().isNull()) {
if (job.GetValue(radius_input_).data().toDouble() > 0.0
&& job.GetValue(opacity_input_).data().toDouble() > 0.0) {
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
} else {
table.Push(job.GetValue(tex_input_), this);
table.Push(job.GetValue(tex_input_));
}
}
+22 -27
View File
@@ -27,26 +27,21 @@ namespace olive {
MatrixGenerator::MatrixGenerator()
{
position_input_ = new NodeInput("pos_in", NodeParam::kVec2, QVector2D());
AddInput(position_input_);
position_input_ = new NodeInput(this, QStringLiteral("pos_in"), NodeValue::kVec2, QVector2D());
rotation_input_ = new NodeInput("rot_in", NodeParam::kFloat, 0.0f);
AddInput(rotation_input_);
rotation_input_ = new NodeInput(this, QStringLiteral("rot_in"), NodeValue::kFloat, 0.0f);
scale_input_ = new NodeInput("scale_in", NodeParam::kVec2, QVector2D(1.0f, 1.0f));
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", QStringLiteral("percent"));
scale_input_->setProperty("disabley", true);
AddInput(scale_input_);
uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true);
uniform_scale_input_->set_is_keyframable(false);
uniform_scale_input_->set_connectable(false);
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(uniform_scale_input_);
anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2, QVector2D());
AddInput(anchor_input_);
anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D());
}
Node *MatrixGenerator::copy() const
@@ -93,7 +88,7 @@ NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const
// Push matrix output
QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false);
NodeValueTable output = value.Merge();
output.Push(NodeParam::kMatrix, mat, this);
output.Push(NodeValue::kMatrix, mat, this);
return output;
}
@@ -106,47 +101,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take,
if (!ignore_anchor) {
if (take) {
// Take and store
anchor = value[anchor_input_].Take(NodeParam::kVec2).value<QVector2D>();
anchor = value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
} else {
// Get and store
anchor = value[anchor_input_].Get(NodeParam::kVec2).value<QVector2D>();
anchor = value[anchor_input_].Get(NodeValue::kVec2).value<QVector2D>();
}
} else if (take) {
// Just take
value[anchor_input_].Take(NodeParam::kVec2).value<QVector2D>();
value[anchor_input_].Take(NodeValue::kVec2).value<QVector2D>();
}
if (!ignore_scale) {
if (take) {
scale = value[scale_input_].Take(NodeParam::kVec2).value<QVector2D>();
scale = value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
} else {
scale = value[scale_input_].Get(NodeParam::kVec2).value<QVector2D>();
scale = value[scale_input_].Get(NodeValue::kVec2).value<QVector2D>();
}
} else if (take) {
value[scale_input_].Take(NodeParam::kVec2).value<QVector2D>();
value[scale_input_].Take(NodeValue::kVec2).value<QVector2D>();
}
if (!ignore_position) {
if (take) {
position = value[position_input_].Take(NodeParam::kVec2).value<QVector2D>();
position = value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
} else {
position = value[position_input_].Get(NodeParam::kVec2).value<QVector2D>();
position = value[position_input_].Get(NodeValue::kVec2).value<QVector2D>();
}
} else if (take) {
value[position_input_].Take(NodeParam::kVec2).value<QVector2D>();
value[position_input_].Take(NodeValue::kVec2).value<QVector2D>();
}
if (take) {
return GenerateMatrix(position,
value[rotation_input_].Take(NodeParam::kFloat).toFloat(),
value[rotation_input_].Take(NodeValue::kFloat).toFloat(),
scale,
value[uniform_scale_input_].Take(NodeParam::kBoolean).toBool(),
value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(),
anchor);
} else {
return GenerateMatrix(position,
value[rotation_input_].Get(NodeParam::kFloat).toFloat(),
value[rotation_input_].Get(NodeValue::kFloat).toFloat(),
scale,
value[uniform_scale_input_].Get(NodeParam::kBoolean).toBool(),
value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(),
anchor);
}
@@ -183,7 +178,7 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
void MatrixGenerator::UniformScaleChanged()
{
scale_input_->setProperty("disabley", uniform_scale_input_->get_standard_value().toBool());
scale_input_->setProperty("disabley", uniform_scale_input_->GetStandardValue().toBool());
}
}
+22 -28
View File
@@ -27,31 +27,23 @@ namespace olive {
PolygonGenerator::PolygonGenerator()
{
points_input_ = new NodeInputArray("points_in", NodeParam::kVec2);
AddInput(points_input_);
points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2);
points_input_->SetIsArray(true);
color_input_ = new NodeInput("color_in", NodeParam::kColor);
AddInput(color_input_);
color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0)));
// Default to "a color" that isn't
color_input_->set_standard_value(1.0, 0);
color_input_->set_standard_value(1.0, 1);
color_input_->set_standard_value(1.0, 2);
color_input_->set_standard_value(1.0, 3);
// FIXME: Test code
points_input_->SetSize(5);
points_input_->At(0)->set_standard_value(960, 0);
points_input_->At(0)->set_standard_value(240, 1);
points_input_->At(1)->set_standard_value(640, 0);
points_input_->At(1)->set_standard_value(480, 1);
points_input_->At(2)->set_standard_value(760, 0);
points_input_->At(2)->set_standard_value(800, 1);
points_input_->At(3)->set_standard_value(1100, 0);
points_input_->At(3)->set_standard_value(800, 1);
points_input_->At(4)->set_standard_value(1280, 0);
points_input_->At(4)->set_standard_value(480, 1);
// End test
// 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);
}
Node *PolygonGenerator::copy() const
@@ -98,11 +90,11 @@ NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const
job.InsertValue(points_input_, value);
job.InsertValue(color_input_, value);
job.InsertValue(QStringLiteral("resolution_in"), ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2));
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
job.SetAlphaChannelRequired(true);
NodeValueTable table = value.Merge();
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
return table;
}
@@ -175,10 +167,12 @@ void PolygonGenerator::GizmoRelease()
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
{
QVector<QPointF> points(points_input_->GetSize());
// 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<QPointF> points(array_tbl.size());
for (int i=0;i<points_input_->GetSize();i++) {
QVector2D v = db[points_input_->At(i)].Get(NodeParam::kVec2).value<QVector2D>();
for (int i=0;i<points_input_->ArraySize();i++) {
QVector2D v = array_tbl.at(i).Get(NodeValue::kVec2).value<QVector2D>();
v *= scale;
+1 -1
View File
@@ -56,7 +56,7 @@ private:
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
NodeInputArray* points_input_;
NodeInput* points_input_;
NodeInput* color_input_;
+4 -4
View File
@@ -27,10 +27,10 @@ namespace olive {
SolidGenerator::SolidGenerator()
{
// Default to a color that isn't black
color_input_ = new NodeInput("color_in",
NodeInput::kColor,
color_input_ = new NodeInput(this,
QStringLiteral("color_in"),
NodeValue::kColor,
QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f)));
AddInput(color_input_);
}
Node *SolidGenerator::copy() const
@@ -69,7 +69,7 @@ NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const
job.InsertValue(color_input_, value);
NodeValueTable table = value.Merge();
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
return table;
}
+22 -22
View File
@@ -32,29 +32,29 @@ enum TextVerticalAlign {
TextGenerator::TextGenerator()
{
text_input_ = new NodeInput(QStringLiteral("text_in"),
NodeParam::kText,
text_input_ = new NodeInput(this,
QStringLiteral("text_in"),
NodeValue::kText,
tr("Sample Text"));
AddInput(text_input_);
color_input_ = new NodeInput(QStringLiteral("color_in"),
NodeParam::kColor,
color_input_ = new NodeInput(this,
QStringLiteral("color_in"),
NodeValue::kColor,
QVariant::fromValue(Color(1.0f, 1.0f, 1.0)));
AddInput(color_input_);
valign_input_ = new NodeInput(QStringLiteral("valign_in"),
NodeParam::kCombo,
valign_input_ = new NodeInput(this,
QStringLiteral("valign_in"),
NodeValue::kCombo,
1);
AddInput(valign_input_);
font_input_ = new NodeInput(QStringLiteral("font_in"),
NodeParam::kFont);
AddInput(font_input_);
font_input_ = new NodeInput(this,
QStringLiteral("font_in"),
NodeValue::kFont);
font_size_input_ = new NodeInput(QStringLiteral("font_size_in"),
NodeParam::kFloat,
font_size_input_ = new NodeInput(this,
QStringLiteral("font_size_in"),
NodeValue::kFloat,
72.0f);
AddInput(font_size_input_);
}
Node *TextGenerator::copy() const
@@ -104,8 +104,8 @@ NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const
NodeValueTable table = value.Merge();
if (!job.GetValue(text_input_).data.toString().isEmpty()) {
table.Push(NodeParam::kGenerateJob, QVariant::fromValue(job), this);
if (!job.GetValue(text_input_).data().toString().isEmpty()) {
table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
}
return table;
@@ -124,14 +124,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(font_input_).data().toString());
default_font.setPointSizeF(job.GetValue(font_size_input_).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(text_input_).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 +144,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(valign_input_).data().toInt());
int doc_height = text_doc.size().height();
switch (valign) {
@@ -165,7 +165,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(color_input_).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];
+10 -119
View File
@@ -22,8 +22,7 @@
namespace olive {
NodeGraph::NodeGraph() :
operation_stack_(0)
NodeGraph::NodeGraph()
{
}
@@ -35,133 +34,25 @@ void NodeGraph::Clear()
node_children_.clear();
}
void NodeGraph::AddNode(Node *node)
void NodeGraph::childEvent(QChildEvent *event)
{
if (ContainsNode(node)) {
return;
}
Item::childEvent(event);
node->setParent(this);
Node* node = dynamic_cast<Node*>(event->child());
connect(node, &Node::EdgeAdded, this, &NodeGraph::SignalEdgeAdded);
connect(node, &Node::EdgeRemoved, this, &NodeGraph::SignalEdgeRemoved);
if (node) {
if (event->type() == QEvent::ChildAdded) {
node_children_.append(node);
emit NodeAdded(node);
}
void NodeGraph::BeginOperation()
{
operation_stack_++;
}
} else if (event->type() == QEvent::ChildRemoved) {
void NodeGraph::EndOperation()
{
operation_stack_--;
if (!operation_stack_) {
// Signal everything that we cached during the operation
// First, signal the removed edges
foreach (NodeEdgePtr e, cached_removed_edges_) {
emit EdgeRemoved(e);
}
cached_removed_edges_.clear();
// Next, signal the removed nodes
foreach (Node* n, cached_removed_nodes_) {
emit NodeRemoved(n);
}
cached_removed_nodes_.clear();
// Next, signal the added nodes
foreach (Node* n, cached_added_nodes_) {
emit NodeAdded(n);
}
cached_added_nodes_.clear();
// Finally, signal the added edges
foreach (NodeEdgePtr e, cached_added_edges_) {
emit EdgeAdded(e);
}
cached_added_edges_.clear();
}
}
void NodeGraph::SignalNodeAdded(Node* node)
{
if (!operation_stack_) {
emit NodeAdded(node);
} else if (!cached_removed_nodes_.removeOne(node)) {
// If we already removed this node during the operation (appending a signal to
// cached_removed_nodes_), we just remove that instead of appending a new signal. However if we
// didn't (removeOne returning false), only then do we append an add signal
cached_added_nodes_.append(node);
}
}
void NodeGraph::SignalNodeRemoved(Node *node)
{
if (!operation_stack_) {
node_children_.removeOne(node);
emit NodeRemoved(node);
} else if (!cached_added_nodes_.removeOne(node)) {
// See SignalNodeAdded() for explanation of this
cached_removed_nodes_.append(node);
}
}
}
void NodeGraph::SignalEdgeAdded(NodeEdgePtr edge)
{
if (!operation_stack_) {
emit EdgeAdded(edge);
} else if (!cached_removed_edges_.removeOne(edge)) {
// See SignalNodeAdded() for explanation of this
cached_added_edges_.append(edge);
}
}
void NodeGraph::SignalEdgeRemoved(NodeEdgePtr edge)
{
if (!operation_stack_) {
emit EdgeRemoved(edge);
} else if (!cached_added_edges_.removeOne(edge)) {
// See SignalNodeAdded() for explanation of this
cached_removed_edges_.append(edge);
}
}
void NodeGraph::TakeNode(Node *node, QObject* new_parent)
{
if (!ContainsNode(node)) {
return;
}
if (!node->CanBeDeleted()) {
qWarning() << "Tried to delete a Node that's been flagged as not deletable";
return;
}
node->DisconnectAll();
disconnect(node, &Node::EdgeAdded, this, &NodeGraph::EdgeAdded);
disconnect(node, &Node::EdgeRemoved, this, &NodeGraph::EdgeRemoved);
node->setParent(new_parent);
node_children_.removeAll(node);
emit NodeRemoved(node);
}
const QList<Node *> &NodeGraph::nodes() const
{
return node_children_;
}
bool NodeGraph::ContainsNode(Node *n) const
{
return (n->parent() == this);
}
}
+7 -46
View File
@@ -46,32 +46,13 @@ public:
*/
void Clear();
/**
* @brief Add a node to this graph
*
* The node will get added to this graph. It is not automatically connected to anything, any connections will need to
* be made manually after the node is added. The graph takes ownership of the Node.
*/
void AddNode(Node* node);
/**
* @brief Removes a Node from the graph BUT doesn't destroy it. Ownership is passed to `new_parent`.
*/
void TakeNode(Node* node, QObject* new_parent = nullptr);
/**
* @brief Retrieve a complete list of the nodes belonging to this graph
*/
const QList<Node*>& nodes() const;
/**
* @brief Returns whether a certain Node is in the graph or not
*/
bool ContainsNode(Node* n) const;
void BeginOperation();
void EndOperation();
const QVector<Node*>& nodes() const
{
return node_children_;
}
signals:
/**
@@ -84,31 +65,11 @@ signals:
*/
void NodeRemoved(Node* node);
/**
* @brief Signal emitted when a member node of this graph has been connected to another (creating an "edge")
*/
void EdgeAdded(NodeEdgePtr edge);
/**
* @brief Signal emitted when a member node of this graph has been disconnected from another (removing an "edge")
*/
void EdgeRemoved(NodeEdgePtr edge);
protected:
virtual void childEvent(QChildEvent* event) override;
private:
QList<Node*> node_children_;
int operation_stack_;
QList<Node*> cached_added_nodes_;
QList<Node*> cached_removed_nodes_;
QList<NodeEdgePtr> cached_added_edges_;
QList<NodeEdgePtr> cached_removed_edges_;
private slots:
void SignalNodeAdded(Node *node);
void SignalNodeRemoved(Node* node);
void SignalEdgeAdded(NodeEdgePtr edge);
void SignalEdgeRemoved(NodeEdgePtr edge);
QVector<Node*> node_children_;
};
+370 -766
View File
File diff suppressed because it is too large Load Diff
+289 -223
View File
@@ -23,19 +23,21 @@
#include "common/timerange.h"
#include "keyframe.h"
#include "param.h"
#include "node/connectable.h"
#include "node/inputimmediate.h"
#include "node/value.h"
namespace olive {
class Node;
/**
* @brief A node parameter designed to take either user input or data from another node
*/
class NodeInput : public NodeParam
class NodeInput : public NodeConnectable
{
Q_OBJECT
public:
using KeyframeTrack = QList<NodeKeyframePtr>;
/**
* @brief NodeInput Constructor
*
@@ -45,22 +47,46 @@ public:
* 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(const QString &id, const DataType& type, const QVector<QVariant>& default_value);
NodeInput(const QString &id, const DataType& type, const QVariant& default_value);
NodeInput(const QString &id, const DataType& type);
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVector<QVariant>& 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 bool IsArray() const;
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 Returns kInput
* @brief Deliberately shadow QObject parent, since we only expect NodeInput to have Node as a parent
*/
virtual Type type() override;
virtual QString name() override;
virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) override;
virtual void Save(QXmlStreamWriter* writer) const override;
Node* parent() const;
/**
* @brief The data type this parameter outputs
@@ -68,190 +94,160 @@ public:
* This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be
* connected to it.
*/
const DataType& data_type() const;
NodeValue::Type GetDataType() const
{
return data_type_;
}
void SetDataType(NodeValue::Type type)
{
data_type_ = type;
emit DataTypeChanged(type);
}
const QHash<int, Node*>& edges() const
{
return input_connections();
}
bool IsConnected(int element = -1) const
{
return input_connections().contains(element);
}
/**
* @brief If this input is connected to an output, retrieve the output parameter
*
* @return
*
* The output parameter if connected or nullptr if not
* @brief Returns TRUE if the value is expected to always be the same (i.e. no keyframes and
* not connected to anything)
*/
NodeOutput* get_connected_output() const;
bool IsStatic(int element = -1) const
{
return !IsConnected(element) && !GetImmediate(element)->is_keyframing();
}
/**
* @brief If this input is connected to an output, retrieve the Node whose output is connected
*
* @return
*
* The connected Node if connected or nullptr if not
*/
Node* get_connected_node() const;
Node* GetConnectedNode(int element = -1) const
{
return input_connections().value(element);
}
/**
* @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 get_value_at_time(const rational& time) const;
bool IsConnectable() const
{
return connectable_;
}
QVector<QVariant> get_split_values_at_time(const rational& time) const;
void SetConnectable(bool e)
{
connectable_ = e;
}
/**
* @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 get_value_at_time_for_track(const rational& time, int track) const;
const QVector<NodeKeyframeTrack> &GetKeyframeTracks(int element = -1) const
{
return GetImmediate(element)->keyframe_tracks();
}
/**
* @brief Retrieve a list of keyframe objects for all tracks at a given time
*
* List may be empty if this input is not keyframing or has no keyframes at this time.
*/
QList<NodeKeyframePtr> get_keyframe_at_time(const rational& time) const;
NodeKeyframe* GetKeyframeAtTimeOnTrack(const rational& time, int track, int element) const
{
return GetImmediate(element)->get_keyframe_at_time_on_track(time, track);
}
/**
* @brief Retrieve the keyframe object at a given time for a given track
*
* @return
*
* The keyframe object at this time or nullptr if there isn't one or if is_keyframing() is false.
*/
NodeKeyframePtr get_keyframe_at_time_on_track(const rational& time, int track) const;
/**
* @brief Gets the closest keyframe to a time
*
* If is_keyframing() is false or keyframes_ is empty, this will return nullptr.
*/
NodeKeyframePtr get_closest_keyframe_to_time_on_track(const rational& time, int track) const;
/**
* @brief Get closest keyframe that's before the time on any track
*
* If no keyframe is before this time, returns nullptr.
*/
NodeKeyframePtr get_closest_keyframe_before_time(const rational& time) const;
/**
* @brief Get closest keyframe that's before the time on any track
*
* If no keyframe is before this time, returns nullptr.
*/
NodeKeyframePtr get_closest_keyframe_after_time(const rational& time) const;
/**
* @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes)
*/
NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time, int track) const;
/**
* @brief Retrieve the number of
*/
int get_number_of_keyframe_tracks() const;
/**
* @brief Gets the earliest keyframe on any track
*/
NodeKeyframePtr get_earliest_keyframe() const;
/**
* @brief Gets the latest keyframe on any track
*/
NodeKeyframePtr get_latest_keyframe() const;
/**
* @brief Inserts a keyframe at the given time and returns a reference to it
*/
void insert_keyframe(NodeKeyframePtr key);
/**
* @brief Removes the keyframe
*/
void remove_keyframe(NodeKeyframePtr key);
/**
* @brief Hacky convenience function to turn a raw pointer into a shared pointer
*/
NodeKeyframePtr get_keyframe_shared_ptr_from_raw(NodeKeyframe *raw) const;
/**
* @brief Return whether a keyframe exists at this time
*
* If is_keyframing() is false, this will always return false. This checks all tracks and will return true if *any*
* track has a keyframe.
*/
bool has_keyframe_at_time(const rational &time) const;
/**
* @brief Return whether keyframing is enabled on this input or not
*/
bool is_keyframing() const;
/**
* @brief Set whether keyframing is enabled on this input or not
*/
void set_is_keyframing(bool k);
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 is_keyframable() const;
bool IsKeyframable() const;
/**
* @brief Returns whether the value that this input returns is always the same or is expected to change
*
* Equivalent to `!(is_connected() || is_keyframing())`
*/
bool is_static() const;
bool IsKeyframing(int element = -1) const
{
return GetImmediate(element)->is_keyframing();
}
/**
* @brief Get non-keyframed value
*/
QVariant get_standard_value() const;
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 QVector<QVariant>& get_split_standard_value() const;
const QVector<QVariant>& 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 set_standard_value(const QVariant& value, int track = 0);
void SetStandardValue(const QVariant& value, int element = -1)
{
SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element);
}
/**
* @brief Return list of keyframes in this parameter
*/
const QVector<KeyframeTrack> &keyframe_tracks() const;
void SetSplitStandardValue(const QVector<QVariant>& 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 set_is_keyframable(bool k);
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);
QVector<QVariant> split_normal_value_into_track_values(const QVariant &value) const;
QVariant combine_track_values_into_normal_value(const QVector<QVariant>& split) const;
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element);
QStringList get_combobox_strings() const;
void set_combobox_strings(const QStringList& strings);
static QString ValueToString(const DataType& data_type, const QVariant& value, bool value_is_a_key_track);
static QVariant StringToValue(const DataType &data_type, const QString &string, bool value_is_a_key_track);
void GetDependencies(QVector<Node *> &list, bool traverse, bool exclusive_only) const;
QVariant GetDefaultValue() const;
QVariant GetDefaultValue() const
{
return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_);
}
const QVector<QVariant>& GetSplitDefaultValue() const
{
return default_value_;
}
QVariant GetDefaultValueForTrack(int track) const;
@@ -261,57 +257,139 @@ public:
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)
{
Q_ASSERT(IsKeyframable());
GetImmediate(element)->set_is_keyframing(keyframing);
emit KeyframeEnableChanged(keyframing, element);
}
void ArrayAppend()
{
ArrayResize(ArraySize() + 1);
}
void ArrayInsert(int index);
void ArrayRemove(int index);
void ArrayPrepend();
void ArrayResize(int size);
int ArraySize() const
{
return array_size_;
}
void ArrayRemoveLast();
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;
QVector<QVariant> 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);
}
signals:
void ValueChanged(const olive::TimeRange& range);
void NameChanged(const QString& name);
void KeyframeEnableChanged(bool);
void ValueChanged(const olive::TimeRange& range, int element);
void KeyframeAdded(NodeKeyframePtr key);
void KeyframeAdded(NodeKeyframe* key);
void KeyframeRemoved(NodeKeyframePtr key);
void KeyframeRemoved(NodeKeyframe* key);
void PropertyChanged(const QString& s, const QVariant& v);
void ArraySizeChanged(int size);
void KeyframeEnableChanged(bool enabled, int element);
void DataTypeChanged(NodeValue::Type type);
protected:
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
virtual void SaveInternal(QXmlStreamWriter* writer) const;
virtual bool event(QEvent* e) override;
private:
void Init(NodeParam::DataType type);
virtual void childEvent(QChildEvent* e) override;
void SetDefaultValue(const QVector<QVariant> &default_value);
private:
void Init(Node *parent, const QString& id, NodeValue::Type type, const QVector<QVariant> &default_val);
void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
void SaveImmediate(QXmlStreamWriter *writer, int element) const;
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);
void SaveConnections(QXmlStreamWriter* writer) const;
static void ValidateVectorString(QStringList* list, int count);
/**
* @brief Returns whether a data type can be interpolated or not
*/
static bool type_can_be_interpolated(DataType type);
/**
* @brief We use Qt signals/slots for keyframe communication but store them as shared ptrs. This function converts
* a raw ptr to a list index
*/
int FindIndexOfKeyframeFromRawPtr(NodeKeyframe* raw_ptr) const;
/**
* @brief Internal insert function, automatically does an insertion sort based on the keyframe's time
*/
void insert_keyframe_internal(NodeKeyframePtr key);
/**
* @brief Return whether the standard value should be used over keyframe data
*/
bool is_using_standard_value(int track) 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
@@ -321,51 +399,39 @@ private:
/**
* @brief Gets a time range between the previous and next keyframes of index
*/
TimeRange get_range_around_index(int index, int track) const;
TimeRange get_range_around_index(int index, int track, int element) const;
NodeInputImmediate* primary_;
QVector<NodeInputImmediate*> subinputs_;
QVector<QVariant> default_value_;
/**
* @brief Convenience function - equivalent to calling `emit ValueChanged(range.in(), range.out())`
* @brief Unique identifier of this input within this node
*/
void emit_time_range(const TimeRange& range);
QString id_;
/**
* @brief Convenience function - equivalent to calling `emit_time_range(get_range_affected_by_keyframe(key))`
* @brief User displayable name of input
*/
void emit_range_affected_by_keyframe(NodeKeyframe* key);
/**
* @brief Internal list of accepted data types
*
* Use can_accept_type() to check if a type is in this list
*/
DataType data_type_;
QString name_;
/**
* @brief Internal keyframable value
*/
bool keyframable_;
/**
* @brief Non-keyframed value
*/
QVector<QVariant> standard_value_;
bool connectable_;
bool is_array_;
int array_size_;
/**
* @brief Default value that can be reset if the user requests
* @brief Default data type
*/
QVector<QVariant> default_value_;
/**
* @brief Internal keyframe array
*
* If keyframing is enabled, this data is used instead of standard_value.
*/
QVector< QList<NodeKeyframePtr> > keyframe_tracks_;
/**
* @brief Internal keyframing enabled setting
*/
bool keyframing_;
NodeValue::Type data_type_;
private slots:
/**
+8 -14
View File
@@ -28,11 +28,10 @@ namespace olive {
MediaInput::MediaInput() :
connected_footage_(nullptr)
{
footage_input_ = new NodeInput("footage_in", NodeInput::kFootage);
footage_input_->set_connectable(false);
footage_input_->set_is_keyframable(false);
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(footage_input_);
}
QVector<Node::CategoryID> MediaInput::Category() const
@@ -42,17 +41,12 @@ QVector<Node::CategoryID> MediaInput::Category() const
Stream *MediaInput::stream() const
{
return Node::ValueToPtr<Stream>(footage_input_->get_standard_value());
return Node::ValueToPtr<Stream>(footage_input_->GetStandardValue());
}
void MediaInput::SetStream(Stream* s)
{
footage_input_->set_standard_value(Node::PtrToValue(s));
}
bool MediaInput::IsMedia() const
{
return true;
footage_input_->SetStandardValue(Node::PtrToValue(s));
}
void MediaInput::Retranslate()
@@ -68,7 +62,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
rational media_duration = Timecode::timestamp_to_time(connected_footage_->duration(),
connected_footage_->timebase());
table.Push(NodeInput::kRational, QVariant::fromValue(media_duration), this, "length");
table.Push(NodeValue::kRational, QVariant::fromValue(media_duration), this, false, QStringLiteral("length"));
}
return table;
@@ -76,7 +70,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
void MediaInput::FootageChanged()
{
Stream* new_footage = footage_input_->get_standard_value().value<Stream*>();
Stream* new_footage = footage_input_->GetStandardValue().value<Stream*>();
if (new_footage == connected_footage_) {
return;
@@ -95,7 +89,7 @@ void MediaInput::FootageChanged()
void MediaInput::FootageParametersChanged()
{
InvalidateCache(TimeRange(0, RATIONAL_MAX), footage_input_, footage_input_);
InvalidateCache(TimeRange(0, RATIONAL_MAX), InputConnection(footage_input_));
}
}
-2
View File
@@ -61,8 +61,6 @@ public:
Stream* stream() const;
void SetStream(Stream *s);
virtual bool IsMedia() const override;
virtual void Retranslate() override;
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
+4 -3
View File
@@ -55,9 +55,10 @@ NodeValueTable TimeInput::Value(NodeValueDatabase &value) const
{
NodeValueTable table = value.Merge();
table.Push(NodeParam::kFloat,
value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")),
table.Push(NodeValue::kFloat,
value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")),
this,
false,
QStringLiteral("time"));
return table;
@@ -68,7 +69,7 @@ void TimeInput::Hash(QCryptographicHash &hash, const rational &time) const
Node::Hash(hash, time);
// Make sure time is hashed
hash.addData(NodeParam::ValueToBytes(NodeParam::kRational, QVariant::fromValue(time)));
hash.addData(NodeValue::ValueToBytes(NodeValue::kRational, QVariant::fromValue(time)));
}
}
-238
View File
@@ -1,238 +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 "inputarray.h"
#include <QApplication>
#include "common/xmlutils.h"
#include "node.h"
namespace olive {
NodeInputArray::NodeInputArray(const QString &id, const DataType &type, const QVariant &default_value) :
NodeInput(id, type, default_value),
default_value_(default_value)
{
}
NodeInputArray::~NodeInputArray()
{
// Clear all connected edges (make sure our override is called)
DisconnectAll();
}
bool NodeInputArray::IsArray() const
{
return true;
}
int NodeInputArray::GetSize() const
{
return sub_params_.size();
}
void NodeInputArray::Prepend()
{
InsertAt(0);
}
void NodeInputArray::SetSize(int size)
{
int old_size = GetSize();
if (size == old_size) {
return;
}
if (size < old_size) {
// If the new size is less, delete all extraneous parameters
for (int i=size;i<old_size;i++) {
sub_params_.at(i)->DisconnectAll();
}
for (int i=size;i<old_size;i++) {
delete sub_params_.at(i);
}
}
sub_params_.resize(size);
if (size > old_size) {
// If the new size is greater, create valid parameters for each slot
for (int i=old_size;i<size;i++) {
QString sub_id = id();
sub_id.append(QString::number(i));
Q_ASSERT(!parentNode()->HasParamWithID(sub_id));
NodeInput* new_param = new NodeInput(sub_id, data_type(), default_value_);
new_param->setParent(this);
sub_params_.replace(i, new_param);
connect(new_param, &NodeInput::ValueChanged, this, &NodeInput::ValueChanged);
connect(new_param, &NodeInput::EdgeAdded, this, &NodeInputArray::SubParamEdgeAdded);
connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInputArray::SubParamEdgeRemoved);
}
}
emit SizeChanged(size);
}
bool NodeInputArray::ContainsSubParameter(NodeInput *input) const
{
return sub_params_.contains(input);
}
int NodeInputArray::IndexOfSubParameter(NodeInput *input) const
{
return sub_params_.indexOf(input);
}
NodeInput *NodeInputArray::At(int index) const
{
return sub_params_.at(index);
}
NodeInput *NodeInputArray::First() const
{
return sub_params_.first();
}
NodeInput *NodeInputArray::Last() const
{
return sub_params_.last();
}
const QVector<NodeInput *> &NodeInputArray::sub_params()
{
return sub_params_;
}
void NodeInputArray::DisconnectAll()
{
NodeParam::DisconnectAll();
foreach (NodeInput* input, sub_params_) {
input->DisconnectAll();
}
}
void NodeInputArray::InsertAt(int index)
{
// Add another input at the end
Append();
// Shift all connections from index down
for (int i=sub_params_.size()-1;i>index;i--) {
NodeInput* this_param = sub_params_.at(i);
NodeInput* prev_param = sub_params_.at(i-1);
if (this_param->is_connected()) {
// Disconnect whatever is at this parameter (presumably its connection has already been copied so we can just remove it)
NodeParam::DisconnectEdge(this_param->edges().first());
}
if (prev_param->is_connected()) {
// Get edge here (only one since it's an input)
NodeEdgePtr edge = prev_param->edges().first();
// Disconnect it
NodeParam::DisconnectEdge(edge);
// Create a new edge between it and the next one down
NodeParam::ConnectEdge(edge->output(),
this_param);
}
}
}
void NodeInputArray::Append()
{
SetSize(GetSize() + 1);
}
void NodeInputArray::RemoveLast()
{
SetSize(GetSize() - 1);
}
void NodeInputArray::RemoveAt(int index)
{
// Shift all connections from index down
for (int i=index;i<sub_params_.size();i++) {
NodeInput* this_param = sub_params_.at(i);
if (this_param->is_connected()) {
// Disconnect current edge
NodeParam::DisconnectEdge(this_param->edges().first());
}
if (i < sub_params_.size() - 1) {
NodeInput* next_param = sub_params_.at(i + 1);
if (next_param->is_connected()) {
// Get edge from next param
NodeEdgePtr edge = next_param->edges().first();
// Disconnect it
NodeParam::DisconnectEdge(edge);
// Reconnect it to this param
NodeParam::ConnectEdge(edge->output(),
this_param);
}
}
}
RemoveLast();
}
void NodeInputArray::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt* cancelled)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("subparameters")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
Append();
At(GetSize() - 1)->Load(reader, xml_node_data, cancelled);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
}
void NodeInputArray::SaveInternal(QXmlStreamWriter *writer) const
{
writer->writeStartElement("subparameters");
foreach (NodeInput* sub, sub_params_) {
writer->writeStartElement(QStringLiteral("input"));
sub->Save(writer);
writer->writeEndElement();
}
writer->writeEndElement(); // subparameters
}
}
-79
View File
@@ -1,79 +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 INPUTARRAY_H
#define INPUTARRAY_H
#include "input.h"
namespace olive {
class NodeInputArray : public NodeInput
{
Q_OBJECT
public:
NodeInputArray(const QString &id, const DataType& type, const QVariant& default_value = 0);
virtual ~NodeInputArray() override;
virtual bool IsArray() const override;
int GetSize() const;
void Prepend();
void Append();
void InsertAt(int index);
void RemoveLast();
void RemoveAt(int index);
void SetSize(int size);
bool ContainsSubParameter(NodeInput* input) const;
int IndexOfSubParameter(NodeInput* input) const;
NodeInput* First() const;
NodeInput* Last() const;
NodeInput* At(int index) const;
const QVector<NodeInput*>& sub_params();
virtual void DisconnectAll() override;
signals:
void SizeChanged(int size);
void SubParamEdgeAdded(NodeEdgePtr edge);
void SubParamEdgeRemoved(NodeEdgePtr edge);
protected:
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt *cancelled) override;
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
private:
QVector<NodeInput*> sub_params_;
QVariant default_value_;
};
}
#endif // INPUTARRAY_H
+15 -19
View File
@@ -37,7 +37,7 @@ bool NodeInputDragger::IsStarted() const
return input_;
}
void NodeInputDragger::Start(NodeInput *input, const rational &time, int track)
void NodeInputDragger::Start(NodeInput *input, const rational &time, int track, int element)
{
Q_ASSERT(!input_);
@@ -45,27 +45,23 @@ void NodeInputDragger::Start(NodeInput *input, const rational &time, int track)
input_ = input;
time_ = time;
track_ = track;
element_ = element;
// Cache current value
start_value_ = input_->get_value_at_time_for_track(time, track);
start_value_ = input_->GetValueAtTimeForTrack(time, track, element_);
// Determine whether we are creating a keyframe or not
if (input_->is_keyframing()) {
dragging_key_ = input_->get_keyframe_at_time_on_track(time, track);
if (input_->IsKeyframing(element_)) {
dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_);
drag_created_key_ = !dragging_key_;
if (drag_created_key_) {
dragging_key_ = NodeKeyframe::Create(time,
dragging_key_ = new NodeKeyframe(time,
start_value_,
input_->get_best_keyframe_type_for_time(time, track),
track);
// We disable default signal emitting during the drag
//input_->blockSignals(true);
input_->insert_keyframe(dragging_key_);
//input_->blockSignals(false);
emit input_->KeyframeAdded(dragging_key_);
input_->GetBestKeyframeTypeForTime(time, track, element_),
track,
element_,
input_);
}
}
}
@@ -95,10 +91,10 @@ void NodeInputDragger::Drag(QVariant value)
//input_->blockSignals(true);
if (input_->is_keyframing()) {
if (input_->IsKeyframing(element_)) {
dragging_key_->set_value(value);
} else {
input_->set_standard_value(value, track_);
input_->SetStandardValueOnTrack(value, track_, element_);
}
//input_->blockSignals(false);
@@ -112,10 +108,10 @@ void NodeInputDragger::End()
QUndoCommand* command = new QUndoCommand();
if (input_->is_keyframing()) {
if (input_->IsKeyframing(element_)) {
if (drag_created_key_) {
// We created a keyframe in this process
new NodeParamInsertKeyframeCommand(input_, dragging_key_, true, command);
new NodeParamInsertKeyframeCommand(input_, dragging_key_, command);
}
// We just set a keyframe's value
@@ -124,7 +120,7 @@ void NodeInputDragger::End()
new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_, command);
} else {
// We just set the standard value
new NodeParamSetStandardValueCommand(input_, track_, end_value_, start_value_, command);
new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_, command);
}
Core::instance()->undo_stack()->push(command);
+4 -2
View File
@@ -32,7 +32,7 @@ public:
bool IsStarted() const;
void Start(NodeInput* input, const rational& time, int track);
void Start(NodeInput* input, const rational& time, int track, int element = -1);
void Drag(QVariant value);
@@ -45,11 +45,13 @@ private:
rational time_;
int element_;
QVariant start_value_;
QVariant end_value_;
NodeKeyframePtr dragging_key_;
NodeKeyframe* dragging_key_;
bool drag_created_key_;
+243
View File
@@ -0,0 +1,243 @@
/***
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 "inputimmediate.h"
#include "common/bezier.h"
#include "common/lerp.h"
#include "common/tohex.h"
#include "input.h"
namespace olive {
NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const QVector<QVariant> &default_val) :
keyframing_(false)
{
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
keyframe_tracks_.resize(track_size);
standard_value_.resize(track_size);
set_split_standard_value(default_val);
}
void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track)
{
standard_value_.replace(track, value);
}
void NodeInputImmediate::set_split_standard_value(const QVector<QVariant> &value)
{
for (int i=0; i<value.size() && i<standard_value_.size(); i++) {
standard_value_[i] = value[i];
}
}
QVector<NodeKeyframe*> NodeInputImmediate::get_keyframe_at_time(const rational &time) const
{
QVector<NodeKeyframe*> keys;
for (int i=0;i<keyframe_tracks_.size();i++) {
NodeKeyframe* key_at_time = get_keyframe_at_time_on_track(time, i);
if (key_at_time) {
keys.append(key_at_time);
}
}
return keys;
}
NodeKeyframe* NodeInputImmediate::get_keyframe_at_time_on_track(const rational &time, int track) const
{
if (!is_using_standard_value(track)) {
foreach (NodeKeyframe* key, keyframe_tracks_.at(track)) {
if (key->time() == time) {
return key;
}
}
}
return nullptr;
}
NodeKeyframe* NodeInputImmediate::get_closest_keyframe_to_time_on_track(const rational &time, int track) const
{
if (is_using_standard_value(track)) {
return nullptr;
}
const NodeKeyframeTrack& key_track = keyframe_tracks_.at(track);
if (time <= key_track.first()->time()) {
return key_track.first();
}
if (time >= key_track.last()->time()) {
return key_track.last();
}
for (int i=1;i<key_track.size();i++) {
NodeKeyframe* prev_key = key_track.at(i-1);
NodeKeyframe* next_key = key_track.at(i);
if (prev_key->time() <= time && next_key->time() >= time) {
// Return whichever is closer
rational prev_diff = time - prev_key->time();
rational next_diff = next_key->time() - time;
if (next_diff < prev_diff) {
return next_key;
} else {
return prev_key;
}
}
}
return nullptr;
}
NodeKeyframe *NodeInputImmediate::get_closest_keyframe_before_time(const rational &time) const
{
NodeKeyframe* key = nullptr;
foreach (const NodeKeyframeTrack& track, keyframe_tracks_) {
foreach (NodeKeyframe* k, track) {
if (k->time() >= time) {
break;
} else if (!key || k->time() > key->time()) {
key = k;
}
}
}
return key;
}
NodeKeyframe* NodeInputImmediate::get_closest_keyframe_after_time(const rational &time) const
{
NodeKeyframe* key = nullptr;
foreach (const NodeKeyframeTrack& track, keyframe_tracks_) {
for (int i=track.size()-1;i>=0;i--) {
NodeKeyframe* k = track.at(i);
if (k->time() <= time) {
break;
} else if (!key || k->time() < key->time()) {
key = k;
}
}
}
return key;
}
NodeKeyframe::Type NodeInputImmediate::get_best_keyframe_type_for_time(const rational &time, int track) const
{
NodeKeyframe* closest_key = get_closest_keyframe_to_time_on_track(time, track);
if (closest_key) {
return closest_key->type();
}
return NodeKeyframe::kDefaultType;
}
bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const
{
if (!is_keyframing()) {
return false;
}
// Loop through keyframes to see if any match
foreach (const NodeKeyframeTrack& track, keyframe_tracks_) {
foreach (NodeKeyframe* key, track) {
if (key->time() == time) {
return true;
}
}
}
// None match
return false;
}
NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const
{
NodeKeyframe* earliest = nullptr;
foreach (const NodeKeyframeTrack& track, keyframe_tracks_) {
if (!track.isEmpty()) {
NodeKeyframe* earliest_in_track = track.first();
if (!earliest
|| earliest_in_track->time() < earliest->time()) {
earliest = earliest_in_track;
}
}
}
return earliest;
}
NodeKeyframe *NodeInputImmediate::get_latest_keyframe() const
{
NodeKeyframe* latest = nullptr;
foreach (const NodeKeyframeTrack& track, keyframe_tracks_) {
if (!track.isEmpty()) {
NodeKeyframe* latest_in_track = track.last();
if (!latest
|| latest_in_track->time() > latest->time()) {
latest = latest_in_track;
}
}
}
return latest;
}
void NodeInputImmediate::insert_keyframe(NodeKeyframe* key)
{
NodeKeyframeTrack& key_track = keyframe_tracks_[key->track()];
for (int i=0;i<key_track.size();i++) {
NodeKeyframe* compare = key_track.at(i);
// Ensure we aren't trying to insert two keyframes at the same time
Q_ASSERT(compare->time() != key->time());
if (compare->time() > key->time()) {
key_track.insert(i, key);
return;
}
}
key_track.append(key);
}
void NodeInputImmediate::remove_keyframe(NodeKeyframe *key)
{
keyframe_tracks_[key->track()].removeOne(key);
}
}
+168
View File
@@ -0,0 +1,168 @@
/***
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 NODEINPUTIMMEDIATE_H
#define NODEINPUTIMMEDIATE_H
#include "common/timerange.h"
#include "common/xmlutils.h"
#include "node/keyframe.h"
#include "node/value.h"
namespace olive {
class NodeInput;
class NodeInputImmediate
{
public:
NodeInputImmediate(NodeValue::Type type, const QVector<QVariant>& default_val);
/**
* @brief Internal insert function, automatically does an insertion sort based on the keyframe's time
*/
void insert_keyframe(NodeKeyframe* key);
void remove_keyframe(NodeKeyframe* key);
/**
* @brief Get non-keyframed value split into components (the way it's stored)
*/
const QVector<QVariant>& get_split_standard_value() const
{
return standard_value_;
}
void set_standard_value_on_track(const QVariant &value, int track = 0);
void set_split_standard_value(const QVector<QVariant>& value);
/**
* @brief Retrieve a list of keyframe objects for all tracks at a given time
*
* List may be empty if this input is not keyframing or has no keyframes at this time.
*/
QVector<NodeKeyframe*> get_keyframe_at_time(const rational& time) const;
/**
* @brief Retrieve the keyframe object at a given time for a given track
*
* @return
*
* The keyframe object at this time or nullptr if there isn't one or if is_keyframing() is false.
*/
NodeKeyframe* get_keyframe_at_time_on_track(const rational& time, int track) const;
/**
* @brief Gets the closest keyframe to a time
*
* If is_keyframing() is false or keyframes_ is empty, this will return nullptr.
*/
NodeKeyframe* get_closest_keyframe_to_time_on_track(const rational& time, int track) const;
/**
* @brief Get closest keyframe that's before the time on any track
*
* If no keyframe is before this time, returns nullptr.
*/
NodeKeyframe* get_closest_keyframe_before_time(const rational& time) const;
/**
* @brief Get closest keyframe that's before the time on any track
*
* If no keyframe is before this time, returns nullptr.
*/
NodeKeyframe* get_closest_keyframe_after_time(const rational& time) const;
/**
* @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes)
*/
NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time, int track) const;
/**
* @brief Return list of keyframes in this parameter
*/
const QVector<NodeKeyframeTrack> &keyframe_tracks() const
{
return keyframe_tracks_;
}
/**
* @brief Return whether keyframing is enabled on this input or not
*/
bool is_keyframing() const
{
return keyframing_;
}
/**
* @brief Set whether keyframing is enabled on this input or not
*/
void set_is_keyframing(bool k)
{
keyframing_ = k;
}
/**
* @brief Gets the earliest keyframe on any track
*/
NodeKeyframe* get_earliest_keyframe() const;
/**
* @brief Gets the latest keyframe on any track
*/
NodeKeyframe* get_latest_keyframe() const;
/**
* @brief Return whether a keyframe exists at this time
*
* If is_keyframing() is false, this will always return false. This checks all tracks and will return true if *any*
* track has a keyframe.
*/
bool has_keyframe_at_time(const rational &time) const;
bool is_using_standard_value(int track) const
{
return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty());
}
private:
/**
* @brief Non-keyframed value
*/
QVector<QVariant> standard_value_;
/**
* @brief Internal keyframe array
*
* If keyframing is enabled, this data is used instead of standard_value.
*/
QVector<NodeKeyframeTrack> keyframe_tracks_;
/**
* @brief Internal keyframing enabled setting
*/
bool keyframing_;
};
}
#endif // NODEINPUTIMMEDIATE_H
+13 -25
View File
@@ -20,34 +20,37 @@
#include "keyframe.h"
#include "input.h"
namespace olive {
const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear;
NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track) :
parent_(nullptr),
NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track, int element, QObject *parent) :
time_(time),
value_(value),
type_(type),
bezier_control_in_(QPointF(-1.0, 0.0)),
bezier_control_out_(QPointF(1.0, 0.0)),
track_(track)
track_(track),
element_(element)
{
setParent(parent);
}
NodeKeyframePtr NodeKeyframe::Create(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int& track)
NodeKeyframe *NodeKeyframe::copy(QObject* parent) const
{
return std::make_shared<NodeKeyframe>(time, value, type, track);
}
NodeKeyframePtr NodeKeyframe::copy() const
{
NodeKeyframePtr copy = std::make_shared<NodeKeyframe>(time_, value_, type_, track_);
NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element_, parent);
copy->bezier_control_in_ = bezier_control_in_;
copy->bezier_control_out_ = bezier_control_out_;
return copy;
}
NodeInput *NodeKeyframe::parent() const
{
return static_cast<NodeInput*>(QObject::parent());
}
const rational &NodeKeyframe::time() const
{
return time_;
@@ -121,11 +124,6 @@ void NodeKeyframe::set_bezier_control(NodeKeyframe::BezierType type, const QPoin
}
}
const int &NodeKeyframe::track() const
{
return track_;
}
NodeKeyframe::BezierType NodeKeyframe::get_opposing_bezier_type(NodeKeyframe::BezierType type)
{
if (type == kInHandle) {
@@ -135,14 +133,4 @@ NodeKeyframe::BezierType NodeKeyframe::get_opposing_bezier_type(NodeKeyframe::Be
}
}
NodeInput *NodeKeyframe::parent() const
{
return parent_;
}
void NodeKeyframe::set_parent(NodeInput *parent)
{
parent_ = parent;
}
}
+17 -12
View File
@@ -30,9 +30,7 @@
namespace olive {
class NodeInput;
class NodeKeyframe;
using NodeKeyframePtr = std::shared_ptr<NodeKeyframe>;
class NodeInputImmediate;
/**
* @brief A point of data to be used at a certain time and interpolated with other data
@@ -63,11 +61,11 @@ public:
/**
* @brief NodeKeyframe Constructor
*/
NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track);
NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track, int element, QObject* parent = nullptr);
static NodeKeyframePtr Create(const rational& time, const QVariant& value, const Type& type, const int &track);
NodeKeyframe* copy(QObject* parent = nullptr) const;
NodeKeyframePtr copy() const;
NodeInput* parent() const;
/**
* @brief The time this keyframe is set at
@@ -111,16 +109,21 @@ public:
* For the majority of keyfreames, this will be 0, but for some types, such as kVec2, this will be 0 for X keyframes
* and 1 for Y keyframes, etc.
*/
const int& track() const;
int track() const
{
return track_;
}
int element() const
{
return element_;
}
/**
* @brief Convenience function for getting the opposite handle type (e.g. kInHandle <-> kOutHandle)
*/
static BezierType get_opposing_bezier_type(BezierType type);
NodeInput* parent() const;
void set_parent(NodeInput* parent);
signals:
/**
* @brief Signal emitted when this keyframe's time is changed
@@ -148,8 +151,6 @@ signals:
void BezierControlOutChanged(const QPointF& d);
private:
NodeInput* parent_;
rational time_;
QVariant value_;
@@ -162,8 +163,12 @@ private:
int track_;
int element_;
};
using NodeKeyframeTrack = QVector<NodeKeyframe*>;
}
Q_DECLARE_METATYPE(olive::NodeKeyframe::Type)
+5 -8
View File
@@ -24,20 +24,17 @@ namespace olive {
MathNode::MathNode()
{
method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kCombo);
method_in_->set_connectable(false);
method_in_->set_is_keyframable(false);
AddInput(method_in_);
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
method_in_->SetConnectable(false);
method_in_->SetKeyframable(false);
param_a_in_ = new NodeInput(QStringLiteral("param_a_in"), NodeParam::kFloat, 0.0);
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(param_a_in_);
param_b_in_ = new NodeInput(QStringLiteral("param_b_in"), NodeParam::kFloat, 0.0);
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(param_b_in_);
}
Node *MathNode::copy() const
+2 -2
View File
@@ -44,12 +44,12 @@ public:
Operation GetOperation() const
{
return static_cast<Operation>(method_in_->get_standard_value().toInt());
return static_cast<Operation>(method_in_->GetStandardValue().toInt());
}
void SetOperation(Operation o)
{
method_in_->set_standard_value(o);
method_in_->SetStandardValue(o);
}
NodeInput* param_a_in() const
+56 -56
View File
@@ -35,16 +35,16 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
Operation op = static_cast<Operation>(code_id.at(0).toInt());
Pairing pairing = static_cast<Pairing>(code_id.at(1).toInt());
NodeParam::DataType type_a = static_cast<NodeParam::DataType>(code_id.at(2).toInt());
NodeParam::DataType type_b = static_cast<NodeParam::DataType>(code_id.at(3).toInt());
NodeValue::Type type_a = static_cast<NodeValue::Type>(code_id.at(2).toInt());
NodeValue::Type type_b = static_cast<NodeValue::Type>(code_id.at(3).toInt());
QString operation, frag, vert;
if (pairing == kPairTextureMatrix && op == kOpMultiply) {
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in : param_b_in;
NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in : param_a_in;
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;
// No-op frag shader (can we return QString() instead?)
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id());
@@ -78,7 +78,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
case kOpPower:
if (pairing == kPairTextureNumber) {
// The "number" in this operation has to be declared a vec4
if (type_a & NodeParam::kNumber) {
if (NodeValue::type_is_numeric(type_a)) {
operation = QStringLiteral("pow(%2, vec4(%1))");
} else {
operation = QStringLiteral("pow(%1, vec4(%2))");
@@ -111,23 +111,23 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
return ShaderCode(frag, vert);
}
QString MathNodeBase::GetShaderUniformType(const NodeParam::DataType &type)
QString MathNodeBase::GetShaderUniformType(const olive::NodeValue::Type &type)
{
switch (type) {
case NodeParam::kTexture:
case NodeValue::kTexture:
return QStringLiteral("sampler2D");
case NodeParam::kColor:
case NodeValue::kColor:
return QStringLiteral("vec4");
case NodeParam::kMatrix:
case NodeValue::kMatrix:
return QStringLiteral("mat4");
default:
return QStringLiteral("float");
}
}
QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeParam::DataType &type, const QString& coord_op)
QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op)
{
if (type == NodeParam::kTexture) {
if (type == NodeValue::kTexture) {
return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op);
}
@@ -138,26 +138,26 @@ QVector4D MathNodeBase::RetrieveVector(const NodeValue &val)
{
// QVariant doesn't know that QVector*D can convert themselves so we do it here
switch (val.type()) {
case NodeParam::kVec2:
case NodeValue::kVec2:
return val.data().value<QVector2D>();
case NodeParam::kVec3:
case NodeValue::kVec3:
return val.data().value<QVector3D>();
case NodeParam::kVec4:
case NodeValue::kVec4:
default:
return val.data().value<QVector4D>();
}
}
void MathNodeBase::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) const
void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type type, const QVector4D &vec) const
{
switch (type) {
case NodeParam::kVec2:
case NodeValue::kVec2:
output->Push(type, QVector2D(vec), this);
break;
case NodeParam::kVec3:
case NodeValue::kVec3:
output->Push(type, QVector3D(vec), this);
break;
case NodeParam::kVec4:
case NodeValue::kVec4:
output->Push(type, vec, this);
break;
default:
@@ -173,13 +173,13 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
case kPairNumberNumber:
{
if (val_a.type() == NodeParam::kRational && val_b.type() == NodeParam::kRational && operation != kOpPower) {
if (val_a.type() == NodeValue::kRational && val_b.type() == NodeValue::kRational && operation != kOpPower) {
// Preserve rationals
output.Push(NodeParam::kRational,
output.Push(NodeValue::kRational,
QVariant::fromValue(PerformAddSubMultDiv<rational, rational>(operation, val_a.data().value<rational>(), val_b.data().value<rational>())),
this);
} else {
output.Push(NodeParam::kFloat,
output.Push(NodeValue::kFloat,
PerformAll<float, float>(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)),
this);
}
@@ -198,8 +198,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
case kPairMatrixVec:
{
QMatrix4x4 matrix = (val_a.type() == NodeParam::kMatrix) ? val_a.data().value<QMatrix4x4>() : val_b.data().value<QMatrix4x4>();
QVector4D vec = (val_a.type() == NodeParam::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a);
QMatrix4x4 matrix = (val_a.type() == NodeValue::kMatrix) ? val_a.data().value<QMatrix4x4>() : val_b.data().value<QMatrix4x4>();
QVector4D vec = (val_a.type() == NodeValue::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a);
// Only valid operation is multiply
PushVector(&output,
@@ -210,8 +210,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
case kPairVecNumber:
{
QVector4D vec = (val_a.type() & NodeParam::kVector) ? RetrieveVector(val_a) : RetrieveVector(val_b);
float number = RetrieveNumber((val_a.type() & NodeParam::kMatrix) ? val_b : val_a);
QVector4D vec = (NodeValue::type_is_vector(val_a.type()) ? RetrieveVector(val_a) : RetrieveVector(val_b));
float number = RetrieveNumber((val_a.type() & NodeValue::kMatrix) ? val_b : val_a);
// Only multiply and divide are valid operations
PushVector(&output, val_a.type(), PerformMultDiv<QVector4D, float>(operation, vec, number));
@@ -222,7 +222,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
{
QMatrix4x4 mat_a = val_a.data().value<QMatrix4x4>();
QMatrix4x4 mat_b = val_b.data().value<QMatrix4x4>();
output.Push(NodeParam::kMatrix, PerformAddSubMult<QMatrix4x4, QMatrix4x4>(operation, mat_a, mat_b), this);
output.Push(NodeValue::kMatrix, PerformAddSubMult<QMatrix4x4, QMatrix4x4>(operation, mat_a, mat_b), this);
break;
}
@@ -232,18 +232,18 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
Color col_b = val_b.data().value<Color>();
// Only add and subtract are valid operations
output.Push(NodeParam::kColor, QVariant::fromValue(PerformAddSub<Color, Color>(operation, col_a, col_b)), this);
output.Push(NodeValue::kColor, QVariant::fromValue(PerformAddSub<Color, Color>(operation, col_a, col_b)), this);
break;
}
case kPairNumberColor:
{
Color col = (val_a.type() == NodeParam::kColor) ? val_a.data().value<Color>() : val_b.data().value<Color>();
float num = (val_a.type() == NodeParam::kColor) ? val_b.data().toFloat() : val_a.data().toFloat();
Color col = (val_a.type() == NodeValue::kColor) ? val_a.data().value<Color>() : val_b.data().value<Color>();
float num = (val_a.type() == NodeValue::kColor) ? val_b.data().toFloat() : val_a.data().toFloat();
// Only multiply and divide are valid operations
output.Push(NodeParam::kColor, QVariant::fromValue(PerformMult<Color, float>(operation, col, num)), this);
output.Push(NodeValue::kColor, QVariant::fromValue(PerformMult<Color, float>(operation, col, num)), this);
break;
}
@@ -277,7 +277,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
}
}
output.Push(NodeParam::kSamples, QVariant::fromValue(mixed_samples), this);
output.Push(NodeValue::kSamples, QVariant::fromValue(mixed_samples), this);
break;
}
@@ -297,8 +297,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
bool operation_is_noop = false;
const NodeValue& number_val = val_a.type() == NodeParam::kTexture ? val_b : val_a;
const NodeValue& texture_val = val_a.type() == NodeParam::kTexture ? val_a : val_b;
const NodeValue& number_val = val_a.type() == NodeValue::kTexture ? val_b : val_a;
const NodeValue& texture_val = val_a.type() == NodeValue::kTexture ? val_a : val_b;
TexturePtr texture = texture_val.data().value<TexturePtr>();
if (!texture) {
@@ -309,7 +309,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
}
} else if (pairing == kPairTextureMatrix) {
// Only allow matrix multiplication
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value<QVector2D>();
QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height());
QMatrix4x4 adjusted_matrix = TransformDistortNode::AdjustMatrixByResolutions(number_val.data().value<QMatrix4x4>(),
@@ -320,8 +320,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
operation_is_noop = true;
} else {
// Replace with adjusted matrix
job.InsertValue(val_a.type() == NodeParam::kTexture ? param_b_in : param_a_in,
ShaderValue(adjusted_matrix, NodeParam::kMatrix));
job.InsertValue(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in,
NodeValue(NodeValue::kMatrix, adjusted_matrix, this));
// It's likely an alpha channel will result from this operation
job.SetAlphaChannelRequired(true);
@@ -333,7 +333,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
output.Push(texture_val);
} else {
// Push shader job
output.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
output.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
}
break;
}
@@ -341,16 +341,16 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
case kPairSampleNumber:
{
// Queue a sample job
const NodeValue& number_val = val_a.type() == NodeParam::kSamples ? val_b : val_a;
NodeInput* number_param = val_a.type() == NodeParam::kSamples ? param_b_in : param_a_in;
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;
float number = RetrieveNumber(number_val);
SampleJob job(val_a.type() == NodeParam::kSamples ? val_a : val_b);
job.InsertValue(number_param, ShaderValue(number, NodeParam::kFloat));
SampleJob job(val_a.type() == NodeValue::kSamples ? val_a : val_b);
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
if (job.HasSamples()) {
if (number_param->is_static()) {
if (number_param->IsStatic()) {
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++) {
@@ -359,9 +359,9 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
}
}
output.Push(NodeParam::kSamples, QVariant::fromValue(job.samples()), this);
output.Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this);
} else {
output.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this);
output.Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
}
}
break;
@@ -378,12 +378,12 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, NodeInput *param_a_in, NodeInput *param_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(NodeParam::kNumber);
NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber);
if (number_val.type() == NodeParam::kNone) {
number_val = values[param_b_in].GetWithMeta(NodeParam::kNumber);
if (number_val.type() == NodeValue::kNone) {
number_val = values[param_b_in].GetWithMeta(NodeValue::kNumber);
if (number_val.type() == NodeParam::kNone) {
if (number_val.type() == NodeValue::kNone) {
return;
}
}
@@ -397,7 +397,7 @@ void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBas
float MathNodeBase::RetrieveNumber(const NodeValue &val)
{
if (val.type() == NodeParam::kRational) {
if (val.type() == NodeValue::kRational) {
return val.data().value<rational>().toDouble();
} else {
return val.data().toFloat();
@@ -467,32 +467,32 @@ QVector<int> MathNodeBase::PairingCalculator::GetPairLikelihood(const NodeValueT
QVector<int> likelihood(kPairCount, -1);
for (int i=0;i<table.Count();i++) {
NodeParam::DataType type = table.at(i).type();
NodeValue::Type type = table.at(i).type();
int weight = i;
if (type & NodeParam::kVector) {
if (NodeValue::type_is_vector(type)) {
likelihood.replace(kPairVecVec, weight);
likelihood.replace(kPairVecNumber, weight);
likelihood.replace(kPairMatrixVec, weight);
} else if (type & NodeParam::kMatrix) {
} else if (type == NodeValue::kMatrix) {
likelihood.replace(kPairMatrixMatrix, weight);
likelihood.replace(kPairMatrixVec, weight);
likelihood.replace(kPairTextureMatrix, weight);
} else if (type & NodeParam::kColor) {
} else if (type == NodeValue::kColor) {
likelihood.replace(kPairColorColor, weight);
likelihood.replace(kPairNumberColor, weight);
likelihood.replace(kPairTextureColor, weight);
} else if (type & NodeParam::kNumber) {
} else if (NodeValue::type_is_numeric(type)) {
likelihood.replace(kPairNumberNumber, weight);
likelihood.replace(kPairVecNumber, weight);
likelihood.replace(kPairNumberColor, weight);
likelihood.replace(kPairTextureNumber, weight);
likelihood.replace(kPairSampleNumber, weight);
} else if (type & NodeParam::kSamples) {
} else if (type == NodeValue::kSamples) {
likelihood.replace(kPairSampleSample, weight);
likelihood.replace(kPairSampleNumber, weight);
} else if (type & NodeParam::kTexture) {
} else if (type == NodeValue::kTexture) {
likelihood.replace(kPairTextureTexture, weight);
likelihood.replace(kPairTextureNumber, weight);
likelihood.replace(kPairTextureColor, weight);
+3 -3
View File
@@ -99,9 +99,9 @@ protected:
template<typename T, typename U>
static T PerformAddSubMultDiv(Operation operation, T a, U b);
static QString GetShaderUniformType(const NodeParam::DataType& type);
static QString GetShaderUniformType(const NodeValue::Type& type);
static QString GetShaderVariableCall(const QString& input_id, const NodeParam::DataType& type, const QString &coord_op = QString());
static QString GetShaderVariableCall(const QString& input_id, const NodeValue::Type& type, const QString &coord_op = QString());
static QVector4D RetrieveVector(const NodeValue& val);
@@ -111,7 +111,7 @@ protected:
ShaderCode GetShaderCodeInternal(const QString &shader_id, NodeInput* param_a_in, NodeInput* param_b_in) const;
void PushVector(NodeValueTable* output, NodeParam::DataType type, const QVector4D& vec) const;
void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const;
NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput* param_a_in, const NodeValue &val_a, NodeInput* param_b_in, const NodeValue& val_b) const;
+18 -13
View File
@@ -24,11 +24,9 @@ namespace olive {
MergeNode::MergeNode()
{
base_in_ = new NodeInput("base_in", NodeParam::kTexture);
AddInput(base_in_);
base_in_ = new NodeInput(this, QStringLiteral("base_in"), NodeValue::kTexture);
blend_in_ = new NodeInput("blend_in", NodeParam::kTexture);
AddInput(blend_in_);
blend_in_ = new NodeInput(this, QStringLiteral("blend_in"), NodeValue::kTexture);
}
Node *MergeNode::copy() const
@@ -77,19 +75,19 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
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(base_in_).data().value<TexturePtr>();
TexturePtr blend_tex = job.GetValue(blend_in_).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_), this);
table.Push(job.GetValue(blend_in_));
} else if (!blend_tex) {
// We only have a base texture, no need to alpha over
table.Push(job.GetValue(base_in_), this);
table.Push(job.GetValue(base_in_));
} else {
// We have both textures, push the job
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
}
}
@@ -108,12 +106,19 @@ NodeInput *MergeNode::blend_in() const
void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
{
if (base_in_->is_connected()) {
base_in_->get_connected_node()->Hash(hash, time);
// If only one of these is connected, the merge is a no-op, so we only leave a fingerprint if
// both are connected
if (base_in_->IsConnected() && blend_in_->IsConnected()) {
// Leave fingerprint of merge node
hash.addData(id().toUtf8());
}
if (blend_in_->is_connected()) {
blend_in_->get_connected_node()->Hash(hash, time);
if (base_in_->IsConnected()) {
base_in_->GetConnectedNode()->Hash(hash, time);
}
if (blend_in_->IsConnected()) {
blend_in_->GetConnectedNode()->Hash(hash, time);
}
}
+7 -9
View File
@@ -24,13 +24,11 @@ namespace olive {
TrigonometryNode::TrigonometryNode()
{
method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kCombo);
method_in_->set_connectable(false);
method_in_->set_is_keyframable(false);
AddInput(method_in_);
method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo);
method_in_->SetConnectable(false);
method_in_->SetKeyframable(false);
x_in_ = new NodeInput(QStringLiteral("x_in"), NodeParam::kFloat, 0.0);
AddInput(x_in_);
x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0);
}
olive::Node *olive::TrigonometryNode::copy() const
@@ -79,11 +77,11 @@ void TrigonometryNode::Retranslate()
NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const
{
float x = value[x_in_].Take(NodeParam::kFloat).toFloat();
float x = value[x_in_].Take(NodeValue::kFloat).toFloat();
NodeValueTable table = value.Merge();
switch (static_cast<Operation>(method_in_->get_standard_value().toInt())) {
switch (static_cast<Operation>(method_in_->GetStandardValue().toInt())) {
case kOpSine:
x = qSin(x);
break;
@@ -113,7 +111,7 @@ NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const
break;
}
table.Push(NodeParam::kFloat, x, this);
table.Push(NodeValue::kFloat, x, this);
return table;
}
+187 -369
View File
@@ -37,27 +37,16 @@ namespace olive {
Node::Node() :
can_be_deleted_(true)
{
output_ = new NodeOutput("node_out");
AddParameter(output_);
}
Node::~Node()
{
DisconnectAll();
}
// We delete in the Node destructor rather than relying on the QObject system because the parameter may need to
// perform actions on this Node object and we want them to be done before the Node object is fully destroyed
foreach (NodeParam* param, params_) {
// We disconnect input signals because these will try to send invalidate cache signals that may involve the derived
// class (which is now destroyed). Any node that this is connected to will handle cache invalidation so it's a waste
// of time anyway.
if (param->type() == NodeParam::kInput) {
DisconnectInput(static_cast<NodeInput*>(param));
}
delete param;
}
NodeGraph *Node::parent() const
{
return static_cast<NodeGraph*>(QObject::parent());
}
void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled)
@@ -67,7 +56,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto
return;
}
if (reader->name() == QStringLiteral("input") || reader->name() == QStringLiteral("output")) {
if (reader->name() == QStringLiteral("input")) {
QString param_id;
XMLAttributeLoop(reader, attr) {
@@ -83,13 +72,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto
continue;
}
NodeParam* param;
if (reader->name() == QStringLiteral("input")) {
param = GetInputWithID(param_id);
} else {
param = GetOutputWithID(param_id);
}
NodeInput* param = GetInputWithID(param_id);
if (!param) {
qDebug() << "No parameter in" << id() << "with parameter" << param_id;
@@ -135,19 +118,12 @@ void Node::Save(QXmlStreamWriter *writer) const
writer->writeTextElement(QStringLiteral("label"), GetLabel());
foreach (NodeParam* param, parameters()) {
switch (param->type()) {
case NodeParam::kInput:
foreach (NodeInput* input, inputs_) {
writer->writeStartElement(QStringLiteral("input"));
break;
case NodeParam::kOutput:
writer->writeStartElement(QStringLiteral("output"));
break;
}
param->Save(writer);
input->Save(writer);
writer->writeEndElement(); // input/output
writer->writeEndElement(); // input
}
writer->writeStartElement(QStringLiteral("custom"));
@@ -170,30 +146,33 @@ void Node::Retranslate()
{
}
void Node::AddParameter(NodeParam *param)
void Node::RemoveNodesAndExclusiveDependencies(Node *node, QUndoCommand *command)
{
// Ensure no other param with this ID has been added to this Node (since that defeats the purpose)
Q_ASSERT(!HasParamWithID(param->id()));
// Remove main node
RemoveNodeAndDisconnect(node, command);
if (params_.contains(param)) {
return;
// Remove exclusive dependencies
QVector<Node*> deps = node->GetExclusiveDependencies();
foreach (Node* d, deps) {
RemoveNodeAndDisconnect(d, command);
}
}
void Node::RemoveNodeAndDisconnect(Node *node, QUndoCommand *command)
{
// Disconnect everything
foreach (const InputConnection& conn, node->output_connections()) {
new NodeEdgeRemoveCommand(node, conn.input, conn.element, command);
}
param->setParent(this);
// Keep main output as the last parameter, assume if there are no parameters that this is the output parameter
if (params_.isEmpty()) {
params_.append(param);
} else {
params_.insert(params_.size()-1, param);
foreach (NodeInput* input, node->inputs_) {
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
new NodeEdgeRemoveCommand(it.value(), input, it.key(), command);
}
}
connect(param, &NodeParam::EdgeAdded, this, &Node::EdgeAdded);
connect(param, &NodeParam::EdgeRemoved, this, &Node::EdgeRemoved);
if (param->type() == NodeParam::kInput) {
ConnectInput(static_cast<NodeInput*>(param));
}
// Remove node
new NodeRemoveCommand(node, command);
}
NodeValueTable Node::Value(NodeValueDatabase &value) const
@@ -201,32 +180,26 @@ NodeValueTable Node::Value(NodeValueDatabase &value) const
return value.Merge();
}
void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
void Node::InvalidateCache(const TimeRange &range, const InputConnection &from)
{
Q_UNUSED(from)
SendInvalidateCache(range, source);
SendInvalidateCache(range);
}
void Node::BeginOperation()
{
foreach (NodeParam* param, params_) {
if (param->type() == NodeParam::kOutput) {
foreach (NodeEdgePtr edge, param->edges()) {
edge->input()->parentNode()->BeginOperation();
}
}
// Ripple through graph
foreach (const InputConnection& conn, output_connections()) {
conn.input->parent()->BeginOperation();
}
}
void Node::EndOperation()
{
foreach (NodeParam* param, params_) {
if (param->type() == NodeParam::kOutput) {
foreach (NodeEdgePtr edge, param->edges()) {
edge->input()->parentNode()->EndOperation();
}
}
// Ripple through graph
foreach (const InputConnection& conn, output_connections()) {
conn.input->parent()->EndOperation();
}
}
@@ -260,7 +233,7 @@ QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCom
if (command) {
new NodeAddCommand(graph, c, command);
} else {
graph->AddNode(c);
c->setParent(graph);
}
// Store in array at the same index as source
@@ -274,27 +247,20 @@ QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCom
void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *> &dst, QUndoCommand *command)
{
int nb_nodes = src.size();
for (int i=0; i<src.size(); i++) {
foreach (NodeInput* input, src.at(i)->inputs()) {
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
int connection_index = src.indexOf(it.value());
for (int i=0; i<nb_nodes; i++) {
// Find any interconnections
QVector<NodeInput*> inputs = src.at(i)->GetInputsIncludingArrays();
for (int j=0; j<nb_nodes; j++) {
if (i == j) {
continue;
}
foreach (NodeInput* input, inputs) {
if (input->get_connected_node() == src.at(j)) {
if (connection_index > -1) {
// Found a connection
NodeOutput* copy_output = dst.at(j)->GetOutputWithID(input->get_connected_output()->id());
NodeInput* copy_input = dst.at(i)->GetInputWithID(input->id());
Node* dst_output = dst.at(connection_index);
NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id());
if (command) {
new NodeEdgeAddCommand(copy_output, copy_input, command);
new NodeEdgeAddCommand(dst_output, dst_input, it.key(), command);
} else {
NodeParam::ConnectEdge(copy_output, copy_input);
ConnectEdge(dst_output, dst_input, it.key());
}
}
}
@@ -302,23 +268,19 @@ void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *>
}
}
void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source)
void Node::SendInvalidateCache(const TimeRange &range)
{
// Loop through all parameters (there should be no children that are not NodeParams)
foreach (NodeParam* param, params_) {
// If the Node is an output, relay the signal to any Nodes that are connected to it
if (param->type() == NodeParam::kOutput) {
foreach (NodeEdgePtr edge, param->edges()) {
NodeInput* connected_input = edge->input();
Node* connected_node = connected_input->parentNode();
foreach (const InputConnection& conn, output_connections()) {
// Send clear cache signal to the Node
connected_node->InvalidateCache(range, connected_input, source);
}
}
conn.input->parent()->InvalidateCache(range, conn);
}
}
void Node::IgnoreConnectionSignalsFrom(NodeInput *input)
{
ignore_connections_.append(input);
}
void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &)
{
reader->skipCurrentElement();
@@ -330,43 +292,7 @@ void Node::SaveInternal(QXmlStreamWriter *) const
QVector<NodeInput *> Node::GetInputsToHash() const
{
return GetInputsIncludingArrays();
}
void GetInputsIncludingArraysInternal(NodeInputArray* array, QVector<NodeInput *>& list)
{
foreach (NodeInput* input, array->sub_params()) {
list.append(input);
if (input->IsArray()) {
GetInputsIncludingArraysInternal(static_cast<NodeInputArray*>(input), list);
}
}
}
QVector<NodeInput *> Node::GetInputsIncludingArrays() const
{
QVector<NodeInput *> inputs;
foreach (NodeParam* param, params_) {
if (param->type() == NodeParam::kInput) {
NodeInput* input = static_cast<NodeInput*>(param);
inputs.append(input);
if (input->IsArray()) {
GetInputsIncludingArraysInternal(static_cast<NodeInputArray*>(input), inputs);
}
}
}
return inputs;
}
QVector<NodeOutput *> Node::GetOutputs() const
{
// The current design only uses one output per node. This function returns a list just in case that changes.
return {output_};
return inputs_;
}
bool Node::HasGizmos() const
@@ -414,23 +340,78 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const
foreach (NodeInput* input, inputs) {
// For each input, try to hash its value
HashInputElement(hash, input, -1, time);
for (int i=0; i<input->ArraySize(); i++) {
HashInputElement(hash, input, i, time);
}
}
}
void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
{
Q_ASSERT(source->id() == destination->id());
const QVector<NodeInput*>& src_param = source->inputs_;
const QVector<NodeInput*>& dst_param = destination->inputs_;
for (int i=0;i<src_param.size();i++) {
NodeInput* src = static_cast<NodeInput*>(src_param.at(i));
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
NodeInput::CopyValues(src, dst, include_connections);
}
destination->SetPosition(source->GetPosition());
destination->SetLabel(source->GetLabel());
}
bool Node::CanBeDeleted() const
{
return can_be_deleted_;
}
void Node::SetCanBeDeleted(bool s)
{
can_be_deleted_ = s;
}
/**
* @brief Recursively collects dependencies of Node `n` and appends them to QList `list`
*
* @param traverse
*
* TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate
* dependencies.
*/
QVector<Node *> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const
{
QVector<Node*> list;
foreach (NodeInput* i, inputs_) {
i->GetDependencies(list, traverse, exclusive_only);
}
return list;
}
void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int element, const rational &time) const
{
// Get time adjustment
// For a single frame, we only care about one of the times
rational input_time = InputTimeAdjustment(input, TimeRange(time, time)).in();
if (input->is_connected()) {
if (input->IsConnected(element)) {
// Traverse down this edge
input->get_connected_node()->Hash(hash, input_time);
input->GetConnectedNode(element)->Hash(hash, input_time);
} else {
// Grab the value at this time
QVariant value = input->get_value_at_time(input_time);
hash.addData(NodeParam::ValueToBytes(input->data_type(), value));
QVariant value = input->GetValueAtTime(input_time, element);
hash.addData(NodeValue::ValueToBytes(input->GetDataType(), value));
}
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
if (input->data_type() == NodeParam::kFootage) {
Stream* stream = Node::ValueToPtr<Stream>(input->get_standard_value());
if (input->GetDataType() == NodeValue::kFootage) {
Stream* stream = Node::ValueToPtr<Stream>(input->GetStandardValue(element));
if (stream) {
// Add footage details to hash
@@ -472,84 +453,19 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const
}
}
}
}
}
void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
void Node::InputConnectionChanged(Node *source, int element)
{
Q_ASSERT(source->id() == destination->id());
Q_UNUSED(source)
const QVector<NodeParam*>& src_param = source->params_;
const QVector<NodeParam*>& dst_param = destination->params_;
NodeInput* input = static_cast<NodeInput*>(sender());
for (int i=0;i<src_param.size();i++) {
NodeParam* p = src_param.at(i);
if (p->type() == NodeParam::kInput) {
NodeInput* src = static_cast<NodeInput*>(p);
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
NodeInput::CopyValues(src, dst, include_connections);
}
if (ignore_connections_.contains(input)) {
return;
}
destination->SetPosition(source->GetPosition());
destination->SetLabel(source->GetLabel());
}
bool Node::CanBeDeleted() const
{
return can_be_deleted_;
}
void Node::SetCanBeDeleted(bool s)
{
can_be_deleted_ = s;
}
bool Node::IsBlock() const
{
return false;
}
bool Node::IsTrack() const
{
return false;
}
bool Node::IsMedia() const
{
return false;
}
const QVector<NodeParam *>& Node::parameters() const
{
return params_;
}
int Node::IndexOfParameter(NodeParam *param) const
{
return params_.indexOf(param);
}
/**
* @brief Recursively collects dependencies of Node `n` and appends them to QList `list`
*
* @param traverse
*
* TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate
* dependencies.
*/
QVector<Node *> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const {
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
QVector<Node*> list;
foreach (NodeInput* i, inputs) {
i->GetDependencies(list, traverse, exclusive_only);
}
return list;
InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), {input, element});
}
QVector<Node *> Node::GetDependencies() const
@@ -586,9 +502,7 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const
NodeInput *Node::GetInputWithID(const QString &id) const
{
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
foreach (NodeInput* i, inputs_) {
if (i->id() == id) {
return i;
}
@@ -597,25 +511,10 @@ NodeInput *Node::GetInputWithID(const QString &id) const
return nullptr;
}
NodeOutput *Node::GetOutputWithID(const QString &id) const
{
foreach (NodeParam* p, params_) {
if (p->type() == NodeParam::kOutput
&& p->id() == id) {
return static_cast<NodeOutput*>(p);
}
}
return nullptr;
}
bool Node::OutputsTo(Node *n, bool recursively) const
{
QVector<NodeOutput*> outputs = GetOutputs();
foreach (NodeOutput* output, outputs) {
foreach (NodeEdgePtr edge, output->edges()) {
Node* connected = edge->input()->parentNode();
foreach (const InputConnection& conn, output_connections()) {
Node* connected = conn.input->parent();
if (connected == n) {
return true;
@@ -623,18 +522,14 @@ bool Node::OutputsTo(Node *n, bool recursively) const
return true;
}
}
}
return false;
}
bool Node::OutputsTo(const QString &id, bool recursively) const
{
QVector<NodeOutput*> outputs = GetOutputs();
foreach (NodeOutput* output, outputs) {
foreach (NodeEdgePtr edge, output->edges()) {
Node* connected = edge->input()->parentNode();
foreach (const InputConnection& conn, output_connections()) {
Node* connected = conn.input->parent();
if (connected->id() == id) {
return true;
@@ -642,27 +537,19 @@ bool Node::OutputsTo(const QString &id, bool recursively) const
return true;
}
}
}
return false;
}
bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) const
bool Node::OutputsTo(NodeInput *input, bool recursively) const
{
QVector<NodeOutput*> outputs = GetOutputs();
foreach (NodeOutput* output, outputs) {
foreach (NodeEdgePtr edge, output->edges()) {
NodeInput* connected = edge->input();
foreach (const InputConnection& conn, output_connections()) {
NodeInput* connected = conn.input;
if (connected == input) {
return true;
} else if (include_arrays && input->IsArray()
&& static_cast<NodeInputArray*>(input)->sub_params().contains(connected)) {
} else if (recursively && connected->parent()->OutputsTo(input, recursively)) {
return true;
} else if (recursively && connected->parentNode()->OutputsTo(input, recursively, include_arrays)) {
return true;
}
}
}
@@ -671,11 +558,9 @@ bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) co
bool Node::InputsFrom(Node *n, bool recursively) const
{
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
foreach (NodeEdgePtr edge, input->edges()) {
Node* connected = edge->output()->parentNode();
foreach (NodeInput* input, inputs_) {
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
Node* connected = it.value();
if (connected == n) {
return true;
@@ -690,11 +575,9 @@ bool Node::InputsFrom(Node *n, bool recursively) const
bool Node::InputsFrom(const QString &id, bool recursively) const
{
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
foreach (NodeEdgePtr edge, input->edges()) {
Node* connected = edge->output()->parentNode();
foreach (NodeInput* input, inputs_) {
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
Node* connected = it.value();
if (connected->id() == id) {
return true;
@@ -712,11 +595,8 @@ int Node::GetRoutesTo(Node *n) const
bool outputs_directly = false;
int routes = 0;
QVector<NodeOutput*> outputs = GetOutputs();
foreach (NodeOutput* o, outputs) {
foreach (NodeEdgePtr edge, o->edges()) {
Node* connected_node = edge->input()->parentNode();
foreach (const InputConnection& conn, edges()) {
Node* connected_node = conn.input->parent();
if (connected_node == n) {
outputs_directly = true;
@@ -724,7 +604,6 @@ int Node::GetRoutesTo(Node *n) const
routes += connected_node->GetRoutesTo(n);
}
}
}
if (outputs_directly) {
routes++;
@@ -733,30 +612,11 @@ int Node::GetRoutesTo(Node *n) const
return routes;
}
bool Node::HasInputs() const
{
return HasParamOfType(NodeParam::kInput, false);
}
bool Node::HasOutputs() const
{
return HasParamOfType(NodeParam::kOutput, false);
}
bool Node::HasConnectedInputs() const
{
return HasParamOfType(NodeParam::kInput, true);
}
bool Node::HasConnectedOutputs() const
{
return HasParamOfType(NodeParam::kOutput, true);
}
void Node::DisconnectAll()
{
foreach (NodeParam* param, params_) {
param->DisconnectAll();
// Disconnect outputs (inputs will be disconnected in their respective destructors)
while (!edges().isEmpty()) {
DisconnectEdge(this, edges().first().input, edges().first().element);
}
}
@@ -793,19 +653,16 @@ QString Node::GetCategoryName(const CategoryID &c)
return tr("Uncategorized");
}
QVector<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, NodeParam::Type direction)
QVector<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, bool input_dir)
{
QVector<TimeRange> paths_found;
if (direction == NodeParam::kInput) {
// Get list of all inputs
QVector<NodeInput *> inputs = GetInputsIncludingArrays();
if (input_dir) {
// If this input is connected, traverse it to see if we stumble across the specified `node`
foreach (NodeInput* input, inputs) {
if (input->is_connected()) {
foreach (NodeInput* input, inputs_) {
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
TimeRange input_adjustment = InputTimeAdjustment(input, time);
Node* connected = input->get_connected_node();
Node* connected = input->GetConnectedNode(it.key());
if (connected == target) {
// We found the target, no need to keep traversing
@@ -814,28 +671,21 @@ QVector<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, No
}
} else {
// We did NOT find the target, traverse this
paths_found.append(connected->TransformTimeTo(input_adjustment, target, direction));
paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir));
}
}
}
} else {
// Get list of all outputs
QVector<NodeOutput*> outputs = GetOutputs();
// If this input is connected, traverse it to see if we stumble across the specified `node`
foreach (NodeOutput* output, outputs) {
if (output->is_connected()) {
foreach (NodeEdgePtr edge, output->edges()) {
Node* input_node = edge->input()->parentNode();
foreach (const InputConnection& conn, edges()) {
Node* input_node = conn.input->parent();
TimeRange output_adjustment = input_node->OutputTimeAdjustment(edge->input(), time);
TimeRange output_adjustment = input_node->OutputTimeAdjustment(conn.input, time);
if (input_node == target) {
paths_found.append(output_adjustment);
} else {
paths_found.append(input_node->TransformTimeTo(output_adjustment, target, direction));
}
}
paths_found.append(input_node->TransformTimeTo(output_adjustment, target, input_dir));
}
}
}
@@ -850,10 +700,8 @@ QVariant Node::PtrToValue(void *ptr)
bool Node::HasParamWithID(const QString &id) const
{
foreach (NodeParam* p, params_)
{
if (p->id() == id)
{
foreach (NodeInput* i, inputs_) {
if (i->id() == id) {
return true;
}
}
@@ -861,11 +709,6 @@ bool Node::HasParamWithID(const QString &id) const
return false;
}
NodeOutput *Node::output() const
{
return output_;
}
const QPointF &Node::GetPosition() const
{
return position_;
@@ -878,63 +721,9 @@ void Node::SetPosition(const QPointF &pos)
emit PositionChanged(position_);
}
void Node::AddInput(NodeInput *input)
void Node::InputChanged(const TimeRange& range, int element)
{
AddParameter(input);
}
bool Node::HasParamOfType(NodeParam::Type type, bool must_be_connected) const
{
foreach (NodeParam* p, params_) {
if (p->type() == type
&& (p->is_connected() || !must_be_connected)) {
return true;
}
}
return false;
}
void Node::ConnectInput(NodeInput *input)
{
connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
connect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged);
connect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged);
if (input->IsArray()) {
NodeInputArray* array = static_cast<NodeInputArray*>(input);
connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged);
connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged);
connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded);
connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved);
}
}
void Node::DisconnectInput(NodeInput *input)
{
if (input->IsArray()) {
NodeInputArray* array = static_cast<NodeInputArray*>(input);
disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged);
disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged);
disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded);
disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved);
}
disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
disconnect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged);
disconnect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged);
}
void Node::InputChanged(const TimeRange& range)
{
InvalidateCache(range, static_cast<NodeInput*>(sender()), static_cast<NodeInput*>(sender()));
}
void Node::InputConnectionChanged(NodeEdgePtr edge)
{
InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input());
InvalidateCache(range, InputConnection(static_cast<NodeInput*>(sender()), element));
}
QRectF Node::CreateGizmoHandleRect(const QPointF &pt, int radius)
@@ -967,4 +756,33 @@ void Node::DrawAndExpandGizmoHandles(QPainter *p, int handle_radius, QRectF *rec
}
}
void Node::childEvent(QChildEvent *event)
{
NodeInput* input = dynamic_cast<NodeInput*>(event->child());
if (input) {
if (event->type() == QEvent::ChildAdded) {
// Ensure no other param with this ID has been added to this Node (since that defeats the purpose)
Q_ASSERT(!HasParamWithID(input->id()));
// Keep main output as the last parameter, assume if there are no parameters that this is the output parameter
inputs_.append(input);
connect(input, &NodeInput::InputConnected, this, &Node::InputConnected);
connect(input, &NodeInput::InputDisconnected, this, &Node::InputDisconnected);
connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
connect(input, &NodeInput::InputConnected, this, &Node::InputConnectionChanged);
connect(input, &NodeInput::InputDisconnected, this, &Node::InputConnectionChanged);
} else if (event->type() == QEvent::ChildRemoved) {
disconnect(input, &NodeInput::InputConnected, this, &Node::InputConnected);
disconnect(input, &NodeInput::InputDisconnected, this, &Node::InputDisconnected);
disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
disconnect(input, &NodeInput::InputConnected, this, &Node::InputConnectionChanged);
disconnect(input, &NodeInput::InputDisconnected, this, &Node::InputConnectionChanged);
inputs_.removeOne(input);
}
}
}
}
+64 -121
View File
@@ -31,9 +31,8 @@
#include "codec/samplebuffer.h"
#include "common/rational.h"
#include "common/xmlutils.h"
#include "node/connectable.h"
#include "node/input.h"
#include "node/inputarray.h"
#include "node/output.h"
#include "node/value.h"
#include "render/audioparams.h"
#include "render/job/generatejob.h"
@@ -43,6 +42,8 @@
namespace olive {
class NodeGraph;
/**
* @brief A single processing unit that can be connected with others to create intricate processing systems
*
@@ -57,7 +58,7 @@ namespace olive {
* 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 QObject
class Node : public NodeConnectable
{
Q_OBJECT
public:
@@ -91,6 +92,11 @@ public:
*/
virtual Node* copy() const = 0;
/**
* @brief Convenience function - assumes parent is a NodeGraph
*/
NodeGraph* parent() const;
/**
* @brief Clear current node variables and replace them with
*/
@@ -151,13 +157,28 @@ public:
/**
* @brief Return a list of NodeParams
*/
const QVector<NodeParam*>& parameters() const;
const QVector<NodeInput*>& parameters() const
{
return inputs_;
}
const QVector<NodeInput*>& inputs() const
{
return inputs_;
}
static void RemoveNodesAndExclusiveDependencies(Node* node, QUndoCommand* command);
static void RemoveNodeAndDisconnect(Node* node, QUndoCommand* command);
/**
* @brief Return the index of a parameter
* @return Parameter index or -1 if this parameter is not part of this Node
*/
int IndexOfParameter(NodeParam* param) const;
int IndexOfParameter(NodeInput* param) const
{
return inputs_.indexOf(param);
}
/**
* @brief Return a list of all Nodes that this Node's inputs are connected to (does not include this Node)
@@ -201,11 +222,6 @@ public:
*/
NodeInput* GetInputWithID(const QString& id) const;
/**
* @brief Returns the output with the specified ID (or nullptr if it doesn't exist)
*/
NodeOutput* GetOutputWithID(const QString& id) const;
/**
* @brief Returns whether this Node outputs to `n`
*
@@ -228,7 +244,7 @@ public:
/**
* @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node.
*/
bool OutputsTo(NodeInput* input, bool recursively, bool include_arrays) const;
bool OutputsTo(NodeInput* input, bool recursively) const;
/**
* @brief Returns whether this node ever receives an input from a particular node instance
@@ -245,26 +261,6 @@ public:
*/
int GetRoutesTo(Node* n) const;
/**
* @brief Return whether this Node has input parameters
*/
bool HasInputs() const;
/**
* @brief Return whether this Node has output parameters
*/
bool HasOutputs() const;
/**
* @brief Return whether this Node has input parameters and at least one of them is connected
*/
bool HasConnectedInputs() const;
/**
* @brief Return whether this Node has output parameters and at least one of them is connected
*/
bool HasConnectedOutputs() const;
/**
* @brief Severs all input and output connections
*/
@@ -278,7 +274,7 @@ public:
/**
* @brief Transforms time from this node through the connections it takes to get to the specified node
*/
QVector<TimeRange> TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction);
QVector<TimeRange> TransformTimeTo(const TimeRange& time, Node* target, bool input_dir);
/**
* @brief Find nodes of a certain type that this Node takes inputs from
@@ -312,7 +308,7 @@ 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, NodeInput* from, NodeInput* source);
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection());
/**
* @brief Limits cache invalidation temporarily
@@ -363,31 +359,6 @@ public:
*/
void SetCanBeDeleted(bool s);
/**
* @brief Returns whether this Node is a "Block" type or not
*
* You shouldn't ever need to override this since all derivatives of Block will automatically have this set to true.
* It's just a more convenient way of checking than dynamic_casting.
*/
virtual bool IsBlock() const;
/**
* @brief Returns whether this Node is a "Track" type or not
*
* You shouldn't ever need to override this since all derivatives of Track will automatically have this set to true.
* It's just a more convenient way of checking than dynamic_casting.
*/
virtual bool IsTrack() const;
/**
* @brief Returns whether this Node is a "Media" type or not
*
* You shouldn't ever need to override this since all derivatives of Media will automatically have this set to true.
* It's just a more convenient way of checking than dynamic_casting.
*/
virtual bool IsMedia() const;
/**
* @brief The main processing function
*
@@ -408,16 +379,10 @@ public:
*/
bool HasParamWithID(const QString& id) const;
NodeOutput* output() const;
const QPointF& GetPosition() const;
void SetPosition(const QPointF& pos);
QVector<NodeInput*> GetInputsIncludingArrays() const;
QVector<NodeOutput*> GetOutputs() const;
virtual bool HasGizmos() const;
virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p);
@@ -431,12 +396,22 @@ public:
virtual void Hash(QCryptographicHash& hash, const rational &time) const;
const QVector<InputConnection>& edges() const
{
return output_connections();
}
protected:
void AddInput(NodeInput* input);
void SendInvalidateCache(const TimeRange &range);
void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range);
void SendInvalidateCache(const TimeRange &range, NodeInput *source);
/**
* @brief Don't send cache invalidation signals if `input` is connected or disconnected
*
* By default, when a node is connected or disconnected from input, the Node assumes that the
* 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 IgnoreConnectionSignalsFrom(NodeInput* input);
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data);
@@ -462,30 +437,14 @@ protected:
static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count);
protected slots:
void InputChanged(const olive::TimeRange &range);
virtual void childEvent(QChildEvent* event) override;
void InputConnectionChanged(NodeEdgePtr edge);
protected slots:
void InputChanged(const olive::TimeRange &range, int element);
void InputConnectionChanged(Node* source, int element);
signals:
/**
* @brief Signal emitted when a node is connected to another node (creating an "edge")
*
* @param edge
*
* The edge that was added
*/
void EdgeAdded(NodeEdgePtr edge);
/**
* @brief Signal emitted when a node is disconnected from another node (removing an "edge")
*
* @param edge
*
* The edge that was removed
*/
void EdgeRemoved(NodeEdgePtr edge);
/**
* @brief Signal emitted whenever the position is set through SetPosition()
*/
@@ -497,41 +456,25 @@ signals:
void LabelChanged(const QString& s);
private:
/**
* @brief Add a parameter to this node
*
* The Node takes ownership of this parameter.
*
* This can be either an output or an input at any time. Parameters will always appear in the order they're added.
*/
void AddParameter(NodeParam* param);
bool HasParamOfType(NodeParam::Type type, bool must_be_connected) const;
void ConnectInput(NodeInput* input);
void DisconnectInput(NodeInput* input);
template<class T>
static void FindInputNodeInternal(const Node* n, QVector<T *>& list);
template<class T>
static void FindOutputNodeInternal(const Node* n, QVector<T *>& list);
QVector<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
QVector<Node*> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
QVector<NodeParam *> params_;
void HashInputElement(QCryptographicHash& hash, NodeInput* input, int element, const rational& time) const;
QVector<NodeInput*> inputs_;
QVector<NodeInput*> ignore_connections_;
/**
* @brief Internal variable for whether this Node can be deleted or not
*/
bool can_be_deleted_;
/**
* @brief Primary node output
*/
NodeOutput* output_;
/**
* @brief UI position for NodeViews
*/
@@ -542,23 +485,22 @@ private:
*/
QString label_;
private slots:
};
template<class T>
void Node::FindInputNodeInternal(const Node* n, QVector<T *> &list)
{
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
if (input->is_connected()) {
Node* connected = input->get_connected_node();
T* cast_test = dynamic_cast<T*>(connected);
foreach (NodeInput* input, n->inputs_) {
foreach (Node* edge, input->edges()) {
T* cast_test = dynamic_cast<T*>(edge);
if (cast_test) {
list.append(cast_test);
}
FindInputNodeInternal<T>(connected, list);
FindInputNodeInternal<T>(edge, list);
}
}
}
@@ -580,9 +522,10 @@ T* Node::ValueToPtr(const QVariant &ptr)
}
template<class T>
void Node::FindOutputNodeInternal(const Node* n, QVector<T *>& list) {
foreach (NodeEdgePtr edge, n->output()->edges()) {
Node* connected = edge->input()->parentNode();
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);
if (cast_test) {
+1 -1
View File
@@ -148,7 +148,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(Sequence *graph, Q
foreach (Item* item, footage) {
foreach (Stream* s, static_cast<Footage*>(item)->streams()) {
if (s == loaded_stream) {
con.input->set_standard_value(Node::PtrToValue(s));
con.input->SetStandardValue(Node::PtrToValue(s), con.element);
found = true;
break;
}
-71
View File
@@ -1,71 +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 "output.h"
#include "common/xmlutils.h"
#include "node/node.h"
namespace olive {
NodeOutput::NodeOutput(const QString &id) :
NodeParam(id)
{
}
NodeParam::Type NodeOutput::type()
{
return kOutput;
}
QString NodeOutput::name()
{
if (name_.isEmpty()) {
return tr("Output");
}
return NodeParam::name();
}
void NodeOutput::Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
{
XMLAttributeLoop(reader, attr) {
if (cancelled && *cancelled) {
return;
}
if (attr.name() == "ptr") {
quintptr saved_ptr = attr.value().toULongLong();
xml_node_data.output_ptrs.insert(saved_ptr, this);
}
}
reader->skipCurrentElement();
}
void NodeOutput::Save(QXmlStreamWriter *writer) const
{
writer->writeAttribute("id", id());
writer->writeAttribute("ptr", QString::number(reinterpret_cast<quintptr>(this)));
}
}
-58
View File
@@ -1,58 +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 NODEOUTPUT_H
#define NODEOUTPUT_H
#include "common/timerange.h"
#include "param.h"
namespace olive {
/**
* @brief A node parameter designed to serve data to the input of another node
*/
class NodeOutput : public NodeParam
{
Q_OBJECT
public:
/**
* @brief NodeOutput Constructor
*/
NodeOutput(const QString& id);
/**
* @brief Returns kOutput
*/
virtual Type type() override;
virtual QString name() override;
virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) override;
virtual void Save(QXmlStreamWriter* writer) const override;
private:
};
}
#endif // NODEOUTPUT_H
+2 -2
View File
@@ -16,9 +16,9 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/output/track/track.h
node/output/track/track.cpp
node/output/track/tracklist.h
node/output/track/track.h
node/output/track/tracklist.cpp
node/output/track/tracklist.h
PARENT_SCOPE
)
+115 -98
View File
@@ -38,18 +38,18 @@ TrackOutput::TrackOutput() :
index_(-1),
locked_(false)
{
block_input_ = new NodeInputArray("block_in", NodeParam::kAny);
block_input_->set_is_keyframable(false);
AddInput(block_input_);
connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected);
connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected);
disconnect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::InputConnectionChanged);
disconnect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::InputConnectionChanged);
block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone);
block_input_->SetKeyframable(false);
connect(block_input_, &NodeInput::InputConnected, this, &TrackOutput::BlockConnected);
connect(block_input_, &NodeInput::InputDisconnected, this, &TrackOutput::BlockDisconnected);
muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean);
muted_input_->set_is_keyframable(false);
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
// on our end
IgnoreConnectionSignalsFrom(block_input_);
muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean);
muted_input_->SetKeyframable(false);
connect(muted_input_, &NodeInput::ValueChanged, this, &TrackOutput::MutedInputValueChanged);
AddInput(muted_input_);
// Set default height
track_height_ = kTrackHeightDefault;
@@ -245,21 +245,16 @@ QList<Block *> TrackOutput::BlocksAtTimeRange(const TimeRange &range) const
return list;
}
const QList<Block *> &TrackOutput::Blocks() const
{
return block_cache_;
}
void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
void TrackOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
{
TimeRange limited;
if (block_input_->sub_params().contains(from)
&& from->get_connected_node()
&& from->get_connected_node()->IsBlock()) {
// Limit the range signal to the corresponding block
Block* b = static_cast<Block*>(from->get_connected_node());
Block* b;
if (from.input == block_input_
&& from.element >= 0
&& (b = dynamic_cast<Block*>(from.input->GetConnectedNode(from.element)))) {
// Limit the range signal to the corresponding block
if (range.out() <= b->in() || range.in() >= b->out()) {
return;
}
@@ -269,7 +264,7 @@ void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeI
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length()));
}
Node::InvalidateCache(limited, from, source);
Node::InvalidateCache(limited, from);
}
void TrackOutput::InsertBlockBefore(Block* block, Block* after)
@@ -294,13 +289,13 @@ void TrackOutput::PrependBlock(Block *block)
{
BeginOperation();
block_input_->Prepend();
NodeParam::ConnectEdge(block->output(), block_input_->First());
block_input_->ArrayPrepend();
Node::ConnectEdge(block, block_input_, 0);
EndOperation();
// Everything has shifted at this point
Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
Node::InvalidateCache(TimeRange(0, track_length()), InputConnection());
}
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
@@ -308,26 +303,25 @@ void TrackOutput::InsertBlockAtIndex(Block *block, int index)
BeginOperation();
int insert_index = GetInputIndexFromCacheIndex(index);
block_input_->InsertAt(insert_index);
NodeParam::ConnectEdge(block->output(),
block_input_->At(insert_index));
block_input_->ArrayInsert(insert_index);
Node::ConnectEdge(block, block_input_, insert_index);
EndOperation();
Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
Node::InvalidateCache(TimeRange(block->in(), track_length()));
}
void TrackOutput::AppendBlock(Block *block)
{
BeginOperation();
block_input_->Append();
NodeParam::ConnectEdge(block->output(), block_input_->Last());
block_input_->ArrayAppend();
Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1);
EndOperation();
// Invalidate area that block was added to
Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
Node::InvalidateCache(TimeRange(block->in(), track_length()));
}
void TrackOutput::RippleRemoveBlock(Block *block)
@@ -337,11 +331,11 @@ void TrackOutput::RippleRemoveBlock(Block *block)
rational remove_in = block->in();
rational remove_out = block->out();
block_input_->RemoveAt(GetInputIndexFromCacheIndex(block));
block_input_->ArrayRemove(GetInputIndexFromCacheIndex(block));
EndOperation();
Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out)), block_input_, block_input_);
Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out)));
}
void TrackOutput::ReplaceBlock(Block *old, Block *replace)
@@ -350,30 +344,26 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace)
int index_of_old_block = GetInputIndexFromCacheIndex(old);
NodeParam::DisconnectEdge(old->output(),
block_input_->At(index_of_old_block));
DisconnectEdge(old, block_input_, index_of_old_block);
NodeParam::ConnectEdge(replace->output(),
block_input_->At(index_of_old_block));
ConnectEdge(replace, block_input_, index_of_old_block);
EndOperation();
if (old->length() == replace->length()) {
Node::InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_);
Node::InvalidateCache(TimeRange(replace->in(), replace->out()));
} else {
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_);
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX));
}
}
TrackOutput *TrackOutput::TrackFromBlock(const Block *block)
{
NodeOutput* output = block->output();
foreach (const InputConnection& conn, block->edges()) {
TrackOutput* track = dynamic_cast<TrackOutput*>(conn.input->parent());
foreach (NodeEdgePtr edge, output->edges()) {
Node* n = edge->input()->parentNode();
if (n->IsTrack()) {
return static_cast<TrackOutput*>(n);
if (track) {
return track;
}
}
@@ -385,11 +375,6 @@ const rational &TrackOutput::track_length() const
return track_length_;
}
bool TrackOutput::IsTrack() const
{
return true;
}
QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index)
{
// Starts tracks at 1 rather than 0
@@ -409,7 +394,7 @@ QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index)
bool TrackOutput::IsMuted() const
{
return muted_input_->get_standard_value().toBool();
return muted_input_->GetStandardValue().toBool();
}
bool TrackOutput::IsLocked() const
@@ -417,7 +402,7 @@ bool TrackOutput::IsLocked() const
return locked_;
}
NodeInputArray *TrackOutput::block_input() const
NodeInput *TrackOutput::block_input() const
{
return block_input_;
}
@@ -434,8 +419,8 @@ void TrackOutput::Hash(QCryptographicHash &hash, const rational &time) const
void TrackOutput::SetMuted(bool e)
{
muted_input_->set_standard_value(e);
Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
muted_input_->SetStandardValue(e);
Node::InvalidateCache(TimeRange(0, track_length()));
}
void TrackOutput::SetLocked(bool e)
@@ -472,8 +457,8 @@ int TrackOutput::GetInputIndexFromCacheIndex(int cache_index)
int TrackOutput::GetInputIndexFromCacheIndex(Block *block)
{
for (int i=0; i<block_input_->GetSize(); i++) {
if (block_input_->At(i)->get_connected_node() == block) {
for (int i=0; i<block_input_->ArraySize(); i++) {
if (block_input_->GetConnectedNode(i) == block) {
return i;
}
}
@@ -490,66 +475,99 @@ void TrackOutput::SetLengthInternal(const rational &r, bool invalidate)
emit TrackLengthChanged();
if (invalidate) {
Node::InvalidateCache(invalidate_range,
block_input_,
block_input_);
Node::InvalidateCache(invalidate_range);
}
}
}
void TrackOutput::BlockConnected(NodeEdgePtr edge)
void TrackOutput::BlockConnected(Node *node, int element)
{
QList<Block*> new_block_list;
foreach (NodeInput* i, block_input_->sub_params()) {
Node* connected = i->get_connected_node();
if (connected
&& connected->IsBlock()
&& !new_block_list.contains(static_cast<Block*>(connected))) {
Block* b = static_cast<Block*>(connected);
if (!new_block_list.isEmpty()) {
new_block_list.last()->set_next(b);
b->set_previous(new_block_list.last());
if (element == -1) {
// User has replaced the entire array, we will invalidate everything
InputConnectionChanged(node, element);
return;
}
new_block_list.append(b);
// Check if a block was connected, if not, ignore
Block* block = dynamic_cast<Block*>(node);
if (!block_cache_.contains(b)) {
// Make connections to this block
connect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
emit BlockAdded(b);
}
}
if (!block) {
return;
}
if (!new_block_list.isEmpty()) {
new_block_list.first()->set_previous(nullptr);
new_block_list.last()->set_next(nullptr);
}
Block *previous = nullptr, *next = nullptr;
int new_index;
for (int i=element-1; i>=0; i--) {
// Find previous block
previous = dynamic_cast<Block*>(block_input_->GetConnectedNode(i));
for (new_index = 0; new_index < block_cache_.size(); new_index++) {
if (block_cache_.at(new_index) != new_block_list.at(new_index)) {
if (previous) {
break;
}
}
block_cache_ = new_block_list;
// Find cache index
int cache_index;
if (previous) {
// Insert block just after the previous block we found
cache_index = block_cache_.indexOf(previous) + 1;
UpdateInOutFrom(new_index);
// Use current previous' next as our next
next = previous->next();
} else {
// Didn't find a previous, so insert block at 0 (prepend it)
cache_index = 0;
InputConnectionChanged(edge);
if (!block_cache_.isEmpty()) {
next = block_cache_.first();
}
}
// Insert at index
block_cache_.insert(cache_index, block);
// Update previous/next
if (previous) {
previous->set_next(block);
block->set_previous(previous);
}
if (next) {
block->set_next(next);
next->set_previous(block);
}
// Update ins/outs
UpdateInOutFrom(cache_index);
// Connect to the block
connect(block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
// Invalidate cache now that block should have an in point
Node::InvalidateCache(TimeRange(block->in(), track_length()));
// Emit block added signal
emit BlockAdded(block);
}
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
void TrackOutput::BlockDisconnected(Node* node, int element)
{
Block* b = static_cast<Block*>(edge->output_node());
if (element == -1) {
// User has replaced the entire array, we will invalidate everything
InputConnectionChanged(node, element);
return;
}
if (block_cache_.contains(b)) {
Block* b = dynamic_cast<Block*>(node);
if (!b) {
return;
}
TimeRange invalidate_range(b->in(), track_length());
// FIXME: What happens if a user connects the same block twice? This must be addressed in the
// upcoming timeline rewrite.
block_cache_.removeOne(b);
Block* previous = b->previous();
@@ -577,9 +595,8 @@ void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
disconnect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
emit BlockRemoved(b);
}
InputConnectionChanged(edge);
Node::InvalidateCache(invalidate_range);
}
void TrackOutput::BlockLengthChanged()
@@ -595,7 +612,7 @@ void TrackOutput::BlockLengthChanged()
TimeRange invalidate_region(qMin(old_out, new_out), track_length());
Node::InvalidateCache(invalidate_region, block_input_, block_input_);
Node::InvalidateCache(invalidate_region);
}
void TrackOutput::MutedInputValueChanged()
+9 -8
View File
@@ -150,9 +150,12 @@ public:
*/
QList<Block*> BlocksAtTimeRange(const TimeRange& range) const;
const QList<Block *> &Blocks() const;
const QList<Block *> &Blocks() const
{
return block_cache_;
}
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override;
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
/**
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
@@ -200,15 +203,13 @@ public:
const rational& track_length() const;
virtual bool IsTrack() const override;
static QString GetDefaultTrackName(Timeline::TrackType type, int index);
bool IsMuted() const;
bool IsLocked() const;
NodeInputArray* block_input() const;
NodeInput* block_input() const;
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
@@ -277,7 +278,7 @@ private:
QList<Block*> block_cache_;
NodeInputArray* block_input_;
NodeInput* block_input_;
NodeInput* muted_input_;
@@ -294,9 +295,9 @@ private:
AudioVisualWaveform waveform_;
private slots:
void BlockConnected(NodeEdgePtr edge);
void BlockConnected(Node* node, int element);
void BlockDisconnected(NodeEdgePtr edge);
void BlockDisconnected(Node* node, int element);
void BlockLengthChanged();
+27 -109
View File
@@ -27,13 +27,13 @@
namespace olive {
TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInputArray *track_input) :
TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInput *track_input) :
QObject(parent),
track_input_(track_input),
type_(type)
{
connect(track_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected);
connect(track_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected);
connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected);
connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected);
}
const Timeline::TrackType &TrackList::type() const
@@ -75,103 +75,26 @@ int TrackList::GetTrackCount() const
return track_cache_.size();
}
TrackOutput* TrackList::AddTrack()
void TrackList::TrackConnected(Node *node, int element)
{
TrackOutput* track = new TrackOutput();
GetParentGraph()->AddNode(track);
track_input_->Append();
// Connect this track directly to this output
NodeParam::ConnectEdge(track->output(),
track_input_->At(track_input_->GetSize() - 1));
// Auto-merge with previous track
if (track_input_->GetSize() > 1) {
TrackOutput* last_track = nullptr;
for (int i=track_cache_.size()-1;i>=0;i--) {
TrackOutput* test_track = track_cache_.at(i);
if (test_track && test_track != track) {
last_track = test_track;
break;
}
}
if (last_track && last_track->output()->is_connected()) {
foreach (NodeEdgePtr edge, last_track->output()->edges()) {
if (!track_input_->ContainsSubParameter(edge->input())) {
switch (type_) {
case Timeline::kTrackTypeVideo:
{
MergeNode* blend = new MergeNode();
GetParentGraph()->AddNode(blend);
NodeParam::ConnectEdge(track->output(), blend->blend_in());
NodeParam::ConnectEdge(last_track->output(), blend->base_in());
NodeParam::ConnectEdge(blend->output(), edge->input());
break;
}
case Timeline::kTrackTypeAudio:
{
MathNode* add = new MathNode();
GetParentGraph()->AddNode(add);
NodeParam::ConnectEdge(track->output(), add->param_a_in());
NodeParam::ConnectEdge(last_track->output(), add->param_b_in());
NodeParam::ConnectEdge(add->output(), edge->input());
break;
}
default:
break;
}
}
}
}
}
return track;
}
void TrackList::RemoveTrack(QObject* new_parent)
{
if (track_cache_.isEmpty()) {
if (element == -1) {
// FIXME: Should probably merge into Viewer so we can actually do this
//static_cast<ViewerOutput*>(parent())->InputConnectionChanged(node, element);
return;
}
TrackOutput* track = track_cache_.last();
TrackOutput* track = dynamic_cast<TrackOutput*>(node);
GetParentGraph()->TakeNode(track);
if (!new_parent) {
delete track;
} else {
track->setParent(new_parent);
if (!track) {
return;
}
track_input_->RemoveLast();
}
void TrackList::TrackConnected(NodeEdgePtr edge)
{
int input_index = track_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(input_index >= 0);
Node* connected_node = edge->output()->parentNode();
if (connected_node->IsTrack()) {
TrackOutput* connected_track = static_cast<TrackOutput*>(connected_node);
{
// Find "real" index
TrackOutput* next = nullptr;
for (int i=input_index+1; i<track_input_->GetSize(); i++) {
Node* that_track = track_input_->At(i)->get_connected_node();
for (int i=element+1; i<track_input_->ArraySize(); i++) {
next = dynamic_cast<TrackOutput*>(track_input_->GetConnectedNode(i));
if (that_track && that_track->IsTrack()) {
next = static_cast<TrackOutput*>(that_track);
if (next) {
break;
}
}
@@ -181,45 +104,41 @@ void TrackList::TrackConnected(NodeEdgePtr edge)
if (next) {
// Insert track before "next"
track_index = track_cache_.indexOf(next);
track_cache_.insert(track_index, connected_track);
track_cache_.insert(track_index, track);
} else {
// No "next", this track must come at the end
track_index = track_cache_.size();
track_cache_.append(connected_track);
track_cache_.append(track);
}
// Update track indexes in the list (including this track)
UpdateTrackIndexesFrom(track_index);
}
connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
connect(connected_track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
connect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
connect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
connect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
connect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
connected_track->set_track_type(type_);
track->set_track_type(type_);
emit TrackListChanged();
// This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine
// the track's index
emit TrackAdded(connected_track);
emit TrackAdded(track);
UpdateTotalLength();
}
}
void TrackList::TrackDisconnected(NodeEdgePtr edge)
void TrackList::TrackDisconnected(Node *node, int element)
{
int track_index = track_input_->IndexOfSubParameter(edge->input());
Q_UNUSED(element)
Q_ASSERT(track_index >= 0);
TrackOutput* track = dynamic_cast<TrackOutput*>(node);
Node* connected_node = edge->output()->parentNode();
if (connected_node->IsTrack()) {
TrackOutput* track = static_cast<TrackOutput*>(connected_node);
if (!track) {
return;
}
int index_of_track = track_cache_.indexOf(track);
track_cache_.removeAt(index_of_track);
@@ -241,7 +160,6 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge)
emit TrackListChanged();
UpdateTotalLength();
}
}
void TrackList::UpdateTrackIndexesFrom(int index)
+11 -9
View File
@@ -31,10 +31,11 @@ namespace olive {
class ViewerOutput;
class TrackList : public QObject {
class TrackList : public QObject
{
Q_OBJECT
public:
TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInputArray* track_input);
TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInput* track_input);
const Timeline::TrackType& type() const;
@@ -42,16 +43,17 @@ public:
TrackOutput* GetTrackAt(int index) const;
TrackOutput *AddTrack();
void RemoveTrack(QObject *new_parent);
const rational& GetTotalLength() const;
int GetTrackCount() const;
NodeGraph* GetParentGraph() const;
NodeInput* track_input() const
{
return track_input_;
}
signals:
void BlockAdded(Block* block, int index);
@@ -75,7 +77,7 @@ private:
*/
QVector<TrackOutput*> track_cache_;
NodeInputArray* track_input_;
NodeInput* track_input_;
rational total_length_;
@@ -85,12 +87,12 @@ private slots:
/**
* @brief Slot for when the track connection is added
*/
void TrackConnected(NodeEdgePtr edge);
void TrackConnected(Node* node, int element);
/**
* @brief Slot for when the track connection is removed
*/
void TrackDisconnected(NodeEdgePtr edge);
void TrackDisconnected(Node* node, int element);
/**
* @brief Slot for when a connected Track has added a Block so we can update the UI
+32 -62
View File
@@ -29,11 +29,9 @@ ViewerOutput::ViewerOutput() :
audio_playback_cache_(this),
operation_stack_(0)
{
texture_input_ = new NodeInput("tex_in", NodeInput::kTexture);
AddInput(texture_input_);
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
samples_input_ = new NodeInput("samples_in", NodeInput::kSamples);
AddInput(samples_input_);
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
// Create TrackList instances
track_inputs_.resize(Timeline::kTrackTypeCount);
@@ -41,10 +39,8 @@ ViewerOutput::ViewerOutput() :
for (int i=0;i<Timeline::kTrackTypeCount;i++) {
// Create track input
NodeInputArray* track_input = new NodeInputArray(QStringLiteral("track_in_%1").arg(i), NodeParam::kAny);
AddInput(track_input);
disconnect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &ViewerOutput::InputConnectionChanged);
disconnect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &ViewerOutput::InputConnectionChanged);
NodeInput* track_input = new NodeInput(this, QStringLiteral("track_in_%1").arg(i), NodeValue::kNone);
IgnoreConnectionSignalsFrom(track_input);
track_inputs_.replace(i, track_input);
TrackList* list = new TrackList(this, static_cast<Timeline::TrackType>(i), track_input);
@@ -52,7 +48,7 @@ ViewerOutput::ViewerOutput() :
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
connect(list, &TrackList::BlockAdded, this, &ViewerOutput::TrackListAddedBlock);
connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::SignalBlockRemoved);
connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::BlockRemoved);
connect(list, &TrackList::TrackAdded, this, &ViewerOutput::TrackListAddedTrack);
connect(list, &TrackList::TrackRemoved, this, &ViewerOutput::TrackRemoved);
connect(list, &TrackList::TrackHeightChanged, this, &ViewerOutput::TrackHeightChangedSlot);
@@ -112,17 +108,15 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
ShiftAudioCache(from, to);
}
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
{
emit GraphChangedFrom(source);
if (operation_stack_ == 0) {
if (from == texture_input_ || from == samples_input_) {
if (from.input == texture_input_ || from.input == samples_input_) {
TimeRange invalidated_range(qMax(rational(), range.in()),
qMin(GetLength(), range.out()));
if (invalidated_range.in() != invalidated_range.out()) {
if (from == texture_input_) {
if (from.input == texture_input_) {
video_frame_cache_.Invalidate(invalidated_range);
} else {
audio_playback_cache_.Invalidate(invalidated_range);
@@ -133,7 +127,7 @@ void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, Node
VerifyLength();
}
Node::InvalidateCache(range, from, source);
Node::InvalidateCache(range, from);
}
void ViewerOutput::set_video_params(const VideoParams &video)
@@ -215,26 +209,33 @@ void ViewerOutput::VerifyLength()
NodeTraverser traverser;
rational video_length;
rational video_length, audio_length, subtitle_length;
if (texture_input_->is_connected()) {
NodeValueTable t = traverser.GenerateTable(texture_input_->get_connected_node(), 0, 0);
video_length = t.Get(NodeParam::kNumber, "length").value<rational>();
{
video_length = track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength();
if (video_length.isNull() && texture_input_->IsConnected()) {
NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0);
video_length = t.Get(NodeValue::kRational, "length").value<rational>();
}
rational audio_length;
if (samples_input_->is_connected()) {
NodeValueTable t = traverser.GenerateTable(samples_input_->get_connected_node(), 0, 0);
audio_length = t.Get(NodeParam::kNumber, "length").value<rational>();
}
video_length = qMax(video_length, track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength());
audio_length = qMax(audio_length, track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength());
rational subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
video_frame_cache_.SetLength(video_length);
}
{
audio_length = track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength();
if (audio_length.isNull() && samples_input_->IsConnected()) {
NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0);
audio_length = t.Get(NodeValue::kRational, "length").value<rational>();
}
audio_playback_cache_.SetLength(audio_length);
}
{
subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
}
rational real_length = qMax(subtitle_length, qMax(video_length, audio_length));
@@ -283,27 +284,6 @@ void ViewerOutput::set_media_name(const QString &name)
emit MediaNameChanged(media_name_);
}
void ViewerOutput::SignalBlockAdded(Block *block, const TrackReference& track)
{
if (!operation_stack_) {
emit BlockAdded(block, track);
} else {
cached_block_removed_.removeOne(block);
cached_block_added_.insert(block, track);
}
}
void ViewerOutput::SignalBlockRemoved(Block *block)
{
if (!operation_stack_) {
emit BlockRemoved({block});
} else {
// We keep track of all blocks that are removed, even if we don't end up signalling them
cached_block_added_.remove(block);
cached_block_removed_.append(block);
}
}
void ViewerOutput::BeginOperation()
{
operation_stack_++;
@@ -315,23 +295,13 @@ void ViewerOutput::EndOperation()
{
operation_stack_--;
if (!operation_stack_) {
for (auto it=cached_block_added_.cbegin(); it!=cached_block_added_.cend(); it++) {
emit BlockAdded(it.key(), it.value());
}
cached_block_added_.clear();
emit BlockRemoved(cached_block_removed_);
cached_block_removed_.clear();
}
Node::EndOperation();
}
void ViewerOutput::TrackListAddedBlock(Block *block, int index)
{
Timeline::TrackType type = static_cast<TrackList*>(sender())->type();
SignalBlockAdded(block, TrackReference(type, index));
emit BlockAdded(block, TrackReference(type, index));
}
void ViewerOutput::TrackListAddedTrack(TrackOutput *track)
+2 -10
View File
@@ -68,7 +68,7 @@ public:
return samples_input_;
}
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
const VideoParams& video_params() const {
return video_params_;
@@ -127,8 +127,6 @@ public:
signals:
void TimebaseChanged(const rational&);
void GraphChangedFrom(NodeInput* source);
void LengthChanged(const rational& length);
void SizeChanged(int width, int height);
@@ -141,7 +139,7 @@ signals:
void AudioParamsChanged();
void BlockAdded(Block* block, TrackReference track);
void BlockRemoved(const QList<Block*>& blocks);
void BlockRemoved(Block* block);
void TrackAdded(TrackOutput* track, Timeline::TrackType type);
void TrackRemoved(TrackOutput* track);
@@ -151,9 +149,6 @@ signals:
void MediaNameChanged(const QString& name);
private:
QMap<Block*, TrackReference> cached_block_added_;
QList<Block*> cached_block_removed_;
QUuid uuid_;
NodeInput* texture_input_;
@@ -191,9 +186,6 @@ private slots:
void TrackHeightChangedSlot(int index, int height);
void SignalBlockAdded(Block *block, const TrackReference &track);
void SignalBlockRemoved(Block *block);
};
}
-289
View File
@@ -1,289 +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 "param.h"
#include <QDebug>
#include <QMatrix4x4>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include "node/node.h"
#include "node/input.h"
#include "node/output.h"
#include "render/color.h"
namespace olive {
NodeParam::NodeParam(const QString &id) :
id_(id),
connectable_(true)
{
Q_ASSERT(!id_.isEmpty());
}
NodeParam::~NodeParam()
{
// Clear all connected edges
DisconnectAll();
}
const QString NodeParam::id() const
{
return id_;
}
QString NodeParam::name()
{
if (name_.isEmpty()) {
return tr("Value");
}
return name_;
}
void NodeParam::set_name(const QString &name)
{
name_ = name;
}
Node *NodeParam::parentNode() const
{
QObject* p = parent();
while (p) {
Node* cast_test = dynamic_cast<Node*>(p);
if (cast_test) {
return cast_test;
} else {
p = p->parent();
}
}
return nullptr;
}
int NodeParam::index()
{
return parentNode()->IndexOfParameter(this);
}
bool NodeParam::is_connected() const
{
return !edges_.isEmpty();
}
bool NodeParam::is_connectable() const
{
return connectable_;
}
void NodeParam::set_connectable(bool connectable)
{
connectable_ = connectable;
}
const QVector<NodeEdgePtr> &NodeParam::edges()
{
return edges_;
}
void NodeParam::DisconnectAll()
{
while (!edges_.isEmpty()) {
DisconnectEdge(edges_.last());
}
}
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
{
if (!input->is_connectable()) {
return nullptr;
}
// If the input can only accept one input (the default) and has one already, disconnect it
DisconnectForNewOutput(input);
// Make sure it's not a duplicate of an edge that already exists
foreach (NodeEdgePtr existing, input->edges()) {
if (existing->output() == output) {
return nullptr;
}
}
// Ensure both nodes are in the same graph
Q_ASSERT(output->parentNode()->parent() == input->parentNode()->parent());
NodeEdgePtr edge = std::make_shared<NodeEdge>(output, input);
// The nodes should never be the same, and since we lock both nodes here, this can lead to a entire program freeze
// that's difficult to diagnose. This makes that issue very clear.
Q_ASSERT(output->parentNode() != input->parentNode());
output->edges_.append(edge);
input->edges_.append(edge);
// Emit a signal than an edge was added (only one signal needs emitting)
emit input->EdgeAdded(edge);
return edge;
}
void NodeParam::DisconnectEdge(NodeEdgePtr edge)
{
NodeOutput* output = edge->output();
NodeInput* input = edge->input();
output->edges_.removeOne(edge);
input->edges_.removeOne(edge);
emit input->EdgeRemoved(edge);
}
void NodeParam::DisconnectEdge(NodeOutput *output, NodeInput *input)
{
for (int i=0;i<output->edges_.size();i++) {
NodeEdgePtr edge = output->edges_.at(i);
if (edge->input() == input) {
DisconnectEdge(edge);
break;
}
}
}
NodeEdgePtr NodeParam::DisconnectForNewOutput(NodeInput *input)
{
// If the input can only accept one input (the default) and has one already, disconnect it
if (!input->edges_.isEmpty()) {
NodeEdgePtr edge = input->edges_.first();
DisconnectEdge(edge);
return edge;
}
return nullptr;
}
QString NodeParam::GetPrettyDataTypeName(const NodeParam::DataType &type)
{
switch (type) {
case kNone:
return tr("None");
case kInt:
case kCombo:
return tr("Integer");
case kFloat:
return tr("Float");
case kRational:
return tr("Rational");
case kBoolean:
return tr("Boolean");
case kColor:
return tr("Color");
case kMatrix:
return tr("Matrix");
case kText:
return tr("Text");
case kFont:
return tr("Font");
case kFile:
return tr("File");
case kTexture:
return tr("Texture");
case kSamples:
return tr("Samples");
case kFootage:
return tr("Footage");
case kVec2:
return tr("Vector 2D");
case kVec3:
return tr("Vector 3D");
case kVec4:
return tr("Vector 4D");
case kDecimal:
case kNumber:
case kString:
case kBuffer:
case kVector:
case kShaderJob:
case kSampleJob:
case kGenerateJob:
case kAny:
break;
}
return tr("Unknown");
}
QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVariant &value)
{
switch (type) {
case kInt: return ValueToBytesInternal<int64_t>(value);
case kFloat: return ValueToBytesInternal<double>(value);
case kColor: return ValueToBytesInternal<Color>(value);
case kText: return value.toString().toUtf8();
case kBoolean: return ValueToBytesInternal<bool>(value);
case kFont: return value.toString().toUtf8();
case kFile: return value.toString().toUtf8();
case kMatrix: return ValueToBytesInternal<QMatrix4x4>(value);
case kRational: return ValueToBytesInternal<rational>(value);
case kVec2: return ValueToBytesInternal<QVector2D>(value);
case kVec3: return ValueToBytesInternal<QVector3D>(value);
case kVec4: return ValueToBytesInternal<QVector4D>(value);
case kCombo: return ValueToBytesInternal<int>(value);
// These types have no persistent input
case kNone:
case kFootage:
case kTexture:
case kSamples:
case kDecimal:
case kNumber:
case kString:
case kBuffer:
case kVector:
case kShaderJob:
case kSampleJob:
case kGenerateJob:
case kAny:
break;
}
return QByteArray();
}
template<typename T>
QByteArray NodeParam::ValueToBytesInternal(const QVariant &v)
{
QByteArray bytes;
int size_of_type = sizeof(T);
bytes.resize(size_of_type);
T raw_val = v.value<T>();
memcpy(bytes.data(), &raw_val, static_cast<size_t>(size_of_type));
return bytes;
}
}
-437
View File
@@ -1,437 +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 NODEPARAM_H
#define NODEPARAM_H
#include <QObject>
#include <QVariant>
#include <QVector>
#include <QXmlStreamWriter>
#include "common/rational.h"
#include "common/xmlutils.h"
#include "node/edge.h"
namespace olive {
class Node;
/**
* @brief A base parameter of a Node
*
* The main data points of a Node. NodeParams are added to Nodes so that Node::Process() can use data acquired either
* directly as a value set by the user, or through the output of another NodeParam.
*
* This is an abstract base class. In most cases you'll want NodeInput or NodeOutput.
*/
class NodeParam : public QObject
{
Q_OBJECT
public:
/**
* @brief The type of parameter this is
*/
enum Type {
kInput,
kOutput
};
/**
* @brief The types of data that can be passed between Nodes
*/
enum DataType {
kNone = 0x0,
/**
****************************** SPECIFIC IDENTIFIERS ******************************
*/
/**
* Integer type
*
* Resolves to int64_t.
*/
kInt = 0x1,
/**
* Decimal (floating-point) type
*
* Resolves to `double`.
*/
kFloat = 0x2,
/**
* Decimal (rational) type
*
* Resolves to `double`.
*/
kRational = 0x4,
/**
* Boolean type
*
* Resolves to `bool`.
*/
kBoolean = 0x8,
/**
* Floating-point type
*
* Resolves to `Color`.
*
* Colors passed around the nodes should always be in reference space and preferably use
*/
kColor = 0x10,
/**
* Matrix type
*
* Resolves to `QMatrix4x4`.
*/
kMatrix = 0x20,
/**
* Text type
*
* Resolves to `QString`.
*/
kText = 0x40,
/**
* Font type
*
* Resolves to `QFont`.
*/
kFont = 0x80,
/**
* File type
*
* Resolves to a `QString` containing an absolute file path.
*/
kFile = 0x100,
/**
* Image buffer type
*
* True value type depends on the render engine used.
*/
kTexture = 0x200,
/**
* Audio samples type
*
* Resolves to `SampleBufferPtr`.
*/
kSamples = 0x400,
/**
* Footage stream identifier type
*
* Resolves to `StreamPtr`.
*/
kFootage = 0x800,
/**
* Two-dimensional vector (XY) type
*
* Resolves to `QVector2D`.
*/
kVec2 = 0x1000,
/**
* Three-dimensional vector (XYZ) type
*
* Resolves to `QVector3D`.
*/
kVec3 = 0x2000,
/**
* Four-dimensional vector (XYZW) type
*
* Resolves to `QVector4D`.
*/
kVec4 = 0x4000,
/**
* ComboBox type
*
* Resolves to `int` - the index currently selected
*/
kCombo = 0x8000,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated shader job needs to
* run. This value will usually be taken from a table and a kTexture value will be pushed to
* take its place.
*/
kShaderJob = 0x10000,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated sample job needs to
* take place. This value will usually be taken from a table and a kSamples value will be
* pushed to take its place.
*/
kSampleJob = 0x20000,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated sample job needs to
* take place. This value will usually be taken from a table and a kSamples value will be
* pushed to take its place.
*/
kGenerateJob = 0x40000,
/**
****************************** BROAD IDENTIFIERS ******************************
*/
/**
* Identifier for type that contains a decimal number
*
* Includes kFloat and kRational.
*/
kDecimal = 0x6,
/**
* Identifier for type that contains a number of any kind (whole or decimal)
*
* Includes kInt, kFloat, and kRational.
*/
kNumber = 0x7,
/**
* Identifier for type that contains a text string of any kind.
*
* Includes kText and kFile.
*/
kString = 0x140,
/**
* Identifier for type that contains a either an image or audio buffer
*
* Includes kTexture and kSamples.
*/
kBuffer = 0x600,
/**
* Identifier for type that contains a vector (two- to four-dimensional)
*
* Includes kVec2, kVec3, kVec4, and kColor.
*/
kVector = 0x7010,
/**
* Identifier for any type
*
* Matches with all types except for kNone
*/
kAny = 0xFFFFFFFF
};
/**
* @brief NodeParam Constructor
*/
NodeParam(const QString& id);
virtual ~NodeParam() override;
/**
* @brief Load function
*/
virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) = 0;
/**
* @brief Save function
*/
virtual void Save(QXmlStreamWriter* writer) const = 0;
/**
* @brief Return ID of this parameter
*/
const QString id() const;
/**
* @brief The type of node parameter this is
*
* This must be set in subclasses, but most of the time you should probably subclass from NodeInput and NodeOutput
* anyway.
*/
virtual Type type() = 0;
/**
* @brief Name of this parameter to be shown to the user
*/
virtual QString name();
void set_name(const QString& name);
/**
* @brief Node parent object
*
* Nodes and NodeParams use the QObject parent-child system. This function is a convenience function for
* static_cast<Node*>(QObject::parent())
*/
Node* parentNode() const;
/**
* @brief Return the row index of this parameter in the parent node (primarily used for UI drawing functions)
*/
int index();
/**
* @brief Returns whether anything is connected to this parameter or not
*/
bool is_connected() const;
bool is_connectable() const;
void set_connectable(bool connectable);
/**
* @brief Return a list of edges (aka connections to other nodes)
*
* This list can't be modified directly. Use ConnectEdge() and DisconnectEdge() instead for that.
*/
const QVector<NodeEdgePtr>& edges();
/**
* @brief Disconnect any edges connecting this parameter to other parameters
*/
virtual void DisconnectAll();
/**
* @brief Connect an output parameter to an input parameter
*
* This function makes no attempt to check whether the two NodeParams have compatible data types. This should be done
* beforehand or behavior is undefined.
*
* If the input already has an edge connected and can't accept multiple edges, that edge is disconnected before an
* attempt at a new connection is made. This function returns the new NodeEdge created by this connection.
*
* If the input *can* accept multiple edges but is already connected to this output, no new connection is made (since
* the connection already exists). In this situation, nullptr is returned.
*
* This function emits EdgeAdded().
*/
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input);
/**
* @brief Disconnect an edge
*
* This function emits EdgeRemoved(NodeEdgePtr edge).
*
* @param edge
*
* Edge to disconnect.
*/
static void DisconnectEdge(NodeEdgePtr edge);
/**
* @brief Disconnect an edge
*
* Sometimes this function is preferable if you don't know what the edge object is (or with undo commands where the
* edge object may change despite the connection being between the same parameters).
*
* This function emits EdgeRemoved(NodeEdgePtr edge).
*
* @param edge
*
* Edge to disconnect.
*/
static void DisconnectEdge(NodeOutput* output, NodeInput* input);
/**
* @brief If an input has an edge and can't take multiple, this function disconnects them and returns the edge object
*
* This is used just before a connection is about to be made. If an input is already connected to an output, but
* can't take multiple inputs, that connection will need to be removed before the new connection can be made.
* This function check if it's necessary to remove the edge from an input before connecting a new edge, and removes
* and returns it if so.
*
* If the input does NOT have anything connected, or it does but the input CAN accept multiple connections, nothing
* is disconnected and nullptr is returned.
*/
static NodeEdgePtr DisconnectForNewOutput(NodeInput* input);
/**
* @brief Get a human-readable translated name for a certain data type
*/
static QString GetPrettyDataTypeName(const DataType &type);
/**
* @brief Convert a value from a NodeParam into bytes
*/
static QByteArray ValueToBytes(const DataType &type, const QVariant& value);
signals:
/**
* @brief Signal emitted when an edge is added to this parameter
*
* See ConnectEdge() for usage. Only one of the two parameters needs to emit this signal when a connection is made,
* because otherwise two of exactly the same signal will be emitted.
*/
void EdgeAdded(NodeEdgePtr edge);
/**
* @brief Signal emitted when an edge is removed from this parameter
*
* See DisconnectEdge() for usage. Only one of the two parameters needs to emit this signal when a connection is
* removed, because otherwise two of exactly the same signal will be emitted.
*/
void EdgeRemoved(NodeEdgePtr edge);
protected:
/**
* @brief Internal list of edges
*/
QVector<NodeEdgePtr> edges_;
/**
* @brief Internal name string
*/
QString name_;
private:
/**
* @brief Internal function for returning a value in the form of bytes
*/
template<typename T>
static QByteArray ValueToBytesInternal(const QVariant& v);
/**
* @brief Internal ID string
*/
QString id_;
/**
* @brief Internal connectable value
*/
bool connectable_;
};
}
#endif // NODEPARAM_H
+57 -28
View File
@@ -29,9 +29,7 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
NodeValueDatabase database;
// We need to insert tables into the database for each input
QVector<NodeInput*> inputs = node->GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
foreach (NodeInput* input, node->parameters()) {
if (IsCancelled()) {
return NodeValueDatabase();
}
@@ -48,26 +46,57 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& range)
{
if (input->is_connected()) {
// Value will equal something from the connected node, follow it
return GenerateTable(input->get_connected_node(), range);
} else if (!input->IsArray()) {
// Push onto the table the value at this time from the input
QVariant input_value = input->get_value_at_time(range.in());
// If input is connected, retrieve value directly
if (input->IsConnected()) {
NodeValueTable table;
table.Push(input->data_type(), input_value, input->parentNode());
return table;
// Value will equal something from the connected node, follow it
return GenerateTable(input->GetConnectedNode(), range);
} else {
// Store node
Node* node = static_cast<Node*>(input->parent());
QVariant return_val;
if (input->IsArray()) {
// Value is an array, we will return a list of NodeValueTables
QVector<NodeValueTable> array_tbl(input->ArraySize());
for (int i=0; i<array_tbl.size(); i++) {
NodeValueTable& sub_tbl = array_tbl[i];
if (input->IsConnected(i)) {
sub_tbl = GenerateTable(input->GetConnectedNode(i), range);
} else {
QVariant input_value = input->GetValueAtTime(range.in(), i);
sub_tbl.Push(input->GetDataType(), input_value, node);
}
}
return NodeValueTable();
return_val = QVariant::fromValue(array_tbl);
} else {
// Not connected or an array, just pull the immediate
return_val = input->GetValueAtTime(range.in());
}
NodeValueTable return_table;
return_table.Push(input->GetDataType(), return_val, node, true);
return return_table;
}
}
NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range)
{
if (n->IsTrack()) {
const TrackOutput* track = dynamic_cast<const TrackOutput*>(n);
if (track) {
// If the range is not wholly contained in this Block, we'll need to do some extra processing
return GenerateBlockTable(static_cast<const TrackOutput*>(n), range);
return GenerateBlockTable(track, range);
}
// FIXME: Cache certain values here if we've already processed them before
@@ -156,9 +185,9 @@ void NodeTraverser::AddGlobalsToDatabase(NodeValueDatabase &db, const TimeRange&
{
// Insert global variables
NodeValueTable global;
global.Push(NodeParam::kFloat, range.in().toDouble(), nullptr, QStringLiteral("time_in"));
global.Push(NodeParam::kFloat, range.out().toDouble(), nullptr, QStringLiteral("time_out"));
global.Push(NodeParam::kVec2, GenerateResolution(), nullptr, QStringLiteral("resolution"));
global.Push(NodeValue::kFloat, range.in().toDouble(), nullptr, false, QStringLiteral("time_in"));
global.Push(NodeValue::kFloat, range.out().toDouble(), nullptr, false, QStringLiteral("time_out"));
global.Push(NodeValue::kVec2, GenerateResolution(), nullptr, false, QStringLiteral("resolution"));
db.Insert(QStringLiteral("global"), global);
}
@@ -170,7 +199,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
// Convert footage to image/sample buffers
QVariant cached_frame = GetCachedFrame(node, range.in());
if (!cached_frame.isNull()) {
output_params.Push(NodeParam::kTexture, cached_frame, node);
output_params.Push(NodeValue::kTexture, cached_frame, node);
// No more to do here
got_cached_frame = true;
@@ -187,7 +216,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
const NodeValue& v = output_params.at(i);
QList<NodeValue>* take_this_value_list = nullptr;
if (v.type() == NodeParam::kFootage) {
if (v.type() == NodeValue::kFootage) {
Stream* s = Node::ValueToPtr<Stream>(v.data());
if (s) {
@@ -197,11 +226,11 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
take_this_value_list = &audio_footage_to_retrieve;
}
}
} else if (v.type() == NodeParam::kShaderJob) {
} else if (v.type() == NodeValue::kShaderJob) {
take_this_value_list = &shader_jobs_to_run;
} else if (v.type() == NodeParam::kSampleJob) {
} else if (v.type() == NodeValue::kSampleJob) {
take_this_value_list = &sample_jobs_to_run;
} else if (v.type() == NodeParam::kGenerateJob) {
} else if (v.type() == NodeValue::kGenerateJob) {
take_this_value_list = &generate_jobs_to_run;
}
@@ -221,7 +250,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
QVariant value = ProcessVideoFootage(stream, range.in());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
output_params.Push(NodeValue::kTexture, value, node);
}
}
}
@@ -231,7 +260,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
QVariant value = ProcessShader(node, range, v.data().value<ShaderJob>());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
output_params.Push(NodeValue::kTexture, value, node);
}
}
@@ -240,7 +269,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
QVariant value = ProcessFrameGeneration(node, v.data().value<GenerateJob>());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
output_params.Push(NodeValue::kTexture, value, node);
}
}
}
@@ -254,7 +283,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
QVariant value = ProcessAudioFootage(stream, range);
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
output_params.Push(NodeValue::kSamples, value, node);
}
}
}
@@ -264,7 +293,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
QVariant value = ProcessSamples(node, range, v.data().value<SampleJob>());
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
output_params.Push(NodeValue::kSamples, value, node);
}
}
}
+304 -36
View File
@@ -20,8 +20,307 @@
#include "value.h"
#include <QMatrix4x4>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include "common/tohex.h"
#include "node/input.h"
#include "render/color.h"
namespace olive {
const QVector<NodeValue::Type> NodeValue::kNumber = {kFloat, kInt, kRational};
const QVector<NodeValue::Type> NodeValue::kBuffer = {kTexture, kSamples};
const QVector<NodeValue::Type> NodeValue::kVector = {kVec2, kVec3, kVec4, kColor};
QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool value_is_a_key_track)
{
if (!value_is_a_key_track && data_type == kVec2) {
QVector2D vec = value.value<QVector2D>();
return QStringLiteral("%1:%2").arg(QString::number(vec.x()),
QString::number(vec.y()));
} else if (!value_is_a_key_track && data_type == kVec3) {
QVector3D vec = value.value<QVector3D>();
return QStringLiteral("%1:%2:%3").arg(QString::number(vec.x()),
QString::number(vec.y()),
QString::number(vec.z()));
} else if (!value_is_a_key_track && data_type == kVec4) {
QVector4D vec = value.value<QVector4D>();
return QStringLiteral("%1:%2:%3:%4").arg(QString::number(vec.x()),
QString::number(vec.y()),
QString::number(vec.z()),
QString::number(vec.w()));
} else if (!value_is_a_key_track && data_type == kColor) {
Color c = value.value<Color>();
return QStringLiteral("%1:%2:%3:%4").arg(QString::number(c.red()),
QString::number(c.green()),
QString::number(c.blue()),
QString::number(c.alpha()));
} else if (data_type == kRational) {
return value.value<rational>().toString();
} else if (data_type == kFootage) {
return QString::number(value.value<quintptr>());
} else if (data_type == kTexture
|| data_type == kSamples) {
// These data types need no XML representation
return QString();
} else if (data_type == kInt) {
return QString::number(value.value<int64_t>());
} else {
if (value.canConvert<QString>()) {
return value.toString();
}
if (!value.isNull()) {
qWarning() << "Failed to convert type" << ToHex(data_type) << "to string";
}
return QString();
}
}
template<typename T>
QByteArray ValueToBytesInternal(const QVariant &v)
{
QByteArray bytes;
int size_of_type = sizeof(T);
bytes.resize(size_of_type);
T raw_val = v.value<T>();
memcpy(bytes.data(), &raw_val, static_cast<size_t>(size_of_type));
return bytes;
}
QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
{
switch (type) {
case kInt: return ValueToBytesInternal<int64_t>(value);
case kFloat: return ValueToBytesInternal<double>(value);
case kColor: return ValueToBytesInternal<Color>(value);
case kText: return value.toString().toUtf8();
case kBoolean: return ValueToBytesInternal<bool>(value);
case kFont: return value.toString().toUtf8();
case kFile: return value.toString().toUtf8();
case kMatrix: return ValueToBytesInternal<QMatrix4x4>(value);
case kRational: return ValueToBytesInternal<rational>(value);
case kVec2: return ValueToBytesInternal<QVector2D>(value);
case kVec3: return ValueToBytesInternal<QVector3D>(value);
case kVec4: return ValueToBytesInternal<QVector4D>(value);
case kCombo: return ValueToBytesInternal<int>(value);
// These types have no persistent input
case kNone:
case kFootage:
case kTexture:
case kSamples:
case kShaderJob:
case kSampleJob:
case kGenerateJob:
break;
}
return QByteArray();
}
QVector<QVariant> NodeValue::split_normal_value_into_track_values(Type type, const QVariant &value)
{
QVector<QVariant> vals(get_number_of_keyframe_tracks(type));
switch (type) {
case kVec2:
{
QVector2D vec = value.value<QVector2D>();
vals.replace(0, vec.x());
vals.replace(1, vec.y());
break;
}
case kVec3:
{
QVector3D vec = value.value<QVector3D>();
vals.replace(0, vec.x());
vals.replace(1, vec.y());
vals.replace(2, vec.z());
break;
}
case kVec4:
{
QVector4D vec = value.value<QVector4D>();
vals.replace(0, vec.x());
vals.replace(1, vec.y());
vals.replace(2, vec.z());
vals.replace(3, vec.w());
break;
}
case kColor:
{
Color c = value.value<Color>();
vals.replace(0, c.red());
vals.replace(1, c.green());
vals.replace(2, c.blue());
vals.replace(3, c.alpha());
break;
}
default:
vals.replace(0, value);
}
return vals;
}
QVariant NodeValue::combine_track_values_into_normal_value(Type type, const QVector<QVariant> &split)
{
switch (type) {
case kVec2:
{
return QVector2D(split.at(0).toFloat(),
split.at(1).toFloat());
}
case kVec3:
{
return QVector3D(split.at(0).toFloat(),
split.at(1).toFloat(),
split.at(2).toFloat());
}
case kVec4:
{
return QVector4D(split.at(0).toFloat(),
split.at(1).toFloat(),
split.at(2).toFloat(),
split.at(3).toFloat());
}
case kColor:
{
return QVariant::fromValue(Color(split.at(0).toFloat(),
split.at(1).toFloat(),
split.at(2).toFloat(),
split.at(3).toFloat()));
}
default:
return split.first();
}
}
int NodeValue::get_number_of_keyframe_tracks(Type type)
{
switch (type) {
case NodeValue::kVec2:
return 2;
case NodeValue::kVec3:
return 3;
case NodeValue::kVec4:
case NodeValue::kColor:
return 4;
default:
return 1;
}
}
QVariant NodeValue::StringToValue(Type data_type, const QString &string, bool value_is_a_key_track)
{
if (!value_is_a_key_track && data_type == kVec2) {
QStringList vals = string.split(':');
ValidateVectorString(&vals, 2);
return QVector2D(vals.at(0).toFloat(), vals.at(1).toFloat());
} else if (!value_is_a_key_track && data_type == kVec3) {
QStringList vals = string.split(':');
ValidateVectorString(&vals, 3);
return QVector3D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat());
} else if (!value_is_a_key_track && data_type == kVec4) {
QStringList vals = string.split(':');
ValidateVectorString(&vals, 4);
return QVector4D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat(), vals.at(3).toFloat());
} else if (!value_is_a_key_track && data_type == kColor) {
QStringList vals = string.split(':');
ValidateVectorString(&vals, 4);
return QVariant::fromValue(Color(vals.at(0).toDouble(), vals.at(1).toDouble(), vals.at(2).toDouble(), vals.at(3).toDouble()));
} else if (data_type == kInt) {
return QVariant::fromValue(string.toLongLong());
} else if (data_type == kRational) {
return QVariant::fromValue(rational::fromString(string));
} else {
return string;
}
}
void NodeValue::ValidateVectorString(QStringList* list, int count)
{
while (list->size() < count) {
list->append(QStringLiteral("0"));
}
}
QString NodeValue::GetPrettyDataTypeName(Type type)
{
switch (type) {
case kNone:
return QCoreApplication::translate("NodeValue", "None");
case kInt:
case kCombo:
return QCoreApplication::translate("NodeValue", "Integer");
case kFloat:
return QCoreApplication::translate("NodeValue", "Float");
case kRational:
return QCoreApplication::translate("NodeValue", "Rational");
case kBoolean:
return QCoreApplication::translate("NodeValue", "Boolean");
case kColor:
return QCoreApplication::translate("NodeValue", "Color");
case kMatrix:
return QCoreApplication::translate("NodeValue", "Matrix");
case kText:
return QCoreApplication::translate("NodeValue", "Text");
case kFont:
return QCoreApplication::translate("NodeValue", "Font");
case kFile:
return QCoreApplication::translate("NodeValue", "File");
case kTexture:
return QCoreApplication::translate("NodeValue", "Texture");
case kSamples:
return QCoreApplication::translate("NodeValue", "Samples");
case kFootage:
return QCoreApplication::translate("NodeValue", "Footage");
case kVec2:
return QCoreApplication::translate("NodeValue", "Vector 2D");
case kVec3:
return QCoreApplication::translate("NodeValue", "Vector 3D");
case kVec4:
return QCoreApplication::translate("NodeValue", "Vector 4D");
case kShaderJob:
case kSampleJob:
case kGenerateJob:
break;
}
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_;
@@ -32,31 +331,7 @@ NodeValueTable NodeValueDatabase::Merge() const
return NodeValueTable::Merge(copy.values());
}
NodeValue::NodeValue() :
type_(NodeParam::kNone),
from_(nullptr)
{
}
NodeValue::NodeValue(const NodeParam::DataType &type, const QVariant &data, const Node *from, const QString &tag) :
type_(type),
data_(data),
from_(from),
tag_(tag)
{
}
bool NodeValue::operator==(const NodeValue &rhs) const
{
return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_;
}
QVariant NodeValueTable::Get(const NodeParam::DataType &type, const QString &tag) const
{
return GetWithMeta(type, tag).data();
}
NodeValue NodeValueTable::GetWithMeta(const NodeParam::DataType &type, const QString &tag) const
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
{
int value_index = GetInternal(type, tag);
@@ -67,12 +342,7 @@ NodeValue NodeValueTable::GetWithMeta(const NodeParam::DataType &type, const QSt
return NodeValue();
}
QVariant NodeValueTable::Take(const NodeParam::DataType &type, const QString &tag)
{
return TakeWithMeta(type, tag).data();
}
NodeValue NodeValueTable::TakeWithMeta(const NodeParam::DataType &type, const QString &tag)
NodeValue NodeValueTable::TakeWithMeta(const QVector<NodeValue::Type> &type, const QString &tag)
{
int value_index = GetInternal(type, tag);
@@ -83,7 +353,7 @@ NodeValue NodeValueTable::TakeWithMeta(const NodeParam::DataType &type, const QS
return NodeValue();
}
bool NodeValueTable::Has(const NodeParam::DataType &type) const
bool NodeValueTable::Has(NodeValue::Type type) const
{
for (int i=values_.size() - 1;i>=0;i--) {
const NodeValue& v = values_.at(i);
@@ -110,8 +380,6 @@ void NodeValueTable::Remove(const NodeValue &v)
NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
{
if (tables.size() == 1) {
return tables.first();
}
@@ -134,14 +402,14 @@ NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
return merged_table;
}
int NodeValueTable::GetInternal(const NodeParam::DataType &type, const QString &tag) const
int NodeValueTable::GetInternal(const QVector<NodeValue::Type>& types, const QString &tag) const
{
int index = -1;
for (int i=values_.size() - 1;i>=0;i--) {
const NodeValue& v = values_.at(i);
if (v.type() & type) {
if (types.contains(v.type())) {
index = i;
if (tag.isEmpty() || tag == v.tag()) {
+275 -36
View File
@@ -22,19 +22,189 @@
#define VALUE_H
#include <QString>
#include "input.h"
#include "render/shadervalue.h"
#include <QVariant>
namespace olive {
class Node;
class NodeInput;
class NodeValue
{
public:
NodeValue();
NodeValue(const NodeParam::DataType& type, const QVariant& data, const Node* from, const QString& tag = QString());
/**
* @brief The types of data that can be passed between Nodes
*/
enum Type {
kNone,
const NodeParam::DataType& type() const
/**
****************************** SPECIFIC IDENTIFIERS ******************************
*/
/**
* Integer type
*
* Resolves to int64_t.
*/
kInt,
/**
* Decimal (floating-point) type
*
* Resolves to `double`.
*/
kFloat,
/**
* Decimal (rational) type
*
* Resolves to `double`.
*/
kRational,
/**
* Boolean type
*
* Resolves to `bool`.
*/
kBoolean,
/**
* Floating-point type
*
* Resolves to `Color`.
*
* Colors passed around the nodes should always be in reference space and preferably use
*/
kColor,
/**
* Matrix type
*
* Resolves to `QMatrix4x4`.
*/
kMatrix,
/**
* Text type
*
* Resolves to `QString`.
*/
kText,
/**
* Font type
*
* Resolves to `QFont`.
*/
kFont,
/**
* File type
*
* Resolves to a `QString` containing an absolute file path.
*/
kFile,
/**
* Image buffer type
*
* True value type depends on the render engine used.
*/
kTexture,
/**
* Audio samples type
*
* Resolves to `SampleBufferPtr`.
*/
kSamples,
/**
* Footage stream identifier type
*
* Resolves to `StreamPtr`.
*/
kFootage,
/**
* Two-dimensional vector (XY) type
*
* Resolves to `QVector2D`.
*/
kVec2,
/**
* Three-dimensional vector (XYZ) type
*
* Resolves to `QVector3D`.
*/
kVec3,
/**
* Four-dimensional vector (XYZW) type
*
* Resolves to `QVector4D`.
*/
kVec4,
/**
* ComboBox type
*
* Resolves to `int` - the index currently selected
*/
kCombo,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated shader job needs to
* run. This value will usually be taken from a table and a kTexture value will be pushed to
* take its place.
*/
kShaderJob,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated sample job needs to
* take place. This value will usually be taken from a table and a kSamples value will be
* pushed to take its place.
*/
kSampleJob,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated sample job needs to
* take place. This value will usually be taken from a table and a kSamples value will be
* pushed to take its place.
*/
kGenerateJob
};
static const QVector<Type> kNumber;
static const QVector<Type> kBuffer;
static const QVector<Type> kVector;
NodeValue() :
type_(kNone),
from_(nullptr),
array_(false)
{
}
NodeValue(Type type, const QVariant& data, const Node* from = nullptr, bool array = false, const QString& tag = QString()) :
type_(type),
data_(data),
from_(from),
tag_(tag),
array_(array)
{
}
Type type() const
{
return type_;
}
@@ -54,13 +224,63 @@ public:
return from_;
}
bool operator==(const NodeValue& rhs) const;
bool array() const
{
return array_;
}
bool operator==(const NodeValue& rhs) const
{
return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_;
}
static QString GetPrettyDataTypeName(Type type);
static QString ValueToString(Type data_type, const QVariant& value, bool value_is_a_key_track);
static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track);
/**
* @brief Convert a value from a NodeParam into bytes
*/
static QByteArray ValueToBytes(Type type, const QVariant& value);
static QVector<QVariant> split_normal_value_into_track_values(Type type, const QVariant &value);
static QVariant combine_track_values_into_normal_value(Type type, const QVector<QVariant>& split);
/**
* @brief Returns whether a data type can be interpolated or not
*/
static bool type_can_be_interpolated(NodeValue::Type type)
{
return type == kFloat
|| type == kVec2
|| type == kVec3
|| type == kVec4
|| type == kColor;
}
static bool type_is_numeric(NodeValue::Type type)
{
return kNumber.contains(type);
}
static bool type_is_vector(NodeValue::Type type)
{
return kVector.contains(type);
}
static int get_number_of_keyframe_tracks(Type type);
static void ValidateVectorString(QStringList* list, int count);
private:
NodeParam::DataType type_;
Type type_;
QVariant data_;
const Node* from_;
QString tag_;
bool array_;
};
@@ -69,24 +289,52 @@ class NodeValueTable
public:
NodeValueTable() = default;
QVariant Get(const NodeParam::DataType& type, const QString& tag = QString()) const;
NodeValue GetWithMeta(const NodeParam::DataType& type, const QString& tag = QString()) const;
QVariant Take(const NodeParam::DataType& type, const QString& tag = QString());
NodeValue TakeWithMeta(const NodeParam::DataType& type, const QString& tag = QString());
QVariant Get(NodeValue::Type type, const QString& tag = QString()) const
{
QVector<NodeValue::Type> types = {type};
return Get(types, tag);
}
QVariant Get(const QVector<NodeValue::Type>& type, const QString& tag = QString()) const
{
return GetWithMeta(type, tag).data();
}
NodeValue GetWithMeta(NodeValue::Type type, const QString& tag = QString()) const
{
QVector<NodeValue::Type> types = {type};
return GetWithMeta(types, tag);
}
NodeValue GetWithMeta(const QVector<NodeValue::Type>& type, const QString& tag = QString()) const;
QVariant Take(NodeValue::Type type, const QString& tag = QString())
{
QVector<NodeValue::Type> types = {type};
return Take(types, tag);
}
QVariant Take(const QVector<NodeValue::Type>& type, const QString& tag = QString())
{
return TakeWithMeta(type, tag).data();
}
NodeValue TakeWithMeta(NodeValue::Type type, const QString& tag = QString())
{
QVector<NodeValue::Type> types = {type};
return TakeWithMeta(types, tag);
}
NodeValue TakeWithMeta(const QVector<NodeValue::Type>& type, const QString& tag = QString());
void Push(const NodeValue& value)
{
values_.append(value);
}
void Push(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString())
void Push(NodeValue::Type type, const QVariant& data, const Node *from, bool array = false, const QString& tag = QString())
{
Push(NodeValue(type, data, from, tag));
}
void Push(const ShaderValue &value, const Node *from)
{
Push(value.type, value.data, from, value.tag);
Push(NodeValue(type, data, from, array, tag));
}
void Prepend(const NodeValue& value)
@@ -94,14 +342,9 @@ public:
values_.prepend(value);
}
void Prepend(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString())
void Prepend(NodeValue::Type type, const QVariant& data, const Node *from, bool array = false, const QString& tag = QString())
{
Prepend(NodeValue(type, data, from, tag));
}
void Prepend(const ShaderValue &value, const Node *from)
{
Prepend(value.type, value.data, from, value.tag);
Prepend(NodeValue(type, data, from, array, tag));
}
const NodeValue& at(int index) const
@@ -118,7 +361,7 @@ public:
return values_.size();
}
bool Has(const NodeParam::DataType& type) const;
bool Has(NodeValue::Type type) const;
void Remove(const NodeValue& v);
bool isEmpty() const
@@ -129,7 +372,7 @@ public:
static NodeValueTable Merge(QList<NodeValueTable> tables);
private:
int GetInternal(const NodeParam::DataType& type, const QString& tag) const;
int GetInternal(const QVector<NodeValue::Type> &type, const QString& tag) const;
QList<NodeValue> values_;
@@ -145,20 +388,14 @@ public:
return tables_[input_id];
}
NodeValueTable& operator[](const NodeInput* input)
{
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)
{
tables_.insert(key->id(), value);
}
void Insert(const NodeInput* key, const NodeValueTable& value);
NodeValueTable Merge() const;
@@ -184,6 +421,8 @@ private:
};
using NodeValueMap = QHash<QString, NodeValue>;
}
Q_DECLARE_METATYPE(olive::NodeValue)
+2 -2
View File
@@ -29,8 +29,8 @@ NodePanel::NodePanel(QWidget *parent) :
node_view_ = new NodeView(this);
// Connect node view signals to this panel
connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
//connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
//connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
// Set it as the main widget of this panel
SetWidgetWithPadding(node_view_);
+4 -2
View File
@@ -88,12 +88,14 @@ public slots:
void SelectBlocks(const QVector<Block*>& nodes)
{
node_view_->SelectBlocks(nodes);
qDebug() << "Stub";
//node_view_->SelectBlocks(nodes);
}
void DeselectBlocks(const QVector<Block*>& nodes)
{
node_view_->DeselectBlocks(nodes);
qDebug() << "Stub";
//node_view_->DeselectBlocks(nodes);
}
signals:
-2
View File
@@ -21,8 +21,6 @@
#ifndef FOLDER_H
#define FOLDER_H
#include "node/param.h"
#include "project/item/footage/footage.h"
#include "project/item/item.h"
namespace olive {
+7 -14
View File
@@ -64,27 +64,18 @@ QString Item::rate()
return QString();
}
const Item *Item::root() const
{
const Item* item = this;
while (item->item_parent()) {
item = item->item_parent();
}
return item;
}
Project *Item::project() const
{
const Item* root_item = root();
return root_item->project_;
return project_;
}
void Item::set_project(Project *project)
{
project_ = project;
foreach (Item* i, item_children_) {
i->set_project(project_);
}
}
QVector<Item *> Item::get_children_of_type(Type type, bool recursive) const
@@ -129,11 +120,13 @@ void Item::childEvent(QChildEvent *event)
item_children_.append(cast_test);
cast_test->item_parent_ = this;
cast_test->set_project(project_);
} else if (event->type() == QEvent::ChildRemoved) {
item_children_.removeOne(cast_test);
cast_test->item_parent_ = nullptr;
cast_test->set_project(nullptr);
}
}
+1 -3
View File
@@ -30,7 +30,6 @@
#include "common/threadedobject.h"
#include "common/xmlutils.h"
#include "node/param.h"
#include "project/item/footage/stream.h"
namespace olive {
@@ -101,9 +100,8 @@ public:
return item_parent_;
}
const Item* root() const;
Project* project() const;
void set_project(Project* project);
QVector<Item*> get_children_of_type(Type type, bool recursive) const;
+13 -7
View File
@@ -42,7 +42,7 @@ Sequence::Sequence()
{
viewer_output_ = new ViewerOutput();
viewer_output_->SetCanBeDeleted(false);
AddNode(viewer_output_);
viewer_output_->setParent(this);
}
void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt *cancelled)
@@ -158,8 +158,7 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint v
if (node) {
node->Load(reader, xml_node_data, cancelled);
AddNode(node);
node->setParent(this);
}
} else {
reader->skipCurrentElement();
@@ -222,10 +221,17 @@ void Sequence::Save(QXmlStreamWriter *writer) const
void Sequence::add_default_nodes()
{
// Create tracks and connect them to the viewer
Node* video_track_output = viewer_output_->track_list(Timeline::kTrackTypeVideo)->AddTrack();
Node* audio_track_output = viewer_output_->track_list(Timeline::kTrackTypeAudio)->AddTrack();
NodeParam::ConnectEdge(video_track_output->output(), viewer_output_->texture_input());
NodeParam::ConnectEdge(audio_track_output->output(), viewer_output_->samples_input());
TrackOutput* video_track = new TrackOutput();
video_track->setParent(this);
viewer_output_->track_input(Timeline::kTrackTypeVideo)->ArrayAppend();
Node::ConnectEdge(video_track, viewer_output_->track_input(Timeline::kTrackTypeVideo), 0);
Node::ConnectEdge(video_track, viewer_output_->texture_input());
TrackOutput* audio_track = new TrackOutput();
audio_track->setParent(this);
viewer_output_->track_input(Timeline::kTrackTypeAudio)->ArrayAppend();
Node::ConnectEdge(audio_track, viewer_output_->track_input(Timeline::kTrackTypeAudio), 0);
Node::ConnectEdge(audio_track, viewer_output_->samples_input());
}
Item::Type Sequence::type() const
+1 -1
View File
@@ -88,7 +88,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) {
if (con.footage) {
con.input->set_standard_value(QVariant::fromValue(xml_node_data.footage_ptrs.value(con.footage)));
con.input->SetStandardValue(QVariant::fromValue(xml_node_data.footage_ptrs.value(con.footage)), con.element);
}
}
}
-1
View File
@@ -52,7 +52,6 @@ set(OLIVE_SOURCES
render/renderprocessor.cpp
render/renderprocessor.h
render/shadercode.h
render/shadervalue.h
render/stillimagecache.h
render/texture.cpp
render/texture.h
+5 -36
View File
@@ -22,8 +22,6 @@
#define ACCELERATEDJOB_H
#include "node/input.h"
#include "node/inputarray.h"
#include "render/shadervalue.h"
#include "node/value.h"
namespace olive {
@@ -32,58 +30,29 @@ class AcceleratedJob {
public:
AcceleratedJob() = default;
ShaderValue GetValue(NodeInput* input) const
NodeValue GetValue(NodeInput* input) const
{
return value_map_.value(input->id());
}
ShaderValue GetValue(const QString& input) const
NodeValue GetValue(const QString& input) const
{
return value_map_.value(input);
}
void InsertValue(NodeInput* input, NodeValueDatabase& value)
{
ShaderValue shader_val;
shader_val.type = input->data_type();
shader_val.array = input->IsArray();
if (input->IsArray()) {
NodeInputArray* array = static_cast<NodeInputArray*>(input);
QVector<QVariant> values(array->GetSize());
for (int j=0;j<array->GetSize();j++) {
NodeInput* subparam = array->At(j);
values[j] = value[subparam].Take(subparam->data_type());
InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType()));
}
shader_val.data = QVariant::fromValue(values);
} else {
NodeValue node_val = value[input].TakeWithMeta(input->data_type());
shader_val.data = node_val.data();
shader_val.tag = node_val.tag();
}
InsertValue(input->id(), shader_val);
}
void InsertValue(const QString& input, const ShaderValue& value)
void InsertValue(const QString& input, const NodeValue& value)
{
value_map_.insert(input, value);
}
void InsertValue(NodeInput* input, const ShaderValue& value)
{
value_map_.insert(input->id(), value);
}
void InsertValue(NodeInput* input, const NodeValue& value)
{
ShaderValue s(value.data(), value.type());
s.tag = value.tag();
value_map_.insert(input->id(), s);
value_map_.insert(input->id(), value);
}
const NodeValueMap &GetValues() const
+1 -1
View File
@@ -40,7 +40,7 @@ public:
SampleJob(NodeInput* from, NodeValueDatabase& db)
{
samples_ = db[from].Take(NodeParam::kSamples).value<SampleBufferPtr>();
samples_ = db[from].Take(NodeValue::kSamples).value<SampleBufferPtr>();
}
SampleBufferPtr samples() const
+34 -40
View File
@@ -367,51 +367,50 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
}
// This variable is used in the shader, let's set it
const ShaderValue& value = it.value();
const NodeValue& value = it.value();
if (value.array) {
if (value.array()) {
qWarning() << "FIXME: Array support is currently a stub";
}
switch (value.type) {
case NodeInput::kInt:
switch (value.type()) {
case NodeValue::kInt:
// kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to
// over/underflows if the number is large enough, but the likelihood of that is quite low.
shader->setUniformValue(variable_location, value.data.toInt());
shader->setUniformValue(variable_location, value.data().toInt());
break;
case NodeInput::kFloat:
case NodeValue::kFloat:
// kFloat technically specifies a double but as above, OpenGL doesn't support those.
shader->setUniformValue(variable_location, value.data.toFloat());
shader->setUniformValue(variable_location, value.data().toFloat());
break;
case NodeInput::kVec2:
shader->setUniformValue(variable_location, value.data.value<QVector2D>());
case NodeValue::kVec2:
shader->setUniformValue(variable_location, value.data().value<QVector2D>());
break;
case NodeInput::kVec3:
shader->setUniformValue(variable_location, value.data.value<QVector3D>());
case NodeValue::kVec3:
shader->setUniformValue(variable_location, value.data().value<QVector3D>());
break;
case NodeInput::kVec4:
shader->setUniformValue(variable_location, value.data.value<QVector4D>());
case NodeValue::kVec4:
shader->setUniformValue(variable_location, value.data().value<QVector4D>());
break;
case NodeInput::kMatrix:
shader->setUniformValue(variable_location, value.data.value<QMatrix4x4>());
case NodeValue::kMatrix:
shader->setUniformValue(variable_location, value.data().value<QMatrix4x4>());
break;
case NodeInput::kCombo:
shader->setUniformValue(variable_location, value.data.value<int>());
case NodeValue::kCombo:
shader->setUniformValue(variable_location, value.data().value<int>());
break;
case NodeInput::kColor:
case NodeValue::kColor:
{
Color color = value.data.value<Color>();
Color color = value.data().value<Color>();
shader->setUniformValue(variable_location,
color.red(), color.green(), color.blue(), color.alpha());
break;
}
case NodeInput::kBoolean:
shader->setUniformValue(variable_location, value.data.toBool());
case NodeValue::kBoolean:
shader->setUniformValue(variable_location, value.data().toBool());
break;
case NodeInput::kBuffer:
case NodeInput::kTexture:
case NodeValue::kTexture:
{
TexturePtr texture = value.data.value<TexturePtr>();
TexturePtr texture = value.data().value<TexturePtr>();
// Set value to bound texture
shader->setUniformValue(variable_location, textures_to_bind.size());
@@ -433,21 +432,16 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
}
break;
}
case NodeInput::kSamples:
case NodeInput::kText:
case NodeInput::kRational:
case NodeInput::kFont:
case NodeInput::kFile:
case NodeInput::kDecimal:
case NodeInput::kNumber:
case NodeInput::kString:
case NodeInput::kVector:
case NodeInput::kShaderJob:
case NodeInput::kSampleJob:
case NodeInput::kGenerateJob:
case NodeInput::kFootage:
case NodeInput::kNone:
case NodeInput::kAny:
case NodeValue::kSamples:
case NodeValue::kText:
case NodeValue::kRational:
case NodeValue::kFont:
case NodeValue::kFile:
case NodeValue::kShaderJob:
case NodeValue::kSampleJob:
case NodeValue::kGenerateJob:
case NodeValue::kFootage:
case NodeValue::kNone:
break;
}
}
@@ -469,7 +463,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
// Ensure matrix is set, at least to identity
shader->setUniformValue("ove_mvpmat",
job.GetValue(QStringLiteral("ove_mvpmat")).data.value<QMatrix4x4>());
job.GetValue(QStringLiteral("ove_mvpmat")).data().value<QMatrix4x4>());
// Set the viewport to the "physical" resolution of the destination
functions_->glViewport(0, 0,
+123 -171
View File
@@ -61,66 +61,13 @@ void PreviewAutoCacher::SetPaused(bool paused)
}
}
void PreviewAutoCacher::NodeGraphChanged(NodeInput *source)
{
// We need to determine:
// - If we don't have this input, assume that it's coming soon and ignore it
// - If we do, is this input a child of another input we're already copying?
// - Or are any of the queued inputs children of this one?
// First we need to find our copy of the input being queued
Node* our_copy_node = copy_map_.value(source->parentNode());
// If we don't have this node yet, assume it's coming in a later copy in which case it'll be
// copied then
if (!our_copy_node) {
// Assert that there are updates coming
Q_ASSERT(!graph_update_queue_.isEmpty());
return;
}
// If we're here, we must have this node. Determine if we're already copying a "parent" of this
for (int i=0; i<graph_update_queue_.size(); i++) {
NodeInput* queued_input = graph_update_queue_.at(i);
// If this input is already queued, nothing to be done
if (source == queued_input) {
return;
}
// Check if this input supersedes an already queued input
if ((source->IsArray() && static_cast<NodeInputArray*>(source)->sub_params().contains(queued_input))
|| queued_input->parentNode()->OutputsTo(source, true, true)) {
// In which case, we don't need to queue it and can queue our own
graph_update_queue_.removeAt(i);
disconnect(queued_input, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved);
i--;
}
// Check if the source is a member of this array, in which case it'll be copied eventually anyway
if (queued_input->IsArray()
&& static_cast<NodeInputArray*>(queued_input)->sub_params().contains(source)) {
return;
}
// Check if this dependency graph is already queued
if (source->parentNode()->OutputsTo(queued_input, true, true)) {
// In which case, no further copy is necessary
return;
}
}
graph_update_queue_.append(source);
connect(source, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved);
}
void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, const QVector<rational> &times, qint64 job_time)
{
std::vector<QByteArray> existing_hashes;
foreach (const rational& time, times) {
// See if hash already exists in disk cache
QByteArray hash = RenderManager::Hash(viewer->texture_input()->get_connected_node(), viewer->video_params(), time);
QByteArray hash = RenderManager::Hash(viewer->texture_input()->GetConnectedNode(), 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());
@@ -292,13 +239,6 @@ void PreviewAutoCacher::VideoDownloaded()
delete watcher;
}
void PreviewAutoCacher::QueuedInputRemoved()
{
NodeInput* i = static_cast<NodeInput*>(sender());
disconnect(i, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved);
graph_update_queue_.removeOne(i);
}
void PreviewAutoCacher::VideoParamsChanged()
{
// In case the user is pressing the mouse at this exact moment
@@ -324,27 +264,28 @@ void PreviewAutoCacher::SingleFrameFinished()
delete watcher;
}
//#define PRINT_UPDATE_QUEUE_INFO
void PreviewAutoCacher::ProcessUpdateQueue()
{
#ifdef PRINT_UPDATE_QUEUE_INFO
qint64 t = QDateTime::currentMSecsSinceEpoch();
qDebug() << "Processing update queue of" << graph_update_queue_.size() << "elements:";
#endif
while (!graph_update_queue_.isEmpty()) {
NodeInput* i = graph_update_queue_.takeFirst();
#ifdef PRINT_UPDATE_QUEUE_INFO
qDebug() << " " << i->parentNode()->id() << i->id();
#endif
disconnect(i, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved);
CopyNodeInputValue(i);
foreach (const QueuedJob& job, graph_update_queue_) {
switch (job.type) {
case QueuedJob::kNodeAdded:
AddNode(job.node);
break;
case QueuedJob::kNodeRemoved:
RemoveNode(job.node);
break;
case QueuedJob::kEdgeAdded:
AddEdge(job.node, job.input, job.element);
break;
case QueuedJob::kEdgeRemoved:
RemoveEdge(job.node, job.input, job.element);
break;
case QueuedJob::kValueChanged:
CopyValue(job.input, job.element);
break;
}
#ifdef PRINT_UPDATE_QUEUE_INFO
qDebug() << "Update queue took:" << (QDateTime::currentMSecsSinceEpoch() - t);
#endif
}
graph_update_queue_.clear();
last_update_time_ = QDateTime::currentMSecsSinceEpoch();
}
@@ -356,6 +297,63 @@ bool PreviewAutoCacher::HasActiveJobs() const
|| !video_tasks_.isEmpty();
}
void PreviewAutoCacher::AddNode(Node *node)
{
// Copy node
Node* copy = node->copy();
// Insert into map
copy_map_.insert(node, copy);
// Copy parameters
Node::CopyInputs(node, copy, false);
// Connect to each input
foreach (NodeInput* input, node->parameters()) {
connect(input, &NodeInput::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
connect(input, &NodeInput::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
connect(input, &NodeInput::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
}
}
void PreviewAutoCacher::RemoveNode(Node *node)
{
// Find our copy and remove it
Node* copy = copy_map_.take(node);
// Delete it
delete copy;
// Disconnect from inputs
foreach (NodeInput* input, node->parameters()) {
disconnect(input, &NodeInput::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
disconnect(input, &NodeInput::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
disconnect(input, &NodeInput::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
}
}
void PreviewAutoCacher::AddEdge(Node *output, NodeInput *input, int element)
{
Node* our_output = copy_map_.value(output);
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
Node::ConnectEdge(our_output, our_input, element);
}
void PreviewAutoCacher::RemoveEdge(Node *output, NodeInput *input, int element)
{
Node* our_output = copy_map_.value(output);
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
Node::DisconnectEdge(our_output, our_input, element);
}
void PreviewAutoCacher::CopyValue(NodeInput *input, int element)
{
NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id());
NodeInput::CopyValuesOfElement(input, our_input, element);
}
void PreviewAutoCacher::SetPlayhead(const rational &playhead)
{
cache_range_ = TimeRange(playhead - Config::Current()["DiskCacheBehind"].value<rational>(),
@@ -440,90 +438,31 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait)
}
}
void PreviewAutoCacher::CopyNodeInputValue(NodeInput *input)
void PreviewAutoCacher::NodeAdded(Node *node)
{
// Find our copy of this parameter
Node* our_copy_node = copy_map_.value(input->parentNode());
Q_ASSERT(our_copy_node);
NodeInput* our_copy = our_copy_node->GetInputWithID(input->id());
// Copy the standard/keyframe values between these two inputs
NodeInput::CopyValues(input,
our_copy,
false,
false);
// Handle connections
if (input->is_connected() || our_copy->is_connected()) {
// If one of the inputs is connected, it's likely this change came from connecting or
// disconnecting whatever was connected to it
// We start by removing all old dependencies from the map
QVector<Node*> old_deps = our_copy->GetExclusiveDependencies();
foreach (Node* i, old_deps) {
copy_map_.take(copy_map_.key(i))->deleteLater();
}
// And clear any other edges
while (!our_copy->edges().isEmpty()) {
NodeParam::DisconnectEdge(our_copy->edges().first());
}
// Then we copy all node dependencies and connections (if there are any)
CopyNodeMakeConnection(input, our_copy);
}
// Call on sub-elements too
if (input->IsArray()) {
foreach (NodeInput* i, static_cast<NodeInputArray*>(input)->sub_params()) {
CopyNodeInputValue(i);
}
}
graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1});
}
Node* PreviewAutoCacher::CopyNodeConnections(Node* src_node)
void PreviewAutoCacher::NodeRemoved(Node *node)
{
// Check if this node is already in the map
Node* dst_node = copy_map_.value(src_node);
// If not, create it now
if (!dst_node) {
dst_node = src_node->copy();
if (dst_node->IsTrack()) {
// Hack that ensures the track type is set since we don't bother copying the whole timeline
static_cast<TrackOutput*>(dst_node)->set_track_type(static_cast<TrackOutput*>(src_node)->track_type());
}
copy_map_.insert(src_node, dst_node);
}
// Make sure its values are copied
Node::CopyInputs(src_node, dst_node, false);
// Copy all connections
QVector<NodeInput*> src_node_inputs = src_node->GetInputsIncludingArrays();
QVector<NodeInput*> dst_node_inputs = dst_node->GetInputsIncludingArrays();
for (int i=0;i<src_node_inputs.size();i++) {
NodeInput* src_input = src_node_inputs.at(i);
CopyNodeMakeConnection(src_input, dst_node_inputs.at(i));
}
return dst_node;
graph_update_queue_.append({QueuedJob::kNodeRemoved, node, nullptr, -1});
}
void PreviewAutoCacher::CopyNodeMakeConnection(NodeInput* src_input, NodeInput* dst_input)
void PreviewAutoCacher::EdgeAdded(Node *output, int element)
{
if (src_input->is_connected()) {
Node* dst_node = CopyNodeConnections(src_input->get_connected_node());
graph_update_queue_.append({QueuedJob::kEdgeAdded, output, static_cast<NodeInput*>(sender()), element});
}
NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id());
void PreviewAutoCacher::EdgeRemoved(Node *output, int element)
{
graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, static_cast<NodeInput*>(sender()), element});
}
NodeParam::ConnectEdge(corresponding_output,
dst_input);
}
void PreviewAutoCacher::ValueChanged(const TimeRange &range, int element)
{
Q_UNUSED(range)
graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, static_cast<NodeInput*>(sender()), element});
}
void PreviewAutoCacher::TryRender()
@@ -701,12 +640,13 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
video_params_changed_ = false;
audio_params_changed_ = false;
// Disconnect signal (will be a no-op if the signal was never connected)
disconnect(viewer_node_,
&ViewerOutput::GraphChangedFrom,
this,
&PreviewAutoCacher::NodeGraphChanged);
// Disconnect signals for future node additions/deletions
NodeGraph* graph = viewer_node_->parent();
connect(graph, &NodeGraph::NodeAdded, this, &PreviewAutoCacher::NodeAdded);
connect(graph, &NodeGraph::NodeRemoved, this, &PreviewAutoCacher::NodeRemoved);
// Disconnect signal (will be a no-op if the signal was never connected)
disconnect(viewer_node_,
&ViewerOutput::VideoParamsChanged,
this,
@@ -732,28 +672,40 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
if (viewer_node_) {
// Copy graph
copied_viewer_node_ = static_cast<ViewerOutput*>(viewer_node_->copy());
copy_map_.insert(viewer_node_, copied_viewer_node_);
NodeGraph* graph = viewer_node_->parent();
// Add all nodes
foreach (Node* node, graph->nodes()) {
AddNode(node);
}
// Find copied viewer node
copied_viewer_node_ = static_cast<ViewerOutput*>(copy_map_.value(viewer_node_));
// Copy parameters
copied_viewer_node_->set_video_params(viewer_node_->video_params());
copied_viewer_node_->set_audio_params(viewer_node_->audio_params());
// We begin an operation and never end it which prevents the copy from unnecessarily
// invalidating its own cache
copied_viewer_node_->BeginOperation();
// 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.value(), input, it.key());
}
}
}
NodeGraphChanged(viewer_node_->texture_input());
NodeGraphChanged(viewer_node_->samples_input());
ProcessUpdateQueue();
// Connect signals for future node additions/deletions
connect(graph, &NodeGraph::NodeAdded, this, &PreviewAutoCacher::NodeAdded);
connect(graph, &NodeGraph::NodeRemoved, this, &PreviewAutoCacher::NodeRemoved);
// Copy invalidated ranges - used to determine which frames need hashing
invalidated_video_ = viewer_node_->video_frame_cache()->GetInvalidatedRanges();
invalidated_audio_ = viewer_node_->audio_playback_cache()->GetInvalidatedRanges();
connect(viewer_node_,
&ViewerOutput::GraphChangedFrom,
this,
&PreviewAutoCacher::NodeGraphChanged);
// We begin an operation and never end it which prevents the copy from unnecessarily
// invalidating its own cache
copied_viewer_node_->BeginOperation();
connect(viewer_node_,
&ViewerOutput::VideoParamsChanged,
+35 -17
View File
@@ -4,6 +4,7 @@
#include <QtConcurrent/QtConcurrent>
#include "config/config.h"
#include "node/graph.h"
#include "node/node.h"
#include "node/output/viewer/viewer.h"
#include "render/colormanager.h"
@@ -93,15 +94,19 @@ public slots:
/**
* @brief Main handler for when the NodeGraph changes
*/
void NodeGraphChanged(NodeInput *source);
void NodeAdded(Node* node);
void NodeRemoved(Node* node);
void EdgeAdded(Node* output, int element);
void EdgeRemoved(Node* output, int element);
void ValueChanged(const TimeRange& range, int element);
private:
static void GenerateHashes(ViewerOutput* viewer, FrameHashCache *cache, const QVector<rational>& times, qint64 job_time);
void CopyNodeInputValue(NodeInput* input);
Node *CopyNodeConnections(Node *src_node);
void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input);
void TryRender();
/**
@@ -114,12 +119,34 @@ private:
bool HasActiveJobs() const;
QList<NodeInput*> graph_update_queue_;
QHash<Node*, Node*> copy_map_;
ViewerOutput* copied_viewer_node_;
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);
class QueuedJob {
public:
enum Type {
kNodeAdded,
kNodeRemoved,
kEdgeAdded,
kEdgeRemoved,
kValueChanged
};
Type type;
Node* node;
NodeInput* input;
int element;
};
ViewerOutput* viewer_node_;
QVector<QueuedJob> graph_update_queue_;
QHash<Node*, Node*> copy_map_;
ViewerOutput* copied_viewer_node_;
bool paused_;
TimeRange cache_range_;
@@ -184,15 +211,6 @@ private slots:
*/
void VideoDownloaded();
/**
* @brief Handler for when a NodeInput has been deleted so we clear it from the queue
*
* FIXME: This is hacky. It also might not be necessary anymore with recent changes to the
* node system, but I haven't tested yet. Either way, PreviewAutoCacher should probably
* be able to pick up on these sorts of things without such a slot.
*/
void QueuedInputRemoved();
void VideoParamsChanged();
void AudioParamsChanged();
+5 -5
View File
@@ -236,8 +236,8 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(source), NodeParam::kTexture));
job.InsertValue(QStringLiteral("ove_mvpmat"), ShaderValue(matrix, NodeParam::kMatrix));
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
AlphaAssociated associated;
if (source->channel_count() == VideoParams::kRGBAChannelCount) {
@@ -252,14 +252,14 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
// No assoc/deassoc required
associated = kAlphaNone;
}
job.InsertValue(QStringLiteral("ove_maintex_alpha"), ShaderValue(associated, NodeParam::kInt));
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, associated));
foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) {
job.InsertValue(l.name, ShaderValue(QVariant::fromValue(l.texture), NodeParam::kTexture));
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT& l, color_ctx.lut1d_textures) {
job.InsertValue(l.name, ShaderValue(QVariant::fromValue(l.texture), NodeParam::kTexture));
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
+11 -11
View File
@@ -61,7 +61,7 @@ void RenderProcessor::Run()
NodeValueTable table = ProcessInput(viewer->texture_input(),
TimeRange(time, time + video_params.time_base()));
TexturePtr texture = table.Get(NodeParam::kTexture).value<TexturePtr>();
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
// Set up output frame parameters
VideoParams frame_params = ticket_->property("vparam").value<VideoParams>();
@@ -112,8 +112,8 @@ void RenderProcessor::Run()
} else {
// No color transform, just blit
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), {QVariant::fromValue(texture), NodeParam::kTexture});
job.InsertValue(QStringLiteral("ove_mvpmat"), {matrix, NodeParam::kMatrix});
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture)));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get());
}
@@ -135,7 +135,7 @@ void RenderProcessor::Run()
NodeValueTable table = ProcessInput(viewer->samples_input(), time);
ticket_->Finish(table.Get(NodeParam::kSamples), IsCancelled());
ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled());
break;
}
case RenderManager::kTypeVideoDownload:
@@ -211,7 +211,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con
// Destination buffer
NodeValueTable table = GenerateTable(b, range_for_block);
SampleBufferPtr samples_from_this_block = table.Take(NodeParam::kSamples).value<SampleBufferPtr>();
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value<SampleBufferPtr>();
if (!samples_from_this_block) {
// If we retrieved no samples from this block, do nothing
@@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con
}
// FIXME: Doesn't handle reversing
if (b->speed_input()->is_keyframing() || b->speed_input()->is_connected()) {
if (b->speed_input()->IsKeyframing() || b->speed_input()->IsConnected()) {
// FIXME: We'll need to calculate the speed hoo boy
} else {
double speed_value = b->speed_input()->get_standard_value().toDouble();
double speed_value = b->speed_input()->GetStandardValue().toDouble();
if (qIsNull(speed_value)) {
// Just silence, don't think there's any other practical application of 0 speed audio
@@ -253,7 +253,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con
ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
}
merged_table.Push(NodeParam::kSamples, QVariant::fromValue(block_range_buffer), track);
merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track);
return merged_table;
@@ -409,8 +409,8 @@ QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range
bool input_textures_have_alpha = false;
for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) {
if (it.value().type == NodeParam::kTexture) {
TexturePtr tex = it.value().data.value<TexturePtr>();
if (it.value().type() == NodeValue::kTexture) {
TexturePtr tex = it.value().data().value<TexturePtr>();
if (tex && tex->channel_count() == VideoParams::kRGBAChannelCount) {
input_textures_have_alpha = true;
break;
@@ -458,7 +458,7 @@ QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &rang
if (corresponding_input) {
value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time));
} else {
value.Push(j.value(), node);
value.Push(j.value());
}
value_db.Insert(j.key(), value);
-55
View File
@@ -1,55 +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 SHADERVALUE_H
#define SHADERVALUE_H
#include "node/param.h"
namespace olive {
struct ShaderValue
{
ShaderValue()
{
type = NodeParam::kNone;
array = false;
}
ShaderValue(QVariant data_in, NodeParam::DataType type_in, bool array_in = false)
{
data = data_in;
type = type_in;
array = array_in;
}
NodeParam::DataType type;
QVariant data;
bool array;
QString tag;
};
using NodeValueMap = QHash<QString, ShaderValue>;
}
#endif // SHADERVALUE_H
+1 -1
View File
@@ -34,7 +34,7 @@ PreCacheTask::PreCacheTask(VideoStream *footage, Sequence* sequence) :
video_node_ = new MediaInput();
video_node_->SetStream(footage);
NodeParam::ConnectEdge(video_node_->output(), viewer()->texture_input());
Node::ConnectEdge(video_node_, viewer()->texture_input());
SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(),
QString::number(footage->index())));
+1 -1
View File
@@ -49,7 +49,7 @@ private:
};
uint qHash(const TrackReference& r, uint seed);
uint qHash(const TrackReference& r, uint seed = 0);
}
+1 -1
View File
@@ -29,9 +29,9 @@ add_subdirectory(keyframeview)
add_subdirectory(manageddisplay)
add_subdirectory(menu)
add_subdirectory(nodecombobox)
add_subdirectory(nodeparamview)
add_subdirectory(nodetableview)
add_subdirectory(nodetreeview)
add_subdirectory(nodeparamview)
add_subdirectory(nodeview)
add_subdirectory(panel)
add_subdirectory(path)
@@ -29,7 +29,7 @@
namespace olive {
BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
BezierControlPointItem::BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
QGraphicsRectItem(parent),
key_(key),
mode_(mode),
@@ -38,12 +38,12 @@ BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe
{
setFlag(QGraphicsItem::ItemIsMovable);
connect(key.get(), &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
connect(key, &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
if (mode_ == NodeKeyframe::kInHandle) {
connect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
connect(key, &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
} else {
connect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
connect(key, &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
}
@@ -64,7 +64,7 @@ void BezierControlPointItem::SetYScale(double scale)
UpdatePos();
}
NodeKeyframePtr BezierControlPointItem::key() const
NodeKeyframe* BezierControlPointItem::key() const
{
return key_;
}
@@ -30,13 +30,13 @@ namespace olive {
class BezierControlPointItem : public QObject, public QGraphicsRectItem
{
public:
BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
void SetXScale(double scale);
void SetYScale(double scale);
NodeKeyframePtr key() const;
NodeKeyframe* key() const;
const NodeKeyframe::BezierType& mode() const;
@@ -50,7 +50,7 @@ protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
NodeKeyframePtr key_;
NodeKeyframe* key_;
NodeKeyframe::BezierType mode_;
+55 -31
View File
@@ -20,8 +20,9 @@
#include "curveview.h"
#include <QScrollBar>
#include <QHash>
#include <QMouseEvent>
#include <QScrollBar>
#include <QtMath>
#include <cfloat>
@@ -67,16 +68,9 @@ void CurveView::ConnectInput(NodeInput *input)
return;
}
// Add keyframes from this input
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
foreach (NodeKeyframePtr key, track) {
this->AddKeyframe(key);
}
if (!keyframe_colors_.contains(&track)) {
// Generate a random color for this input
keyframe_colors_.insert(&track, QColor::fromHsv(std::rand()%360, std::rand()%255, 255));
}
// Add keyframes from each subelement including primary (-1)
for (int i=-1; i<input->ArraySize(); i++) {
ConnectInputElement(input, i);
}
// Append to the list
@@ -89,9 +83,7 @@ void CurveView::ConnectInput(NodeInput *input)
void CurveView::DisconnectNode(Node *node)
{
QVector<NodeInput*> inputs = node->GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
foreach (NodeInput* i, node->parameters()) {
DisconnectInput(i);
}
}
@@ -166,24 +158,30 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
// Draw keyframe lines
foreach (NodeInput* input, connected_inputs_) {
if (input->is_keyframing()) {
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
for (int j=-1; j<input->ArraySize(); j++) {
if (input->IsKeyframing(j)) {
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(j);
for (int k=0; k<tracks.size(); k++) {
const NodeKeyframeTrack& track = tracks.at(k);
if (!track.isEmpty()) {
painter->setPen(QPen(keyframe_colors_.value(&track), qMax(1, fontMetrics().height() / 4)));
painter->setPen(QPen(keyframe_colors_.value(GetKeyframeTrackUniqueID(input, j, k)),
qMax(1, fontMetrics().height() / 4)));
QVector<QLineF> keyframe_lines;
// Draw straight line leading to first keyframe
QPointF first_key_pos = item_map().value(track.first().get())->pos();
QPointF first_key_pos = item_map().value(track.first())->pos();
keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos));
// Draw lines between each keyframe
for (int i=1;i<track.size();i++) {
NodeKeyframePtr before = track.at(i-1);
NodeKeyframePtr after = track.at(i);
NodeKeyframe* before = track.at(i-1);
NodeKeyframe* after = track.at(i);
KeyframeViewItem* before_item = item_map().value(before.get());
KeyframeViewItem* after_item = item_map().value(after.get());
KeyframeViewItem* before_item = item_map().value(before);
KeyframeViewItem* after_item = item_map().value(after);
if (before->type() == NodeKeyframe::kHold) {
// Draw a hold keyframe (basically a right angle)
@@ -238,7 +236,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
}
// Draw straight line leading from end keyframe
QPointF last_key_pos = item_map().value(track.last().get())->pos();
QPointF last_key_pos = item_map().value(track.last())->pos();
keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y())));
painter->drawLines(keyframe_lines);
@@ -246,6 +244,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
}
}
}
}
// Draw bezier control point lines
if (!bezier_control_points_.isEmpty()) {
@@ -284,7 +283,7 @@ void CurveView::VerticalScaleChangedEvent(double scale)
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator iterator;
for (iterator=item_map().begin();iterator!=item_map().end();iterator++) {
SetItemYFromKeyframeValue(iterator.value()->key().get(), iterator.value());
SetItemYFromKeyframeValue(iterator.value()->key(), iterator.value());
}
}
@@ -306,6 +305,26 @@ void CurveView::ContextMenuEvent(Menu &m)
//QAction* reset_zoom_action = m.addAction(tr("Reset Zoom"));
}
void CurveView::ConnectInputElement(NodeInput *input, int element)
{
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(element);
for (int i=0; i<tracks.size(); i++) {
const NodeKeyframeTrack& track = tracks.at(i);
foreach (NodeKeyframe* key, track) {
this->AddKeyframe(key);
}
uint h = GetKeyframeTrackUniqueID(input, element, i);
if (!keyframe_colors_.contains(h)) {
// Generate a random color for this input
keyframe_colors_.insert(h, QColor::fromHsv(std::rand()%360, std::rand()%255, 255));
}
}
}
qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key)
{
return GetItemYFromKeyframeValue(key->value().toDouble());
@@ -340,6 +359,11 @@ void CurveView::CreateBezierControlPoints(KeyframeViewItem* item)
connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
}
uint CurveView::GetKeyframeTrackUniqueID(NodeInput *input, int element, int track)
{
return ::qHash(input) ^ ::qHash(element) ^ ::qHash(track);
}
void CurveView::KeyframeValueChanged()
{
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
@@ -398,10 +422,10 @@ void CurveView::ZoomToFit()
double max_val = DBL_MIN;
for (auto i=item_map().constBegin(); i!=item_map().constEnd(); i++) {
rational transformed_time = GetAdjustedTime(i.key()->parent()->parentNode(),
rational transformed_time = GetAdjustedTime(i.key()->parent()->parent(),
GetTimeTarget(),
i.key()->time(),
NodeParam::kOutput);
false);
min_time = qMin(transformed_time, min_time);
max_time = qMax(transformed_time, max_time);
@@ -421,14 +445,14 @@ void CurveView::ZoomToFit()
verticalScrollBar()->setValue(GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height()));
}
void CurveView::AddKeyframe(NodeKeyframePtr key)
void CurveView::AddKeyframe(NodeKeyframe* key)
{
KeyframeViewItem* item = AddKeyframeInternal(key);
SetItemYFromKeyframeValue(key.get(), item);
item->SetOverrideBrush(keyframe_colors_.value(&key->parent()->keyframe_tracks().at(key->track())));
SetItemYFromKeyframeValue(key, item);
item->SetOverrideBrush(keyframe_colors_.value(GetKeyframeTrackUniqueID(key->parent(), key->element(), key->track())));
connect(key.get(), &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
connect(key.get(), &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
}
}
+6 -2
View File
@@ -45,7 +45,7 @@ public:
void DisconnectInput(NodeInput* input);
public slots:
void AddKeyframe(NodeKeyframePtr key);
void AddKeyframe(NodeKeyframe* key);
void ZoomToFit();
@@ -63,6 +63,8 @@ protected:
virtual void ContextMenuEvent(Menu &m) override;
private:
void ConnectInputElement(NodeInput* input, int element);
qreal GetItemYFromKeyframeValue(NodeKeyframe* key);
qreal GetItemYFromKeyframeValue(double value);
@@ -74,7 +76,9 @@ private:
void CreateBezierControlPoints(KeyframeViewItem *item);
QMap<const NodeInput::KeyframeTrack*, QColor> keyframe_colors_;
uint GetKeyframeTrackUniqueID(NodeInput* input, int element, int track);
QMap<uint, QColor> keyframe_colors_;
int text_padding_;
+1 -3
View File
@@ -215,9 +215,7 @@ void CurveWidget::UpdateBridgeTime(const int64_t &timestamp)
void CurveWidget::ConnectNode(Node *n)
{
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
foreach (NodeInput* i, n->parameters()) {
if (tree_view_->IsInputEnabled(i)) {
view_->ConnectInput(i);
}
+1 -1
View File
@@ -42,7 +42,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
rect.setHeight(max_scroll_);
}
void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
void KeyframeView::AddKeyframe(NodeKeyframe* key, int y)
{
QPoint global_pt(0, y);
QPoint local_pt = mapFromGlobal(global_pt);
+1 -1
View File
@@ -42,7 +42,7 @@ protected:
virtual void SceneRectUpdateEvent(QRectF& rect) override;
public slots:
void AddKeyframe(NodeKeyframePtr key, int y);
void AddKeyframe(NodeKeyframe* key, int y);
private:
int max_scroll_;
+22 -26
View File
@@ -63,11 +63,7 @@ void KeyframeViewBase::DeleteSelected()
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
if (i.value()->isSelected()) {
NodeInput* input_parent = i.key()->parent();
new NodeParamRemoveKeyframeCommand(input_parent,
input_parent->get_keyframe_shared_ptr_from_raw(i.key()),
command);
new NodeParamRemoveKeyframeCommand(i.key(), command);
}
}
@@ -76,38 +72,38 @@ void KeyframeViewBase::DeleteSelected()
void KeyframeViewBase::RemoveKeyframesOfNode(Node *n)
{
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
foreach (NodeInput* i, n->inputs()) {
RemoveKeyframesOfInput(i);
}
}
void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *i)
void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *input)
{
foreach (const NodeInput::KeyframeTrack& track, i->keyframe_tracks()) {
foreach (NodeKeyframePtr key, track) {
for (int i=-1; i<input->ArraySize(); i++) {
foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i)) {
foreach (NodeKeyframe* key, track) {
RemoveKeyframe(key);
}
}
}
}
void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key)
void KeyframeViewBase::RemoveKeyframe(NodeKeyframe* key)
{
KeyframeAboutToBeRemoved(key.get());
KeyframeAboutToBeRemoved(key);
delete item_map_.take(key.get());
delete item_map_.take(key);
}
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframePtr key)
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframe* key)
{
KeyframeViewItem* item = item_map_.value(key.get());
KeyframeViewItem* item = item_map_.value(key);
if (!item) {
item = new KeyframeViewItem(key);
item->SetTimeTarget(GetTimeTarget());
item->SetScale(GetScale());
item_map_.insert(key.get(), item);
item_map_.insert(key, item);
scene()->addItem(item);
}
@@ -151,7 +147,7 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event)
selected_keys_.replace(i, {key,
key->x(),
GetAdjustedTime(key->key()->parent()->parentNode(), GetTimeTarget(), key->key()->time(), NodeParam::kOutput),
GetAdjustedTime(key->key()->parent()->parent(), GetTimeTarget(), key->key()->time(), false),
key->key()->value().toDouble()});
}
}
@@ -184,9 +180,9 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
//input_parent->blockSignals(true);
rational node_time = GetAdjustedTime(GetTimeTarget(),
keypair.key->key()->parent()->parentNode(),
keypair.key->key()->parent()->parent(),
CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()),
NodeParam::kInput);
true);
keypair.key->key()->set_time(node_time);
@@ -235,9 +231,9 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event)
// Calculate the new time for this keyframe
rational node_time = GetAdjustedTime(GetTimeTarget(),
keypair.key->key()->parent()->parentNode(),
keypair.key->key()->parent()->parent(),
CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()),
NodeParam::kInput);
true);
@@ -493,7 +489,7 @@ void KeyframeViewBase::ShowContextMenu()
void KeyframeViewBase::ShowKeyframePropertiesDialog()
{
QList<QGraphicsItem*> items = scene()->selectedItems();
QList<NodeKeyframePtr> keys;
QVector<NodeKeyframe*> keys;
foreach (QGraphicsItem* item, items) {
keys.append(static_cast<KeyframeViewItem*>(item)->key());
@@ -521,15 +517,15 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors()
rational key_time = key_item->key()->time();
QList<NodeKeyframePtr> keys = key_item->key()->parent()->get_keyframe_at_time(key_time);
QVector<NodeKeyframe*> keys = key_item->key()->parent()->GetKeyframesAtTime(key_time, key_item->key()->element());
foreach (NodeKeyframePtr k, keys) {
foreach (NodeKeyframe* k, keys) {
if (k == key_item->key()) {
continue;
}
// Ensure this key is not already selected
KeyframeViewItem* item = item_map_.value(k.get());
KeyframeViewItem* item = item_map_.value(k);
item->setSelected(true);
}
+3 -3
View File
@@ -42,13 +42,13 @@ public:
void RemoveKeyframesOfNode(Node* n);
void RemoveKeyframesOfInput(NodeInput* i);
void RemoveKeyframesOfInput(NodeInput* input);
public slots:
void RemoveKeyframe(NodeKeyframePtr key);
void RemoveKeyframe(NodeKeyframe* key);
protected:
virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframePtr key);
virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframe* key);
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
+5 -5
View File
@@ -30,7 +30,7 @@
namespace olive {
KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
KeyframeViewItem::KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent) :
QGraphicsRectItem(parent),
key_(key),
scale_(1.0),
@@ -39,8 +39,8 @@ KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
{
setFlag(QGraphicsItem::ItemIsSelectable);
connect(key.get(), &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos);
connect(key.get(), &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw);
connect(key, &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos);
connect(key, &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw);
int keyframe_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "Oi");
int half_sz = keyframe_size/2;
@@ -69,7 +69,7 @@ void KeyframeViewItem::SetOverrideBrush(const QBrush &b)
setBrush(b);
}
NodeKeyframePtr KeyframeViewItem::key() const
NodeKeyframe* KeyframeViewItem::key() const
{
return key_;
}
@@ -117,7 +117,7 @@ void KeyframeViewItem::TimeTargetChangedEvent(Node *)
void KeyframeViewItem::UpdatePos()
{
rational adjusted = GetAdjustedTime(key_->parent()->parentNode(), GetTimeTarget(), key_->time(), NodeParam::kOutput);
rational adjusted = GetAdjustedTime(key_->parent()->parent(), GetTimeTarget(), key_->time(), false);
setPos(adjusted.toDouble() * scale_, vert_center_);
}
+3 -3
View File
@@ -32,7 +32,7 @@ class KeyframeViewItem : public QObject, public QGraphicsRectItem, public TimeTa
{
Q_OBJECT
public:
KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent = nullptr);
KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent = nullptr);
void SetOverrideY(qreal vertical_center);
@@ -40,7 +40,7 @@ public:
void SetOverrideBrush(const QBrush& b);
NodeKeyframePtr key() const;
NodeKeyframe* key() const;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
@@ -48,7 +48,7 @@ protected:
virtual void TimeTargetChangedEvent(Node* ) override;
private:
NodeKeyframePtr key_;
NodeKeyframe* key_;
double scale_;

Some files were not shown because too many files have changed in this diff Show More