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:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
uint qHash(const TimeRange& r, uint seed);
|
||||
uint qHash(const TimeRange& r, uint seed = 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
// If node is not a block, this will just be null
|
||||
connected_out_block_ = dynamic_cast<Block*>(node);
|
||||
}
|
||||
|
||||
Block* block = static_cast<Block*>(edge->output()->parentNode());
|
||||
|
||||
if (edge->input() == out_block_input_) {
|
||||
connected_out_block_ = block;
|
||||
} else {
|
||||
connected_in_block_ = block;
|
||||
}
|
||||
}
|
||||
|
||||
void TransitionBlock::BlockDisconnected(NodeEdgePtr edge)
|
||||
void TransitionBlock::OutBlockDisconnected()
|
||||
{
|
||||
if (edge->input() == out_block_input_) {
|
||||
connected_out_block_ = nullptr;
|
||||
} else {
|
||||
connected_in_block_ = nullptr;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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()};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ private:
|
||||
|
||||
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
|
||||
|
||||
NodeInputArray* points_input_;
|
||||
NodeInput* points_input_;
|
||||
|
||||
NodeInput* color_input_;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+371
-767
File diff suppressed because it is too large
Load Diff
+289
-223
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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_));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+184
-366
@@ -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,63 +146,60 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
void Node::RemoveNodeAndDisconnect(Node *node, QUndoCommand *command)
|
||||
{
|
||||
// Disconnect everything
|
||||
foreach (const InputConnection& conn, node->output_connections()) {
|
||||
new NodeEdgeRemoveCommand(node, conn.input, conn.element, 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));
|
||||
foreach (NodeInput* input, node->inputs_) {
|
||||
for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) {
|
||||
new NodeEdgeRemoveCommand(it.value(), input, it.key(), command);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove node
|
||||
new NodeRemoveCommand(node, command);
|
||||
}
|
||||
|
||||
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,21 +268,17 @@ 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 &)
|
||||
@@ -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
|
||||
@@ -473,83 +454,18 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+63
-120
@@ -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,21 +456,6 @@ 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);
|
||||
|
||||
@@ -520,18 +464,17 @@ private:
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
|
||||
+116
-99
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
|
||||
{
|
||||
Block* b = static_cast<Block*>(edge->output_node());
|
||||
// Insert at index
|
||||
block_cache_.insert(cache_index, block);
|
||||
|
||||
if (block_cache_.contains(b)) {
|
||||
// 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(Node* node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InputConnectionChanged(node, element);
|
||||
return;
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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,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);
|
||||
@@ -242,7 +161,6 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge)
|
||||
|
||||
UpdateTotalLength();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackList::UpdateTrackIndexesFrom(int index)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+124
-172
@@ -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> ×, 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();
|
||||
graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1});
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
graph_update_queue_.append({QueuedJob::kNodeRemoved, node, nullptr, -1});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id());
|
||||
|
||||
NodeParam::ConnectEdge(corresponding_output,
|
||||
dst_input);
|
||||
graph_update_queue_.append({QueuedJob::kEdgeAdded, output, static_cast<NodeInput*>(sender()), element});
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::EdgeRemoved(Node *output, int element)
|
||||
{
|
||||
graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, static_cast<NodeInput*>(sender()), element});
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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())));
|
||||
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
uint qHash(const TrackReference& r, uint seed);
|
||||
uint qHash(const TrackReference& r, uint seed = 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -215,9 +215,7 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
KeyframeAboutToBeRemoved(key.get());
|
||||
|
||||
delete item_map_.take(key.get());
|
||||
}
|
||||
|
||||
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframePtr key)
|
||||
void KeyframeViewBase::RemoveKeyframe(NodeKeyframe* key)
|
||||
{
|
||||
KeyframeViewItem* item = item_map_.value(key.get());
|
||||
KeyframeAboutToBeRemoved(key);
|
||||
|
||||
delete item_map_.take(key);
|
||||
}
|
||||
|
||||
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframe* key)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user