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 @@
+
+
+
+
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