diff --git a/src/definition/DefinitionNode.cpp b/src/definition/DefinitionNode.cpp index 2c89a98..a6c44db 100644 --- a/src/definition/DefinitionNode.cpp +++ b/src/definition/DefinitionNode.cpp @@ -2,6 +2,7 @@ #include "Logs.h" #include "PackStream.h" +#include "DefinitionWatcher.h" #include "DefinitionDiff.h" #include "DiffManager.h" @@ -157,10 +158,25 @@ bool DefinitionNode::applyDiff(const DefinitionDiff *diff, bool) } } -void DefinitionNode::addWatcher(DefinitionWatcher *watcher) +void DefinitionNode::generateInitDiffs(std::vector *) const +{ +} + +void DefinitionNode::addWatcher(DefinitionWatcher *watcher, bool init_diff) { if (root && root->diffs) { + if (init_diff) + { + std::vector diffs; + generateInitDiffs(&diffs); + + for (auto diff: diffs) + { + watcher->nodeChanged(this, diff); + delete diff; + } + } root->diffs->addWatcher(this, watcher); } } diff --git a/src/definition/DefinitionNode.h b/src/definition/DefinitionNode.h index b6363ab..5fb3307 100644 --- a/src/definition/DefinitionNode.h +++ b/src/definition/DefinitionNode.h @@ -61,12 +61,21 @@ public: */ virtual bool applyDiff(const DefinitionDiff *diff, bool backward=false); + /** + * Fill a diff array to be applied to initialize a proper state for a watcher. + * + * This method should be overridden by subclasses. + */ + virtual void generateInitDiffs(std::vector *diffs) const; + /** * Add a watcher over this node. * * The watcher will receive DefinitionDiff objects when this node changes. + * + * If *init_diff* is set to true, a first diff (or several) will be be pushed immediately to initialize the state. */ - void addWatcher(DefinitionWatcher *watcher); + void addWatcher(DefinitionWatcher *watcher, bool init_diff=false); protected: void addChild(DefinitionNode* child); diff --git a/src/definition/DefinitionWatcher.h b/src/definition/DefinitionWatcher.h index 1a6217c..b209e69 100644 --- a/src/definition/DefinitionWatcher.h +++ b/src/definition/DefinitionWatcher.h @@ -15,6 +15,11 @@ class DEFINITIONSHARED_EXPORT DefinitionWatcher { public: DefinitionWatcher(); + + /** + * Abstract method called when a node changed. + */ + virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) = 0; }; } diff --git a/src/definition/DiffManager.cpp b/src/definition/DiffManager.cpp index 385afda..ea8ce25 100644 --- a/src/definition/DiffManager.cpp +++ b/src/definition/DiffManager.cpp @@ -2,6 +2,7 @@ #include "DefinitionNode.h" #include "DefinitionDiff.h" +#include "DefinitionWatcher.h" DiffManager::DiffManager(DefinitionNode *tree): tree(tree) @@ -9,6 +10,15 @@ DiffManager::DiffManager(DefinitionNode *tree): undone = 0; } +DiffManager::~DiffManager() +{ + for (auto diff: diffs) + { + delete diff; + } + diffs.clear(); +} + void DiffManager::addWatcher(const DefinitionNode *node, DefinitionWatcher *watcher) { watchers[node].push_back(watcher); @@ -19,6 +29,7 @@ void DiffManager::addDiff(DefinitionNode *node, const DefinitionDiff *diff) while (undone > 0) { // truncate diffs ahead + delete diffs.back(); diffs.pop_back(); undone--; } @@ -28,9 +39,9 @@ void DiffManager::addDiff(DefinitionNode *node, const DefinitionDiff *diff) // TODO Delayed commit (with merge of consecutive diffs) node->applyDiff(diff); - for (auto &watcher: watchers[node]) + for (auto watcher: watchers[node]) { - // TODO + watcher->nodeChanged(node, diff); } } diff --git a/src/definition/DiffManager.h b/src/definition/DiffManager.h index 7af1b26..f0998ce 100644 --- a/src/definition/DiffManager.h +++ b/src/definition/DiffManager.h @@ -18,6 +18,7 @@ class DEFINITIONSHARED_EXPORT DiffManager { public: DiffManager(DefinitionNode *tree); + ~DiffManager(); /** * Add a watcher for a specific node. diff --git a/src/definition/FloatNode.cpp b/src/definition/FloatNode.cpp index 94daeda..2bca647 100644 --- a/src/definition/FloatNode.cpp +++ b/src/definition/FloatNode.cpp @@ -52,6 +52,11 @@ const FloatDiff *FloatNode::produceDiff(double new_value) const return new FloatDiff(this, value, new_value); } +void FloatNode::generateInitDiffs(std::vector *diffs) const +{ + diffs->push_back(produceDiff(value)); +} + bool FloatNode::applyDiff(const DefinitionDiff *diff, bool backward) { if (!DefinitionNode::applyDiff(diff, backward)) diff --git a/src/definition/FloatNode.h b/src/definition/FloatNode.h index dba95d8..6a8bb68 100644 --- a/src/definition/FloatNode.h +++ b/src/definition/FloatNode.h @@ -30,6 +30,7 @@ public: */ void setValue(double new_value); const FloatDiff *produceDiff(double new_value) const; + void generateInitDiffs(std::vector *diffs) const; virtual bool applyDiff(const DefinitionDiff *diff, bool backward=false) override; private: double value; diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index 8ac68a6..ef00cc1 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -15,8 +15,7 @@ TerrainDefinition::TerrainDefinition(DefinitionNode* parent): height_map = new TerrainHeightMap(this); addChild(height_map); - water_height = -0.3; - _water_height = new FloatNode(this, "water_height", -0.3); + water_height = new FloatNode(this, "water_height", -0.3); _height_noise = new NoiseGenerator; } @@ -67,7 +66,6 @@ void TerrainDefinition::save(PackStream* stream) const stream->write(&height); stream->write(&scaling); stream->write(&shadow_smoothing); - stream->write(&water_height); _height_noise->save(stream); } @@ -78,7 +76,6 @@ void TerrainDefinition::load(PackStream* stream) stream->read(&height); stream->read(&scaling); stream->read(&shadow_smoothing); - stream->read(&water_height); _height_noise->load(stream); validate(); @@ -96,7 +93,7 @@ double TerrainDefinition::getGridHeight(int x, int z, bool with_painting) return h; } -double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, bool with_painting) +double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, bool with_painting, bool water_offset) { double h; x /= scaling; @@ -109,7 +106,7 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, if (scaled) { - return (h - water_height) * height * scaling; + return (water_offset ? (h - water_height->getValue()) : h) * height * scaling; } else { @@ -117,6 +114,11 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, } } +double TerrainDefinition::getWaterOffset() const +{ + return -water_height->getValue() * height * scaling; +} + HeightInfo TerrainDefinition::getHeightInfo() { HeightInfo result; @@ -124,7 +126,7 @@ HeightInfo TerrainDefinition::getHeightInfo() result.min_height = _min_height; result.max_height = _max_height; /* TODO This is duplicated in ter_render.c (_realGetWaterHeight) */ - result.base_height = water_height * height * scaling; + result.base_height = water_height->getValue() * height * scaling; return result; } diff --git a/src/definition/TerrainDefinition.h b/src/definition/TerrainDefinition.h index 626d8ea..27bcc2f 100644 --- a/src/definition/TerrainDefinition.h +++ b/src/definition/TerrainDefinition.h @@ -27,8 +27,11 @@ public: virtual void copy(DefinitionNode* destination) const override; virtual void validate() override; + inline FloatNode *propWaterHeight() const {return water_height;} + double getGridHeight(int x, int z, bool with_painting); - double getInterpolatedHeight(double x, double z, bool scaled, bool with_painting); + double getInterpolatedHeight(double x, double z, bool scaled, bool with_painting, bool water_offset=true); + double getWaterOffset() const; unsigned long getMemoryStats(); HeightInfo getHeightInfo(); @@ -46,15 +49,13 @@ public: TerrainHeightMap* height_map; - double water_height; - double _detail; NoiseGenerator* _height_noise; double _min_height; double _max_height; private: - FloatNode *_water_height; + FloatNode *water_height; }; } diff --git a/src/interface/modeler/quickapp/MainModelerWindow.cpp b/src/interface/modeler/quickapp/MainModelerWindow.cpp index 947a9d8..d8494ca 100644 --- a/src/interface/modeler/quickapp/MainModelerWindow.cpp +++ b/src/interface/modeler/quickapp/MainModelerWindow.cpp @@ -10,6 +10,7 @@ #include "RenderPreviewProvider.h" #include "RenderProcess.h" #include "RenderConfig.h" +#include "DiffManager.h" #include #include @@ -103,4 +104,25 @@ void MainModelerWindow::keyReleaseEvent(QKeyEvent *event) QGuiApplication::instance()->exit(); } } + else if (event->key() == Qt::Key_Z) + { + if (event->modifiers() & Qt::ControlModifier) + { + if (event->modifiers() & Qt::ShiftModifier) + { + scenery->getDiffManager()->redo(); + } + else + { + scenery->getDiffManager()->undo(); + } + } + } + else if (event->key() == Qt::Key_Y) + { + if (event->modifiers() & Qt::ControlModifier) + { + scenery->getDiffManager()->undo(); + } + } } diff --git a/src/interface/modeler/quickapp/WaterModeler.cpp b/src/interface/modeler/quickapp/WaterModeler.cpp index f54960a..4a497a0 100644 --- a/src/interface/modeler/quickapp/WaterModeler.cpp +++ b/src/interface/modeler/quickapp/WaterModeler.cpp @@ -1,6 +1,10 @@ #include "WaterModeler.h" #include "MainModelerWindow.h" +#include "Scenery.h" +#include "TerrainDefinition.h" +#include "FloatNode.h" +#include "Logs.h" WaterModeler::WaterModeler(MainModelerWindow *main): main(main) @@ -8,12 +12,17 @@ WaterModeler::WaterModeler(MainModelerWindow *main): QObject *item = main->findQmlObject("water_level"); if (item) { + item->setProperty("value", propWaterHeight()->getValue() * 0.5 + 0.5); connect(item, SIGNAL(changed(double)), this, SLOT(waterLevelChanged(double))); } } -void WaterModeler::waterLevelChanged(double) +void WaterModeler::waterLevelChanged(double value) { - // TODO - //qDebug() << "water level : " << value; + propWaterHeight()->setValue(value * 2.0 - 1.0); +} + +FloatNode *WaterModeler::propWaterHeight() const +{ + return main->getScenery()->getTerrain()->propWaterHeight(); } diff --git a/src/interface/modeler/quickapp/WaterModeler.h b/src/interface/modeler/quickapp/WaterModeler.h index fac513e..ceb2b10 100644 --- a/src/interface/modeler/quickapp/WaterModeler.h +++ b/src/interface/modeler/quickapp/WaterModeler.h @@ -18,6 +18,7 @@ public slots: void waterLevelChanged(double value); private: + FloatNode *propWaterHeight() const; MainModelerWindow *main; }; diff --git a/src/render/opengl/ExplorerChunkTerrain.cpp b/src/render/opengl/ExplorerChunkTerrain.cpp index 2d9f297..baaca0a 100644 --- a/src/render/opengl/ExplorerChunkTerrain.cpp +++ b/src/render/opengl/ExplorerChunkTerrain.cpp @@ -9,7 +9,7 @@ #include "TerrainRenderer.h" #include "VertexArray.h" -ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height): +ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks): _renderer(renderer) { priority = 0.0; @@ -32,7 +32,6 @@ ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, d distance_to_camera = 0.0; - _water_height = water_height; overwater = false; tessellation_count = 33; @@ -90,8 +89,8 @@ bool ExplorerChunkTerrain::maintain() double x = _startx + _tessellation_step * (float)i; double z = _startz + _tessellation_step * (float)j; - double height = _renderer->getTerrainRenderer()->getHeight(x, z, true); - if (height >= _water_height) + double height = _renderer->getTerrainRenderer()->getHeight(x, z, true, false); + if (height >= 0.0) { overwater = true; } @@ -270,7 +269,7 @@ void ExplorerChunkTerrain::render(QOpenGLShaderProgram* program, OpenGLFunctions int tessellation_size = _tessellation_current_size; _lock_data.unlock(); - if (tessellation_size <= 1 or not overwater) + if (tessellation_size <= 1) { return; } diff --git a/src/render/opengl/ExplorerChunkTerrain.h b/src/render/opengl/ExplorerChunkTerrain.h index 6402299..cc8d420 100644 --- a/src/render/opengl/ExplorerChunkTerrain.h +++ b/src/render/opengl/ExplorerChunkTerrain.h @@ -20,7 +20,7 @@ public: } TerrainVertex; public: - ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height); + ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks); ~ExplorerChunkTerrain(); bool maintain(); @@ -42,8 +42,6 @@ private: double _size; double _overall_step; - double _water_height; - int tessellation_count; VertexArray *tessellated; int _tessellation_max_size; diff --git a/src/render/opengl/OpenGLTerrain.cpp b/src/render/opengl/OpenGLTerrain.cpp index a6c7ec9..dfce72c 100644 --- a/src/render/opengl/OpenGLTerrain.cpp +++ b/src/render/opengl/OpenGLTerrain.cpp @@ -9,6 +9,8 @@ #include "WaterRenderer.h" #include "CameraDefinition.h" #include "Scenery.h" +#include "FloatNode.h" +#include "FloatDiff.h" class ChunkMaintenanceThreads:public ParallelPool { @@ -62,12 +64,11 @@ void OpenGLTerrain::initialize() double size = 800.0; double chunksize = size / (double) chunks; double start = -size / 2.0; - double water_height = renderer->getWaterRenderer()->getHeightInfo().base_height; for (int i = 0; i < chunks; i++) { for (int j = 0; j < chunks; j++) { - ExplorerChunkTerrain* chunk = new ExplorerChunkTerrain(renderer, start + chunksize * (double) i, start + chunksize * (double) j, chunksize, chunks, water_height); + ExplorerChunkTerrain* chunk = new ExplorerChunkTerrain(renderer, start + chunksize * (double) i, start + chunksize * (double) j, chunksize, chunks); _chunks.append(chunk); _updateQueue.append(chunk); } @@ -75,6 +76,9 @@ void OpenGLTerrain::initialize() // Start chunks maintenance work->start(); + + // Watch for definition changes + renderer->getScenery()->getTerrain()->propWaterHeight()->addWatcher(this); } void OpenGLTerrain::update() @@ -142,3 +146,12 @@ void OpenGLTerrain::performChunksMaintenance() qSort(_updateQueue.begin(), _updateQueue.end(), _cmpChunks); _lock_chunks.unlock(); } + + +void OpenGLTerrain::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) +{ + if (node->getPath() == "/terrain/water_height") + { + resetTextures(); + } +} diff --git a/src/render/opengl/OpenGLTerrain.h b/src/render/opengl/OpenGLTerrain.h index a888f5e..fa6cbec 100644 --- a/src/render/opengl/OpenGLTerrain.h +++ b/src/render/opengl/OpenGLTerrain.h @@ -4,6 +4,7 @@ #include "opengl_global.h" #include "OpenGLPart.h" +#include "DefinitionWatcher.h" #include #include @@ -12,7 +13,7 @@ namespace paysages { namespace opengl { -class OPENGLSHARED_EXPORT OpenGLTerrain:public OpenGLPart +class OPENGLSHARED_EXPORT OpenGLTerrain: public OpenGLPart, public DefinitionWatcher { public: OpenGLTerrain(OpenGLRenderer* renderer); @@ -30,6 +31,7 @@ public: void performChunksMaintenance(); + virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override; private: OpenGLShaderProgram* program; @@ -38,6 +40,7 @@ private: QVector _chunks; QList _updateQueue; QMutex _lock_chunks; + }; } diff --git a/src/render/opengl/OpenGLWater.cpp b/src/render/opengl/OpenGLWater.cpp index 88f6a00..111e5e1 100644 --- a/src/render/opengl/OpenGLWater.cpp +++ b/src/render/opengl/OpenGLWater.cpp @@ -8,6 +8,8 @@ #include "WaterDefinition.h" #include "SurfaceMaterial.h" #include "NoiseFunctionSimplex.h" +#include "FloatNode.h" +#include "FloatDiff.h" OpenGLWater::OpenGLWater(OpenGLRenderer *renderer): OpenGLPart(renderer) @@ -34,6 +36,9 @@ void OpenGLWater::initialize() setVertex(1, -1.0f, 0.0f, 1.0f); setVertex(2, 1.0f, 0.0f, -1.0f); setVertex(3, 1.0f, 0.0f, 1.0f); + + // Watch for definition changes + renderer->getScenery()->getTerrain()->propWaterHeight()->addWatcher(this, true); } void OpenGLWater::update() @@ -58,3 +63,11 @@ void OpenGLWater::setVertex(int i, float x, float y, float z) vertices[i * 3 + 1] = y; vertices[i * 3 + 2] = z; } + +void OpenGLWater::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) +{ + if (node->getPath() == "/terrain/water_height") + { + renderer->getSharedState()->set("waterOffset", renderer->getScenery()->getTerrain()->getWaterOffset()); + } +} diff --git a/src/render/opengl/OpenGLWater.h b/src/render/opengl/OpenGLWater.h index a30d4bc..c64ae2e 100644 --- a/src/render/opengl/OpenGLWater.h +++ b/src/render/opengl/OpenGLWater.h @@ -4,11 +4,12 @@ #include "opengl_global.h" #include "OpenGLPart.h" +#include "DefinitionWatcher.h" namespace paysages { namespace opengl { -class OPENGLSHARED_EXPORT OpenGLWater: public OpenGLPart +class OPENGLSHARED_EXPORT OpenGLWater: public OpenGLPart, public DefinitionWatcher { public: OpenGLWater(OpenGLRenderer* renderer); @@ -18,6 +19,7 @@ public: virtual void update() override; virtual void render() override; + virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override; private: void setVertex(int i, float x, float y, float z); diff --git a/src/render/opengl/shaders/terrain.vert b/src/render/opengl/shaders/terrain.vert index c4fd270..697e1e0 100644 --- a/src/render/opengl/shaders/terrain.vert +++ b/src/render/opengl/shaders/terrain.vert @@ -3,10 +3,12 @@ attribute highp vec2 uv; uniform highp mat4 viewMatrix; varying vec3 unprojected; varying vec2 texcoord; +uniform float waterOffset; void main(void) { - unprojected = vertex.xyz; + vec4 final = vertex + vec4(0, waterOffset, 0, 0); + unprojected = final.xyz; texcoord = uv; - gl_Position = viewMatrix * vertex; + gl_Position = viewMatrix * final; } diff --git a/src/render/software/TerrainRenderer.cpp b/src/render/software/TerrainRenderer.cpp index a087b36..1e5ecb3 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, bool with_painting) +double TerrainRenderer::getHeight(double x, double z, bool with_painting, bool water_offset) { - return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, with_painting); + return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, with_painting, water_offset); } static inline Vector3 _getNormal4(Vector3 center, Vector3 north, Vector3 east, Vector3 south, Vector3 west) diff --git a/src/render/software/TerrainRenderer.h b/src/render/software/TerrainRenderer.h index 529e1ed..7301ae0 100644 --- a/src/render/software/TerrainRenderer.h +++ b/src/render/software/TerrainRenderer.h @@ -27,7 +27,7 @@ public: virtual void update(); virtual RayCastingResult castRay(const Vector3 &start, const Vector3 &direction); - virtual double getHeight(double x, double z, bool with_painting); + virtual double getHeight(double x, double z, bool with_painting, bool water_offset=true); 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/DiffManager_Test.cpp b/src/tests/DiffManager_Test.cpp index 4e60b5e..332aed3 100644 --- a/src/tests/DiffManager_Test.cpp +++ b/src/tests/DiffManager_Test.cpp @@ -2,6 +2,8 @@ #include "DiffManager.h" #include "DefinitionNode.h" +#include "DefinitionWatcher.h" +#include "FloatDiff.h" #include "FloatNode.h" TEST(DiffManager, undoRedo) @@ -111,3 +113,52 @@ TEST(DiffManager, undoBranch) diffs->redo(); EXPECT_DOUBLE_EQ(4.4, leaf.getValue()); } + +class TestWatcher: public DefinitionWatcher +{ +public: + TestWatcher(DefinitionNode* expected_node, double expected_old_value, double expected_new_value): + DefinitionWatcher(), expected_node(expected_node), expected_old_value(expected_old_value), expected_new_value(expected_new_value) + { + calls = 0; + } + + virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override + { + EXPECT_EQ(expected_node, node); + ASSERT_EQ("float", diff->getTypeName()); + const FloatDiff *float_diff = (const FloatDiff *)diff; + EXPECT_EQ(expected_old_value, float_diff->getOldValue()); + EXPECT_EQ(expected_new_value, float_diff->getNewValue()); + calls++; + } + + int calls; + DefinitionNode* expected_node; + double expected_old_value; + double expected_new_value; +}; + +TEST(DiffManager, addWatcher) +{ + FloatNode node(NULL, "node"); + TestWatcher watcher(&node, 1.3, -4.0); + + node.setValue(1.3); + EXPECT_EQ(0, watcher.calls); + + node.addWatcher(&watcher); + EXPECT_EQ(0, watcher.calls); + + node.setValue(-4.0); + EXPECT_EQ(1, watcher.calls); +} + +TEST(DiffManager, addWatcherWithInitDiffs) +{ + FloatNode node(NULL, "node", 1.3); + TestWatcher watcher(&node, 1.3, 1.3); + + node.addWatcher(&watcher, true); + EXPECT_EQ(1, watcher.calls); +} diff --git a/src/tests/TerrainPainting_Test.cpp b/src/tests/TerrainPainting_Test.cpp index 1df9b3f..ab96151 100644 --- a/src/tests/TerrainPainting_Test.cpp +++ b/src/tests/TerrainPainting_Test.cpp @@ -5,6 +5,7 @@ #include "TerrainDefinition.h" #include "TerrainHeightMap.h" #include "PaintedGridBrush.h" +#include "FloatNode.h" /* Noise sin period is defined at 20.0 */ #define X_FACTOR (M_PI / 10.0) @@ -31,7 +32,7 @@ protected: terrain->height = 3.0; terrain->scaling = 1.0; terrain->_height_noise->clearLevels(); - terrain->water_height = 0.0; + terrain->propWaterHeight()->setValue(0.0); NoiseGenerator::NoiseLevel level = {1.0, 2.0, -1.0}; terrain->_height_noise->addLevel(level); noise_state.resetOffsets();