refactored some classes

This commit is contained in:
itsmattkc
2019-05-08 00:41:44 +10:00
parent 127a773c54
commit 9a13776b2c
133 changed files with 1698 additions and 1627 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
#include "boolinput.h"
BoolInput::BoolInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
BoolInput::BoolInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
BoolField* bool_field = new BoolField(this);
connect(bool_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool)));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef BOOLINPUT_H
#define BOOLINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class BoolInput : public EffectRow
class BoolInput : public NodeIO
{
Q_OBJECT
public:
BoolInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
BoolInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
/**
* @brief Get the boolean value at a given timecode
+2 -2
View File
@@ -1,7 +1,7 @@
#include "colorinput.h"
ColorInput::ColorInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
ColorInput::ColorInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
AddField(new ColorField(this));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef COLORINPUT_H
#define COLORINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class ColorInput : public EffectRow
class ColorInput : public NodeIO
{
Q_OBJECT
public:
ColorInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
ColorInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
/**
* @brief Get the color value at a given timecode
+2 -2
View File
@@ -1,7 +1,7 @@
#include "comboinput.h"
ComboInput::ComboInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
ComboInput::ComboInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
ComboField* combo_field = new ComboField(this);
connect(combo_field, SIGNAL(DataChanged(const QVariant&)), this, SIGNAL(DataChanged(const QVariant&)));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef COMBOINPUT_H
#define COMBOINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class ComboInput : public EffectRow
class ComboInput : public NodeIO
{
Q_OBJECT
public:
ComboInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
ComboInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
/**
* @brief Add an item to this ComboInput
+2 -2
View File
@@ -1,7 +1,7 @@
#include "fileinput.h"
FileInput::FileInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
FileInput::FileInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
AddField(new FileField(this));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef FILEINPUT_H
#define FILEINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class FileInput : public EffectRow
class FileInput : public NodeIO
{
Q_OBJECT
public:
FileInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
FileInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
/**
* @brief Get the filename at the given timecode
+2 -2
View File
@@ -1,7 +1,7 @@
#include "fontinput.h"
FontInput::FontInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
FontInput::FontInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
AddField(new FontField(this));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef FONTINPUT_H
#define FONTINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class FontInput : public EffectRow
class FontInput : public NodeIO
{
Q_OBJECT
public:
FontInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
FontInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
/**
* @brief Get the font family name at the given timecode
+2 -2
View File
@@ -1,7 +1,7 @@
#include "stringinput.h"
StringInput::StringInput(Node* parent, const QString& id, const QString& name, bool rich_text, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable)
StringInput::StringInput(OldEffectNode* parent, const QString& id, const QString& name, bool rich_text, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable)
{
AddField(new StringField(this, rich_text));
+3 -3
View File
@@ -1,13 +1,13 @@
#ifndef STRINGINPUT_H
#define STRINGINPUT_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class StringInput : public EffectRow
class StringInput : public NodeIO
{
Q_OBJECT
public:
StringInput(Node* parent,
StringInput(OldEffectNode* parent,
const QString& id,
const QString& name,
bool rich_text = true,
+6 -6
View File
@@ -4,8 +4,8 @@
#include <QVector4D>
#include <QDebug>
VecInput::VecInput(Node* parent, const QString& id, const QString& name, int values, bool savable, bool keyframable) :
EffectRow(parent, id, name, savable, keyframable),
VecInput::VecInput(OldEffectNode* parent, const QString& id, const QString& name, int values, bool savable, bool keyframable) :
NodeIO(parent, id, name, savable, keyframable),
single_value_mode_(false),
values_(values)
{
@@ -79,7 +79,7 @@ void VecInput::SetSingleValueMode(bool on)
}
}
DoubleInput::DoubleInput(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
DoubleInput::DoubleInput(OldEffectNode *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
VecInput(parent, id, name, 1, savable, keyframable)
{
}
@@ -89,7 +89,7 @@ double DoubleInput::GetDoubleAt(double timecode)
return static_cast<DoubleField*>(Field(0))->GetDoubleAt(timecode);
}
Vec2Input::Vec2Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
Vec2Input::Vec2Input(OldEffectNode *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
VecInput(parent, id, name, 2, savable, keyframable)
{
}
@@ -121,7 +121,7 @@ void Vec2Input::SetValueAt(double timecode, const QVariant &value)
static_cast<DoubleField*>(Field(1))->SetValueAt(timecode, vec2.y());
}
Vec3Input::Vec3Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
Vec3Input::Vec3Input(OldEffectNode *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
VecInput(parent, id, name, 3, savable, keyframable)
{
}
@@ -156,7 +156,7 @@ void Vec3Input::SetValueAt(double timecode, const QVariant &value)
static_cast<DoubleField*>(Field(2))->SetValueAt(timecode, vec3.z());
}
Vec4Input::Vec4Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
Vec4Input::Vec4Input(OldEffectNode *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
VecInput(parent, id, name, 4, savable, keyframable)
{
}
+7 -7
View File
@@ -3,13 +3,13 @@
#include <QVector2D>
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class VecInput : public EffectRow
class VecInput : public NodeIO
{
Q_OBJECT
public:
VecInput(Node* parent, const QString& id, const QString& name, int values, bool savable = true, bool keyframable = true);
VecInput(OldEffectNode* parent, const QString& id, const QString& name, int values, bool savable = true, bool keyframable = true);
void SetMinimum(double minimum);
void SetMaximum(double maximum);
@@ -43,14 +43,14 @@ private:
class DoubleInput : public VecInput
{
public:
DoubleInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
DoubleInput(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
double GetDoubleAt(double timecode);
};
class Vec2Input : public VecInput {
public:
Vec2Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
Vec2Input(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
QVector2D GetVector2DAt(double timecode);
virtual QVariant GetValueAt(double timecode) override;
@@ -59,7 +59,7 @@ public:
class Vec3Input : public VecInput {
public:
Vec3Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
Vec3Input(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
QVector3D GetVector3DAt(double timecode);
virtual QVariant GetValueAt(double timecode) override;
@@ -68,7 +68,7 @@ public:
class Vec4Input : public VecInput {
public:
Vec4Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
Vec4Input(OldEffectNode* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
QVector4D GetVector4DAt(double timecode);
virtual QVariant GetValueAt(double timecode) override;
+6 -943
View File
@@ -1,943 +1,6 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 "node.h"
#include <QCheckBox>
#include <QGridLayout>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QOpenGLContext>
#include <QDir>
#include <QPainter>
#include <QtMath>
#include <QMenu>
#include <QApplication>
#include <QFileDialog>
#include "panels/panels.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "ui/collapsiblewidget.h"
#include "panels/project.h"
#include "undo/undo.h"
#include "timeline/sequence.h"
#include "timeline/clip.h"
#include "panels/timeline.h"
#include "panels/effectcontrols.h"
#include "panels/grapheditor.h"
#include "global/debug.h"
#include "global/path.h"
#include "ui/mainwindow.h"
#include "ui/menu.h"
#include "global/math.h"
#include "global/clipboard.h"
#include "global/config.h"
#include "effects/transition.h"
#include "undo/undostack.h"
#include "rendering/shadergenerators.h"
#include "global/timing.h"
#include "nodes/nodes.h"
#include "effects/effectloaders.h"
QVector<NodePtr> olive::node_library;
Node::Node(Clip* c) :
parent_clip(c),
flags_(0),
shader_program_(nullptr),
texture(0),
tex_width_(0),
tex_height_(0),
isOpen(false),
bound(false),
iterations(1),
enabled_(true),
expanded_(true),
texture_ctx(nullptr)
{
}
Node::~Node() {
if (isOpen) {
close();
}
// Clear graph editor if it's using one of these rows
if (panel_graph_editor != nullptr) {
for (int i=0;i<row_count();i++) {
if (row(i) == panel_graph_editor->get_row()) {
panel_graph_editor->set_row(nullptr);
break;
}
}
}
}
QString Node::category()
{
return QString();
}
bool Node::IsCreatable()
{
return true;
}
void Node::AddRow(EffectRow *row)
{
row->setParent(this);
rows.append(row);
}
int Node::IndexOfRow(EffectRow *row)
{
return rows.indexOf(row);
}
void Node::copy_field_keyframes(NodePtr e) {
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
EffectRow* copy_row = e->rows.at(i);
copy_row->SetKeyframingInternal(row->IsKeyframing());
for (int j=0;j<row->FieldCount();j++) {
// Get field from this (the source) effect
EffectField* field = row->Field(j);
// Get field from the destination effect
EffectField* copy_field = copy_row->Field(j);
// Copy keyframes between effects
copy_field->keyframes = field->keyframes;
// Copy persistet data between effects
copy_field->persistent_data_ = field->persistent_data_;
}
}
}
EffectRow* Node::row(int i) {
return rows.at(i);
}
int Node::row_count() {
return rows.size();
}
EffectGizmo *Node::add_gizmo(int type) {
EffectGizmo* gizmo = new EffectGizmo(this, type);
gizmos.append(gizmo);
return gizmo;
}
EffectGizmo *Node::gizmo(int i) {
return gizmos.at(i);
}
int Node::gizmo_count() {
return gizmos.size();
}
QVector<NodeEdgePtr> Node::GetAllEdges()
{
QVector<NodeEdgePtr> edges;
for (int i=0;i<row_count();i++) {
edges.append(row(i)->edges());
}
return edges;
}
void Node::refresh() {}
void Node::FieldChanged() {
// Update the UI if a field has been modified, but don't both if this effect is inactive
if (parent_clip != nullptr) {
update_ui(false);
}
}
void Node::delete_self() {
olive::undo_stack.push(new EffectDeleteCommand(this));
update_ui(true);
}
void Node::move_up() {
int index_of_effect = parent_clip->IndexOfEffect(this);
if (index_of_effect == 0) {
return;
}
MoveEffectCommand* command = new MoveEffectCommand();
command->clip = parent_clip;
command->from = index_of_effect;
command->to = command->from - 1;
olive::undo_stack.push(command);
panel_effect_controls->Reload();
panel_sequence_viewer->viewer_widget()->frame_update();
}
void Node::move_down() {
int index_of_effect = parent_clip->IndexOfEffect(this);
if (index_of_effect == parent_clip->effects.size()-1) {
return;
}
MoveEffectCommand* command = new MoveEffectCommand();
command->clip = parent_clip;
command->from = index_of_effect;
command->to = command->from + 1;
olive::undo_stack.push(command);
panel_effect_controls->Reload();
panel_sequence_viewer->viewer_widget()->frame_update();
}
void Node::save_to_file() {
// save effect settings to file
QString file = QFileDialog::getSaveFileName(olive::MainWindow,
tr("Save Effect Settings"),
QString(),
tr("Effect XML Settings %1").arg("(*.xml)"));
// if the user picked a file
if (!file.isEmpty()) {
// ensure file ends with .xml extension
if (!file.endsWith(".xml", Qt::CaseInsensitive)) {
file.append(".xml");
}
QFile file_handle(file);
if (file_handle.open(QFile::WriteOnly)) {
file_handle.write(save_to_string());
file_handle.close();
} else {
QMessageBox::critical(olive::MainWindow,
tr("Save Settings Failed"),
tr("Failed to open \"%1\" for writing.").arg(file),
QMessageBox::Ok);
}
}
}
void Node::load_from_file() {
// load effect settings from file
QString file = QFileDialog::getOpenFileName(olive::MainWindow,
tr("Load Effect Settings"),
QString(),
tr("Effect XML Settings %1").arg("(*.xml)"));
// if the user picked a file
if (!file.isEmpty()) {
QFile file_handle(file);
if (file_handle.open(QFile::ReadOnly)) {
olive::undo_stack.push(new SetEffectData(this, file_handle.readAll()));
file_handle.close();
update_ui(false);
} else {
QMessageBox::critical(olive::MainWindow,
tr("Load Settings Failed"),
tr("Failed to open \"%1\" for reading.").arg(file),
QMessageBox::Ok);
}
}
}
bool Node::AlwaysUpdate()
{
return false;
}
bool Node::IsEnabled() {
return enabled_;
}
bool Node::IsExpanded()
{
return expanded_;
}
void Node::SetExpanded(bool e)
{
expanded_ = e;
}
void Node::SetPos(const QPointF &pos)
{
pos_ = pos;
}
void Node::SetEnabled(bool b) {
enabled_ = b;
emit EnabledChanged(b);
}
void Node::load(QXmlStreamReader& stream) {
/*
int row_count = 0;
QString tag = stream.name().toString();
while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) {
stream.readNext();
if (stream.name() == "row" && stream.isStartElement()) {
if (row_count < rows.size()) {
EffectRow* row = rows.at(row_count);
while (!stream.atEnd() && !(stream.name() == "row" && stream.isEndElement())) {
stream.readNext();
// read field
if (stream.name() == "field" && stream.isStartElement()) {
int field_number = -1;
// match field using ID
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "id") {
for (int l=0;l<row->FieldCount();l++) {
if (row->Field(l)->id() == attr.value()) {
field_number = l;
break;
}
}
break;
}
}
if (field_number > -1) {
EffectField* field = row->Field(field_number);
// get current field value
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "value") {
field->persistent_data_ = field->ConvertStringToValue(attr.value().toString());
break;
}
}
while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) {
stream.readNext();
// read keyframes
if (stream.name() == "key" && stream.isStartElement()) {
row->SetKeyframingInternal(true);
EffectKeyframe key;
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "value") {
key.data = field->ConvertStringToValue(attr.value().toString());
} else if (attr.name() == "frame") {
key.time = attr.value().toLong();
} else if (attr.name() == "type") {
key.type = attr.value().toInt();
} else if (attr.name() == "prehx") {
key.pre_handle_x = attr.value().toDouble();
} else if (attr.name() == "prehy") {
key.pre_handle_y = attr.value().toDouble();
} else if (attr.name() == "posthx") {
key.post_handle_x = attr.value().toDouble();
} else if (attr.name() == "posthy") {
key.post_handle_y = attr.value().toDouble();
}
}
field->keyframes.append(key);
}
}
field->Changed();
}
}
}
} else {
qCritical() << "Too many rows for effect" << id << ". Project might be corrupt. (Got" << row_count << ", expected <" << rows.size()-1 << ")";
}
row_count++;
} else if (stream.isStartElement()) {
custom_load(stream);
}
}
*/
}
void Node::custom_load(QXmlStreamReader &) {}
void Node::save(QXmlStreamWriter& stream) {
/*
stream.writeAttribute("name", meta->category + "/" + meta->name);
stream.writeAttribute("enabled", QString::number(IsEnabled()));
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
if (row->IsSavable()) {
stream.writeStartElement("row"); // row
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
if (!field->id().isEmpty()) {
stream.writeStartElement("field"); // field
stream.writeAttribute("id", field->id());
stream.writeAttribute("value", field->ConvertValueToString(field->persistent_data_));
for (int k=0;k<field->keyframes.size();k++) {
const EffectKeyframe& key = field->keyframes.at(k);
stream.writeStartElement("key");
stream.writeAttribute("value", field->ConvertValueToString(key.data));
stream.writeAttribute("frame", QString::number(key.time));
stream.writeAttribute("type", QString::number(key.type));
stream.writeAttribute("prehx", QString::number(key.pre_handle_x));
stream.writeAttribute("prehy", QString::number(key.pre_handle_y));
stream.writeAttribute("posthx", QString::number(key.post_handle_x));
stream.writeAttribute("posthy", QString::number(key.post_handle_y));
stream.writeEndElement(); // key
}
stream.writeEndElement(); // field
}
}
stream.writeEndElement(); // row
}
}
*/
}
void Node::load_from_string(const QByteArray &s) {
// clear existing keyframe data
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
row->SetKeyframingInternal(false);
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
field->keyframes.clear();
}
}
// write settings with xml writer
QXmlStreamReader stream(s);
bool found_id = false;
while (!stream.atEnd()) {
stream.readNext();
// find the effect opening tag
if (stream.name() == "effect" && stream.isStartElement()) {
// check the name to see if it matches this effect
const QXmlStreamAttributes& attributes = stream.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "id") {
load(stream);
found_id = true;
break;
}
}
// we've found what we're looking for
break;
}
}
if (!found_id) {
QMessageBox::critical(olive::MainWindow,
tr("Load Settings Failed"),
tr("This settings file doesn't match this effect."),
QMessageBox::Ok);
}
}
QByteArray Node::save_to_string() {
QByteArray save_data;
// write settings to string with xml writer
QXmlStreamWriter stream(&save_data);
stream.writeStartDocument();
stream.writeStartElement("effect");
// pass off to standard saving function
save(stream);
stream.writeEndElement(); // effect
stream.writeEndDocument();
return save_data;
}
bool Node::is_open() {
return isOpen;
}
void Node::validate_meta_path() {
/*
if (!meta->path.isEmpty() || (shader_vert_path_.isEmpty() && shader_frag_path_.isEmpty())) return;
QList<QString> effects_paths = get_effects_paths();
const QString& test_fn = shader_vert_path_.isEmpty() ? shader_frag_path_ : shader_vert_path_;
for (int i=0;i<effects_paths.size();i++) {
if (QFileInfo::exists(effects_paths.at(i) + "/" + test_fn)) {
for (int j=0;j<olive::effects.size();j++) {
if (&olive::effects.at(j) == meta) {
olive::effects[j].path = effects_paths.at(i);
return;
}
}
return;
}
}
*/
}
void Node::open() {
/*
if (isOpen) {
qWarning() << "Tried to open an effect that was already open";
close();
}
if (olive::runtime_config.shaders_are_enabled && (Flags() & ShaderFlag)) {
if (QOpenGLContext::currentContext() == nullptr) {
qWarning() << "No current context to create a shader program for - will retry next repaint";
} else {
validate_meta_path();
QString frag_shader_str;
QString frag_file_url = QDir(file).filePath(shader_frag_path_);
QFile frag_file(frag_file_url);
if (frag_file.open(QFile::ReadOnly)) {
frag_shader_str = frag_file.readAll();
frag_file.close();
} else {
qWarning() << "Failed to open" << frag_file_url;
}
if (!frag_shader_str.isEmpty()) {
QString shader_func;
if (!shader_function_name_.isEmpty()) {
shader_func = shader_function_name_;
} else {
shader_func = "process";
}
shader_program_ = olive::shader::GetPipeline(shader_func, frag_shader_str);
}
isOpen = true;
}
} else {
isOpen = true;
}
*/
isOpen = true;
}
void Node::close() {
if (!isOpen) {
qWarning() << "Tried to close an effect that was already closed";
}
delete_texture();
shader_program_ = nullptr;
isOpen = false;
}
bool Node::is_shader_linked() {
return shader_program_ != nullptr && shader_program_->isLinked();
}
QOpenGLShaderProgram *Node::GetShaderPipeline()
{
return shader_program_.get();
}
int Node::Flags()
{
return flags_;
}
void Node::SetFlags(int flags)
{
flags_ = flags;
}
int Node::getIterations() {
return iterations;
}
void Node::setIterations(int i) {
iterations = i;
}
const QPointF &Node::pos()
{
return pos_;
}
void Node::process_image(double, uint8_t *, uint8_t *, int){}
NodePtr Node::copy(Clip *c) {
NodePtr copy = Create(c);
copy->SetEnabled(IsEnabled());
copy_field_keyframes(copy);
return copy;
}
void Node::process_shader(double timecode, GLTextureCoords&, int iteration) {
/*
shader_program_->bind();
shader_program_->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height());
shader_program_->setUniformValue("time", GLfloat(timecode));
shader_program_->setUniformValue("iteration", iteration);
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
if (!field->id().isEmpty()) {
switch (field->type()) {
case EffectField::EFFECT_FIELD_DOUBLE:
{
DoubleField* double_field = static_cast<DoubleField*>(field);
shader_program_->setUniformValue(double_field->id().toUtf8().constData(),
GLfloat(double_field->GetDoubleAt(timecode)));
}
break;
case EffectField::EFFECT_FIELD_COLOR:
{
ColorField* color_field = static_cast<ColorField *>(field);
shader_program_->setUniformValue(
color_field->id().toUtf8().constData(),
GLfloat(color_field->GetColorAt(timecode).redF()),
GLfloat(color_field->GetColorAt(timecode).greenF()),
GLfloat(color_field->GetColorAt(timecode).blueF())
);
}
break;
case EffectField::EFFECT_FIELD_BOOL:
shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool());
break;
case EffectField::EFFECT_FIELD_COMBO:
shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt());
break;
// can you even send a string to a uniform value?
case EffectField::EFFECT_FIELD_STRING:
case EffectField::EFFECT_FIELD_FONT:
case EffectField::EFFECT_FIELD_FILE:
case EffectField::EFFECT_FIELD_UI:
break;
}
}
}
}
shader_program_->release();
*/
}
void Node::process_coords(double, GLTextureCoords&, int) {}
GLuint Node::process_superimpose(QOpenGLContext* ctx, double timecode) {
bool dimensions_changed = false;
bool redrew_image = false;
int width = parent_clip->media_width();
int height = parent_clip->media_height();
if (width != img.width() || height != img.height()) {
img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied);
dimensions_changed = true;
}
if (valueHasChanged(timecode) || dimensions_changed || AlwaysUpdate()) {
redraw(timecode);
redrew_image = true;
}
QOpenGLFunctions* f = ctx->functions();
if (texture == 0 || tex_width_ != img.width() || tex_height_ != img.height()) {
delete_texture();
tex_width_ = img.width();
tex_height_ = img.height();
// create texture object
f->glGenTextures(1, &texture);
f->glBindTexture(GL_TEXTURE_2D, texture);
// set texture filtering to bilinear
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA8, tex_width_, tex_height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits()
);
f->glBindTexture(GL_TEXTURE_2D, 0);
redrew_image = false;
}
if (redrew_image) {
f->glBindTexture(GL_TEXTURE_2D, texture);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_width_, tex_height_, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits());
f->glBindTexture(GL_TEXTURE_2D, 0);
}
return texture;
}
void Node::process_audio(double, double, float **, int, int, int) {}
void Node::gizmo_draw(double, GLTextureCoords &) {}
void Node::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) {
// Loop through each gizmo to find `gizmo`
for (int i=0;i<gizmos.size();i++) {
if (gizmos.at(i) == gizmo) {
// If (!done && gizmo_dragging_actions_.isEmpty()), that means the drag just started and we're going to save
// the current state of the attach fields' keyframes in KeyframeDataChange objects to make the changes undoable
// by the user later.
if (!done && gizmo_dragging_actions_.isEmpty()) {
if (gizmo->x_field1 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->x_field1));
}
if (gizmo->y_field1 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->y_field1));
}
if (gizmo->x_field2 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->x_field2));
}
if (gizmo->y_field2 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->y_field2));
}
}
// Update the field values
if (gizmo->x_field1 != nullptr) {
gizmo->x_field1->SetValueAt(timecode,
gizmo->x_field1->GetDoubleAt(timecode) + x_movement*gizmo->x_field_multi1);
}
if (gizmo->y_field1 != nullptr) {
gizmo->y_field1->SetValueAt(timecode,
gizmo->y_field1->GetDoubleAt(timecode) + y_movement*gizmo->y_field_multi1);
}
if (gizmo->x_field2 != nullptr) {
gizmo->x_field2->SetValueAt(timecode,
gizmo->x_field2->GetDoubleAt(timecode) + x_movement*gizmo->x_field_multi2);
}
if (gizmo->y_field2 != nullptr) {
gizmo->y_field2->SetValueAt(timecode,
gizmo->y_field2->GetDoubleAt(timecode) + y_movement*gizmo->y_field_multi2);
}
// If (done && !gizmo_dragging_actions_.isEmpty()), that means the drag just ended and we're going to save
// the new state of the attach fields' keyframes in KeyframeDataChange objects to make the changes undoable
// by the user later.
if (done && !gizmo_dragging_actions_.isEmpty()) {
// Store all the KeyframeDataChange objects into a ComboAction to send to the undo stack (makes them all
// undoable together rather than having to be undone individually).
ComboAction* ca = new ComboAction();
for (int j=0;j<gizmo_dragging_actions_.size();j++) {
// Set the current state of the keyframes as the "new" keyframes (the old values were set earlier when the
// KeyframeDataChange object was constructed).
gizmo_dragging_actions_.at(j)->SetNewKeyframes();
// Add this KeyframeDataChange object to the ComboAction
ca->append(gizmo_dragging_actions_.at(j));
}
olive::undo_stack.push(ca);
gizmo_dragging_actions_.clear();
}
break;
}
}
}
void Node::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& projection) {
for (int i=0;i<gizmos.size();i++) {
EffectGizmo* g = gizmos.at(i);
for (int j=0;j<g->get_point_count();j++) {
// Convert the world point from the gizmo into a screen point relative to the sequence's dimensions
QVector3D screen_pos = g->world_pos.at(j).project(matrix,
projection,
QRect(0,
0,
parent_clip->track()->sequence()->width,
parent_clip->track()->sequence()->height));
g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->track()->sequence()->height-screen_pos.y());
}
}
}
bool Node::are_gizmos_enabled() {
return (gizmos.size() > 0);
}
double Node::Now()
{
return playhead_to_clip_seconds(parent_clip, parent_clip->track()->sequence()->playhead);
}
long Node::NowInFrames()
{
return playhead_to_clip_frame(parent_clip, parent_clip->track()->sequence()->playhead);
}
void Node::redraw(double) {
/*
// run javascript
QPainter p(&img);
painter_wrapper.img = &img;
painter_wrapper.painter = &p;
jsEngine.globalObject().setProperty("painter", wrapper_obj);
jsEngine.globalObject().setProperty("width", parent_clip->media_width());
jsEngine.globalObject().setProperty("height", parent_clip->media_height());
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
for (int j=0;j<row->fieldCount();j++) {
EffectField* field = row->field(j);
if (!field->id.isEmpty()) {
switch (field->type) {
case EffectField::EFFECT_FIELD_DOUBLE:
jsEngine.globalObject().setProperty(field->id, field->get_double_value(timecode));
break;
case EffectField::EFFECT_FIELD_COLOR:
jsEngine.globalObject().setProperty(field->id, field->get_color_value(timecode).name());
break;
case EffectField::EFFECT_FIELD_STRING:
jsEngine.globalObject().setProperty(field->id, field->get_string_value(timecode));
break;
case EffectField::EFFECT_FIELD_BOOL:
jsEngine.globalObject().setProperty(field->id, field->get_bool_value(timecode));
break;
case EffectField::EFFECT_FIELD_COMBO:
jsEngine.globalObject().setProperty(field->id, field->get_combo_index(timecode));
break;
case EffectField::EFFECT_FIELD_FONT:
jsEngine.globalObject().setProperty(field->id, field->get_font_name(timecode));
break;
}
}
}
}
jsEngine.evaluate(script);
*/
}
bool Node::valueHasChanged(double timecode) {
if (cachedValues.isEmpty()) {
for (int i=0;i<row_count();i++) {
EffectRow* crow = row(i);
for (int j=0;j<crow->FieldCount();j++) {
cachedValues.append(crow->Field(j)->GetValueAt(timecode));
}
}
return true;
} else {
bool changed = false;
int index = 0;
for (int i=0;i<row_count();i++) {
EffectRow* crow = row(i);
for (int j=0;j<crow->FieldCount();j++) {
EffectField* field = crow->Field(j);
if (cachedValues.at(index) != field->GetValueAt(timecode)) {
changed = true;
}
cachedValues[index] = field->GetValueAt(timecode);
index++;
}
}
return changed;
}
}
void Node::delete_texture() {
if (texture_ctx != nullptr) {
texture_ctx->functions()->glDeleteTextures(1, &texture);
texture = 0;
texture_ctx = nullptr;
}
}
int GetNodeLibraryIndexFromId(const QString& id) {
for (int i=0;i<olive::node_library.size();i++) {
if (olive::node_library.at(i)->id() == id) {
return i;
}
}
return -1;
}
/*
const EffectMeta* Node::GetMetaFromName(const QString& input) {
int split_index = input.indexOf('/');
QString category;
if (split_index > -1) {
category = input.left(split_index);
}
QString name = input.mid(split_index + 1);
for (int j=0;j<olive::effects.size();j++) {
if (olive::effects.at(j).name == name
&& (olive::effects.at(j).category == category
|| category.isEmpty())) {
return &olive::effects.at(j);
}
}
return nullptr;
}
*/
#include "node.h"
Node::Node(NodeGraph *graph)
{
}
+20 -286
View File
@@ -1,286 +1,20 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 EFFECT_H
#define EFFECT_H
#include <memory>
#include <QObject>
#include <QString>
#include <QVector>
#include <QColor>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMutex>
#include <QThread>
#include <QLabel>
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QMouseEvent>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <random>
#include "timeline/tracktypes.h"
#include "rendering/qopenglshaderprogramptr.h"
#include "inputs.h"
#include "effects/effectgizmo.h"
class EffectGizmo;
class KeyframeDataChange;
class Clip;
using ClipPtr = std::shared_ptr<Clip>;
class Node;
using NodePtr = std::shared_ptr<Node>;
enum NodeType {
kTransformEffect,
kTextInput,
kSolidInput,
kNoiseInput,
kVolumeEffect,
kPanEffect,
kToneInput,
kShakeEffect,
kTimecodeEffect,
kMaskEffect,
kFillLeftRightEffect,
kVstEffect,
kCornerPinEffect,
kRichTextInput,
kMediaInput,
kShaderEffect,
kImageOutput,
kCrossDissolveTransition,
kLinearFadeTransition,
kExponentialFadeTransition,
kLogarithmicFadeTransition,
kInvalidNode
};
double log_volume(double linear);
enum EffectType {
EFFECT_TYPE_INVALID,
EFFECT_TYPE_EFFECT,
EFFECT_TYPE_TRANSITION
};
enum EffectKeyframeType {
EFFECT_KEYFRAME_LINEAR,
EFFECT_KEYFRAME_BEZIER,
EFFECT_KEYFRAME_HOLD
};
struct GLTextureCoords {
QMatrix4x4 matrix;
QVector3D vertex_top_left;
QVector3D vertex_top_right;
QVector3D vertex_bottom_left;
QVector3D vertex_bottom_right;
QVector2D texture_top_left;
QVector2D texture_top_right;
QVector2D texture_bottom_left;
QVector2D texture_bottom_right;
float opacity;
};
class Node : public QObject {
Q_OBJECT
public:
Node(Clip *c);
~Node();
Clip* parent_clip;
virtual QString name() = 0;
virtual QString id() = 0;
virtual QString category();
virtual QString description() = 0;
virtual EffectType type() = 0;
virtual olive::TrackType subtype() = 0;
virtual bool IsCreatable();
virtual NodePtr Create(Clip *c) = 0;
void AddRow(EffectRow* row);
int IndexOfRow(EffectRow* row);
EffectRow* row(int i);
int row_count();
EffectGizmo* add_gizmo(int type);
EffectGizmo* gizmo(int i);
int gizmo_count();
QVector<NodeEdgePtr> GetAllEdges();
bool IsEnabled();
bool IsExpanded();
virtual void refresh();
virtual NodePtr copy(Clip* c);
void copy_field_keyframes(NodePtr e);
virtual void load(QXmlStreamReader& stream);
virtual void custom_load(QXmlStreamReader& stream);
virtual void save(QXmlStreamWriter& stream);
void load_from_string(const QByteArray &s);
QByteArray save_to_string();
// glsl handling
bool is_open();
void open();
void close();
bool is_shader_linked();
QOpenGLShaderProgram* GetShaderPipeline();
enum VideoEffectFlags {
ShaderFlag = 0x1,
CoordsFlag = 0x2,
SuperimposeFlag = 0x4
};
int Flags();
void SetFlags(int flags);
int getIterations();
void setIterations(int i);
const QPointF& pos();
virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size);
virtual void process_shader(double timecode, GLTextureCoords&, int iteration);
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
virtual GLuint process_superimpose(QOpenGLContext *ctx, double timecode);
virtual void process_audio(double timecode_start, double timecode_end, float **samples, int nb_samples, int nb_channels, int type);
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
void gizmo_world_to_screen(const QMatrix4x4 &matrix, const QMatrix4x4 &projection);
bool are_gizmos_enabled();
/**
* @brief Get the current clip/media time
*
* A convenience function that can be plugged into GetValueAt() to get the value wherever the appropriate Sequence's
* playhead it.
*
* @return
*
* Current clip/media time in seconds.
*/
double Now();
/**
* @brief Retrieve the current clip as a frame number
*
* Same as Now() but retrieves the value as a frame number (in the appropriate Sequence's frame rate) instead of
* seconds.
*
* @return
*
* The current clip time in frames
*/
long NowInFrames();
template <typename T>
T randomNumber()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<> distribution(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
return distribution(generator);
}
template <typename T>
T randomFloat()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<> distribution(-1.0, 1.0);
return distribution(generator);
}
public slots:
void FieldChanged();
void SetEnabled(bool b);
void SetExpanded(bool e);
void SetPos(const QPointF& pos);
signals:
void EnabledChanged(bool);
private slots:
void delete_self();
void move_up();
void move_down();
void save_to_file();
void load_from_file();
protected:
// glsl effect
QOpenGLShaderProgramPtr shader_program_;
QString shader_vert_path_;
QString shader_frag_path_;
QString shader_function_name_;
// superimpose effect
QImage img;
GLuint texture;
QOpenGLContext* texture_ctx;
int tex_width_;
int tex_height_;
// enable effect to update constantly
virtual bool AlwaysUpdate();
private:
bool isOpen;
QVector<EffectRow*> rows;
QVector<EffectGizmo*> gizmos;
bool bound;
int iterations;
bool enabled_;
bool expanded_;
int flags_;
QVector<KeyframeDataChange*> gizmo_dragging_actions_;
QPointF pos_;
// superimpose functions
virtual void redraw(double timecode);
bool valueHasChanged(double timecode);
QVector<QVariant> cachedValues;
void delete_texture();
void validate_meta_path();
};
namespace olive {
extern QVector<NodePtr> node_library;
}
#endif // EFFECT_H
#ifndef NODE_H
#define NODE_H
#include <memory>
class NodeGraph;
class Node;
using NodePtr = std::shared_ptr<Node>;
class Node
{
public:
Node(NodeGraph* parent);
private:
NodeGraph* parent_;
};
#endif // NODE_H
+4 -4
View File
@@ -2,20 +2,20 @@
#include <QDebug>
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
NodeEdge::NodeEdge(EffectRow *output, EffectRow *input) :
NodeEdge::NodeEdge(NodeIO *output, NodeIO *input) :
output_(output),
input_(input)
{
}
EffectRow *NodeEdge::output()
NodeIO *NodeEdge::output()
{
return output_;
}
EffectRow *NodeEdge::input()
NodeIO *NodeEdge::input()
{
return input_;
}
+6 -6
View File
@@ -3,7 +3,7 @@
#include <memory>
class EffectRow;
class NodeIO;
class NodeEdge;
using NodeEdgePtr = std::shared_ptr<NodeEdge>;
@@ -26,14 +26,14 @@ public:
*
* Input/to EffectRow that this edge "ends" at
*/
NodeEdge(EffectRow* output, EffectRow* input);
NodeEdge(NodeIO* output, NodeIO* input);
EffectRow* output();
EffectRow* input();
NodeIO* output();
NodeIO* input();
private:
EffectRow* output_;
EffectRow* input_;
NodeIO* output_;
NodeIO* input_;
};
#endif // NODEEDGE_H
+4
View File
@@ -1,6 +1,8 @@
#ifndef NODEGRAPH_H
#define NODEGRAPH_H
#include <QVector>
#include "nodes/node.h"
class NodeGraph
@@ -48,6 +50,8 @@ public:
private:
NodePtr output_node_;
QVector<NodePtr> nodes_;
};
#endif // NODEGRAPH_H
+388
View File
@@ -0,0 +1,388 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 "nodeio.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include "undo/undo.h"
#include "undo/undostack.h"
#include "timeline/clip.h"
#include "timeline/sequence.h"
#include "panels/panels.h"
#include "panels/effectcontrols.h"
#include "panels/viewer.h"
#include "panels/grapheditor.h"
#include "nodes/oldeffectnode.h"
#include "ui/viewerwidget.h"
#include "ui/keyframenavigator.h"
#include "ui/clickablelabel.h"
NodeIO::NodeIO(OldEffectNode *parent,
const QString &id,
const QString &name,
bool savable,
bool keyframable) :
QObject(parent),
id_(id),
name_(name),
keyframable_(keyframable),
keyframing_(false),
savable_(savable),
output_type_(olive::nodes::kInvalid)
{
Q_ASSERT(parent != nullptr);
parent->AddRow(this);
}
void NodeIO::AddField(EffectField *field)
{
field->setParent(this);
connect(field, SIGNAL(Clicked()), this, SIGNAL(Clicked()));
connect(field, SIGNAL(Changed()), this, SIGNAL(Changed()));
fields_.append(field);
}
void NodeIO::AddAcceptedNodeInput(olive::nodes::DataType type)
{
Q_ASSERT(output_type_ == olive::nodes::kInvalid);
accepted_inputs_.append(type);
}
void NodeIO::ConnectEdge(NodeIO *output, NodeIO *input)
{
// Make sure one is an output and one is an input
Q_ASSERT(output->IsNodeInput() != input->IsNodeInput());
// Swap them if necessary
if (input->IsNodeOutput()) {
NodeIO* temp = output;
output = input;
input = temp;
}
// Inputs can only have one edge, so we disconnect it here if there is one
if (!input->node_edges_.isEmpty()) {
DisconnectEdge(input->node_edges_.first());
}
NodeEdgePtr edge = std::make_shared<NodeEdge>(output, input);
output->node_edges_.append(edge);
input->node_edges_.append(edge);
emit output->EdgesChanged();
}
void NodeIO::DisconnectEdge(NodeEdgePtr edge)
{
NodeIO* output = edge->output();
NodeIO* input = edge->input();
output->node_edges_.removeAll(edge);
input->node_edges_.removeAll(edge);
emit output->EdgesChanged();
}
QVector<NodeEdgePtr> NodeIO::edges()
{
return node_edges_;
}
bool NodeIO::IsKeyframing() {
return keyframing_;
}
void NodeIO::SetKeyframingInternal(bool b) {
if (GetParentEffect()->type() != EFFECT_TYPE_TRANSITION) {
keyframing_ = b;
emit KeyframingSetChanged(keyframing_);
}
}
bool NodeIO::IsSavable()
{
return savable_;
}
bool NodeIO::IsKeyframable()
{
return keyframable_;
}
QVariant NodeIO::GetValueAt(double timecode)
{
Q_ASSERT(FieldCount() == 1);
return Field(0)->GetValueAt(timecode);
}
void NodeIO::SetValueAt(double timecode, const QVariant &value)
{
Q_ASSERT(FieldCount() == 1);
Field(0)->SetValueAt(timecode, value);
}
void NodeIO::SetEnabled(bool enabled)
{
for (int i=0;i<FieldCount();i++) {
Field(i)->SetEnabled(enabled);
}
}
void NodeIO::SetOutputDataType(olive::nodes::DataType type)
{
Q_ASSERT(accepted_inputs_.isEmpty());
output_type_ = type;
}
bool NodeIO::CanAcceptDataType(olive::nodes::DataType type)
{
if (!IsNodeInput()) {
return false;
}
return accepted_inputs_.contains(type);
}
olive::nodes::DataType NodeIO::OutputDataType()
{
return output_type_;
}
bool NodeIO::IsNodeInput()
{
return !accepted_inputs_.isEmpty();
}
bool NodeIO::IsNodeOutput()
{
return output_type_ != olive::nodes::kInvalid;
}
void NodeIO::SetKeyframingEnabled(bool enabled) {
if (enabled == keyframing_) {
return;
}
if (enabled) {
ComboAction* ca = new ComboAction();
// Enable keyframing setting on this row
ca->append(new SetIsKeyframing(this, true));
// Prepare each field's data to start keyframing
for (int i=0;i<FieldCount();i++) {
Field(i)->PrepareDataForKeyframing(true, ca);
}
olive::undo_stack.push(ca);
update_ui(false);
} else {
// Confirm with the user whether they really want to disable keyframing
if (QMessageBox::question(panel_effect_controls,
tr("Disable Keyframes"),
tr("Disabling keyframes will delete all current keyframes. "
"Are you sure you want to do this?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
ComboAction* ca = new ComboAction();
// Prepare each field's data to stop keyframing
for (int i=0;i<FieldCount();i++) {
Field(i)->PrepareDataForKeyframing(false, ca);
}
// Disable keyframing setting on this row
ca->append(new SetIsKeyframing(this, false));
olive::undo_stack.push(ca);
update_ui(false);
} else {
SetKeyframingInternal(true);
}
}
}
void NodeIO::GoToPreviousKeyframe() {
long key = LONG_MIN;
Clip* c = GetParentEffect()->parent_clip;
long sequence_playhead = c->track()->sequence()->playhead;
// Used to convert clip frame number to sequence frame number
long time_adjustment = c->timeline_in() - c->clip_in();
// Loop through all of this row's fields
for (int i=0;i<FieldCount();i++) {
EffectField* f = Field(i);
// Loop through all of this field's keyframes for a keyframe EARLIER than the playhead
for (int j=0;j<f->keyframes.size();j++) {
long comp = f->keyframes.at(j).time + time_adjustment;
// Get the closest keyframe
if (comp < sequence_playhead) {
key = qMax(comp, key);
}
}
}
// If we found a keyframe less than the playhead, jump to it
if (key != LONG_MIN) panel_sequence_viewer->seek(key);
}
void NodeIO::ToggleKeyframe() {
Clip* c = GetParentEffect()->parent_clip;
long sequence_playhead = c->track()->sequence()->playhead;
// Used to convert clip frame number to sequence frame number
long time_adjustment = c->timeline_in() - c->clip_in();
QVector<EffectField*> key_fields;
QVector<int> key_field_index;
// See if any keyframes on any fields are at the current time
for (int j=0;j<FieldCount();j++) {
EffectField* f = Field(j);
for (int i=0;i<f->keyframes.size();i++) {
long comp = f->keyframes.at(i).time + time_adjustment;
if (comp == sequence_playhead) {
// Cache the keyframes if they are at the current time
key_fields.append(f);
key_field_index.append(i);
}
}
}
ComboAction* ca = new ComboAction();
if (key_fields.isEmpty()) {
// If we didn't find any current keyframes, create one for each field
SetKeyframeOnAllFields(ca);
} else {
// If we DID find keyframes at this time, delete them
QVector<EffectField*> sorted_key_fields;
QVector<int> sorted_key_field_index;
// Since QVectors shift themselves when removing items, we need to sort these in reverse order
for (int i=0;i<key_field_index.size();i++) {
bool found = false;
for (int j=0;j<sorted_key_field_index.size();j++) {
if (sorted_key_field_index.at(j) < key_field_index.at(i)) {
sorted_key_fields.insert(j, key_fields.at(i));
sorted_key_field_index.insert(j, key_field_index.at(i));
found = true;
break;
}
}
if (!found) {
sorted_key_fields.append(key_fields.at(i));
sorted_key_field_index.append(key_field_index.at(i));
}
}
for (int i=0;i<sorted_key_fields.size();i++) {
ca->append(new KeyframeDelete(sorted_key_fields.at(i), sorted_key_field_index.at(i)));
}
}
olive::undo_stack.push(ca);
update_ui(false);
}
void NodeIO::GoToNextKeyframe() {
long key = LONG_MAX;
Clip* c = GetParentEffect()->parent_clip;
for (int i=0;i<FieldCount();i++) {
EffectField* f = Field(i);
for (int j=0;j<f->keyframes.size();j++) {
long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in();
if (comp > c->track()->sequence()->playhead) {
key = qMin(comp, key);
}
}
}
if (key != LONG_MAX) panel_sequence_viewer->seek(key);
}
void NodeIO::FocusRow() {
panel_graph_editor->set_row(this);
}
void NodeIO::SetKeyframeOnAllFields(ComboAction* ca) {
for (int i=0;i<FieldCount();i++) {
EffectField* field = Field(i);
KeyframeDataChange* kdc = new KeyframeDataChange(field);
field->SetValueAt(GetParentEffect()->Now(), field->GetValueAt(GetParentEffect()->Now()));
kdc->SetNewKeyframes();
ca->append(kdc);
}
panel_effect_controls->update_keyframes();
}
OldEffectNode *NodeIO::GetParentEffect()
{
return static_cast<OldEffectNode*>(parent());
}
const QString &NodeIO::name() {
return name_;
}
EffectField* NodeIO::Field(int i) {
return fields_.at(i);
}
int NodeIO::FieldCount() {
return fields_.size();
}
+466
View File
@@ -0,0 +1,466 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 EFFECTROW_H
#define EFFECTROW_H
#include <QObject>
#include <QVector>
class OldEffectNode;
class QGridLayout;
class EffectField;
class QLabel;
class QPushButton;
class ComboAction;
class QHBoxLayout;
class KeyframeNavigator;
class ClickableLabel;
#include "effects/effectfields.h"
#include "nodes/nodeedge.h"
/**
* @brief The EffectRow class
*
* Primarily a way of grouping EffectField objects together. As UI objects, Effects are largely formatted as a table
* and you can think of EffectRows as the rows of the table and EffectFields as the columns.
*
* Within Olive, keyframing is enabled on the EffectRow (rather than individual EffectFields) so all attached fields
* will be keyframed together.
*
* Unlike EffectField, there's no reason to derive from EffectRow as it's simply a container of fields and a few
* keyframe functions.
*/
class NodeIO : public QObject {
Q_OBJECT
public:
/**
* @brief EffectRow Constructor
*
* @param parent
*
* Every EffectRow object must be attached to a valid Effect object. The Effect object takes ownership of the
* EffectRow and automatically frees it through the QObject parent/child system. EffectRows are never intended to
* change parents throughout their lifetimes.
*
* @param id
*
* Field ID. Must be non-empty. Must also be unique within this Effect. Used for saving/loading values into project
* files so that if ordering of fields are changed, or fields are added/removed from Effects later in development,
* saved values in project files will still link with the correct field. Also used as the uniform variable name
* in GLSL shaders.
*
* @param name
*
* Row name. This is not used as an internal identifier, it's just for the user interface, so it can be translated
* with no issue.
*
* @param savable
*
* Whether the fields in this row should be saved to the project file. This is true by default. If the row contains
* non-value UI widgets, setting this to false is recommended.
*
* @param keyframable
*
* Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent
* the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false.
*/
NodeIO(OldEffectNode* parent,
const QString& id,
const QString& name,
bool savable = true,
bool keyframable = true);
/**
* @brief Retrieve the EffectField at this index. Must be less than FieldCount().
*
* @param i
*
* Index to retrieve the EffectField at.
*
* @return
*
* EffectField at the provided index.
*/
EffectField* Field(int i);
/**
* @brief Number of fields currently contained in this row.
*
* @return The number of fields currently contained in this row as an integer. Any field index (retrieved with
* Field()) is guaranteed to be valid >= 0 and < FieldCount().
*/
int FieldCount();
/**
* @brief Set a keyframe at the current playhead on all fields contained within this row
*
* @param ca
*
* The ComboAction to add this action to. This may not be nullptr.
*/
void SetKeyframeOnAllFields(ComboAction *ca);
/**
* @brief Get parent Effect
*
* Equivalent to `static_cast<Effect*>(parent())`.
*
* @return The parent Effect object that this row is attached to.
*/
OldEffectNode* GetParentEffect();
/**
* @brief Return the row's name
*
* @return The name specified in the constructor as a QString
*/
const QString& name();
/**
* @brief Get the unique identifier of this field set in the constructor
*
* Mostly used for saving/loading or interacting with GLSL-based shader effects (see EffectField() for more details).
*
* @return
*/
const QString& id();
/**
* @brief Get whether this row is keyframing or not
*
* @return True if this row is keyframing.
*/
bool IsKeyframing();
/**
* @brief Set whether this row is keyframing or not.
*
* It's not recommended to use this function for any user-initiated keyframe setting change as it doesn't create any
* undoable actions. Use SetKeyframingEnabled() instead for a user-friendly variant.
*/
void SetKeyframingInternal(bool);
/**
* @brief Get whether this row should be saved into a project file or not
*
* @return True if this row should be saved. This value is set in the constructor.
*/
bool IsSavable();
/**
* @brief Get whether this row can be keyframed or not.
*
* @return True if this row can be keyframed. This value is set in the constructor.
*/
bool IsKeyframable();
/**
* @brief Get value at a given timecode
*
* Functions as a wrapper for EffectField::GetValueAt().
*
* The default function is to return the result of the first EffectField on this EffectRow which should be sufficient
* for most input types. If this EffectRow has more or less than one EffectField, you must override this function in a
* derived class to provide the correct field <-> row coordination or else it will trigger an abort.
*
* @param timecode
*
* Timecode to get the value at
*
* @return
*
* The value of the first EffectField at the given timecode
*/
virtual QVariant GetValueAt(double timecode);
/**
* @brief SetValueAt
*
* Functions as a wrapper for EffectField::SetValueAt().
*
* The default function is to call the function of the first EffectField on this EffectRow which should be sufficient
* for most input types. If this EffectRow has more or less than one EffectField, you must override this function in a
* derived class to provide the correct field <-> row coordination or else it will trigger an abort.
*
* @param timecode
*
* Timecode to set the value at
*
* @param value
*
* Value to set at this timecode
*/
virtual void SetValueAt(double timecode, const QVariant& value);
/**
* @brief Sets the enabled state on all EffectField objects on this row to enabled
*/
void SetEnabled(bool enabled);
/**
* @brief Check if nodes can be connected to this as an input.
*
* Connecting is enabled by adding an accepted node input using AddNodeInput().
*
* @return
*
* TRUE if nodes can be connected as an input.
*/
bool IsNodeInput();
/**
* @brief Check if nodes can be connected to this as an output
*
* Connecting is enabled by setting an output data type in SetOutputDataType().
*
* @return
*
* TRUE if nodes can be connected as an output
*/
bool IsNodeOutput();
/**
* @brief Set output data type
*
* Set the type of data this row outputs to type
*/
void SetOutputDataType(olive::nodes::DataType type);
/**
* @brief Check if this row can accept the data type specified in type
*
* @return
*
* TRUE if this row can accept this data type. If this row is not an input, this function will always return FALSE.
*/
bool CanAcceptDataType(olive::nodes::DataType type);
/**
* @brief Get this row's output data type
*
* @return
*
* If this is not an output, this will always return olive::nodes::kInvalid
*/
olive::nodes::DataType OutputDataType();
/**
* @brief Adds a node data type that can be accepted by this input
*
* Allows this input to take a node connection from a data type specified by type. An input can take several data
* types.
*
* @param type
*
* The data type to add
*/
void AddAcceptedNodeInput(olive::nodes::DataType type);
/**
* @brief Connect two node sockets together
*
* For outputs, the maximum amount of edges is unlimited and this will continually add to a list of edges. For inputs,
* there can only be one edge and adding a second will destroy the first.
*
* @param edge
*
* The edge to add (output) or replace the current edge (input).
*/
static void ConnectEdge(NodeIO* output, NodeIO* input);
/**
* @brief Disconnect an edge
*
* Disconnects two nodes and destroys the edge object connecting them together
*
* @param edge
*
* Edge to be removed
*/
static void DisconnectEdge(NodeEdgePtr edge);
/**
* @brief Get a list of references to all edges currently connected to this row
*/
QVector<NodeEdgePtr> edges();
protected:
/**
* @brief Add a field to this row
*
* Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this
* will add the field to an additional column.
*
* @param Field
*
* The field to add to this row.
*/
void AddField(EffectField* Field);
public slots:
/**
* @brief Go to previous keyframe
*
* Gets the closest keyframe prior to the current playhead and seeks to it.
*
* Attach to KeyframeNavigator::goto_previous_key() signal.
*/
void GoToPreviousKeyframe();
/**
* @brief Toggle a keyframe at this point in time
*
* Either deletes (if any child EffectFields have any keyframes here) or creates (if none do) a keyframe on all
* EffectField children at the current time.
*
* Attach to KeyframeNavigator::toggle_key() signal.
*/
void ToggleKeyframe();
/**
* @brief Go to next keyframe
*
* Gets the closest keyframe after the current playhead and seeks to it.
*
* Attach to KeyframeNavigator::goto_next_key() signal.
*/
void GoToNextKeyframe();
/**
* @brief Slot for whenever this EffectRow is focused.
*
* Connect UI objects gaining focus to this slot. Automatically updates the Graph Editor to attach to this row.
*/
void FocusRow();
signals:
/**
* @brief Keyframing setting changed signal
*
* Emitted whenever keyframing is enabled or disabled.
*
* @param
*
* True if keyframing was enabled, false if keyframing was disabled.
*/
void KeyframingSetChanged(bool);
/**
* @brief Changed signal
*
* Wrapper for EffectField::Changed().
*/
void Changed();
/**
* @brief Clicked signal
*
* Wrapper for EffectField::Clicked().
*/
void Clicked();
/**
* @brief Edges changed signal
*
* Signal emitted any time an edge is connected or disconnected from this row
*/
void EdgesChanged();
private slots:
/**
* @brief Set keyframing enabled state
*
* A user-friendly function for enabling or disabling keyframes on this row. Preferred to SetKeyframingInternal()
* for any user-initiated change. Automatically creates an undoable action so users can undo the enabling/disabling.
* Also confirms with the user when disabling keyframing whether they wish to continue and remove all the current
* keyframes.
*
* Attach to KeyframeNavigator::keyframe_enabled_changed() signal.
*/
void SetKeyframingEnabled(bool);
private:
/**
* @brief Internal unique identifier for this field set in the constructor. Access with id().
*/
QString id_;
/**
* @brief Internal variable for the row's name
*
* Set in the constructor, retrieved with name().
*/
QString name_;
/**
* @brief Internal variable for whether this row can be keyframed.
*
* Set in the constructor, retrieved with IsKeyframable().
*/
bool keyframable_;
/**
* @brief Internal variable for whether this row is currently keyframing.
*
* Set by SetKeyframingInternal() and retrieved with IsKeyframing().
*/
bool keyframing_;
/**
* @brief Internal variable for whether this row should be saved.
*
* Set in the constructor, retrieved with IsSavable().
*/
bool savable_;
/**
* @brief Internal array of EffectField objects.
*
* It is not necessary to delete the elements in this array as they're already children of this QObject, so they'll
* get freed automatically.
*/
QVector<EffectField*> fields_;
/**
* @brief Internal array of accepted node data types.
*
* Is mutally-exclusive with accepted_outputs_, i.e. you cannot have values added to this and also a value set in
* accepted_outputs_.
*/
QVector<olive::nodes::DataType> accepted_inputs_;
/**
* @brief Internal value for what kind of data this row outputs
*
* Is mutally-exclusive with accepted_inputs_, i.e. you cannot have values added to it and also a value set in
* this.
*/
olive::nodes::DataType output_type_;
/**
* @brief Internal array of node edges. Access with AddEdge() and RemoveEdge().
*/
QVector<NodeEdgePtr> node_edges_;
};
#endif // EFFECTROW_H
+7
View File
@@ -0,0 +1,7 @@
#include "nodeclip.h"
NodeClip::NodeClip(NodeGraph *graph) :
Node(graph)
{
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef NODECLIP_H
#define NODECLIP_H
#include "nodes/node.h"
class NodeClip : public Node
{
public:
NodeClip(NodeGraph* graph);
long in();
long out();
long clip_in();
void set_in(long in);
void set_out(long out);
void set_clip_in(long clip_in);
private:
long in_;
long out_;
long clip_in_;
};
#endif // NODECLIP_H
+3 -3
View File
@@ -1,9 +1,9 @@
#include "nodeimageoutput.h"
NodeImageOutput::NodeImageOutput(Clip *c) :
Node(c)
OldEffectNode(c)
{
EffectRow* input_texture = new EffectRow(this, "texture", tr("Texture"), true, false);
NodeIO* input_texture = new NodeIO(this, "texture", tr("Texture"), true, false);
input_texture->AddAcceptedNodeInput(olive::nodes::kTexture);
}
@@ -37,7 +37,7 @@ olive::TrackType NodeImageOutput::subtype()
return olive::kTypeVideo;
}
NodePtr NodeImageOutput::Create(Clip *c)
OldEffectNodePtr NodeImageOutput::Create(Clip *c)
{
return std::make_shared<NodeImageOutput>(c);
}
+3 -3
View File
@@ -1,9 +1,9 @@
#ifndef NODEIMAGEOUTPUT_H
#define NODEIMAGEOUTPUT_H
#include "nodes/node.h"
#include "nodes/oldeffectnode.h"
class NodeImageOutput : public Node
class NodeImageOutput : public OldEffectNode
{
public:
NodeImageOutput(Clip* c);
@@ -14,7 +14,7 @@ public:
virtual QString description() override;
virtual EffectType type() override;
virtual olive::TrackType subtype() override;
virtual NodePtr Create(Clip *c) override;
virtual OldEffectNodePtr Create(Clip *c) override;
};
#endif // NODEIMAGEOUTPUT_H
+4 -4
View File
@@ -1,12 +1,12 @@
#include "nodemedia.h"
NodeMedia::NodeMedia(Clip* c) :
Node(c)
OldEffectNode(c)
{
EffectRow* matrix_input = new EffectRow(this, "matrix", tr("Matrix"), true, false);
NodeIO* matrix_input = new NodeIO(this, "matrix", tr("Matrix"), true, false);
matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix);
EffectRow* texture_output = new EffectRow(this, "texture", tr("Texture"), true, false);
NodeIO* texture_output = new NodeIO(this, "texture", tr("Texture"), true, false);
texture_output->SetOutputDataType(olive::nodes::kTexture);
}
@@ -40,7 +40,7 @@ olive::TrackType NodeMedia::subtype()
return olive::kTypeVideo;
}
NodePtr NodeMedia::Create(Clip *c)
OldEffectNodePtr NodeMedia::Create(Clip *c)
{
return std::make_shared<NodeMedia>(c);
}
+3 -3
View File
@@ -1,9 +1,9 @@
#ifndef MEDIANODE_H
#define MEDIANODE_H
#include "nodes/node.h"
#include "nodes/oldeffectnode.h"
class NodeMedia : public Node
class NodeMedia : public OldEffectNode
{
public:
NodeMedia(Clip *c);
@@ -14,7 +14,7 @@ public:
virtual QString description() override;
virtual EffectType type() override;
virtual olive::TrackType subtype() override;
virtual NodePtr Create(Clip *c) override;
virtual OldEffectNodePtr Create(Clip *c) override;
};
#endif // MEDIANODE_H
+3 -3
View File
@@ -5,7 +5,7 @@ NodeShader::NodeShader(Clip* c,
const QString &id,
const QString &category,
const QString &filename) :
Node(c),
OldEffectNode(c),
name_(name),
id_(id),
category_(category),
@@ -39,7 +39,7 @@ NodeShader::NodeShader(Clip* c,
if (id.isEmpty() || name.isEmpty() || type == olive::nodes::kInvalid) {
qCritical() << "Couldn't load field from" << filename_ << "- ID, type, and name cannot be empty.";
} else {
EffectRow* field = nullptr;
NodeIO* field = nullptr;
switch (type) {
case olive::nodes::kFloat:
@@ -215,7 +215,7 @@ bool NodeShader::IsCreatable()
return !filename_.isEmpty();
}
NodePtr NodeShader::Create(Clip *)
OldEffectNodePtr NodeShader::Create(Clip *)
{
Q_ASSERT(false);
return nullptr;
+3 -3
View File
@@ -1,9 +1,9 @@
#ifndef NODESHADER_H
#define NODESHADER_H
#include "nodes/node.h"
#include "nodes/oldeffectnode.h"
class NodeShader : public Node {
class NodeShader : public OldEffectNode {
Q_OBJECT
public:
NodeShader(Clip *c,
@@ -19,7 +19,7 @@ public:
virtual EffectType type() override;
virtual olive::TrackType subtype() override;
virtual bool IsCreatable() override;
virtual NodePtr Create(Clip *c) override;
virtual OldEffectNodePtr Create(Clip *c) override;
private:
QString name_;
+948
View File
@@ -0,0 +1,948 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 "oldeffectnode.h"
#include <QCheckBox>
#include <QGridLayout>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QOpenGLContext>
#include <QDir>
#include <QPainter>
#include <QtMath>
#include <QMenu>
#include <QApplication>
#include <QFileDialog>
#include "panels/panels.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "ui/collapsiblewidget.h"
#include "panels/project.h"
#include "undo/undo.h"
#include "timeline/sequence.h"
#include "timeline/clip.h"
#include "panels/timeline.h"
#include "panels/effectcontrols.h"
#include "panels/grapheditor.h"
#include "global/debug.h"
#include "global/path.h"
#include "ui/mainwindow.h"
#include "ui/menu.h"
#include "global/math.h"
#include "global/clipboard.h"
#include "global/config.h"
#include "effects/transition.h"
#include "undo/undostack.h"
#include "rendering/shadergenerators.h"
#include "global/timing.h"
#include "nodes/nodes.h"
#include "effects/effectloaders.h"
QVector<OldEffectNodePtr> olive::node_library;
OldEffectNode::OldEffectNode(Clip* c) :
parent_clip(c),
flags_(0),
shader_program_(nullptr),
texture(0),
tex_width_(0),
tex_height_(0),
isOpen(false),
bound(false),
iterations(1),
enabled_(true),
expanded_(true),
texture_ctx(nullptr)
{
}
OldEffectNode::~OldEffectNode() {
if (isOpen) {
close();
}
// Clear graph editor if it's using one of these rows
if (panel_graph_editor != nullptr) {
for (int i=0;i<row_count();i++) {
if (row(i) == panel_graph_editor->get_row()) {
panel_graph_editor->set_row(nullptr);
break;
}
}
}
}
QString OldEffectNode::category()
{
return QString();
}
QString OldEffectNode::description()
{
return QString();
}
bool OldEffectNode::IsCreatable()
{
return true;
}
void OldEffectNode::AddRow(NodeIO *row)
{
row->setParent(this);
rows.append(row);
}
int OldEffectNode::IndexOfRow(NodeIO *row)
{
return rows.indexOf(row);
}
void OldEffectNode::copy_field_keyframes(OldEffectNodePtr e) {
for (int i=0;i<rows.size();i++) {
NodeIO* row = rows.at(i);
NodeIO* copy_row = e->rows.at(i);
copy_row->SetKeyframingInternal(row->IsKeyframing());
for (int j=0;j<row->FieldCount();j++) {
// Get field from this (the source) effect
EffectField* field = row->Field(j);
// Get field from the destination effect
EffectField* copy_field = copy_row->Field(j);
// Copy keyframes between effects
copy_field->keyframes = field->keyframes;
// Copy persistet data between effects
copy_field->persistent_data_ = field->persistent_data_;
}
}
}
NodeIO* OldEffectNode::row(int i) {
return rows.at(i);
}
int OldEffectNode::row_count() {
return rows.size();
}
EffectGizmo *OldEffectNode::add_gizmo(int type) {
EffectGizmo* gizmo = new EffectGizmo(this, type);
gizmos.append(gizmo);
return gizmo;
}
EffectGizmo *OldEffectNode::gizmo(int i) {
return gizmos.at(i);
}
int OldEffectNode::gizmo_count() {
return gizmos.size();
}
QVector<NodeEdgePtr> OldEffectNode::GetAllEdges()
{
QVector<NodeEdgePtr> edges;
for (int i=0;i<row_count();i++) {
edges.append(row(i)->edges());
}
return edges;
}
void OldEffectNode::refresh() {}
void OldEffectNode::FieldChanged() {
// Update the UI if a field has been modified, but don't both if this effect is inactive
if (parent_clip != nullptr) {
update_ui(false);
}
}
void OldEffectNode::delete_self() {
olive::undo_stack.push(new EffectDeleteCommand(this));
update_ui(true);
}
void OldEffectNode::move_up() {
int index_of_effect = parent_clip->IndexOfEffect(this);
if (index_of_effect == 0) {
return;
}
MoveEffectCommand* command = new MoveEffectCommand();
command->clip = parent_clip;
command->from = index_of_effect;
command->to = command->from - 1;
olive::undo_stack.push(command);
panel_effect_controls->Reload();
panel_sequence_viewer->viewer_widget()->frame_update();
}
void OldEffectNode::move_down() {
int index_of_effect = parent_clip->IndexOfEffect(this);
if (index_of_effect == parent_clip->effects.size()-1) {
return;
}
MoveEffectCommand* command = new MoveEffectCommand();
command->clip = parent_clip;
command->from = index_of_effect;
command->to = command->from + 1;
olive::undo_stack.push(command);
panel_effect_controls->Reload();
panel_sequence_viewer->viewer_widget()->frame_update();
}
void OldEffectNode::save_to_file() {
// save effect settings to file
QString file = QFileDialog::getSaveFileName(olive::MainWindow,
tr("Save Effect Settings"),
QString(),
tr("Effect XML Settings %1").arg("(*.xml)"));
// if the user picked a file
if (!file.isEmpty()) {
// ensure file ends with .xml extension
if (!file.endsWith(".xml", Qt::CaseInsensitive)) {
file.append(".xml");
}
QFile file_handle(file);
if (file_handle.open(QFile::WriteOnly)) {
file_handle.write(save_to_string());
file_handle.close();
} else {
QMessageBox::critical(olive::MainWindow,
tr("Save Settings Failed"),
tr("Failed to open \"%1\" for writing.").arg(file),
QMessageBox::Ok);
}
}
}
void OldEffectNode::load_from_file() {
// load effect settings from file
QString file = QFileDialog::getOpenFileName(olive::MainWindow,
tr("Load Effect Settings"),
QString(),
tr("Effect XML Settings %1").arg("(*.xml)"));
// if the user picked a file
if (!file.isEmpty()) {
QFile file_handle(file);
if (file_handle.open(QFile::ReadOnly)) {
olive::undo_stack.push(new SetEffectData(this, file_handle.readAll()));
file_handle.close();
update_ui(false);
} else {
QMessageBox::critical(olive::MainWindow,
tr("Load Settings Failed"),
tr("Failed to open \"%1\" for reading.").arg(file),
QMessageBox::Ok);
}
}
}
bool OldEffectNode::AlwaysUpdate()
{
return false;
}
bool OldEffectNode::IsEnabled() {
return enabled_;
}
bool OldEffectNode::IsExpanded()
{
return expanded_;
}
void OldEffectNode::SetExpanded(bool e)
{
expanded_ = e;
}
void OldEffectNode::SetPos(const QPointF &pos)
{
pos_ = pos;
}
void OldEffectNode::SetEnabled(bool b) {
enabled_ = b;
emit EnabledChanged(b);
}
void OldEffectNode::load(QXmlStreamReader& stream) {
/*
int row_count = 0;
QString tag = stream.name().toString();
while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) {
stream.readNext();
if (stream.name() == "row" && stream.isStartElement()) {
if (row_count < rows.size()) {
EffectRow* row = rows.at(row_count);
while (!stream.atEnd() && !(stream.name() == "row" && stream.isEndElement())) {
stream.readNext();
// read field
if (stream.name() == "field" && stream.isStartElement()) {
int field_number = -1;
// match field using ID
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "id") {
for (int l=0;l<row->FieldCount();l++) {
if (row->Field(l)->id() == attr.value()) {
field_number = l;
break;
}
}
break;
}
}
if (field_number > -1) {
EffectField* field = row->Field(field_number);
// get current field value
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "value") {
field->persistent_data_ = field->ConvertStringToValue(attr.value().toString());
break;
}
}
while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) {
stream.readNext();
// read keyframes
if (stream.name() == "key" && stream.isStartElement()) {
row->SetKeyframingInternal(true);
EffectKeyframe key;
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "value") {
key.data = field->ConvertStringToValue(attr.value().toString());
} else if (attr.name() == "frame") {
key.time = attr.value().toLong();
} else if (attr.name() == "type") {
key.type = attr.value().toInt();
} else if (attr.name() == "prehx") {
key.pre_handle_x = attr.value().toDouble();
} else if (attr.name() == "prehy") {
key.pre_handle_y = attr.value().toDouble();
} else if (attr.name() == "posthx") {
key.post_handle_x = attr.value().toDouble();
} else if (attr.name() == "posthy") {
key.post_handle_y = attr.value().toDouble();
}
}
field->keyframes.append(key);
}
}
field->Changed();
}
}
}
} else {
qCritical() << "Too many rows for effect" << id << ". Project might be corrupt. (Got" << row_count << ", expected <" << rows.size()-1 << ")";
}
row_count++;
} else if (stream.isStartElement()) {
custom_load(stream);
}
}
*/
}
void OldEffectNode::custom_load(QXmlStreamReader &) {}
void OldEffectNode::save(QXmlStreamWriter& stream) {
/*
stream.writeAttribute("name", meta->category + "/" + meta->name);
stream.writeAttribute("enabled", QString::number(IsEnabled()));
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
if (row->IsSavable()) {
stream.writeStartElement("row"); // row
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
if (!field->id().isEmpty()) {
stream.writeStartElement("field"); // field
stream.writeAttribute("id", field->id());
stream.writeAttribute("value", field->ConvertValueToString(field->persistent_data_));
for (int k=0;k<field->keyframes.size();k++) {
const EffectKeyframe& key = field->keyframes.at(k);
stream.writeStartElement("key");
stream.writeAttribute("value", field->ConvertValueToString(key.data));
stream.writeAttribute("frame", QString::number(key.time));
stream.writeAttribute("type", QString::number(key.type));
stream.writeAttribute("prehx", QString::number(key.pre_handle_x));
stream.writeAttribute("prehy", QString::number(key.pre_handle_y));
stream.writeAttribute("posthx", QString::number(key.post_handle_x));
stream.writeAttribute("posthy", QString::number(key.post_handle_y));
stream.writeEndElement(); // key
}
stream.writeEndElement(); // field
}
}
stream.writeEndElement(); // row
}
}
*/
}
void OldEffectNode::load_from_string(const QByteArray &s) {
// clear existing keyframe data
for (int i=0;i<rows.size();i++) {
NodeIO* row = rows.at(i);
row->SetKeyframingInternal(false);
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
field->keyframes.clear();
}
}
// write settings with xml writer
QXmlStreamReader stream(s);
bool found_id = false;
while (!stream.atEnd()) {
stream.readNext();
// find the effect opening tag
if (stream.name() == "effect" && stream.isStartElement()) {
// check the name to see if it matches this effect
const QXmlStreamAttributes& attributes = stream.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "id") {
load(stream);
found_id = true;
break;
}
}
// we've found what we're looking for
break;
}
}
if (!found_id) {
QMessageBox::critical(olive::MainWindow,
tr("Load Settings Failed"),
tr("This settings file doesn't match this effect."),
QMessageBox::Ok);
}
}
QByteArray OldEffectNode::save_to_string() {
QByteArray save_data;
// write settings to string with xml writer
QXmlStreamWriter stream(&save_data);
stream.writeStartDocument();
stream.writeStartElement("effect");
// pass off to standard saving function
save(stream);
stream.writeEndElement(); // effect
stream.writeEndDocument();
return save_data;
}
bool OldEffectNode::is_open() {
return isOpen;
}
void OldEffectNode::validate_meta_path() {
/*
if (!meta->path.isEmpty() || (shader_vert_path_.isEmpty() && shader_frag_path_.isEmpty())) return;
QList<QString> effects_paths = get_effects_paths();
const QString& test_fn = shader_vert_path_.isEmpty() ? shader_frag_path_ : shader_vert_path_;
for (int i=0;i<effects_paths.size();i++) {
if (QFileInfo::exists(effects_paths.at(i) + "/" + test_fn)) {
for (int j=0;j<olive::effects.size();j++) {
if (&olive::effects.at(j) == meta) {
olive::effects[j].path = effects_paths.at(i);
return;
}
}
return;
}
}
*/
}
void OldEffectNode::open() {
/*
if (isOpen) {
qWarning() << "Tried to open an effect that was already open";
close();
}
if (olive::runtime_config.shaders_are_enabled && (Flags() & ShaderFlag)) {
if (QOpenGLContext::currentContext() == nullptr) {
qWarning() << "No current context to create a shader program for - will retry next repaint";
} else {
validate_meta_path();
QString frag_shader_str;
QString frag_file_url = QDir(file).filePath(shader_frag_path_);
QFile frag_file(frag_file_url);
if (frag_file.open(QFile::ReadOnly)) {
frag_shader_str = frag_file.readAll();
frag_file.close();
} else {
qWarning() << "Failed to open" << frag_file_url;
}
if (!frag_shader_str.isEmpty()) {
QString shader_func;
if (!shader_function_name_.isEmpty()) {
shader_func = shader_function_name_;
} else {
shader_func = "process";
}
shader_program_ = olive::shader::GetPipeline(shader_func, frag_shader_str);
}
isOpen = true;
}
} else {
isOpen = true;
}
*/
isOpen = true;
}
void OldEffectNode::close() {
if (!isOpen) {
qWarning() << "Tried to close an effect that was already closed";
}
delete_texture();
shader_program_ = nullptr;
isOpen = false;
}
bool OldEffectNode::is_shader_linked() {
return shader_program_ != nullptr && shader_program_->isLinked();
}
QOpenGLShaderProgram *OldEffectNode::GetShaderPipeline()
{
return shader_program_.get();
}
int OldEffectNode::Flags()
{
return flags_;
}
void OldEffectNode::SetFlags(int flags)
{
flags_ = flags;
}
int OldEffectNode::getIterations() {
return iterations;
}
void OldEffectNode::setIterations(int i) {
iterations = i;
}
const QPointF &OldEffectNode::pos()
{
return pos_;
}
void OldEffectNode::process_image(double, uint8_t *, uint8_t *, int){}
OldEffectNodePtr OldEffectNode::copy(Clip *c) {
OldEffectNodePtr copy = Create(c);
copy->SetEnabled(IsEnabled());
copy_field_keyframes(copy);
return copy;
}
void OldEffectNode::process_shader(double timecode, GLTextureCoords&, int iteration) {
/*
shader_program_->bind();
shader_program_->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height());
shader_program_->setUniformValue("time", GLfloat(timecode));
shader_program_->setUniformValue("iteration", iteration);
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
if (!field->id().isEmpty()) {
switch (field->type()) {
case EffectField::EFFECT_FIELD_DOUBLE:
{
DoubleField* double_field = static_cast<DoubleField*>(field);
shader_program_->setUniformValue(double_field->id().toUtf8().constData(),
GLfloat(double_field->GetDoubleAt(timecode)));
}
break;
case EffectField::EFFECT_FIELD_COLOR:
{
ColorField* color_field = static_cast<ColorField *>(field);
shader_program_->setUniformValue(
color_field->id().toUtf8().constData(),
GLfloat(color_field->GetColorAt(timecode).redF()),
GLfloat(color_field->GetColorAt(timecode).greenF()),
GLfloat(color_field->GetColorAt(timecode).blueF())
);
}
break;
case EffectField::EFFECT_FIELD_BOOL:
shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool());
break;
case EffectField::EFFECT_FIELD_COMBO:
shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt());
break;
// can you even send a string to a uniform value?
case EffectField::EFFECT_FIELD_STRING:
case EffectField::EFFECT_FIELD_FONT:
case EffectField::EFFECT_FIELD_FILE:
case EffectField::EFFECT_FIELD_UI:
break;
}
}
}
}
shader_program_->release();
*/
}
void OldEffectNode::process_coords(double, GLTextureCoords&, int) {}
GLuint OldEffectNode::process_superimpose(QOpenGLContext* ctx, double timecode) {
bool dimensions_changed = false;
bool redrew_image = false;
int width = parent_clip->media_width();
int height = parent_clip->media_height();
if (width != img.width() || height != img.height()) {
img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied);
dimensions_changed = true;
}
if (valueHasChanged(timecode) || dimensions_changed || AlwaysUpdate()) {
redraw(timecode);
redrew_image = true;
}
QOpenGLFunctions* f = ctx->functions();
if (texture == 0 || tex_width_ != img.width() || tex_height_ != img.height()) {
delete_texture();
tex_width_ = img.width();
tex_height_ = img.height();
// create texture object
f->glGenTextures(1, &texture);
f->glBindTexture(GL_TEXTURE_2D, texture);
// set texture filtering to bilinear
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA8, tex_width_, tex_height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits()
);
f->glBindTexture(GL_TEXTURE_2D, 0);
redrew_image = false;
}
if (redrew_image) {
f->glBindTexture(GL_TEXTURE_2D, texture);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_width_, tex_height_, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits());
f->glBindTexture(GL_TEXTURE_2D, 0);
}
return texture;
}
void OldEffectNode::process_audio(double, double, float **, int, int, int) {}
void OldEffectNode::gizmo_draw(double, GLTextureCoords &) {}
void OldEffectNode::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) {
// Loop through each gizmo to find `gizmo`
for (int i=0;i<gizmos.size();i++) {
if (gizmos.at(i) == gizmo) {
// If (!done && gizmo_dragging_actions_.isEmpty()), that means the drag just started and we're going to save
// the current state of the attach fields' keyframes in KeyframeDataChange objects to make the changes undoable
// by the user later.
if (!done && gizmo_dragging_actions_.isEmpty()) {
if (gizmo->x_field1 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->x_field1));
}
if (gizmo->y_field1 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->y_field1));
}
if (gizmo->x_field2 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->x_field2));
}
if (gizmo->y_field2 != nullptr) {
gizmo_dragging_actions_.append(new KeyframeDataChange(gizmo->y_field2));
}
}
// Update the field values
if (gizmo->x_field1 != nullptr) {
gizmo->x_field1->SetValueAt(timecode,
gizmo->x_field1->GetDoubleAt(timecode) + x_movement*gizmo->x_field_multi1);
}
if (gizmo->y_field1 != nullptr) {
gizmo->y_field1->SetValueAt(timecode,
gizmo->y_field1->GetDoubleAt(timecode) + y_movement*gizmo->y_field_multi1);
}
if (gizmo->x_field2 != nullptr) {
gizmo->x_field2->SetValueAt(timecode,
gizmo->x_field2->GetDoubleAt(timecode) + x_movement*gizmo->x_field_multi2);
}
if (gizmo->y_field2 != nullptr) {
gizmo->y_field2->SetValueAt(timecode,
gizmo->y_field2->GetDoubleAt(timecode) + y_movement*gizmo->y_field_multi2);
}
// If (done && !gizmo_dragging_actions_.isEmpty()), that means the drag just ended and we're going to save
// the new state of the attach fields' keyframes in KeyframeDataChange objects to make the changes undoable
// by the user later.
if (done && !gizmo_dragging_actions_.isEmpty()) {
// Store all the KeyframeDataChange objects into a ComboAction to send to the undo stack (makes them all
// undoable together rather than having to be undone individually).
ComboAction* ca = new ComboAction();
for (int j=0;j<gizmo_dragging_actions_.size();j++) {
// Set the current state of the keyframes as the "new" keyframes (the old values were set earlier when the
// KeyframeDataChange object was constructed).
gizmo_dragging_actions_.at(j)->SetNewKeyframes();
// Add this KeyframeDataChange object to the ComboAction
ca->append(gizmo_dragging_actions_.at(j));
}
olive::undo_stack.push(ca);
gizmo_dragging_actions_.clear();
}
break;
}
}
}
void OldEffectNode::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& projection) {
for (int i=0;i<gizmos.size();i++) {
EffectGizmo* g = gizmos.at(i);
for (int j=0;j<g->get_point_count();j++) {
// Convert the world point from the gizmo into a screen point relative to the sequence's dimensions
QVector3D screen_pos = g->world_pos.at(j).project(matrix,
projection,
QRect(0,
0,
parent_clip->track()->sequence()->width,
parent_clip->track()->sequence()->height));
g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->track()->sequence()->height-screen_pos.y());
}
}
}
bool OldEffectNode::are_gizmos_enabled() {
return (gizmos.size() > 0);
}
double OldEffectNode::Now()
{
return playhead_to_clip_seconds(parent_clip, parent_clip->track()->sequence()->playhead);
}
long OldEffectNode::NowInFrames()
{
return playhead_to_clip_frame(parent_clip, parent_clip->track()->sequence()->playhead);
}
void OldEffectNode::redraw(double) {
/*
// run javascript
QPainter p(&img);
painter_wrapper.img = &img;
painter_wrapper.painter = &p;
jsEngine.globalObject().setProperty("painter", wrapper_obj);
jsEngine.globalObject().setProperty("width", parent_clip->media_width());
jsEngine.globalObject().setProperty("height", parent_clip->media_height());
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
for (int j=0;j<row->fieldCount();j++) {
EffectField* field = row->field(j);
if (!field->id.isEmpty()) {
switch (field->type) {
case EffectField::EFFECT_FIELD_DOUBLE:
jsEngine.globalObject().setProperty(field->id, field->get_double_value(timecode));
break;
case EffectField::EFFECT_FIELD_COLOR:
jsEngine.globalObject().setProperty(field->id, field->get_color_value(timecode).name());
break;
case EffectField::EFFECT_FIELD_STRING:
jsEngine.globalObject().setProperty(field->id, field->get_string_value(timecode));
break;
case EffectField::EFFECT_FIELD_BOOL:
jsEngine.globalObject().setProperty(field->id, field->get_bool_value(timecode));
break;
case EffectField::EFFECT_FIELD_COMBO:
jsEngine.globalObject().setProperty(field->id, field->get_combo_index(timecode));
break;
case EffectField::EFFECT_FIELD_FONT:
jsEngine.globalObject().setProperty(field->id, field->get_font_name(timecode));
break;
}
}
}
}
jsEngine.evaluate(script);
*/
}
bool OldEffectNode::valueHasChanged(double timecode) {
if (cachedValues.isEmpty()) {
for (int i=0;i<row_count();i++) {
NodeIO* crow = row(i);
for (int j=0;j<crow->FieldCount();j++) {
cachedValues.append(crow->Field(j)->GetValueAt(timecode));
}
}
return true;
} else {
bool changed = false;
int index = 0;
for (int i=0;i<row_count();i++) {
NodeIO* crow = row(i);
for (int j=0;j<crow->FieldCount();j++) {
EffectField* field = crow->Field(j);
if (cachedValues.at(index) != field->GetValueAt(timecode)) {
changed = true;
}
cachedValues[index] = field->GetValueAt(timecode);
index++;
}
}
return changed;
}
}
void OldEffectNode::delete_texture() {
if (texture_ctx != nullptr) {
texture_ctx->functions()->glDeleteTextures(1, &texture);
texture = 0;
texture_ctx = nullptr;
}
}
int GetNodeLibraryIndexFromId(const QString& id) {
for (int i=0;i<olive::node_library.size();i++) {
if (olive::node_library.at(i)->id() == id) {
return i;
}
}
return -1;
}
/*
const EffectMeta* Node::GetMetaFromName(const QString& input) {
int split_index = input.indexOf('/');
QString category;
if (split_index > -1) {
category = input.left(split_index);
}
QString name = input.mid(split_index + 1);
for (int j=0;j<olive::effects.size();j++) {
if (olive::effects.at(j).name == name
&& (olive::effects.at(j).category == category
|| category.isEmpty())) {
return &olive::effects.at(j);
}
}
return nullptr;
}
*/
+286
View File
@@ -0,0 +1,286 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 EFFECT_H
#define EFFECT_H
#include <memory>
#include <QObject>
#include <QString>
#include <QVector>
#include <QColor>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMutex>
#include <QThread>
#include <QLabel>
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QMouseEvent>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <random>
#include "timeline/tracktypes.h"
#include "rendering/qopenglshaderprogramptr.h"
#include "inputs.h"
#include "effects/effectgizmo.h"
class EffectGizmo;
class KeyframeDataChange;
class Clip;
using ClipPtr = std::shared_ptr<Clip>;
class OldEffectNode;
using OldEffectNodePtr = std::shared_ptr<OldEffectNode>;
enum NodeType {
kTransformEffect,
kTextInput,
kSolidInput,
kNoiseInput,
kVolumeEffect,
kPanEffect,
kToneInput,
kShakeEffect,
kTimecodeEffect,
kMaskEffect,
kFillLeftRightEffect,
kVstEffect,
kCornerPinEffect,
kRichTextInput,
kMediaInput,
kShaderEffect,
kImageOutput,
kCrossDissolveTransition,
kLinearFadeTransition,
kExponentialFadeTransition,
kLogarithmicFadeTransition,
kInvalidNode
};
double log_volume(double linear);
enum EffectType {
EFFECT_TYPE_INVALID,
EFFECT_TYPE_EFFECT,
EFFECT_TYPE_TRANSITION
};
enum EffectKeyframeType {
EFFECT_KEYFRAME_LINEAR,
EFFECT_KEYFRAME_BEZIER,
EFFECT_KEYFRAME_HOLD
};
struct GLTextureCoords {
QMatrix4x4 matrix;
QVector3D vertex_top_left;
QVector3D vertex_top_right;
QVector3D vertex_bottom_left;
QVector3D vertex_bottom_right;
QVector2D texture_top_left;
QVector2D texture_top_right;
QVector2D texture_bottom_left;
QVector2D texture_bottom_right;
float opacity;
};
class OldEffectNode : public QObject {
Q_OBJECT
public:
OldEffectNode(Clip *c);
~OldEffectNode();
Clip* parent_clip;
virtual QString name() = 0;
virtual QString id() = 0;
virtual QString category();
virtual QString description();
virtual EffectType type() = 0;
virtual olive::TrackType subtype() = 0;
virtual bool IsCreatable();
virtual OldEffectNodePtr Create(Clip *c) = 0;
void AddRow(NodeIO* row);
int IndexOfRow(NodeIO* row);
NodeIO* row(int i);
int row_count();
EffectGizmo* add_gizmo(int type);
EffectGizmo* gizmo(int i);
int gizmo_count();
QVector<NodeEdgePtr> GetAllEdges();
bool IsEnabled();
bool IsExpanded();
virtual void refresh();
virtual OldEffectNodePtr copy(Clip* c);
void copy_field_keyframes(OldEffectNodePtr e);
virtual void load(QXmlStreamReader& stream);
virtual void custom_load(QXmlStreamReader& stream);
virtual void save(QXmlStreamWriter& stream);
void load_from_string(const QByteArray &s);
QByteArray save_to_string();
// glsl handling
bool is_open();
void open();
void close();
bool is_shader_linked();
QOpenGLShaderProgram* GetShaderPipeline();
enum VideoEffectFlags {
ShaderFlag = 0x1,
CoordsFlag = 0x2,
SuperimposeFlag = 0x4
};
int Flags();
void SetFlags(int flags);
int getIterations();
void setIterations(int i);
const QPointF& pos();
virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size);
virtual void process_shader(double timecode, GLTextureCoords&, int iteration);
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
virtual GLuint process_superimpose(QOpenGLContext *ctx, double timecode);
virtual void process_audio(double timecode_start, double timecode_end, float **samples, int nb_samples, int nb_channels, int type);
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
void gizmo_world_to_screen(const QMatrix4x4 &matrix, const QMatrix4x4 &projection);
bool are_gizmos_enabled();
/**
* @brief Get the current clip/media time
*
* A convenience function that can be plugged into GetValueAt() to get the value wherever the appropriate Sequence's
* playhead it.
*
* @return
*
* Current clip/media time in seconds.
*/
double Now();
/**
* @brief Retrieve the current clip as a frame number
*
* Same as Now() but retrieves the value as a frame number (in the appropriate Sequence's frame rate) instead of
* seconds.
*
* @return
*
* The current clip time in frames
*/
long NowInFrames();
template <typename T>
T randomNumber()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<> distribution(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
return distribution(generator);
}
template <typename T>
T randomFloat()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<> distribution(-1.0, 1.0);
return distribution(generator);
}
public slots:
void FieldChanged();
void SetEnabled(bool b);
void SetExpanded(bool e);
void SetPos(const QPointF& pos);
signals:
void EnabledChanged(bool);
private slots:
void delete_self();
void move_up();
void move_down();
void save_to_file();
void load_from_file();
protected:
// glsl effect
QOpenGLShaderProgramPtr shader_program_;
QString shader_vert_path_;
QString shader_frag_path_;
QString shader_function_name_;
// superimpose effect
QImage img;
GLuint texture;
QOpenGLContext* texture_ctx;
int tex_width_;
int tex_height_;
// enable effect to update constantly
virtual bool AlwaysUpdate();
private:
bool isOpen;
QVector<NodeIO*> rows;
QVector<EffectGizmo*> gizmos;
bool bound;
int iterations;
bool enabled_;
bool expanded_;
int flags_;
QVector<KeyframeDataChange*> gizmo_dragging_actions_;
QPointF pos_;
// superimpose functions
virtual void redraw(double timecode);
bool valueHasChanged(double timecode);
QVector<QVariant> cachedValues;
void delete_texture();
void validate_meta_path();
};
namespace olive {
extern QVector<OldEffectNodePtr> node_library;
}
#endif // EFFECT_H
+2 -2
View File
@@ -1,7 +1,7 @@
#include "buttonwidget.h"
ButtonWidget::ButtonWidget(Node* parent, const QString& name, const QString& text) :
EffectRow(parent, nullptr, name, false, false)
ButtonWidget::ButtonWidget(OldEffectNode* parent, const QString& name, const QString& text) :
NodeIO(parent, nullptr, name, false, false)
{
ButtonField* button_field = new ButtonField(this, text);
+3 -3
View File
@@ -1,12 +1,12 @@
#ifndef BUTTONWIDGET_H
#define BUTTONWIDGET_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class ButtonWidget : public EffectRow
class ButtonWidget : public NodeIO
{
public:
ButtonWidget(Node* parent, const QString& name, const QString& text);
ButtonWidget(OldEffectNode* parent, const QString& name, const QString& text);
/**
* @brief Wrapper for ButtonField::SetCheckable.
+2 -2
View File
@@ -1,7 +1,7 @@
#include "labelwidget.h"
LabelWidget::LabelWidget(Node *parent, const QString &name, const QString &text) :
EffectRow(parent, nullptr, name, false, false)
LabelWidget::LabelWidget(OldEffectNode *parent, const QString &name, const QString &text) :
NodeIO(parent, nullptr, name, false, false)
{
AddField(new LabelField(this, text));
}
+3 -3
View File
@@ -1,12 +1,12 @@
#ifndef LABELWIDGET_H
#define LABELWIDGET_H
#include "effects/effectrow.h"
#include "nodes/nodeio.h"
class LabelWidget : public EffectRow
class LabelWidget : public NodeIO
{
public:
LabelWidget(Node* parent, const QString& name, const QString& text);
LabelWidget(OldEffectNode* parent, const QString& name, const QString& text);
};
#endif // LABELWIDGET_H