implemented node groups

This commit is contained in:
itsmattkc
2021-11-24 17:58:48 -08:00
parent a94c7169f2
commit 495b07fc55
29 changed files with 947 additions and 203 deletions
+1 -2
View File
@@ -20,6 +20,7 @@ add_subdirectory(color)
add_subdirectory(distort)
add_subdirectory(filter)
add_subdirectory(generator)
add_subdirectory(group)
add_subdirectory(input)
add_subdirectory(math)
add_subdirectory(output)
@@ -34,8 +35,6 @@ set(OLIVE_SOURCES
node/globals.h
node/graph.cpp
node/graph.h
node/group.cpp
node/group.h
node/hashtraverser.cpp
node/hashtraverser.h
node/inputdragger.cpp
-30
View File
@@ -1,30 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "group.h"
namespace olive {
NodeGroup::NodeGroup()
{
}
}
-43
View File
@@ -1,43 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 NODEGROUP_H
#define NODEGROUP_H
#include "node.h"
namespace olive {
class NodeGroup : public Node
{
Q_OBJECT
public:
NodeGroup();
void SetNodes(Node *nodes);
private:
QVector<Node*> nodes_;
};
}
#endif // NODEGROUP_H
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/group/group.cpp
node/group/group.h
PARENT_SCOPE
)
+177
View File
@@ -0,0 +1,177 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "group.h"
#include "node/graph.h"
namespace olive {
NodeGroup::NodeGroup() :
output_passthrough_(nullptr)
{
graph_ = new NodeGraph();
graph_->setParent(this);
}
QString NodeGroup::Name() const
{
if (custom_name_.isEmpty()) {
return tr("Group");
} else {
return custom_name_;
}
}
QString NodeGroup::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.group");
}
QVector<Node::CategoryID> NodeGroup::Category() const
{
return {kCategoryGeneral};
}
QString NodeGroup::Description() const
{
return tr("A group of nodes that is represented as a single node.");
}
void NodeGroup::Retranslate()
{
foreach (Node *n, graph_->nodes()) {
n->Retranslate();
}
}
void NodeGroup::AddNode(Node *node)
{
node->setParent(graph_);
}
void NodeGroup::RemoveNode(Node *node, QObject *new_parent)
{
if (node->parent() == graph_) {
node->setParent(new_parent);
}
}
void NodeGroup::AddInputPassthrough(const NodeInput &input)
{
Q_ASSERT(graph_->nodes().contains(input.node()));
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
// Already passing this input through
return;
}
}
// Add input
QString id = GetGroupInputIDFromInput(input);
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
input_passthroughs_.insert(id, input);
}
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
RemoveInput(it.key());
input_passthroughs_.erase(it);
break;
}
}
}
void NodeGroup::SetOutputPassthrough(Node *node)
{
Q_ASSERT(graph_->nodes().contains(node));
output_passthrough_ = node;
}
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(input.node()->GetUUID().toByteArray());
hash.addData(input.input().toUtf8());
hash.addData((const char*) &input.element(), sizeof(input.element()));
return QString::fromLatin1(hash.result().toHex());
}
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
return true;
}
}
return false;
}
void NodeAddToGroupCommand::redo()
{
previous_parent_ = node_->parent();
group_->AddNode(node_);
}
void NodeAddToGroupCommand::undo()
{
group_->RemoveNode(node_, previous_parent_);
}
void NodeGroupSetCustomNameCommand::redo()
{
old_name_ = group_->GetCustomName();
group_->SetCustomName(new_name_);
}
void NodeGroupSetCustomNameCommand::undo()
{
group_->SetCustomName(old_name_);
}
void NodeGroupAddInputPassthrough::redo()
{
if (!group_->ContainsInputPassthrough(input_)) {
group_->AddInputPassthrough(input_);
actually_added_ = true;
} else {
actually_added_ = false;
}
}
void NodeGroupAddInputPassthrough::undo()
{
if (actually_added_) {
group_->RemoveInputPassthrough(input_);
}
}
}
+176
View File
@@ -0,0 +1,176 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 NODEGROUP_H
#define NODEGROUP_H
#include "node/node.h"
namespace olive {
class NodeGroup : public Node
{
Q_OBJECT
public:
NodeGroup();
NODE_DEFAULT_DESTRUCTOR(NodeGroup)
NODE_COPY_FUNCTION(NodeGroup)
virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
virtual void Retranslate() override;
void AddNode(Node *node);
void RemoveNode(Node *node, QObject *new_parent = nullptr);
void AddInputPassthrough(const NodeInput &input);
void RemoveInputPassthrough(const NodeInput &input);
void SetOutputPassthrough(Node *node);
const QString &GetCustomName() const
{
return custom_name_;
}
void SetCustomName(const QString &name)
{
custom_name_ = name;
// NOTE: Not technically the right signal, but should achieve the right goal
emit LabelChanged(custom_name_);
}
void ClearCustomName()
{
custom_name_.clear();
}
static QString GetGroupInputIDFromInput(const NodeInput &input);
const QHash<QString, NodeInput> &GetInputPassthroughs() const
{
return input_passthroughs_;
}
bool ContainsInputPassthrough(const NodeInput &input) const;
private:
NodeGraph *graph_;
QHash<QString, NodeInput> input_passthroughs_;
Node *output_passthrough_;
QString custom_name_;
};
class NodeAddToGroupCommand : public UndoCommand
{
public:
NodeAddToGroupCommand(Node *node, NodeGroup *group) :
node_(node),
group_(group)
{}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node *node_;
NodeGroup *group_;
QObject *previous_parent_;
};
class NodeGroupSetCustomNameCommand : public UndoCommand
{
public:
NodeGroupSetCustomNameCommand(NodeGroup *group, const QString &name) :
group_(group),
new_name_(name)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
QString old_name_;
QString new_name_;
};
class NodeGroupAddInputPassthrough : public UndoCommand
{
public:
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) :
group_(group),
input_(input),
actually_added_(false)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
NodeInput input_;
bool actually_added_;
};
}
#endif // NODEGROUP_H
+16 -3
View File
@@ -49,6 +49,7 @@ Node::Node() :
operation_stack_(0),
cache_result_(false)
{
uuid_ = QUuid::createUuid();
}
Node::~Node()
@@ -88,6 +89,8 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
} else if (reader->name() == QStringLiteral("label")) {
SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
SetUUID(QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
override_color_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("links")) {
@@ -169,6 +172,7 @@ void Node::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
writer->writeTextElement(QStringLiteral("label"), GetLabel());
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
@@ -219,7 +223,16 @@ void Node::Save(QXmlStreamWriter *writer) const
Project* Node::project() const
{
return dynamic_cast<Project*>(parent());
QObject *t = this->parent();
while (t) {
if (Project *p = dynamic_cast<Project*>(t)) {
return p;
}
t = t->parent();
}
return nullptr;
}
QString Node::ShortName() const
@@ -1089,7 +1102,7 @@ NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const
return nullptr;
}
Node::InputFlags Node::GetInputFlags(const QString &input) const
InputFlags Node::GetInputFlags(const QString &input) const
{
const Input* i = GetInternalInputData(input);
@@ -1343,7 +1356,7 @@ void Node::HashAddNodeSignature(QCryptographicHash &hash) const
hash.addData(id().toUtf8());
}
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags, int index)
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index)
{
if (id.isEmpty()) {
qWarning() << "Rejected adding input with an empty ID on node" << this->id();
+8 -33
View File
@@ -27,6 +27,7 @@
#include <QObject>
#include <QPainter>
#include <QPointF>
#include <QUuid>
#include <QXmlStreamWriter>
#include "codec/frame.h"
@@ -112,6 +113,9 @@ public:
Project* project() const;
const QUuid &GetUUID() const {return uuid_;}
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
/**
* @brief Clear current node variables and replace them with
*/
@@ -935,38 +939,9 @@ public:
};
InputFlags GetInputFlags(const QString& input) const;
protected:
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4,
kInputFlagHidden = 0x8
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const;
void HashAddNodeSignature(QCryptographicHash &hash) const;
@@ -1216,8 +1191,6 @@ private:
return input_ids_.indexOf(input);
}
InputFlags GetInputFlags(const QString& input) const;
Input* GetInternalInputData(const QString& input)
{
int i = GetInternalInputIndex(input);
@@ -1336,6 +1309,8 @@ private:
PositionMap context_positions_;
QUuid uuid_;
private slots:
/**
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
-2
View File
@@ -254,8 +254,6 @@ private:
AudioPlaybackCache audio_playback_cache_;
int operation_stack_;
VideoParams cached_video_params_;
AudioParams cached_audio_params_;
+18
View File
@@ -69,6 +69,15 @@ bool NodeInput::IsArray() const
}
}
InputFlags NodeInput::GetFlags() const
{
if (IsValid()) {
return node_->GetInputFlags(input_);
} else {
return InputFlags(kInputFlagNormal);
}
}
Node *NodeInput::GetConnectedOutput() const
{
if (IsValid()) {
@@ -87,6 +96,15 @@ NodeValue::Type NodeInput::GetDataType() const
}
}
QVariant NodeInput::GetDefaultValue() const
{
if (IsValid()) {
return node_->GetDefaultValue(input_);
} else {
return QVariant();
}
}
QStringList NodeInput::GetComboBoxStrings() const
{
if (IsValid()) {
+36 -1
View File
@@ -31,6 +31,37 @@ namespace olive {
class Node;
class NodeKeyframe;
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4,
kInputFlagHidden = 0x8
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
struct NodeInputPair {
bool operator==(const NodeInputPair& rhs) const
{
@@ -98,7 +129,7 @@ public:
return input_;
}
int element() const
const int &element() const
{
return element_;
}
@@ -123,10 +154,14 @@ public:
bool IsArray() const;
InputFlags GetFlags() const;
Node *GetConnectedOutput() const;
NodeValue::Type GetDataType() const;
QVariant GetDefaultValue() const;
QStringList GetComboBoxStrings() const;
QVariant GetProperty(const QString& key) const;