diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 2d433e223..662a8e73f 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(generator) add_subdirectory(output) +add_subdirectory(processor) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 3fac44ea9..97e7bd4b2 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -1,14 +1,45 @@ #include "solid.h" SolidGenerator::SolidGenerator(QObject *parent) : - Node(parent) + Node(parent), + texture_(nullptr) { + texture_output_ = new NodeOutput(this); + texture_output_->set_data_type(NodeOutput::kTexture); +} +QString SolidGenerator::Name() +{ + return tr("Solid"); +} + +QString SolidGenerator::Category() +{ + return tr("Generator"); +} + +QString SolidGenerator::Description() +{ + return tr("Generate a solid color."); +} + +NodeOutput *SolidGenerator::texture_output() +{ + return texture_output_; } void SolidGenerator::Process(const rational &time) { Q_UNUSED(time) + // FIXME: Test code + if (texture_ == nullptr) { + QImage img(1920, 1080, QImage::Format_RGBA8888_Premultiplied); + img.fill(Qt::red); + texture_ = new QOpenGLTexture(img); + } + + texture_output_->set_value(texture_->textureId()); + // End test code } diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 742999d85..50e7af114 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -1,17 +1,29 @@ #ifndef SOLIDGENERATOR_H #define SOLIDGENERATOR_H +#include + #include "node/node.h" class SolidGenerator : public Node { + Q_OBJECT public: SolidGenerator(QObject* parent = nullptr); + virtual QString Name() override; + virtual QString Category() override; + virtual QString Description() override; + + NodeOutput* texture_output(); + +public slots: virtual void Process(const rational &time) override; private: NodeOutput* texture_output_; + + QOpenGLTexture* texture_; }; #endif // SOLIDGENERATOR_H diff --git a/app/node/node.cpp b/app/node/node.cpp index 23080ce1a..2159d236f 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -24,3 +24,48 @@ Node::Node(QObject *parent) : QObject(parent) { } + +QString Node::Category() +{ + // Return an empty category for any nodes that don't use one + return QString(); +} + +QString Node::Description() +{ + // Return an empty string by default + return QString(); +} + +void Node::InvalidateCache(const rational &start_range, const rational &end_range) +{ + Q_UNUSED(start_range) + Q_UNUSED(end_range) + + /* + ParamList params = Parameters(); + + for (int i=0;itype() == NodeParam::kOutput + && param->) { + + } + } + */ +} + +Node::ParamList Node::Parameters() +{ + const QObjectList& child_list = children(); + + ParamList params; + params.reserve(child_list.size()); + + for (int i=0;i(child_list.at(i)); + } + + return params; +} diff --git a/app/node/node.h b/app/node/node.h index 7b6253ac0..423f3c421 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -23,14 +23,27 @@ #include -#include "node/param.h" +#include "node/input.h" +#include "node/output.h" #include "rational.h" class Node : public QObject { + Q_OBJECT public: Node(QObject* parent = nullptr); + virtual QString Name() = 0; + virtual QString Category(); + virtual QString Description(); + + virtual void InvalidateCache(const rational& start_range, const rational& end_range); + + using ParamList = QList; + + ParamList Parameters(); + +public slots: virtual void Process(const rational& time) = 0; }; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index f03f83730..424de91f0 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -1,6 +1,57 @@ #include "viewer.h" +#include "panel/panelfocusmanager.h" + ViewerOutput::ViewerOutput(QObject* parent) : - Node(parent) + Node(parent), + attached_viewer_(nullptr) { + texture_input_ = new NodeInput(this); + texture_input_->add_data_input(NodeInput::kTexture); +} + +QString ViewerOutput::Name() +{ + return tr("Viewer"); +} + +QString ViewerOutput::Category() +{ + return tr("Output"); +} + +QString ViewerOutput::Description() +{ + return tr("Interface between a Viewer panel and the node system."); +} + +NodeInput *ViewerOutput::texture_input() +{ + return texture_input_; +} + +void ViewerOutput::Process(const rational &time) +{ + if (attached_viewer_ != nullptr) { + // Get the texture from whatever Node is currently connected (usually a Renderer of some kind) + GLuint current_texture = texture_input_->get_value(time).value(); + + // Send the texture to the Viewer + attached_viewer_->SetTexture(current_texture); + } +} + +void ViewerOutput::AttachViewer(ViewerPanel *viewer) +{ + // Disconnect old viewer if there's one attached + if (attached_viewer_ != nullptr) { + disconnect(attached_viewer_, SIGNAL(TimeChanged(const rational&)), this, SLOT(Process(const rational&))); + } + + // FIXME: Currently this attaches to ViewerPanels, but should it attached to Viewers instead? + attached_viewer_ = viewer; + + if (attached_viewer_ != nullptr) { + connect(attached_viewer_, SIGNAL(TimeChanged(const rational&)), this, SLOT(Process(const rational&))); + } } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index d480e5e52..88269f4b4 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -2,14 +2,29 @@ #define VIEWER_H #include "node/node.h" +#include "panel/viewer/viewer.h" class ViewerOutput : public Node { + Q_OBJECT public: ViewerOutput(QObject* parent = nullptr); + virtual QString Name() override; + virtual QString Category() override; + virtual QString Description() override; + + NodeInput* texture_input(); + + void AttachViewer(ViewerPanel* viewer); + +public slots: + virtual void Process(const rational &time) override; + private: NodeInput* texture_input_; + + ViewerPanel* attached_viewer_; }; #endif // VIEWER_H diff --git a/app/render/renderer/CMakeLists.txt b/app/node/processor/CMakeLists.txt similarity index 85% rename from app/render/renderer/CMakeLists.txt rename to app/node/processor/CMakeLists.txt index 14071a2fb..090aa2040 100644 --- a/app/render/renderer/CMakeLists.txt +++ b/app/node/processor/CMakeLists.txt @@ -14,11 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(renderer) + set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/renderer/renderer.h - render/renderer/renderer.cpp - render/renderer/rendererthread.h - render/renderer/rendererthread.cpp PARENT_SCOPE ) diff --git a/app/node/processor/renderer/CMakeLists.txt b/app/node/processor/renderer/CMakeLists.txt new file mode 100644 index 000000000..6f78b5cc4 --- /dev/null +++ b/app/node/processor/renderer/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/processor/renderer/renderer.h + node/processor/renderer/renderer.cpp + node/processor/renderer/rendererthread.h + node/processor/renderer/rendererthread.cpp + PARENT_SCOPE +) diff --git a/app/render/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp similarity index 69% rename from app/render/renderer/renderer.cpp rename to app/node/processor/renderer/renderer.cpp index 315fb4c7c..ca8f26ef5 100644 --- a/app/render/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -20,13 +20,29 @@ #include "renderer.h" -Renderer::Renderer() : +RendererProcessor::RendererProcessor(QObject *parent) : + Node(parent), started_(false) { } -void Renderer::Start() +QString RendererProcessor::Name() +{ + return tr("Renderer"); +} + +QString RendererProcessor::Category() +{ + return tr("Processor"); +} + +QString RendererProcessor::Description() +{ + return tr("A multi-threaded OpenGL hardware-accelerated node compositor."); +} + +void RendererProcessor::Start() { if (started_) { return; @@ -42,7 +58,7 @@ void Renderer::Start() started_ = true; } -void Renderer::Stop() +void RendererProcessor::Stop() { if (!started_) { return; @@ -58,13 +74,7 @@ void Renderer::Stop() threads_.clear(); } -/*RendererThread *Renderer::CurrentThread() +RendererThread *RendererProcessor::CurrentThread() { - for (int i=0;i(QThread::currentThread()); +} diff --git a/app/node/processor/renderer/renderer.h b/app/node/processor/renderer/renderer.h new file mode 100644 index 000000000..b5bb8f323 --- /dev/null +++ b/app/node/processor/renderer/renderer.h @@ -0,0 +1,87 @@ +/*** + + 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 . + +***/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include "node/node.h" +#include "rendererthread.h" + +class RendererProcessor : public Node +{ + Q_OBJECT +public: + /** + * @brief Renderer Constructor + * + * Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop() + * when the Renderer is about to be destroyed. + */ + RendererProcessor(QObject* parent = nullptr); + + virtual QString Name() override; + virtual QString Category() override; + virtual QString Description() override; + + /** + * @brief Set parameters of the Renderer + * + * The Renderer owns the buffers that are used in the rendering process and this function sets the kind of buffers + * to use. The Renderer must be stopped when calling this function. + * + * @param width + * + * Buffer width + * + * @param height + * + * Buffer height + * + * @param format + * + * Buffer pixel format + */ + void SetParameters(const int& width, const int& height, const olive::PixelFormat& format); + + /** + * @brief Allocate and start the multithreaded backend + */ + void Start(); + + /** + * @brief Terminate and deallocate the multithreaded backend + */ + void Stop(); + + /** + * @brief Return current instance of a RenderThread (or nullptr if there is none) + * + * This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if + * the cast fails (e.g. if this function is called from the main thread rather than a RendererThread). + */ + static RendererThread* CurrentThread(); + +private: + QVector threads_; + + bool started_; +}; + +#endif // RENDERER_H diff --git a/app/render/renderer/rendererthread.cpp b/app/node/processor/renderer/rendererthread.cpp similarity index 100% rename from app/render/renderer/rendererthread.cpp rename to app/node/processor/renderer/rendererthread.cpp diff --git a/app/render/renderer/rendererthread.h b/app/node/processor/renderer/rendererthread.h similarity index 100% rename from app/render/renderer/rendererthread.h rename to app/node/processor/renderer/rendererthread.h diff --git a/app/panel/viewer/viewer.cpp b/app/panel/viewer/viewer.cpp index 04683be36..2d48c118f 100644 --- a/app/panel/viewer/viewer.cpp +++ b/app/panel/viewer/viewer.cpp @@ -20,21 +20,25 @@ #include "viewer.h" -#include "widget/viewer/viewer.h" - ViewerPanel::ViewerPanel(QWidget *parent) : PanelWidget(parent) { // QObject system handles deleting this - ViewerWidget* viewer = new ViewerWidget(this); + viewer_ = new ViewerWidget(this); + connect(viewer_, SIGNAL(TimeChanged(const rational&)), this, SIGNAL(TimeChanged(const rational&))); // Set ViewerWidget as the central widget - setWidget(viewer); + setWidget(viewer_); // Set strings Retranslate(); } +void ViewerPanel::SetTexture(GLuint tex) +{ + viewer_->SetTexture(tex); +} + void ViewerPanel::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { diff --git a/app/panel/viewer/viewer.h b/app/panel/viewer/viewer.h index 181441864..eb5eba7e3 100644 --- a/app/panel/viewer/viewer.h +++ b/app/panel/viewer/viewer.h @@ -21,7 +21,10 @@ #ifndef VIEWER_PANEL_H #define VIEWER_PANEL_H +#include + #include "widget/panel/panel.h" +#include "widget/viewer/viewer.h" /** * @brief Dockable wrapper around a ViewerWidget @@ -31,11 +34,26 @@ class ViewerPanel : public PanelWidget { public: ViewerPanel(QWidget* parent); +public slots: + /** + * @brief Set the texture to draw and draw it + * + * Wrapper function for Viewer::SetTexture(). + * + * @param tex + */ + void SetTexture(GLuint tex); + protected: virtual void changeEvent(QEvent* e) override; +signals: + void TimeChanged(const rational&); + private: void Retranslate(); + + ViewerWidget* viewer_; }; #endif // VIEWER_PANEL_H diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index d5c5b2286..3161ab4ff 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -15,7 +15,6 @@ # along with this program. If not, see . add_subdirectory(gl) -add_subdirectory(renderer) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/render/renderer/renderer.h b/app/render/renderer/renderer.h deleted file mode 100644 index 57178022f..000000000 --- a/app/render/renderer/renderer.h +++ /dev/null @@ -1,43 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef RENDERER_H -#define RENDERER_H - -#include "render/renderer/rendererthread.h" - -class Renderer -{ -public: - Renderer(); - - void Start(); - - void Stop(); - - //RendererThread* CurrentThread(); - -private: - QVector threads_; - - bool started_; -}; - -#endif // RENDERER_H diff --git a/app/widget/playbackcontrols/playbackcontrols.cpp b/app/widget/playbackcontrols/playbackcontrols.cpp index a4a0e4bf4..b71595004 100644 --- a/app/widget/playbackcontrols/playbackcontrols.cpp +++ b/app/widget/playbackcontrols/playbackcontrols.cpp @@ -64,26 +64,31 @@ PlaybackControls::PlaybackControls(QWidget *parent) : QPushButton* go_to_start_btn = new QPushButton(); go_to_start_btn->setIcon(olive::icon::GoToStart); lower_middle_layout->addWidget(go_to_start_btn); + connect(go_to_start_btn, SIGNAL(clicked(bool)), this, SIGNAL(BeginClicked())); // Prev Frame Button QPushButton* prev_frame_btn = new QPushButton(); prev_frame_btn->setIcon(olive::icon::PrevFrame); lower_middle_layout->addWidget(prev_frame_btn); + connect(prev_frame_btn, SIGNAL(clicked(bool)), this, SIGNAL(PrevFrameClicked())); // Play/Pause Button QPushButton* play_btn = new QPushButton(); play_btn->setIcon(olive::icon::Play); lower_middle_layout->addWidget(play_btn); + connect(play_btn, SIGNAL(clicked(bool)), this, SIGNAL(PlayClicked())); // Next Frame Button QPushButton* next_frame_btn = new QPushButton(); next_frame_btn->setIcon(olive::icon::NextFrame); lower_middle_layout->addWidget(next_frame_btn); + connect(next_frame_btn, SIGNAL(clicked(bool)), this, SIGNAL(NextFrameClicked())); // Go To End Button QPushButton* go_to_end_btn = new QPushButton(); go_to_end_btn->setIcon(olive::icon::GoToEnd); lower_middle_layout->addWidget(go_to_end_btn); + connect(go_to_end_btn, SIGNAL(clicked(bool)), this, SIGNAL(EndClicked())); lower_middle_layout->addStretch(); diff --git a/app/widget/playbackcontrols/playbackcontrols.h b/app/widget/playbackcontrols/playbackcontrols.h index 59c5aee4e..ffb747c9b 100644 --- a/app/widget/playbackcontrols/playbackcontrols.h +++ b/app/widget/playbackcontrols/playbackcontrols.h @@ -31,6 +31,7 @@ */ class PlaybackControls : public QWidget { + Q_OBJECT public: PlaybackControls(QWidget* parent); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 802fa5718..1ac4139c8 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -40,4 +40,18 @@ ViewerWidget::ViewerWidget(QWidget *parent) : controls_->SetTimecodeEnabled(true); controls_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); layout->addWidget(controls_); + + // FIXME: Test code + connect(controls_, SIGNAL(PlayClicked()), this, SLOT(TemporaryTestFunction())); + // End test code +} + +void ViewerWidget::SetTexture(GLuint tex) +{ + gl_widget_->SetTexture(tex); +} + +void ViewerWidget::TemporaryTestFunction() +{ + emit TimeChanged(69); } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 6cd1325ae..f45425408 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -25,6 +25,7 @@ #include #include +#include "rational.h" #include "viewerglwidget.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -39,10 +40,25 @@ public: void SetPlaybackControlsEnabled(bool enabled); +public slots: + /** + * @brief Set the texture to draw and draw it + * + * Wrapper function for ViewerGLWidget::SetTexture(). + * + * @param tex + */ + void SetTexture(GLuint tex); + +signals: + void TimeChanged(const rational&); + private: ViewerGLWidget* gl_widget_; PlaybackControls* controls_; +private slots: + void TemporaryTestFunction(); }; #endif // VIEWER_WIDGET_H diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index a84d6ac38..61e589f98 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -52,14 +52,16 @@ public: */ ViewerGLWidget(QWidget* parent); +public slots: /** * @brief Set the texture to draw and draw it * - * Use this function + * Use this function to update the viewer. * * @param tex */ void SetTexture(GLuint tex); + protected: /** * @brief Initialize function to set up the OpenGL context upon its construction diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index c3a463617..193f9f2f3 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -32,6 +32,11 @@ // Main menu bar #include "mainmenu.h" +// FIXME: Test code +#include "node/output/viewer/viewer.h" +#include "node/generator/solid/solid.h" +// End test code + olive::MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { @@ -73,4 +78,11 @@ void olive::MainWindow::ProjectOpen(Project* p) TimelinePanel* timeline_panel = new TimelinePanel(this); addDockWidget(Qt::BottomDockWidgetArea, timeline_panel); + + // FIXME: Test code + ViewerOutput* vo = new ViewerOutput(this); + vo->AttachViewer(viewer_panel2); + SolidGenerator* sg = new SolidGenerator(this); + NodeInput::ConnectEdge(sg->texture_output(), vo->texture_input()); + // End test code }