diff --git a/.gitignore b/.gitignore index 8d7a6b3..df7cc34 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ ui_*.h /paysages3d-linux/ /paysages3d-linux.tar.bz2 /config.vim +/callgrind.out.* diff --git a/Makefile b/Makefile index f3cb8b7..d3b49be 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,9 @@ endif run_cli:build LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS) +run_modeler:build + LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/modeler/quickapp/paysages-modeler $(ARGS) + run:build LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/desktop/paysages-gui $(ARGS) diff --git a/graphics/icons.png b/graphics/icons.png new file mode 100644 index 0000000..b3e56b0 Binary files /dev/null and b/graphics/icons.png differ diff --git a/graphics/icons.svg b/graphics/icons.svg new file mode 100644 index 0000000..ab65164 --- /dev/null +++ b/graphics/icons.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/images/logo.svg b/graphics/logo.svg similarity index 100% rename from data/images/logo.svg rename to graphics/logo.svg diff --git a/src/definition/AtmosphereDefinition.cpp b/src/definition/AtmosphereDefinition.cpp index 0cbb27f..7e74e9c 100644 --- a/src/definition/AtmosphereDefinition.cpp +++ b/src/definition/AtmosphereDefinition.cpp @@ -105,6 +105,22 @@ void AtmosphereDefinition::validate() _daytime = (double)hour / 24.0 + (double)minute / 1440.0; } +void AtmosphereDefinition::setDaytime(double value) +{ + if (value >= 0.0) + { + value = fmod(value, 1.0); + } + else + { + value = 1.0 - fmod(-value, 1.0); + } + value *= 1440.0; + hour = value / 60.0; + minute = value - 60.0 * hour; + validate(); +} + void AtmosphereDefinition::applyPreset(AtmospherePreset preset) { sun_color.r = 1.0; diff --git a/src/definition/AtmosphereDefinition.h b/src/definition/AtmosphereDefinition.h index 0067ecf..0fbfa92 100644 --- a/src/definition/AtmosphereDefinition.h +++ b/src/definition/AtmosphereDefinition.h @@ -46,6 +46,11 @@ public: virtual void copy(BaseDefinition* destination) const override; virtual void validate() override; + /** + * Set the daytime from a 0.0-1.0 value. + */ + void setDaytime(double value); + void applyPreset(AtmospherePreset preset); void generateStars(int count); diff --git a/src/definition/Scenery.cpp b/src/definition/Scenery.cpp index 1515b9e..7ced63c 100644 --- a/src/definition/Scenery.cpp +++ b/src/definition/Scenery.cpp @@ -182,7 +182,7 @@ void Scenery::getWater(WaterDefinition* water) void Scenery::checkCameraAboveGround() { Vector3 camera_location = camera->getLocation(); - double terrain_height = terrain->getInterpolatedHeight(camera_location.x, camera_location.z, 1, 1) + 2.0; + double terrain_height = terrain->getInterpolatedHeight(camera_location.x, camera_location.z, true, true) + 2.0; double water_height = 1.5; if (camera_location.y < water_height || camera_location.y < terrain_height) { diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index 8ec30a3..45c5a18 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -82,7 +82,7 @@ void TerrainDefinition::load(PackStream* stream) validate(); } -double TerrainDefinition::getGridHeight(int x, int z, int with_painting) +double TerrainDefinition::getGridHeight(int x, int z, bool with_painting) { double h; @@ -94,7 +94,7 @@ double TerrainDefinition::getGridHeight(int x, int z, int with_painting) return h; } -double TerrainDefinition::getInterpolatedHeight(double x, double z, int scaled, int with_painting) +double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, bool with_painting) { double h; x /= scaling; diff --git a/src/definition/TerrainDefinition.h b/src/definition/TerrainDefinition.h index 560c7f0..c67fac7 100644 --- a/src/definition/TerrainDefinition.h +++ b/src/definition/TerrainDefinition.h @@ -27,8 +27,8 @@ public: virtual void copy(BaseDefinition* destination) const override; virtual void validate() override; - double getGridHeight(int x, int z, int with_painting); - double getInterpolatedHeight(double x, double z, int scaled, int with_painting); + double getGridHeight(int x, int z, bool with_painting); + double getInterpolatedHeight(double x, double z, bool scaled, bool with_painting); unsigned long getMemoryStats(); HeightInfo getHeightInfo(); diff --git a/src/interface/desktop/terrain/paintingbrush.cpp b/src/interface/desktop/terrain/paintingbrush.cpp index a6e8c7c..3ab10df 100644 --- a/src/interface/desktop/terrain/paintingbrush.cpp +++ b/src/interface/desktop/terrain/paintingbrush.cpp @@ -135,7 +135,7 @@ void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double case PAINTING_BRUSH_FLATTEN: if (reverse) { - _height = terrain->getInterpolatedHeight(x * terrain->scaling, z * terrain->scaling, 0, 1); + _height = terrain->getInterpolatedHeight(x * terrain->scaling, z * terrain->scaling, false, true); } else { diff --git a/src/interface/desktop/terrain/widgetheightmap.cpp b/src/interface/desktop/terrain/widgetheightmap.cpp index 5d26e7f..a115012 100644 --- a/src/interface/desktop/terrain/widgetheightmap.cpp +++ b/src/interface/desktop/terrain/widgetheightmap.cpp @@ -218,7 +218,7 @@ void WidgetHeightMap::timerEvent(QTimerEvent*) _last_time = new_time; // Update top camera - Vector3 target = {_target_x, _terrain->getInterpolatedHeight(_target_x, _target_z, 1, 1), _target_z}; + Vector3 target = {_target_x, _terrain->getInterpolatedHeight(_target_x, _target_z, true, true), _target_z}; _top_camera->setLocationCoords(target.x, target.y + 1.0, target.z + 0.1); _top_camera->setTarget(target); _top_camera->setZoomToTarget(_zoom); diff --git a/src/interface/modeler/extension/extension.pro b/src/interface/modeler/extension/extension.pro new file mode 100644 index 0000000..03da2cd --- /dev/null +++ b/src/interface/modeler/extension/extension.pro @@ -0,0 +1,35 @@ +TEMPLATE = lib +TARGET = extension +QT += qml quick +CONFIG += qt plugin + +TARGET = $$qtLibraryTarget($$TARGET) +uri = Paysages + +# Input +SOURCES += \ + extension_plugin.cpp \ + paysages.cpp + +HEADERS += \ + extension_plugin.h \ + paysages.h + +OTHER_FILES = qmldir + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + copy_qmldir.target = $$OUT_PWD/qmldir + copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir + copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\" + QMAKE_EXTRA_TARGETS += copy_qmldir + PRE_TARGETDEPS += $$copy_qmldir.target +} + +qmldir.files = qmldir +unix { + installPath = $$[QT_INSTALL_QML]/$$replace(uri, \\., /) + qmldir.path = $$installPath + target.path = $$installPath + INSTALLS += target qmldir +} + diff --git a/src/interface/modeler/extension/extension_plugin.cpp b/src/interface/modeler/extension/extension_plugin.cpp new file mode 100644 index 0000000..ded9382 --- /dev/null +++ b/src/interface/modeler/extension/extension_plugin.cpp @@ -0,0 +1,12 @@ +#include "extension_plugin.h" +#include "paysages.h" + +#include + +void ExtensionPlugin::registerTypes(const char *uri) +{ + // @uri Paysages + qmlRegisterType(uri, 1, 0, "Paysages"); +} + + diff --git a/src/interface/modeler/extension/extension_plugin.h b/src/interface/modeler/extension/extension_plugin.h new file mode 100644 index 0000000..9471ff3 --- /dev/null +++ b/src/interface/modeler/extension/extension_plugin.h @@ -0,0 +1,16 @@ +#ifndef EXTENSION_PLUGIN_H +#define EXTENSION_PLUGIN_H + +#include + +class ExtensionPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char *uri); +}; + +#endif // EXTENSION_PLUGIN_H + diff --git a/src/interface/modeler/extension/paysages.cpp b/src/interface/modeler/extension/paysages.cpp new file mode 100644 index 0000000..ad88921 --- /dev/null +++ b/src/interface/modeler/extension/paysages.cpp @@ -0,0 +1,16 @@ +#include "paysages.h" + +Paysages::Paysages(QQuickItem *parent): + QQuickItem(parent) +{ + // By default, QQuickItem does not draw anything. If you subclass + // QQuickItem to create a visual item, you will need to uncomment the + // following line and re-implement updatePaintNode() + + // setFlag(ItemHasContents, true); +} + +Paysages::~Paysages() +{ +} + diff --git a/src/interface/modeler/extension/paysages.h b/src/interface/modeler/extension/paysages.h new file mode 100644 index 0000000..0f55ef5 --- /dev/null +++ b/src/interface/modeler/extension/paysages.h @@ -0,0 +1,17 @@ +#ifndef PAYSAGES_H +#define PAYSAGES_H + +#include + +class Paysages : public QQuickItem +{ + Q_OBJECT + Q_DISABLE_COPY(Paysages) + +public: + Paysages(QQuickItem *parent = 0); + ~Paysages(); +}; + +#endif // PAYSAGES_H + diff --git a/src/interface/modeler/extension/qmldir b/src/interface/modeler/extension/qmldir new file mode 100644 index 0000000..f7a7af9 --- /dev/null +++ b/src/interface/modeler/extension/qmldir @@ -0,0 +1,3 @@ +module Paysages +plugin extension + diff --git a/src/interface/modeler/modeler.pro b/src/interface/modeler/modeler.pro new file mode 100644 index 0000000..711f598 --- /dev/null +++ b/src/interface/modeler/modeler.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + quickapp \ + extension diff --git a/src/interface/modeler/quickapp/AtmosphereModeler.cpp b/src/interface/modeler/quickapp/AtmosphereModeler.cpp new file mode 100644 index 0000000..5dcf86d --- /dev/null +++ b/src/interface/modeler/quickapp/AtmosphereModeler.cpp @@ -0,0 +1,26 @@ +#include "AtmosphereModeler.h" + +#include "MainModelerWindow.h" +#include "Scenery.h" +#include "AtmosphereDefinition.h" +#include "OpenGLRenderer.h" +#include "OpenGLSkybox.h" + +AtmosphereModeler::AtmosphereModeler(MainModelerWindow *main): + main(main) +{ + QObject *item = main->findQmlObject("atmosphere_daytime"); + if (item) + { + connect(item, SIGNAL(changed(double)), this, SLOT(daytimeChanged(double))); + } +} + +void AtmosphereModeler::daytimeChanged(double value) +{ + main->getScenery()->getAtmosphere()->setDaytime(value); + + main->getRenderer()->getScenery()->setAtmosphere(main->getScenery()->getAtmosphere()); + + main->getRenderer()->getSkybox()->update(); +} diff --git a/src/interface/modeler/quickapp/AtmosphereModeler.h b/src/interface/modeler/quickapp/AtmosphereModeler.h new file mode 100644 index 0000000..1a5b32d --- /dev/null +++ b/src/interface/modeler/quickapp/AtmosphereModeler.h @@ -0,0 +1,27 @@ +#ifndef ATMOSPHEREMODELER_H +#define ATMOSPHEREMODELER_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +class AtmosphereModeler : public QObject +{ + Q_OBJECT +public: + AtmosphereModeler(MainModelerWindow *main); + +public slots: + void daytimeChanged(double value); + +private: + MainModelerWindow *main; +}; + +} +} + +#endif // ATMOSPHEREMODELER_H diff --git a/src/interface/modeler/quickapp/MainModelerWindow.cpp b/src/interface/modeler/quickapp/MainModelerWindow.cpp new file mode 100644 index 0000000..c21b459 --- /dev/null +++ b/src/interface/modeler/quickapp/MainModelerWindow.cpp @@ -0,0 +1,76 @@ +#include "MainModelerWindow.h" + +#include "OpenGLView.h" +#include "Scenery.h" +#include "OpenGLRenderer.h" +#include "AtmosphereModeler.h" +#include "WaterModeler.h" +#include "ModelerCameras.h" +#include "RenderPreviewProvider.h" +#include "RenderProcess.h" +#include "RenderConfig.h" + +#include + +MainModelerWindow::MainModelerWindow() +{ + scenery = new Scenery(); + scenery->autoPreset(); + renderer = new OpenGLRenderer(scenery); + + 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")); + + cameras = new ModelerCameras(this); + atmosphere = new AtmosphereModeler(this); + water = new WaterModeler(this); +} + +MainModelerWindow::~MainModelerWindow() +{ + delete atmosphere; + delete water; + delete cameras; + + delete render_preview_provider; + delete render_process; + + delete renderer; + delete scenery; +} + +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 new file mode 100644 index 0000000..da676d9 --- /dev/null +++ b/src/interface/modeler/quickapp/MainModelerWindow.h @@ -0,0 +1,43 @@ +#ifndef MAINMODELERWINDOW_H +#define MAINMODELERWINDOW_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +class MainModelerWindow: public QQuickView +{ + Q_OBJECT +public: + MainModelerWindow(); + virtual ~MainModelerWindow(); + + QObject *findQmlObject(const QString& objectName); + + inline Scenery *getScenery() const {return scenery;} + inline OpenGLRenderer *getRenderer() const {return renderer;} + inline ModelerCameras *getCamera() const {return cameras;} + +protected: + virtual void keyReleaseEvent(QKeyEvent *event) override; + +private: + OpenGLRenderer *renderer; + Scenery *scenery; + + AtmosphereModeler *atmosphere; + WaterModeler *water; + + ModelerCameras *cameras; + + RenderPreviewProvider *render_preview_provider; + RenderProcess *render_process; +}; + +} +} + +#endif // MAINMODELERWINDOW_H diff --git a/src/interface/modeler/quickapp/ModelerCameras.cpp b/src/interface/modeler/quickapp/ModelerCameras.cpp new file mode 100644 index 0000000..8e4104f --- /dev/null +++ b/src/interface/modeler/quickapp/ModelerCameras.cpp @@ -0,0 +1,64 @@ +#include "ModelerCameras.h" + +#include "MainModelerWindow.h" +#include "OpenGLRenderer.h" +#include "Scenery.h" +#include "CameraDefinition.h" + +ModelerCameras::ModelerCameras(MainModelerWindow *parent): + QObject(parent), parent(parent) +{ + render = new CameraDefinition(); + topdown = new CameraDefinition(); + current = new CameraDefinition(); + active = render; + + topdown->strafeForward(-10.0); + topdown->strafeUp(25.0); + topdown->rotatePitch(-0.8); + + // Watch GUI choice + QObject *widget = parent->findQmlObject("camera_choice"); + connect(widget, SIGNAL(stateChanged(QString)), this, SLOT(changeActiveCamera(QString))); + + // Start update timer + startTimer(50); +} + +ModelerCameras::~ModelerCameras() +{ + delete current; + delete render; + delete topdown; +} + +void ModelerCameras::processZoom(double value) +{ + active->strafeForward(value); +} + +void ModelerCameras::processScroll(double xvalue, double yvalue) +{ + active->strafeRight(xvalue); + active->strafeUp(yvalue); +} + +void ModelerCameras::timerEvent(QTimerEvent *) +{ + OpenGLRenderer *renderer = parent->getRenderer(); + + current->transitionToAnother(active, 0.3); + renderer->setCamera(current); +} + +void ModelerCameras::changeActiveCamera(const QString &name) +{ + if (name == "Render camera") + { + active = render; + } + else if (name == "Top-down camera") + { + active = topdown; + } +} diff --git a/src/interface/modeler/quickapp/ModelerCameras.h b/src/interface/modeler/quickapp/ModelerCameras.h new file mode 100644 index 0000000..5621203 --- /dev/null +++ b/src/interface/modeler/quickapp/ModelerCameras.h @@ -0,0 +1,49 @@ +#ifndef MODELERCAMERAS_H +#define MODELERCAMERAS_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +/** + * Storage for modeler cameras. + */ +class ModelerCameras: public QObject +{ + Q_OBJECT + +public: + ModelerCameras(MainModelerWindow *parent); + ~ModelerCameras(); + + /** + * Process a zoom request. + */ + void processZoom(double value); + + /** + * Process a scroll request. + */ + void processScroll(double xvalue, double yvalue); + +protected: + void timerEvent(QTimerEvent *event); + +public slots: + void changeActiveCamera(const QString &name); + +private: + MainModelerWindow *parent; + CameraDefinition *active; + CameraDefinition *current; + CameraDefinition *render; + CameraDefinition *topdown; +}; + +} +} + +#endif // MODELERCAMERAS_H diff --git a/src/interface/modeler/quickapp/OpenGLView.cpp b/src/interface/modeler/quickapp/OpenGLView.cpp new file mode 100644 index 0000000..ad3a12b --- /dev/null +++ b/src/interface/modeler/quickapp/OpenGLView.cpp @@ -0,0 +1,92 @@ +#include "OpenGLView.h" + +#include +#include +#include "MainModelerWindow.h" +#include "OpenGLRenderer.h" +#include "ModelerCameras.h" + +OpenGLView::OpenGLView(QQuickItem *parent) : + QQuickItem(parent) +{ + initialized = false; + window = NULL; + renderer = NULL; + + setAcceptedMouseButtons(Qt::AllButtons); + + mouse_button = Qt::NoButton; + + connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*))); + startTimer(50); +} + +void OpenGLView::handleWindowChanged(QQuickWindow *win) +{ + if (win) + { + window = qobject_cast(win); + if (window) + { + renderer = window->getRenderer(); + + connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection); + + win->setClearBeforeRendering(false); + + initialized = false; + } + } +} + +void OpenGLView::paint() +{ + if (not initialized or not renderer) + { + renderer->initialize(); + initialized = true; + } + + renderer->resize(width(), height()); + renderer->prepareOpenGLState(); + renderer->paint(); + + if (window) + { + window->resetOpenGLState(); + } +} + +void OpenGLView::wheelEvent(QWheelEvent *event) +{ + window->getCamera()->processZoom(0.1 * (double)event->angleDelta().y()); +} + +void OpenGLView::mousePressEvent(QMouseEvent *event) +{ + mouse_button = event->button(); + mouse_pos = event->windowPos(); +} + +void OpenGLView::mouseReleaseEvent(QMouseEvent *) +{ + mouse_button = Qt::NoButton; +} + +void OpenGLView::mouseMoveEvent(QMouseEvent *event) +{ + QPointF diff = event->windowPos() - mouse_pos; + if (mouse_button == Qt::MidButton) + { + window->getCamera()->processScroll(-0.1 * diff.x(), 0.1 * diff.y()); + } + mouse_pos = event->windowPos(); +} + +void OpenGLView::timerEvent(QTimerEvent *) +{ + if (window) + { + window->update(); + } +} diff --git a/src/interface/modeler/quickapp/OpenGLView.h b/src/interface/modeler/quickapp/OpenGLView.h new file mode 100644 index 0000000..31fcc5c --- /dev/null +++ b/src/interface/modeler/quickapp/OpenGLView.h @@ -0,0 +1,40 @@ +#ifndef OPENGLVIEW_H +#define OPENGLVIEW_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +class OpenGLView : public QQuickItem +{ + Q_OBJECT +public: + explicit OpenGLView(QQuickItem *parent = 0); + +public slots: + void handleWindowChanged(QQuickWindow *win); + void paint(); + +protected: + virtual void wheelEvent(QWheelEvent *event) override; + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void timerEvent(QTimerEvent *event) override; + +private: + bool initialized; + MainModelerWindow *window; + OpenGLRenderer *renderer; + + Qt::MouseButton mouse_button; + QPointF mouse_pos; +}; + +} +} + +#endif // OPENGLVIEW_H 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/WaterModeler.cpp b/src/interface/modeler/quickapp/WaterModeler.cpp new file mode 100644 index 0000000..a0057db --- /dev/null +++ b/src/interface/modeler/quickapp/WaterModeler.cpp @@ -0,0 +1,19 @@ +#include "WaterModeler.h" + +#include "MainModelerWindow.h" + +WaterModeler::WaterModeler(MainModelerWindow *main): + main(main) +{ + QObject *item = main->findQmlObject("water_level"); + if (item) + { + connect(item, SIGNAL(changed(double)), this, SLOT(waterLevelChanged(double))); + } +} + +void WaterModeler::waterLevelChanged(double value) +{ + // TODO + //qDebug() << "water level : " << value; +} diff --git a/src/interface/modeler/quickapp/WaterModeler.h b/src/interface/modeler/quickapp/WaterModeler.h new file mode 100644 index 0000000..fac513e --- /dev/null +++ b/src/interface/modeler/quickapp/WaterModeler.h @@ -0,0 +1,27 @@ +#ifndef WATERMODELER_H +#define WATERMODELER_H + +#include "modeler_global.h" + +#include + +namespace paysages { +namespace modeler { + +class WaterModeler: public QObject +{ + Q_OBJECT +public: + WaterModeler(MainModelerWindow *main); + +public slots: + void waterLevelChanged(double value); + +private: + MainModelerWindow *main; +}; + +} +} + +#endif // WATERMODELER_H diff --git a/src/interface/modeler/quickapp/deployment.pri b/src/interface/modeler/quickapp/deployment.pri new file mode 100644 index 0000000..5441b63 --- /dev/null +++ b/src/interface/modeler/quickapp/deployment.pri @@ -0,0 +1,27 @@ +android-no-sdk { + target.path = /data/user/qt + export(target.path) + INSTALLS += target +} else:android { + x86 { + target.path = /libs/x86 + } else: armeabi-v7a { + target.path = /libs/armeabi-v7a + } else { + target.path = /libs/armeabi + } + export(target.path) + INSTALLS += target +} else:unix { + isEmpty(target.path) { + qnx { + target.path = /tmp/$${TARGET}/bin + } else { + target.path = /opt/$${TARGET}/bin + } + export(target.path) + } + INSTALLS += target +} + +export(INSTALLS) diff --git a/src/interface/modeler/quickapp/images.qrc b/src/interface/modeler/quickapp/images.qrc new file mode 100644 index 0000000..d8083b0 --- /dev/null +++ b/src/interface/modeler/quickapp/images.qrc @@ -0,0 +1,10 @@ + + + images/tab_atmosphere.png + images/tab_terrain.png + images/tab_textures.png + images/tab_water.png + images/tab_clouds.png + images/tab_render.png + + diff --git a/src/interface/modeler/quickapp/main.cpp b/src/interface/modeler/quickapp/main.cpp new file mode 100644 index 0000000..3d9237a --- /dev/null +++ b/src/interface/modeler/quickapp/main.cpp @@ -0,0 +1,13 @@ +#include + +#include "MainModelerWindow.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + MainModelerWindow view; + view.show(); + + return app.exec(); +} diff --git a/src/interface/modeler/quickapp/modeler_global.h b/src/interface/modeler/quickapp/modeler_global.h new file mode 100644 index 0000000..135e12b --- /dev/null +++ b/src/interface/modeler/quickapp/modeler_global.h @@ -0,0 +1,25 @@ +#ifndef MODELER_GLOBAL_H +#define MODELER_GLOBAL_H + +#include "definition_global.h" +#include "software_global.h" +#include "opengl_global.h" + +namespace paysages { +namespace modeler { + class MainModelerWindow; + class OpenGLView; + + class AtmosphereModeler; + class WaterModeler; + + class RenderPreviewProvider; + class RenderProcess; + + class ModelerCameras; +} +} + +using namespace paysages::modeler; + +#endif // MODELER_GLOBAL_H diff --git a/src/interface/modeler/quickapp/qml.qrc b/src/interface/modeler/quickapp/qml.qrc new file mode 100644 index 0000000..787b561 --- /dev/null +++ b/src/interface/modeler/quickapp/qml.qrc @@ -0,0 +1,7 @@ + + + qml/main.qml + qml/ToolbarButton.qml + qml/OpenGLView.qml + + diff --git a/src/interface/modeler/quickapp/qml/BaseChoice.qml b/src/interface/modeler/quickapp/qml/BaseChoice.qml new file mode 100644 index 0000000..165164d --- /dev/null +++ b/src/interface/modeler/quickapp/qml/BaseChoice.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 + +Item { + default property alias children : inner_layout.children + property int value + width: 100 + height: 32 + + ExclusiveGroup { + id: choice_group + + onCurrentChanged: value = current.value + } + + Row { + id: inner_layout + spacing: 5 + anchors.fill: parent + } + + onChildrenChanged: { + for (var i = 0; i < children.length; i++) + { + children[i].exclusiveGroup = choice_group; + } + } +} diff --git a/src/interface/modeler/quickapp/qml/BaseChoiceItem.qml b/src/interface/modeler/quickapp/qml/BaseChoiceItem.qml new file mode 100644 index 0000000..7e87197 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/BaseChoiceItem.qml @@ -0,0 +1,50 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.1 + +Rectangle { + id: choice_item + property string icon + property bool checked: false + property ExclusiveGroup exclusiveGroup: null + property int value + + color: "#333333" + + signal toggled(bool value) + + width: 20 + height: 20 + + Image { + anchors.fill: parent + source: parent.icon + antialiasing: true + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: checked = true + } + + onExclusiveGroupChanged: { + if (exclusiveGroup) { + exclusiveGroup.bindCheckable(choice_item); + } + } + + onCheckedChanged: choice_item.toggled(checked) + + states: [ + State { + name: "Checked" + when: checked + + PropertyChanges { + target: choice_item + color: "#999999" + } + } + + ] +} diff --git a/src/interface/modeler/quickapp/qml/BasePanel.qml b/src/interface/modeler/quickapp/qml/BasePanel.qml new file mode 100644 index 0000000..14ce20d --- /dev/null +++ b/src/interface/modeler/quickapp/qml/BasePanel.qml @@ -0,0 +1,35 @@ +import QtQuick 2.0 + +Rectangle { + property ToolbarButton tool + id: panel + + opacity: 0 + width: 200 + height: parent.height - 100 + color: "#a0909090" + enabled: visible + + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Behavior on opacity { + PropertyAnimation { + duration: 200 + } + } + + states: [ + State { + name: "Active" + when: tool.selected + + PropertyChanges { + target: panel + visible: true + opacity: 1 + } + } + + ] +} diff --git a/src/interface/modeler/quickapp/qml/BaseSlider.qml b/src/interface/modeler/quickapp/qml/BaseSlider.qml new file mode 100644 index 0000000..58cc14e --- /dev/null +++ b/src/interface/modeler/quickapp/qml/BaseSlider.qml @@ -0,0 +1,7 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 + +Slider { + signal changed(real value) + onValueChanged: changed(value) +} diff --git a/src/interface/modeler/quickapp/qml/CameraChoice.qml b/src/interface/modeler/quickapp/qml/CameraChoice.qml new file mode 100644 index 0000000..8df3ae4 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/CameraChoice.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 + +Rectangle { + id: camera_choice + width: 200 + height: 50 + color: "#90888888" + objectName: "camera_choice" + + Row { + id: inner_space + anchors.centerIn: parent + spacing: 15 + + ToolbarButton { + id: camera_choice_render + picture: "images/tab_display.png" + hovertext: qsTr("Switch to the final camera") + selected: true + } + + ToolbarButton { + id: camera_choice_topdown + picture: "images/display_topdown.png" + hovertext: qsTr("Switch to the top-down camera") + } + } + + states: [ + State { + name: "Render camera" + when: camera_choice_render.selected + }, + State { + name: "Top-down camera" + when: camera_choice_topdown.selected + } + ] +} diff --git a/src/interface/modeler/quickapp/qml/OpenGLView.qml b/src/interface/modeler/quickapp/qml/OpenGLView.qml new file mode 100644 index 0000000..af5804e --- /dev/null +++ b/src/interface/modeler/quickapp/qml/OpenGLView.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Rectangle { + width: 100 + height: 62 +} diff --git a/src/interface/modeler/quickapp/qml/PanelAtmosphereDaytime.qml b/src/interface/modeler/quickapp/qml/PanelAtmosphereDaytime.qml new file mode 100644 index 0000000..08dab9f --- /dev/null +++ b/src/interface/modeler/quickapp/qml/PanelAtmosphereDaytime.qml @@ -0,0 +1,46 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 + +BasePanel { + width: 70 + + objectName: "atmosphere_daytime" + default property real value: day_night.value == 2 ? 1.0 : slider.value * 0.54 + 0.23; + signal changed(real value) + + onValueChanged: changed(value) + + ColumnLayout + { + anchors.fill: parent + anchors.margins: 10 + spacing: 20 + + BaseChoice { + id: day_night + width: parent.width + + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + + BaseChoiceItem { + icon: "images/icon_atmosphere_day.png" + value: 1 + checked: true + } + BaseChoiceItem { + icon: "images/icon_atmosphere_night.png" + value: 2 + } + } + + BaseSlider { + id: slider + orientation: Qt.Vertical + Layout.maximumWidth: 15 + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + visible: day_night.value == 1 + } + } +} diff --git a/src/interface/modeler/quickapp/qml/PanelWaterLevel.qml b/src/interface/modeler/quickapp/qml/PanelWaterLevel.qml new file mode 100644 index 0000000..444ae97 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/PanelWaterLevel.qml @@ -0,0 +1,11 @@ +import QtQuick 2.2 + +BasePanel { + width: 20 + + BaseSlider { + objectName: "water_level" + orientation: Qt.Vertical + anchors.fill: parent + } +} 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/Toolbar.qml b/src/interface/modeler/quickapp/qml/Toolbar.qml new file mode 100644 index 0000000..9abfbc2 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/Toolbar.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +Rectangle { + + default property alias children : inner_space.children + width: 70 + height: parent.height + color: "#50888888" + enabled: opacity > 0 + + Column { + id: inner_space + spacing: (parent.height - children.length * tool_terrain.height) / (children.length + 1) + anchors.centerIn: parent + } + + Behavior on opacity { + PropertyAnimation { + duration: 200 + } + } + + onEnabledChanged: { + if (!enabled) + { + for (var i = 0; i < children.length; i++) + { + children[i].selected = false; + } + } + } +} diff --git a/src/interface/modeler/quickapp/qml/ToolbarButton.qml b/src/interface/modeler/quickapp/qml/ToolbarButton.qml new file mode 100644 index 0000000..727c408 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/ToolbarButton.qml @@ -0,0 +1,87 @@ +import QtQuick 2.0 +import QtGraphicalEffects 1.0 + +Item { + property string picture + property bool selected: false + property bool hovered: false + property string helptext + property string hovertext + + width: image.width + 10 + height: image.height + 10 + + Rectangle { + id: glow + anchors.fill: parent + color: "#cccccc" + radius: 8 + + opacity: parent.selected ? 1.0 : (parent.hovered ? 0.5 : 0.0) + Behavior on opacity { + PropertyAnimation { + duration: 200 + } + } + RectangularGlow { + anchors.fill: glow + glowRadius: 8 + spread: 0.2 + color: "white" + cornerRadius: glow.radius + glowRadius + } + } + + Image { + id: image + source: parent.picture + anchors.centerIn: parent + width: 32 + height: 32 + antialiasing: true + } + DropShadow { + anchors.fill: image + horizontalOffset: 2 + verticalOffset: 2 + radius: 4.0 + samples: 16 + color: "#80000000" + source: image + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onEntered: { + parent.hovered = true; + tooltip_widget.hovertext = hovertext; + } + onExited: { + parent.hovered = false; + tooltip_widget.hovertext = ""; + } + onClicked: { + parent.selected = !parent.selected; + if (parent.selected) + { + var toolbar = parent.parent; + for (var i = 0; i < toolbar.children.length; ++i) + { + var child = toolbar.children[i] + if (child !== parent) + { + child.selected = false; + } + } + tooltip_widget.helptext = helptext; + } + else + { + tooltip_widget.helptext = ""; + } + } + } +} diff --git a/src/interface/modeler/quickapp/qml/Tooltip.qml b/src/interface/modeler/quickapp/qml/Tooltip.qml new file mode 100644 index 0000000..0ec4c3c --- /dev/null +++ b/src/interface/modeler/quickapp/qml/Tooltip.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Rectangle { + property string helptext + property string hovertext + width: content.width + height: content.height + + color: "#99000000" + + Text { + id: content + color: "white" + text: parent.helptext || parent.hovertext + } +} diff --git a/src/interface/modeler/quickapp/qml/app.qrc b/src/interface/modeler/quickapp/qml/app.qrc new file mode 100644 index 0000000..dc6d8a4 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/app.qrc @@ -0,0 +1,31 @@ + + + ToolbarButton.qml + main.qml + images/tab_atmosphere.png + images/tab_clouds.png + images/tab_render.png + images/tab_terrain.png + images/tab_textures.png + images/tab_water.png + images/tab_display.png + Toolbar.qml + images/display_topdown.png + images/help.png + Tooltip.qml + images/icon_water.png + images/icon_water_level.png + BasePanel.qml + PanelWaterLevel.qml + images/icon_atmosphere.png + images/icon_atmosphere_daytime.png + BaseSlider.qml + PanelAtmosphereDaytime.qml + images/icon_atmosphere_day.png + images/icon_atmosphere_night.png + BaseChoice.qml + BaseChoiceItem.qml + RenderDialog.qml + CameraChoice.qml + + diff --git a/src/interface/modeler/quickapp/qml/images/display_topdown.png b/src/interface/modeler/quickapp/qml/images/display_topdown.png new file mode 100644 index 0000000..442706b Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/display_topdown.png differ diff --git a/src/interface/modeler/quickapp/qml/images/help.png b/src/interface/modeler/quickapp/qml/images/help.png new file mode 100644 index 0000000..fa42c5a Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/help.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_atmosphere.png b/src/interface/modeler/quickapp/qml/images/icon_atmosphere.png new file mode 100644 index 0000000..5514dfb Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_atmosphere.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_atmosphere_day.png b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_day.png new file mode 100644 index 0000000..90721c2 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_day.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_atmosphere_daytime.png b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_daytime.png new file mode 100644 index 0000000..97bed42 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_daytime.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_atmosphere_night.png b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_night.png new file mode 100644 index 0000000..6f0aaee Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_atmosphere_night.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_water.png b/src/interface/modeler/quickapp/qml/images/icon_water.png new file mode 100644 index 0000000..b87e23c Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_water.png differ diff --git a/src/interface/modeler/quickapp/qml/images/icon_water_level.png b/src/interface/modeler/quickapp/qml/images/icon_water_level.png new file mode 100644 index 0000000..0fab5ea Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/icon_water_level.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_atmosphere.png b/src/interface/modeler/quickapp/qml/images/tab_atmosphere.png new file mode 100644 index 0000000..d1e3189 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_atmosphere.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_clouds.png b/src/interface/modeler/quickapp/qml/images/tab_clouds.png new file mode 100644 index 0000000..0fe90b2 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_clouds.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_display.png b/src/interface/modeler/quickapp/qml/images/tab_display.png new file mode 100644 index 0000000..f1635ef Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_display.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_render.png b/src/interface/modeler/quickapp/qml/images/tab_render.png new file mode 100644 index 0000000..0715265 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_render.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_terrain.png b/src/interface/modeler/quickapp/qml/images/tab_terrain.png new file mode 100644 index 0000000..343522d Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_terrain.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_textures.png b/src/interface/modeler/quickapp/qml/images/tab_textures.png new file mode 100644 index 0000000..d846a9b Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_textures.png differ diff --git a/src/interface/modeler/quickapp/qml/images/tab_water.png b/src/interface/modeler/quickapp/qml/images/tab_water.png new file mode 100644 index 0000000..1b29d63 Binary files /dev/null and b/src/interface/modeler/quickapp/qml/images/tab_water.png differ diff --git a/src/interface/modeler/quickapp/qml/main.qml b/src/interface/modeler/quickapp/qml/main.qml new file mode 100644 index 0000000..bc18a73 --- /dev/null +++ b/src/interface/modeler/quickapp/qml/main.qml @@ -0,0 +1,158 @@ +import QtQuick 2.2 +import Paysages 1.0 + +OpenGLView { + id: main_ui + state: "Init" + + width: 800 + height: 600 + + Tooltip { + id: tooltip_widget + + anchors.top: parent.top + anchors.right: parent.right + } + + Toolbar { + id: primary_toolbar + color: "#90888888" + + anchors.left: parent.left + + ToolbarButton { + id: tool_display + picture: "images/tab_display.png" + hovertext: qsTr("Display options") + } + ToolbarButton { + id: tool_terrain + picture: "images/tab_terrain.png" + } + ToolbarButton { + id: tool_textures + picture: "images/tab_textures.png" + } + ToolbarButton { + id: tool_water + picture: "images/icon_water.png" + hovertext: "Water tools" + } + ToolbarButton { + id: tool_atmosphere + picture: "images/icon_atmosphere.png" + hovertext: "Atmosphere/weather tools" + } + ToolbarButton { + id: tool_clouds + picture: "images/tab_clouds.png" + } + ToolbarButton { + id: tool_render + picture: "images/tab_render.png" + } + } + + Toolbar { + id: display_toolbar + opacity: 0 + anchors.left: primary_toolbar.right + + ToolbarButton { + id: tool_display_topdown + picture: "images/display_topdown.png" + hovertext: qsTr("Top-down view") + helptext: qsTr("Drag the mouse on the map to change the viewpoint.") + } + } + + Toolbar { + id: water_toolbar + opacity: 0 + anchors.left: primary_toolbar.right + + ToolbarButton { + id: tool_water_level + picture: "images/icon_water_level.png" + hovertext: qsTr("Change the water altitude") + } + } + + Toolbar { + id: atmosphere_toolbar + opacity: 0 + anchors.left: primary_toolbar.right + + ToolbarButton { + id: tool_atmosphere_daytime + picture: "images/icon_atmosphere_daytime.png" + hovertext: qsTr("Change the time of day") + } + } + + CameraChoice { + id: camera_choice + anchors.bottom: main_ui.bottom + anchors.horizontalCenter: main_ui.horizontalCenter + } + + RenderDialog { + id: render_dialog + opacity: 0 + anchors.fill: parent + } + + PanelWaterLevel { + id: panel_water_level + tool: tool_water_level + } + PanelAtmosphereDaytime { + id: panel_atmosphere_daytime + tool: tool_atmosphere_daytime + } + + states: [ + State { + name: "Display Mode" + when: tool_display.selected + + PropertyChanges { + target: display_toolbar + opacity: 1 + } + }, + State { + name: "Water Mode" + when: tool_water.selected + + PropertyChanges { + target: water_toolbar + opacity: 1 + } + }, + State { + name: "Atmosphere Mode" + when: tool_atmosphere.selected + + PropertyChanges { + 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 new file mode 100644 index 0000000..9257aecc --- /dev/null +++ b/src/interface/modeler/quickapp/quickapp.pro @@ -0,0 +1,80 @@ +TEMPLATE = app + +QT += qml quick widgets + +include(../../../common.pri) + +SOURCES += main.cpp \ + OpenGLView.cpp \ + MainModelerWindow.cpp \ + WaterModeler.cpp \ + AtmosphereModeler.cpp \ + RenderPreviewProvider.cpp \ + RenderProcess.cpp \ + ModelerCameras.cpp + +RESOURCES += \ + qml/app.qrc + +TARGET = paysages-modeler + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = ../extension + +# Default rules for deployment. +include(deployment.pri) + +HEADERS += \ + OpenGLView.h \ + modeler_global.h \ + MainModelerWindow.h \ + WaterModeler.h \ + AtmosphereModeler.h \ + RenderPreviewProvider.h \ + RenderProcess.h \ + ModelerCameras.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 +else:unix: LIBS += -L$$OUT_PWD/../../../system/ -lpaysages_system +INCLUDEPATH += $$PWD/../../../system +DEPENDPATH += $$PWD/../../../system + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../basics/release/ -lpaysages_basics +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../basics/debug/ -lpaysages_basics +else:unix: LIBS += -L$$OUT_PWD/../../../basics/ -lpaysages_basics +INCLUDEPATH += $$PWD/../../../basics +DEPENDPATH += $$PWD/../../../basics + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../definition/release/ -lpaysages_definition +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../definition/debug/ -lpaysages_definition +else:unix: LIBS += -L$$OUT_PWD/../../../definition/ -lpaysages_definition +INCLUDEPATH += $$PWD/../../../definition +DEPENDPATH += $$PWD/../../../definition + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../render/software/release/ -lpaysages_render_software +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../render/software/debug/ -lpaysages_render_software +else:unix: LIBS += -L$$OUT_PWD/../../../render/software/ -lpaysages_render_software +INCLUDEPATH += $$PWD/../../../render/software +DEPENDPATH += $$PWD/../../../render/software + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../render/opengl/release/ -lpaysages_render_opengl +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../render/opengl/debug/ -lpaysages_render_opengl +else:unix: LIBS += -L$$OUT_PWD/../../../render/opengl/ -lpaysages_render_opengl +INCLUDEPATH += $$PWD/../../../render/opengl +DEPENDPATH += $$PWD/../../../render/opengl + +OTHER_FILES += \ + qml/main.qml \ + qml/ToolbarButton.qml \ + qml/OpenGLView.qml \ + qml/Toolbar.qml \ + qml/Tooltip.qml \ + qml/BasePanel.qml \ + qml/PanelWaterLevel.qml \ + qml/PanelAtmosphereDaytime.qml \ + qml/BaseSlider.qml \ + qml/BaseChoice.qml \ + qml/BaseChoiceItem.qml \ + qml/RenderDialog.qml \ + qml/CameraChoice.qml diff --git a/src/paysages.pro b/src/paysages.pro index c904bec..2deeb5a 100644 --- a/src/paysages.pro +++ b/src/paysages.pro @@ -9,7 +9,8 @@ SUBDIRS = \ render/preview \ render/opengl \ interface/commandline \ - interface/desktop + interface/desktop \ + interface/modeler exists( tests/googletest/sources/src/gtest-all.cc ) { SUBDIRS += \ diff --git a/src/render/opengl/ExplorerChunkTerrain.cpp b/src/render/opengl/ExplorerChunkTerrain.cpp index 3310814..9e9636f 100644 --- a/src/render/opengl/ExplorerChunkTerrain.cpp +++ b/src/render/opengl/ExplorerChunkTerrain.cpp @@ -85,7 +85,7 @@ bool ExplorerChunkTerrain::maintain() double x = _startx + _tessellation_step * (float)i; double z = _startz + _tessellation_step * (float)j; - double height = _renderer->getTerrainRenderer()->getHeight(x, z, 1); + double height = _renderer->getTerrainRenderer()->getHeight(x, z, true); if (height >= _water_height) { overwater = true; diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 8eb2424..00a80bb 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -16,6 +16,8 @@ OpenGLRenderer::OpenGLRenderer(Scenery* scenery): SoftwareRenderer(scenery) { ready = false; + vp_width = 1; + vp_height = 1; render_quality = 3; @@ -50,25 +52,7 @@ void OpenGLRenderer::initialize() if (ready) { - functions->glClearColor(0.0, 0.0, 0.0, 0.0); - - functions->glDisable(GL_LIGHTING); - - functions->glFrontFace(GL_CCW); - functions->glCullFace(GL_BACK); - functions->glEnable(GL_CULL_FACE); - - functions->glDepthFunc(GL_LESS); - functions->glDepthMask(1); - functions->glEnable(GL_DEPTH_TEST); - - functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - functions->glEnable(GL_LINE_SMOOTH); - functions->glLineWidth(1.0); - - functions->glDisable(GL_FOG); - - functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + prepareOpenGLState(); prepare(); @@ -92,28 +76,63 @@ void OpenGLRenderer::initialize() } } -void OpenGLRenderer::resize(int width, int height) +void OpenGLRenderer::prepareOpenGLState() { if (ready) { - functions->glViewport(0, 0, width, height); + functions->glDisable(GL_LIGHTING); + + functions->glFrontFace(GL_CCW); + functions->glCullFace(GL_BACK); + functions->glEnable(GL_CULL_FACE); + + functions->glDepthFunc(GL_LESS); + functions->glDepthMask(1); + functions->glEnable(GL_DEPTH_TEST); + + functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + functions->glEnable(GL_LINE_SMOOTH); + functions->glLineWidth(1.0); + + functions->glDisable(GL_FOG); + + functions->glEnable(GL_BLEND); + functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + functions->glClearColor(0.0, 0.0, 0.0, 0.0); + functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + functions->glViewport(0, 0, vp_width, vp_height); } +} + +void OpenGLRenderer::setCamera(CameraDefinition *camera) +{ + camera->copy(render_camera); + getScenery()->setCamera(camera); + getScenery()->getCamera(camera); + cameraChangeEvent(camera); +} + +void OpenGLRenderer::resize(int width, int height) +{ + vp_width = width; + vp_height = height; + getScenery()->getCamera()->setRenderSize(width, height); render_camera->setRenderSize(width, height); cameraChangeEvent(getScenery()->getCamera()); + + prepareOpenGLState(); } void OpenGLRenderer::paint() { if (ready) { - functions->glClearColor(0.0, 0.0, 0.0, 0.0); functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - functions->glEnable(GL_BLEND); - functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - skybox->render(); terrain->render(); water->render(); diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index d4daeab..9954d5e 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -17,10 +17,21 @@ public: OpenGLRenderer(Scenery* scenery=0); virtual ~OpenGLRenderer(); + inline OpenGLSkybox *getSkybox() const {return skybox;} + inline OpenGLWater *getWater() const {return water;} + inline OpenGLTerrain *getTerrain() const {return terrain;} + void initialize(); + void prepareOpenGLState(); void resize(int width, int height); void paint(); + /** + * Change the camera location. + * + * This may change the camera passed as argument (to stay above ground for example). + */ + void setCamera(CameraDefinition *camera); void cameraChangeEvent(CameraDefinition* camera); inline OpenGLFunctions* getOpenGlFunctions() const {return functions;} @@ -31,6 +42,8 @@ public: private: bool ready; + int vp_width; + int vp_height; OpenGLFunctions* functions; OpenGLSharedState* shared_state; diff --git a/src/render/opengl/OpenGLSkybox.cpp b/src/render/opengl/OpenGLSkybox.cpp index bdf3139..6b6e879 100644 --- a/src/render/opengl/OpenGLSkybox.cpp +++ b/src/render/opengl/OpenGLSkybox.cpp @@ -25,7 +25,7 @@ void OpenGLSkybox::initialize() { program = createShader("skybox"); program->addVertexSource("skybox"); - program->addFragmentSource("bruneton"); + program->addFragmentSource("atmosphere"); program->addFragmentSource("tonemapping"); program->addFragmentSource("skybox"); diff --git a/src/render/opengl/OpenGLTerrain.cpp b/src/render/opengl/OpenGLTerrain.cpp index b5ecfa2..bfe7a44 100644 --- a/src/render/opengl/OpenGLTerrain.cpp +++ b/src/render/opengl/OpenGLTerrain.cpp @@ -52,7 +52,7 @@ void OpenGLTerrain::initialize() // Prepare shader programs program = createShader("terrain"); program->addVertexSource("terrain"); - program->addFragmentSource("bruneton"); + program->addFragmentSource("atmosphere"); program->addFragmentSource("tonemapping"); program->addFragmentSource("fadeout"); program->addFragmentSource("terrain"); diff --git a/src/render/opengl/OpenGLWater.cpp b/src/render/opengl/OpenGLWater.cpp index fa57d2c..88f6a00 100644 --- a/src/render/opengl/OpenGLWater.cpp +++ b/src/render/opengl/OpenGLWater.cpp @@ -24,7 +24,7 @@ void OpenGLWater::initialize() { program = createShader("water"); program->addVertexSource("water"); - program->addFragmentSource("bruneton"); + program->addFragmentSource("atmosphere"); program->addFragmentSource("tonemapping"); program->addFragmentSource("fadeout"); program->addFragmentSource("noise"); diff --git a/src/render/opengl/shaders/bruneton.frag b/src/render/opengl/shaders/atmosphere.frag similarity index 84% rename from src/render/opengl/shaders/bruneton.frag rename to src/render/opengl/shaders/atmosphere.frag index c9096d9..f486dda 100644 --- a/src/render/opengl/shaders/bruneton.frag +++ b/src/render/opengl/shaders/atmosphere.frag @@ -224,3 +224,53 @@ vec4 getSkyColor(vec3 location, vec3 direction) result += sunTransmittance + vec4(inscattering, 0.0); return result; } + +vec4 applyLighting(vec3 location, vec3 normal, vec4 color, float shininess) +{ + float material_hardness = 0.3; + float material_reflection = 1.0; + + float r0 = Rg + location.y * WORLD_SCALING; + vec3 sun_position = sunDirection * SUN_DISTANCE; + float muS = dot(vec3(0.0, 1.0, 0.0), normalize(sun_position - vec3(0.0, r0, 0.0))); + + vec4 light_color = _transmittanceWithShadow(r0, muS); + + vec4 result = vec4(0.0, 0.0, 0.0, 1.0); + + /* diffused light */ + float diffuse = dot(sunDirection, normal); + float sign = (diffuse < 0.0) ? -1.0 : 1.0; + if (material_hardness <= 0.5) + { + float hardness = material_hardness * 2.0; + diffuse = (1.0 - hardness) * (diffuse * diffuse) * sign + hardness * diffuse; + } + else if (diffuse != 0.0) + { + float hardness = (material_hardness - 0.5) * 2.0; + diffuse = (1.0 - hardness) * diffuse + hardness * sign * sqrt(abs(diffuse)); + } + if (diffuse > 0.0) + { + result += diffuse * color * light_color; + } + + /* specular reflection */ + if (shininess > 0.0 && material_reflection > 0.0) + { + vec3 view = normalize(location - cameraLocation); + vec3 reflect = sunDirection - normal * 2.0 * dot(sunDirection, normal); + float specular = dot(reflect, view); + if (specular > 0.0) + { + specular = pow(specular, shininess) * material_reflection; + if (specular > 0.0) + { + result += specular * light_color; + } + } + } + + return result; +} diff --git a/src/render/opengl/shaders/resources.qrc b/src/render/opengl/shaders/resources.qrc index 72a4bb6..ed4c445 100644 --- a/src/render/opengl/shaders/resources.qrc +++ b/src/render/opengl/shaders/resources.qrc @@ -4,7 +4,7 @@ skybox.vert water.frag water.vert - bruneton.frag + atmosphere.frag tonemapping.frag terrain.frag terrain.vert diff --git a/src/render/opengl/shaders/water.frag b/src/render/opengl/shaders/water.frag index 1f5a9d7..d6b5279 100644 --- a/src/render/opengl/shaders/water.frag +++ b/src/render/opengl/shaders/water.frag @@ -1,32 +1,11 @@ uniform vec4 waterColor; uniform float waterReflection; -vec4 applyLighting(vec3 location, vec3 normal, vec4 color, float shininess) -{ - // TEMP phong lighting implementation for testing - vec3 N = normalize(normal); - vec3 L = sunDirection; - vec3 E = normalize(cameraLocation - location); - vec3 R = normalize(-reflect(L, N)); - - //calculate Ambient Term: - vec4 Iamb = vec4(0.1, 0.1, 0.1, 1.0); - - //calculate Diffuse Term: - vec4 Idiff = vec4(3.0, 3.0, 3.0, 1.0) * color * max(dot(N, L), 0.0); - - // calculate Specular Term: - vec4 Ispec = vec4(3.0, 3.0, 3.0, 1.0) * pow(max(dot(R,E),0.0),0.3*shininess); - - // write Total Color: - return Iamb + Idiff + Ispec; -} - void main(void) { vec3 normal = noiseNormal2d(unprojected.xz, 0.001); - gl_FragColor = applyLighting(unprojected, normal, waterColor, 100.0); + gl_FragColor = applyLighting(unprojected, normal, waterColor, 16.0); vec3 reflected = reflect(unprojected - cameraLocation, normal); reflected.y = max(reflected.y, 0.0); diff --git a/src/render/preview/SceneryTopDownPreviewRenderer.cpp b/src/render/preview/SceneryTopDownPreviewRenderer.cpp index 62199b2..a5b71a6 100644 --- a/src/render/preview/SceneryTopDownPreviewRenderer.cpp +++ b/src/render/preview/SceneryTopDownPreviewRenderer.cpp @@ -46,7 +46,7 @@ void SceneryTopDownPreviewRenderer::updateEvent() Color SceneryTopDownPreviewRenderer::getColor2D(double x, double y, double scaling) { Vector3 location; - double height = getTerrainRenderer()->getHeight(x, y, 1); + double height = getTerrainRenderer()->getHeight(x, y, true); if (height < getWaterRenderer()->getHeightInfo().max_height) { diff --git a/src/render/preview/TerrainShapePreviewRenderer.cpp b/src/render/preview/TerrainShapePreviewRenderer.cpp index f053550..9e5c940 100644 --- a/src/render/preview/TerrainShapePreviewRenderer.cpp +++ b/src/render/preview/TerrainShapePreviewRenderer.cpp @@ -57,7 +57,7 @@ Color TerrainShapePreviewRenderer::getColor2D(double x, double y, double scaling { double height; - height = getTerrainRenderer()->getHeight(x, y, 1); + height = getTerrainRenderer()->getHeight(x, y, true); if (height > 0.0) { return getTerrainRenderer()->getFinalColor(Vector3(x, height, y), 0.000001); diff --git a/src/render/preview/TextureLayerCoveragePreviewRenderer.cpp b/src/render/preview/TextureLayerCoveragePreviewRenderer.cpp index 305a422..217b62b 100644 --- a/src/render/preview/TextureLayerCoveragePreviewRenderer.cpp +++ b/src/render/preview/TextureLayerCoveragePreviewRenderer.cpp @@ -53,7 +53,7 @@ Color TextureLayerCoveragePreviewRenderer::getColor2D(double x, double y, double TexturesRenderer* textures_renderer = getTexturesRenderer(); TerrainRenderer* terrain_renderer = getTerrainRenderer(); - double presence = textures_renderer->getBasePresence(layer, terrain_renderer->getResult(x, y, 1, 0)); + double presence = textures_renderer->getBasePresence(layer, terrain_renderer->getResult(x, y, true, false)); return Color(presence, presence, presence); } diff --git a/src/render/preview/TexturesMixPreviewRenderer.cpp b/src/render/preview/TexturesMixPreviewRenderer.cpp index a030da8..9802c4d 100644 --- a/src/render/preview/TexturesMixPreviewRenderer.cpp +++ b/src/render/preview/TexturesMixPreviewRenderer.cpp @@ -53,6 +53,6 @@ void TexturesMixPreviewRenderer::updateEvent() Color TexturesMixPreviewRenderer::getColor2D(double x, double y, double scaling) { TerrainRenderer* terrain_renderer = getTerrainRenderer(); - Vector3 location(x, terrain_renderer->getHeight(x, y, 1), y); + Vector3 location(x, terrain_renderer->getHeight(x, y, true), y); return terrain_renderer->getFinalColor(location, scaling); } 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; diff --git a/src/render/software/TerrainRasterizer.cpp b/src/render/software/TerrainRasterizer.cpp index 14e7b55..f836dd3 100644 --- a/src/render/software/TerrainRasterizer.cpp +++ b/src/render/software/TerrainRasterizer.cpp @@ -17,7 +17,7 @@ TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id): static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z) { - return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, 1), z); + return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, true), z); } void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail) @@ -52,19 +52,19 @@ void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, do ov1.x = x; ov1.z = z; - dv1 = renderer->getTerrainRenderer()->getResult(x, z, 1, 1).location; + dv1 = renderer->getTerrainRenderer()->getResult(x, z, true, true).location; ov2.x = x; ov2.z = z + size; - dv2 = renderer->getTerrainRenderer()->getResult(x, z + size, 1, 1).location; + dv2 = renderer->getTerrainRenderer()->getResult(x, z + size, true, true).location; ov3.x = x + size; ov3.z = z + size; - dv3 = renderer->getTerrainRenderer()->getResult(x + size, z + size, 1, 1).location; + dv3 = renderer->getTerrainRenderer()->getResult(x + size, z + size, true, true).location; ov4.x = x + size; ov4.z = z; - dv4 = renderer->getTerrainRenderer()->getResult(x + size, z, 1, 1).location; + dv4 = renderer->getTerrainRenderer()->getResult(x + size, z, true, true).location; if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height) { @@ -72,12 +72,12 @@ void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, do } } -static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, int displaced) +static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced) { - chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, 1, displaced).location; - chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, 1, displaced).location; - chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, 1, displaced).location; - chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, 1, displaced).location; + chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true, displaced).location; + chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true, displaced).location; + chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, true, displaced).location; + chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, true, displaced).location; double displacement_power; if (displaced) @@ -125,7 +125,7 @@ static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChun } } -void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, int displaced) +void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displaced) { TerrainChunkInfo chunk; int chunk_factor, chunk_count, i; @@ -199,7 +199,7 @@ void TerrainRasterizer::processChunk(CanvasPortion* canvas, TerrainChunkInfo* ch void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas) { - getTessellationInfo(canvas, 0); + getTessellationInfo(canvas, false); } Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const diff --git a/src/render/software/TerrainRasterizer.h b/src/render/software/TerrainRasterizer.h index 379c046..5958355 100644 --- a/src/render/software/TerrainRasterizer.h +++ b/src/render/software/TerrainRasterizer.h @@ -34,7 +34,7 @@ public: * * The terrain will be broken in chunks, most detailed near the camera. */ - void getTessellationInfo(CanvasPortion* canvas, int displaced); + void getTessellationInfo(CanvasPortion* canvas, bool displaced); /** * Tessellate a terrain chunk, pushing the quads in the render area. diff --git a/src/render/software/TerrainRenderer.cpp b/src/render/software/TerrainRenderer.cpp index e3361fd..a087b36 100644 --- a/src/render/software/TerrainRenderer.cpp +++ b/src/render/software/TerrainRenderer.cpp @@ -23,9 +23,9 @@ void TerrainRenderer::update() walker->update(); } -double TerrainRenderer::getHeight(double x, double z, int with_painting) +double TerrainRenderer::getHeight(double x, double z, bool with_painting) { - return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, 1, with_painting); + return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, with_painting); } static inline Vector3 _getNormal4(Vector3 center, Vector3 north, Vector3 east, Vector3 south, Vector3 west) @@ -50,7 +50,7 @@ static inline Vector3 _getNormal2(Vector3 center, Vector3 east, Vector3 south) return south.sub(center).crossProduct(east.sub(center)).normalize(); } -TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, int with_painting, int with_textures) +TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bool with_painting, bool with_textures) { TerrainResult result; double detail = 0.001; /* TODO */ diff --git a/src/render/software/TerrainRenderer.h b/src/render/software/TerrainRenderer.h index fba3301..529e1ed 100644 --- a/src/render/software/TerrainRenderer.h +++ b/src/render/software/TerrainRenderer.h @@ -27,8 +27,8 @@ public: virtual void update(); virtual RayCastingResult castRay(const Vector3 &start, const Vector3 &direction); - virtual double getHeight(double x, double z, int with_painting); - virtual TerrainResult getResult(double x, double z, int with_painting, int with_textures); + virtual double getHeight(double x, double z, bool with_painting); + virtual TerrainResult getResult(double x, double z, bool with_painting, bool with_textures); virtual Color getFinalColor(const Vector3 &location, double precision); virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override; diff --git a/src/tests/AtmosphereDefinition_Test.cpp b/src/tests/AtmosphereDefinition_Test.cpp new file mode 100644 index 0000000..5e151a0 --- /dev/null +++ b/src/tests/AtmosphereDefinition_Test.cpp @@ -0,0 +1,36 @@ +#include "BaseTestCase.h" + +#include "AtmosphereDefinition.h" + +TEST(AtmosphereDefinition, setDaytime) +{ + AtmosphereDefinition atmo(NULL); + + atmo.setDaytime(0.0); + EXPECT_EQ(atmo.hour, 0); + EXPECT_EQ(atmo.minute, 0); + + atmo.setDaytime(0.1); + EXPECT_EQ(atmo.hour, 2); + EXPECT_EQ(atmo.minute, 24); + + atmo.setDaytime(0.25); + EXPECT_EQ(atmo.hour, 6); + EXPECT_EQ(atmo.minute, 0); + + atmo.setDaytime(0.5); + EXPECT_EQ(atmo.hour, 12); + EXPECT_EQ(atmo.minute, 0); + + atmo.setDaytime(1.0); + EXPECT_EQ(atmo.hour, 0); + EXPECT_EQ(atmo.minute, 0); + + atmo.setDaytime(-0.5); + EXPECT_EQ(atmo.hour, 12); + EXPECT_EQ(atmo.minute, 0); + + atmo.setDaytime(1.5); + EXPECT_EQ(atmo.hour, 12); + EXPECT_EQ(atmo.minute, 0); +} diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 863ddff..4668460 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -20,7 +20,8 @@ SOURCES += main.cpp \ FractalNoise_Test.cpp \ Canvas_Test.cpp \ CanvasPortion_Test.cpp \ - CanvasPreview_Test.cpp + CanvasPreview_Test.cpp \ + AtmosphereDefinition_Test.cpp HEADERS += \ BaseTestCase.h