diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index a1a8c3b..0fa2bf1 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -9,7 +9,6 @@ TerrainDefinition::TerrainDefinition(DefinitionNode* parent): DefinitionNode(parent, "terrain", "terrain") { height = 1.0; - scaling = 1.0; shadow_smoothing = 0.0; height_map = new TerrainHeightMap(this); @@ -36,8 +35,8 @@ void TerrainDefinition::validate() /* Get minimal and maximal height */ _height_noise->getRange(&_min_height, &_max_height); - _min_height *= height * scaling; - _max_height *= height * scaling; + _min_height *= height; + _max_height *= height; /* TODO Alter with heightmap min/max */ } @@ -47,7 +46,6 @@ void TerrainDefinition::copy(DefinitionNode* _destination) const TerrainDefinition* destination = (TerrainDefinition*)_destination; destination->height = height; - destination->scaling = scaling; destination->shadow_smoothing = shadow_smoothing; height_map->copy(destination->height_map); @@ -62,7 +60,6 @@ void TerrainDefinition::save(PackStream* stream) const DefinitionNode::save(stream); stream->write(&height); - stream->write(&scaling); stream->write(&shadow_smoothing); _height_noise->save(stream); } @@ -72,7 +69,6 @@ void TerrainDefinition::load(PackStream* stream) DefinitionNode::load(stream); stream->read(&height); - stream->read(&scaling); stream->read(&shadow_smoothing); _height_noise->load(stream); @@ -94,8 +90,6 @@ double TerrainDefinition::getGridHeight(int x, int z, bool with_painting) double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, bool with_painting, bool water_offset) { double h; - x /= scaling; - z /= scaling; if (!with_painting || !height_map->getInterpolatedValue(x, z, &h)) { @@ -104,7 +98,7 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, if (scaled) { - return (water_offset ? (h - water_height->getValue()) : h) * height * scaling; + return (water_offset ? (h - water_height->getValue()) : h) * height; } else { @@ -114,7 +108,7 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, double TerrainDefinition::getWaterOffset() const { - return -water_height->getValue() * height * scaling; + return -water_height->getValue() * height; } HeightInfo TerrainDefinition::getHeightInfo() @@ -123,7 +117,6 @@ 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 = -getWaterOffset(); return result; @@ -146,7 +139,6 @@ void TerrainDefinition::applyPreset(TerrainPreset preset) _height_noise->addLevelsSimple(resolution - 2, pow(2.0, resolution - 1), -0.7, 0.7, 0.5); _height_noise->normalizeAmplitude(-1.0, 1.0, 0); _height_noise->setFunctionParams(NoiseGenerator::NOISE_FUNCTION_SIMPLEX, 0.0, 0.0); - scaling = 1.0; height = 30.0; shadow_smoothing = 0.03; break; diff --git a/src/definition/TerrainDefinition.h b/src/definition/TerrainDefinition.h index 27bcc2f..65aa757 100644 --- a/src/definition/TerrainDefinition.h +++ b/src/definition/TerrainDefinition.h @@ -44,7 +44,6 @@ public: public: double height; - double scaling; double shadow_smoothing; TerrainHeightMap* height_map; diff --git a/src/definition/TerrainHeightMap.cpp b/src/definition/TerrainHeightMap.cpp index fac6f19..8620b8a 100644 --- a/src/definition/TerrainHeightMap.cpp +++ b/src/definition/TerrainHeightMap.cpp @@ -19,7 +19,7 @@ void TerrainHeightMap::copy(DefinitionNode* _destination) const double TerrainHeightMap::getInitialValue(double x, double y) const { - return terrain->getInterpolatedHeight(x * terrain->scaling, y * terrain->scaling, false, false); + return terrain->getInterpolatedHeight(x, y, false, false); } void TerrainHeightMap::brushElevation(const PaintedGridBrush &brush, double x, double y, double value) diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index bd46567..e1ede13 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -1,4 +1,5 @@ #include "SoftwareCanvasRenderer.h" +#include "TerrainRenderer.h" #include "Scenery.h" #include "CameraDefinition.h" #include "TerrainDefinition.h" @@ -56,10 +57,11 @@ static void testGroundShadowQuality() SoftwareCanvasRenderer renderer(&scenery); renderer.setSize(400, 300); - for (int i = 1; i <= 10; i++) + renderer.setQuality(0.2); + for (int i = 0; i < 6; i++) { // TODO keep same rasterization across renders, or keep rasterization quality low - renderer.render_quality = i; + renderer.getTerrainRenderer()->setQuality((double)i / 5.0); startTestRender(&renderer, "ground_shadow_quality", i); } } diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index f6567da..78a5f33 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -27,7 +27,7 @@ OpenGLRenderer::OpenGLRenderer(Scenery* scenery): view_matrix = new QMatrix4x4; - render_quality = 3; + setQuality(0.3); functions = new OpenGLFunctions(); shared_state = new OpenGLSharedState(); diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index dcca450..c281e1b 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -86,6 +86,14 @@ void SoftwareRenderer::prepare() //fluid_medium->registerMedium(water_renderer); } +void SoftwareRenderer::setQuality(double quality) +{ + terrain_renderer->setQuality(quality); + + // TEMP compat with old code + render_quality = (int)(quality * 9.0) + 1; +} + void SoftwareRenderer::disableAtmosphere() { LightComponent light; diff --git a/src/render/software/SoftwareRenderer.h b/src/render/software/SoftwareRenderer.h index 8334101..c61426c 100644 --- a/src/render/software/SoftwareRenderer.h +++ b/src/render/software/SoftwareRenderer.h @@ -22,8 +22,6 @@ public: int render_quality; CameraDefinition* render_camera; - void* customData[10]; - virtual Vector3 getCameraLocation(const Vector3 &target); virtual Vector3 getCameraDirection(const Vector3 &target); virtual double getPrecision(const Vector3 &location); @@ -38,6 +36,15 @@ public: */ virtual void prepare(); + /*! + * Set the global quality control factor. + * + * Values between 0.0 and 1.0 are standard quality (1.0 is considered a "very good" production quality value). + * + * Values above 1.0 are used for boosting ("extra" quality, for demanding renders). + */ + virtual void setQuality(double quality); + /*! * \brief Disable atmosphere and sky lighting, replacing it by static lights. * diff --git a/src/render/software/TerrainRayWalker.cpp b/src/render/software/TerrainRayWalker.cpp index 640d6a6..8a0cab5 100644 --- a/src/render/software/TerrainRayWalker.cpp +++ b/src/render/software/TerrainRayWalker.cpp @@ -11,6 +11,27 @@ TerrainRayWalker::TerrainRayWalker(SoftwareRenderer* renderer): renderer(renderer) { + setQuality(0.5); +} + +void TerrainRayWalker::setQuality(double displacement_safety, double minimal_step, double maximal_step, double step_factor, double max_distance, double escape_step) +{ + this->displacement_safety = displacement_safety; + this->minimal_step = minimal_step; + this->maximal_step = maximal_step; + this->step_factor = step_factor; + this->max_distance = max_distance; + this->escape_step = escape_step; +} + +void TerrainRayWalker::setQuality(double factor) +{ + setQuality(0.2 + 0.8 * factor, + 1.0 / (factor * factor * 30.0 + 1.0), + 50.0 / (factor * 10.0 + 1.0), + 1.0 / (factor * 10.0 + 1.0), + 10.0 + factor * 200.0, + factor * factor * 100.0); } void TerrainRayWalker::update() @@ -19,16 +40,10 @@ void TerrainRayWalker::update() HeightInfo info = terrain->getHeightInfo(); TexturesDefinition* textures = renderer->getScenery()->getTextures(); - double disp = textures->getMaximalDisplacement(); + displacement_base = textures->getMaximalDisplacement(); - ymin = info.min_height - disp; - ymax = info.max_height + disp; - - ydispmax = disp * (0.5 + (double)renderer->render_quality * 0.05); - ydispmin = -ydispmax; - - minstep = 0.5 * terrain->scaling / (double)renderer->render_quality; - maxstep = 50.0 * terrain->scaling / (double)renderer->render_quality; + ymin = info.min_height - displacement_base; + ymax = info.max_height + displacement_base; } static inline Vector3 _getShiftAxis(const Vector3 &direction) @@ -44,7 +59,7 @@ static inline Vector3 _getShiftAxis(const Vector3 &direction) } } -bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result) +bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, double escape_angle, TerrainHitResult &result) { TerrainRenderer* terrain_renderer = renderer->getTerrainRenderer(); TexturesRenderer* textures_renderer = renderer->getTexturesRenderer(); @@ -56,21 +71,14 @@ bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, dou Vector3 previous_cursor = start; bool hit = false; - double step_length = minstep; + double step_length = minimal_step; double walked_length = 0.0; result.escape_angle = 0.0; if (escape_angle != 0.0) { // Prepare escape - if (renderer->render_quality >= 7) - { - shift_step = escape_angle / (double)(renderer->render_quality * renderer->render_quality); - } - else - { - shift_step = escape_angle / (double)renderer->render_quality; - } + shift_step = escape_angle / escape_step; shift_matrix = Matrix4::newRotateAxis(-shift_step, _getShiftAxis(direction)); } @@ -84,13 +92,13 @@ bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, dou diff = cursor.y - terrain_result.location.y; // If we are very under the terrain, consider a hit - if (diff < ydispmin) + if (diff < -displacement_base * displacement_safety) { hit = true; } // If we are close enough to the terrain, apply displacement - else if (diff < ydispmax) + else if (diff < displacement_base * displacement_safety) { displaced = textures_renderer->displaceTerrain(terrain_result); diff = cursor.y - displaced.y; @@ -128,17 +136,17 @@ bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, dou previous_cursor = cursor; walked_length += step_length; - step_length = diff * 10.0 / (double)renderer->render_quality; - if (step_length < minstep) + step_length = diff * step_factor; + if (step_length < minimal_step) { - step_length = minstep; + step_length = minimal_step; } - else if (step_length > maxstep) + else if (step_length > maximal_step) { - step_length = maxstep; + step_length = maximal_step; } } - } while (not hit and cursor.y < ymax and walked_length < max_length); + } while (not hit and cursor.y < ymax and walked_length < max_distance); return hit or result.escape_angle > 0.0; } diff --git a/src/render/software/TerrainRayWalker.h b/src/render/software/TerrainRayWalker.h index 09ea712..0572e51 100644 --- a/src/render/software/TerrainRayWalker.h +++ b/src/render/software/TerrainRayWalker.h @@ -26,6 +26,19 @@ public: public: TerrainRayWalker(SoftwareRenderer* renderer); + /** + * Set the walker quality. + * + * @param displacement_safety Safety factor (around 1.0) to detect when displacement textures need to be applied + * @param minimal_step Minimal length of a walking step + * @param maximal_step Maximal length of a walking step + * @param step_factor Precision factor of steps, depending on terrain proximity + * @param max_distance Maximal distance allowed to travel before considering an escape + * @param escape_step Angle step when allowing an escape angle + */ + void setQuality(double displacement_safety, double minimal_step, double maximal_step, double step_factor, double max_distance, double escape_step); + void setQuality(double factor); + /*! * \brief Update the walker internal data, from the renderer and scenery. */ @@ -37,20 +50,24 @@ public: * \param start Point of origin of the ray * \param direction Ray direction (normalized vector) * \param escape_angle Maximal angle allowed to escape the terrain on hit (mainly for shadows computing) - * \param max_length Maximum length to walk before considering no hit * \param result Object to store the results info * \return true if there was a hit */ - bool startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result); + bool startWalking(const Vector3 &start, Vector3 direction, double escape_angle, TerrainHitResult &result); private: SoftwareRenderer* renderer; double ymin; double ymax; - double ydispmin; - double ydispmax; - double minstep; - double maxstep; + double displacement_base; + + // Quality control + double displacement_safety; + double minimal_step; + double maximal_step; + double step_factor; + double max_distance; + double escape_step; }; } diff --git a/src/render/software/TerrainRenderer.cpp b/src/render/software/TerrainRenderer.cpp index c5ab0b5..0019283 100644 --- a/src/render/software/TerrainRenderer.cpp +++ b/src/render/software/TerrainRenderer.cpp @@ -10,17 +10,34 @@ TerrainRenderer::TerrainRenderer(SoftwareRenderer* parent): parent(parent) { - walker = new TerrainRayWalker(parent); + walker_ray = new TerrainRayWalker(parent); + walker_shadows = new TerrainRayWalker(parent); + quad_normals = false; } TerrainRenderer::~TerrainRenderer() { - delete walker; + delete walker_ray; + delete walker_shadows; } void TerrainRenderer::update() { - walker->update(); + walker_ray->update(); + walker_shadows->update(); +} + +void TerrainRenderer::setQuality(bool quad_normals, double ray_precision, double shadow_precision) +{ + this->quad_normals = quad_normals; + + walker_ray->setQuality(ray_precision); + walker_shadows->setQuality(shadow_precision); +} + +void TerrainRenderer::setQuality(double factor) +{ + setQuality(factor > 0.6, factor, factor * factor); } double TerrainRenderer::getHeight(double x, double z, bool with_painting, bool water_offset) @@ -135,7 +152,7 @@ RayCastingResult TerrainRenderer::castRay(const Vector3 &start, const Vector3 &d { RayCastingResult result; TerrainRayWalker::TerrainHitResult walk_result; - if (walker->startWalking(start, direction.normalize(), 0.0, 200.0, walk_result)) + if (walker_ray->startWalking(start, direction.normalize(), 0.0, walk_result)) { result.hit = true; result.hit_location = walk_result.hit_location; @@ -176,7 +193,7 @@ bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at) // Walk to find an intersection double escape_angle = definition->shadow_smoothing; // TODO max length should depend on the sun light angle and altitude range - if (walker->startWalking(at, direction_to_light, escape_angle, 100.0, walk_result)) + if (walker_shadows->startWalking(at, direction_to_light, escape_angle, walk_result)) { if (walk_result.escape_angle == 0.0) { diff --git a/src/render/software/TerrainRenderer.h b/src/render/software/TerrainRenderer.h index 7301ae0..0c9448c 100644 --- a/src/render/software/TerrainRenderer.h +++ b/src/render/software/TerrainRenderer.h @@ -25,6 +25,8 @@ public: virtual ~TerrainRenderer(); virtual void update(); + void setQuality(bool quad_normals, double ray_precision, double shadow_precision); + void setQuality(double factor); virtual RayCastingResult castRay(const Vector3 &start, const Vector3 &direction); virtual double getHeight(double x, double z, bool with_painting, bool water_offset=true); @@ -33,8 +35,11 @@ public: virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override; private: - SoftwareRenderer* parent; - TerrainRayWalker* walker; + SoftwareRenderer *parent; + TerrainRayWalker *walker_ray; + TerrainRayWalker *walker_shadows; + + bool quad_normals; }; } diff --git a/src/tests/TerrainPainting_Test.cpp b/src/tests/TerrainPainting_Test.cpp index ab96151..6cf298d 100644 --- a/src/tests/TerrainPainting_Test.cpp +++ b/src/tests/TerrainPainting_Test.cpp @@ -30,7 +30,6 @@ protected: virtual void SetUp() { terrain = new TerrainDefinition(NULL); terrain->height = 3.0; - terrain->scaling = 1.0; terrain->_height_noise->clearLevels(); terrain->propWaterHeight()->setValue(0.0); NoiseGenerator::NoiseLevel level = {1.0, 2.0, -1.0}; @@ -74,21 +73,6 @@ TEST_F(TerrainPainting_Test, grid) EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0.0, 0.0, 1, 0), 0.0); EXPECT_DOUBLE_IN_RANGE(terrain->getInterpolatedHeight(0.5, 0.0, 1, 0), 3.0 * 0.1564, 3.0 * 0.1566); EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1.0, 0.0, 1, 0), 3.0 * sin(1.0 * X_FACTOR)); - - /* Test scaling */ - terrain->scaling = 2.0; - EXPECT_DOUBLE_EQ(terrain->getGridHeight(0, 0, 0), 0.0); - EXPECT_DOUBLE_EQ(terrain->getGridHeight(1, 0, 0), sin(1.0 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getGridHeight(2, 0, 0), sin(2.0 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getGridHeight(3, 0, 0), sin(3.0 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0, 0, 0, 0), 0.0); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1, 0, 0, 0), sin(0.5 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(2, 0, 0, 0), sin(1.0 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(3, 0, 0, 0), sin(1.5 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0, 0, 1, 0), 0.0); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1, 0, 1, 0), 6.0 * sin(0.5 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(2, 0, 1, 0), 6.0 * sin(1.0 * X_FACTOR)); - EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(3, 0, 1, 0), 6.0 * sin(1.5 * X_FACTOR)); } static void _checkBrushResultSides(TerrainDefinition* terrain, PaintedGridBrush*, double center, double midhard, double hard, double midsoft, double soft, double exter, double neg_midhard, double neg_hard, double neg_midsoft, double neg_soft, double neg_exter) @@ -125,7 +109,6 @@ TEST_F(TerrainPainting_Test, brush_flatten) /* Set up */ PaintedGridBrush brush(2.0, 2.0, 4.0); terrain->height = 1.0; - terrain->scaling = 1.0; terrain->_height_noise->forceValue(0.0); /* Test flattening center at 0.5 */ @@ -149,14 +132,6 @@ TEST_F(TerrainPainting_Test, brush_flatten) _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0); _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0); - - /* Test with scaling modifier */ - terrain->height = 10.0; - terrain->scaling = 2.0; - terrain->height_map->clearPainting(); - _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); - terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0); - _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0); } TEST_F(TerrainPainting_Test, brush_reset) @@ -165,7 +140,6 @@ TEST_F(TerrainPainting_Test, brush_reset) PaintedGridBrush brush(2.0, 2.0, 4.0); PaintedGridBrush brush_full(4.0, 0.0, 4.0); terrain->height = 1.0; - terrain->scaling = 1.0; terrain->_height_noise->forceValue(1.0); /* Test resetting at center */ @@ -195,32 +169,4 @@ TEST_F(TerrainPainting_Test, brush_reset) _checkBrushResult(terrain, &brush, 1.1, 1.1, 1.1, 1.1, 1.1, 1.0, 0); terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0); - - /* Test with scaling modifier */ - terrain->height = 10.0; - terrain->scaling = 2.0; - terrain->height_map->clearPainting(); - _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); - terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); - _checkBrushResult(terrain, &brush, 1.1, 1.1, 1.1, 1.1, 1.1, 1.0, 0); - terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); - _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0); -} - -TEST_F(TerrainPainting_Test, brush_reset_basevalue) -{ - /* Set up */ - PaintedGridBrush brush(2.0, 2.0, 4.0); - PaintedGridBrush brush_full(4.0, 0.0, 4.0); - terrain->height = 1.0; - terrain->scaling = 1.0; - - /* Test with scaling and the sinusoid setup (to test the basevalue sampling) */ - terrain->height = 1.0; - terrain->scaling = 2.0; - _checkBrushResult(terrain, &brush, 0.0, 0.309016994375, 0.587785252292, 0.809016994375, 0.951056516295, 1.0, 1); - terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); - _checkBrushResultSides(terrain, &brush, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 2.0, 2.0, 2.0, 2.0, -1.0); - terrain->height_map->brushReset(brush, 0.0, 0.0, 1.0); - _checkBrushResultSides(terrain, &brush, 0.0, 0.309016994375, 0.587785252292, 2.0 - (2.0 - 0.809016994375) * 0.5, 2.0, 1.0, -0.309016994375, -0.587785252292, 2.0 - (2.0 + 0.809016994375) * 0.5, 2.0, -1.0); }