diff --git a/src/interface/modeler/quickapp/MainModelerWindow.cpp b/src/interface/modeler/quickapp/MainModelerWindow.cpp index 9148218..c82ceaf 100644 --- a/src/interface/modeler/quickapp/MainModelerWindow.cpp +++ b/src/interface/modeler/quickapp/MainModelerWindow.cpp @@ -5,6 +5,11 @@ #include "OpenGLRenderer.h" #include "AtmosphereModeler.h" #include "WaterModeler.h" +#include "RenderPreviewProvider.h" +#include "RenderProcess.h" +#include "RenderConfig.h" + +#include MainModelerWindow::MainModelerWindow() { @@ -12,8 +17,13 @@ MainModelerWindow::MainModelerWindow() scenery->autoPreset(); renderer = new OpenGLRenderer(scenery); - qmlRegisterType("Paysages", 1, 0, "OpenGLView"); + render_preview_provider = new RenderPreviewProvider(); + render_process = new RenderProcess(render_preview_provider); + qmlRegisterType("Paysages", 1, 0, "OpenGLView"); + engine()->addImageProvider("renderpreviewprovider", render_preview_provider); + + setMinimumSize(QSize(1000, 800)); setTitle(QObject::tr("Paysages 3D")); setResizeMode(QQuickView::SizeRootObjectToView); setSource(QUrl("qrc:///main.qml")); @@ -27,6 +37,9 @@ MainModelerWindow::~MainModelerWindow() delete atmosphere; delete water; + delete render_preview_provider; + delete render_process; + delete renderer; delete scenery; } @@ -35,3 +48,26 @@ QObject *MainModelerWindow::findQmlObject(const QString &objectName) { return rootObject()->findChild(objectName); } + +void MainModelerWindow::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_F5) + { + // Start render in a thread + render_process->startRender(scenery, RenderConfig(400, 300, 1, 3)); + + // Resize preview + QSize preview_size = render_process->getPreviewSize(); + findQmlObject("preview_image")->setProperty("width", preview_size.width()); + findQmlObject("preview_image")->setProperty("height", preview_size.height()); + + // Show render dialog + rootObject()->setProperty("state", QString("Render Dialog")); + } + else if (event->key() == Qt::Key_Escape) + { + render_process->stopRender(); + + rootObject()->setProperty("state", QString("Init")); + } +} diff --git a/src/interface/modeler/quickapp/MainModelerWindow.h b/src/interface/modeler/quickapp/MainModelerWindow.h index 5d7b5f2..bc159c2 100644 --- a/src/interface/modeler/quickapp/MainModelerWindow.h +++ b/src/interface/modeler/quickapp/MainModelerWindow.h @@ -20,12 +20,18 @@ public: inline Scenery *getScenery() const {return scenery;} inline OpenGLRenderer *getRenderer() const {return renderer;} +protected: + virtual void keyReleaseEvent(QKeyEvent *event) override; + private: OpenGLRenderer *renderer; Scenery *scenery; AtmosphereModeler *atmosphere; WaterModeler *water; + + RenderPreviewProvider *render_preview_provider; + RenderProcess *render_process; }; } diff --git a/src/interface/modeler/quickapp/RenderPreviewProvider.cpp b/src/interface/modeler/quickapp/RenderPreviewProvider.cpp new file mode 100644 index 0000000..b6b5151 --- /dev/null +++ b/src/interface/modeler/quickapp/RenderPreviewProvider.cpp @@ -0,0 +1,85 @@ +#include "RenderPreviewProvider.h" + +#include "Canvas.h" +#include "CanvasPreview.h" +#include "Color.h" + +static inline QColor colorToQColor(Color color) +{ + color.normalize(); + return QColor(color.r * 255.0, color.g * 255.0, color.b * 255.0, color.a * 255.0); +} + +RenderPreviewProvider::RenderPreviewProvider() : + QQuickImageProvider(QQuickImageProvider::Image) +{ + canvas = NULL; + pixbuf = new QImage(1, 1, QImage::Format_ARGB32); +} + +RenderPreviewProvider::~RenderPreviewProvider() +{ + delete pixbuf; +} + +QImage RenderPreviewProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + if (canvas) + { + canvas->getPreview()->updateLive(this); + } + + if (size) + { + *size = pixbuf->size(); + } + + return *pixbuf; +} + +void RenderPreviewProvider::setCanvas(const Canvas *canvas) +{ + if (not this->canvas) + { + this->canvas = canvas; + canvas->getPreview()->initLive(this); + } +} + +void RenderPreviewProvider::releaseCanvas() +{ + if (canvas) + { + canvas->getPreview()->updateLive(this); + canvas = NULL; + } +} + +void RenderPreviewProvider::setToneMapping(const ColorProfile &profile) +{ + if (canvas) + { + canvas->getPreview()->setToneMapping(profile); + canvas->getPreview()->updateLive(this); + } +} + +void RenderPreviewProvider::canvasResized(int width, int height) +{ + if (QSize(width, height) != pixbuf->size()) + { + delete pixbuf; + pixbuf = new QImage(width, height, QImage::Format_ARGB32); + pixbuf->fill(Qt::black); + } +} + +void RenderPreviewProvider::canvasCleared(const Color &col) +{ + pixbuf->fill(colorToQColor(col)); +} + +void RenderPreviewProvider::canvasPainted(int x, int y, const Color &col) +{ + pixbuf->setPixel(x, pixbuf->height() - 1 - y, colorToQColor(col).rgb()); +} diff --git a/src/interface/modeler/quickapp/RenderPreviewProvider.h b/src/interface/modeler/quickapp/RenderPreviewProvider.h new file mode 100644 index 0000000..d11708c --- /dev/null +++ b/src/interface/modeler/quickapp/RenderPreviewProvider.h @@ -0,0 +1,53 @@ +#ifndef RENDERPREVIEWPROVIDER_H +#define RENDERPREVIEWPROVIDER_H + +#include "modeler_global.h" + +#include +#include "CanvasLiveClient.h" + +namespace paysages { +namespace modeler { + +/** + * Provider for a Qml Image content, filled from a canvas rendering. + */ +class RenderPreviewProvider : public QQuickImageProvider, public CanvasLiveClient +{ +public: + RenderPreviewProvider(); + virtual ~RenderPreviewProvider(); + + /** + * Set the canvas to watch and display, null to stop watching. + * + * This function must be called from the graphics thread. + */ + void setCanvas(const Canvas *canvas); + + /** + * Release the bound canvas. + */ + void releaseCanvas(); + + /** + * Set the tone mapping to apply to pixel colors. + */ + void setToneMapping(const ColorProfile &profile); + +protected: + virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; + + virtual void canvasResized(int width, int height) override; + virtual void canvasCleared(const Color &col) override; + virtual void canvasPainted(int x, int y, const Color &col) override; + +private: + QImage* pixbuf; + const Canvas *canvas; +}; + +} +} + +#endif // RENDERPREVIEWPROVIDER_H diff --git a/src/interface/modeler/quickapp/RenderProcess.cpp b/src/interface/modeler/quickapp/RenderProcess.cpp new file mode 100644 index 0000000..4a8589a --- /dev/null +++ b/src/interface/modeler/quickapp/RenderProcess.cpp @@ -0,0 +1,111 @@ +#include "RenderProcess.h" + +#include +#include "SoftwareCanvasRenderer.h" +#include "RenderPreviewProvider.h" +#include "RenderConfig.h" +#include "Thread.h" +#include "Canvas.h" +#include "CanvasPreview.h" + +class RenderThread: public Thread +{ +public: + RenderThread(SoftwareCanvasRenderer *renderer): renderer(renderer) + { + } + virtual void run() override + { + renderer->render(); + } +private: + SoftwareCanvasRenderer *renderer; +}; + +RenderProcess::RenderProcess(RenderPreviewProvider *destination): + destination(destination) +{ + rendering = false; + renderer = NULL; + render_thread = NULL; + + startTimer(100); +} + +RenderProcess::~RenderProcess() +{ + if (rendering) + { + renderer->interrupt(); + } + + rendering = false; + + if (render_thread) + { + render_thread->join(); + delete render_thread; + } + + if (renderer) + { + delete renderer; + } +} + +void RenderProcess::startRender(Scenery *scenery, const RenderConfig &config) +{ + if (rendering) + { + return; + } + + rendering = true; + + if (renderer) + { + delete renderer; + } + + renderer = new SoftwareCanvasRenderer(); + renderer->setScenery(scenery); + renderer->setConfig(config); + + destination->setCanvas(renderer->getCanvas()); + + render_thread = new RenderThread(renderer); + render_thread->start(); +} + +void RenderProcess::stopRender() +{ + if (rendering) + { + renderer->interrupt(); + } +} + +const QSize RenderProcess::getPreviewSize() +{ + if (renderer) + { + return QSize(renderer->getCanvas()->getPreview()->getWidth(), renderer->getCanvas()->getPreview()->getHeight()); + } + else + { + return QSize(10, 10); + } +} + +void RenderProcess::timerEvent(QTimerEvent *) +{ + if (rendering and renderer->isFinished()) + { + destination->releaseCanvas(); + rendering = false; + + render_thread->join(); + delete render_thread; + render_thread = NULL; + } +} diff --git a/src/interface/modeler/quickapp/RenderProcess.h b/src/interface/modeler/quickapp/RenderProcess.h new file mode 100644 index 0000000..366eb51 --- /dev/null +++ b/src/interface/modeler/quickapp/RenderProcess.h @@ -0,0 +1,47 @@ +#ifndef RENDERPROCESS_H +#define RENDERPROCESS_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +class RenderProcess: public QObject +{ + Q_OBJECT + +public: + RenderProcess(RenderPreviewProvider *destination); + virtual ~RenderProcess(); + + /** + * Start the rendering process in a separate thread. + */ + void startRender(Scenery *scenery, const RenderConfig &config); + + /** + * Stop any currently running render. + */ + void stopRender(); + + /** + * Get the size of the preview image. + */ + const QSize getPreviewSize(); + +protected: + virtual void timerEvent(QTimerEvent *event) override; + +private: + RenderPreviewProvider *destination; + bool rendering; + SoftwareCanvasRenderer *renderer; + Thread *render_thread; +}; + +} +} + +#endif // RENDERPROCESS_H diff --git a/src/interface/modeler/quickapp/modeler_global.h b/src/interface/modeler/quickapp/modeler_global.h index d2be172..d2749ac 100644 --- a/src/interface/modeler/quickapp/modeler_global.h +++ b/src/interface/modeler/quickapp/modeler_global.h @@ -12,6 +12,9 @@ namespace modeler { class AtmosphereModeler; class WaterModeler; + + class RenderPreviewProvider; + class RenderProcess; } } diff --git a/src/interface/modeler/quickapp/qml/RenderDialog.qml b/src/interface/modeler/quickapp/qml/RenderDialog.qml new file mode 100644 index 0000000..7f465ad --- /dev/null +++ b/src/interface/modeler/quickapp/qml/RenderDialog.qml @@ -0,0 +1,27 @@ +import QtQuick 2.2 + +Rectangle { + width: 400 + height: 300 + + Image { + id: preview_image + objectName: "preview_image" + anchors.centerIn: parent + width: 100 + height: 100 + source: "" + cache: false + } + + Timer { + interval: 500 + running: true + repeat: true + + onTriggered: { + preview_image.source = ""; + preview_image.source = "image://renderpreviewprovider/live"; + } + } +} diff --git a/src/interface/modeler/quickapp/qml/app.qrc b/src/interface/modeler/quickapp/qml/app.qrc index 3c32d54..6abd781 100644 --- a/src/interface/modeler/quickapp/qml/app.qrc +++ b/src/interface/modeler/quickapp/qml/app.qrc @@ -25,5 +25,6 @@ images/icon_atmosphere_night.png BaseChoice.qml BaseChoiceItem.qml + RenderDialog.qml diff --git a/src/interface/modeler/quickapp/qml/main.qml b/src/interface/modeler/quickapp/qml/main.qml index 422f7ca..38048fd 100644 --- a/src/interface/modeler/quickapp/qml/main.qml +++ b/src/interface/modeler/quickapp/qml/main.qml @@ -91,6 +91,12 @@ OpenGLView { } } + RenderDialog { + id: render_dialog + opacity: 0 + anchors.fill: parent + } + PanelWaterLevel { id: panel_water_level tool: tool_water_level @@ -127,6 +133,19 @@ OpenGLView { target: atmosphere_toolbar opacity: 1 } + }, + State { + name: "Render Dialog" + when: tool_display.selected + + PropertyChanges { + target: primary_toolbar + opacity: 0 + } + PropertyChanges { + target: render_dialog + opacity: 1 + } } ] diff --git a/src/interface/modeler/quickapp/quickapp.pro b/src/interface/modeler/quickapp/quickapp.pro index d1502b4..989f285 100644 --- a/src/interface/modeler/quickapp/quickapp.pro +++ b/src/interface/modeler/quickapp/quickapp.pro @@ -8,7 +8,9 @@ SOURCES += main.cpp \ OpenGLView.cpp \ MainModelerWindow.cpp \ WaterModeler.cpp \ - AtmosphereModeler.cpp + AtmosphereModeler.cpp \ + RenderPreviewProvider.cpp \ + RenderProcess.cpp RESOURCES += \ qml/app.qrc @@ -26,7 +28,9 @@ HEADERS += \ modeler_global.h \ MainModelerWindow.h \ WaterModeler.h \ - AtmosphereModeler.h + AtmosphereModeler.h \ + RenderPreviewProvider.h \ + RenderProcess.h win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../system/release/ -lpaysages_system else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../system/debug/ -lpaysages_system @@ -69,4 +73,5 @@ OTHER_FILES += \ qml/PanelAtmosphereDaytime.qml \ qml/BaseSlider.qml \ qml/BaseChoice.qml \ - qml/BaseChoiceItem.qml + qml/BaseChoiceItem.qml \ + qml/RenderDialog.qml diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index 922b12d..edc48d0 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -17,6 +17,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer() { started = false; + finished = false; interrupted = false; canvas = new Canvas(); progress = 0.0; @@ -95,6 +96,7 @@ void SoftwareCanvasRenderer::render() progress = (double)i / (double)n; } } + finished = true; } void SoftwareCanvasRenderer::interrupt() diff --git a/src/render/software/SoftwareCanvasRenderer.h b/src/render/software/SoftwareCanvasRenderer.h index e157248..605a138 100644 --- a/src/render/software/SoftwareCanvasRenderer.h +++ b/src/render/software/SoftwareCanvasRenderer.h @@ -24,6 +24,7 @@ public: inline const Canvas *getCanvas() const {return canvas;} inline double getProgress() const {return progress;} + inline bool isFinished() const {return finished;} /** * Set the renderer configuration. @@ -78,6 +79,7 @@ private: int samples; std::vector rasterizers; bool started; + bool finished; bool interrupted; ParallelWork *current_work;