diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 7df1940..48e4204 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -17,6 +17,8 @@ OpenGLRenderer::OpenGLRenderer(Scenery* scenery): { ready = false; + render_quality = 3; + functions = new OpenGLFunctions(); shared_state = new OpenGLSharedState(); diff --git a/src/render/preview/SceneryTopDownPreviewRenderer.cpp b/src/render/preview/SceneryTopDownPreviewRenderer.cpp index 80923ff..62199b2 100644 --- a/src/render/preview/SceneryTopDownPreviewRenderer.cpp +++ b/src/render/preview/SceneryTopDownPreviewRenderer.cpp @@ -12,6 +12,7 @@ SceneryTopDownPreviewRenderer::SceneryTopDownPreviewRenderer(Scenery* scenery): scenery(scenery) { clouds_enabled = true; + render_quality = 3; } void SceneryTopDownPreviewRenderer::bindEvent(BasePreview* preview) diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index a876c83..b5fe461 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -134,16 +134,16 @@ void SoftwareRenderer::disableAtmosphere() light.color.g = 1.5; light.color.b = 1.5; light.direction.x = -1.0; - light.direction.y = -0.5; + light.direction.y = -0.3; light.direction.z = 1.0; light.direction = light.direction.normalize(); light.altered = 1; light.reflection = 0.0; lights.push_back(light); - light.color.r = 0.4; - light.color.g = 0.4; - light.color.b = 0.42; + light.color.r = 0.2; + light.color.g = 0.2; + light.color.b = 0.21; light.direction.x = 1.0; light.direction.y = -0.5; light.direction.z = -1.0; diff --git a/src/render/software/TerrainRayWalker.cpp b/src/render/software/TerrainRayWalker.cpp index 000fc2b..13121d5 100644 --- a/src/render/software/TerrainRayWalker.cpp +++ b/src/render/software/TerrainRayWalker.cpp @@ -6,6 +6,7 @@ #include "TexturesDefinition.h" #include "TerrainRenderer.h" #include "TexturesRenderer.h" +#include "Matrix4.h" TerrainRayWalker::TerrainRayWalker(SoftwareRenderer* renderer): renderer(renderer) @@ -23,33 +24,57 @@ void TerrainRayWalker::update() ymin = info.min_height - disp; ymax = info.max_height + disp; - ydispmin = -disp; - ydispmax = disp; + ydispmax = disp * (0.5 + (double)renderer->render_quality * 0.1); + ydispmin = -ydispmax; - minstep = 0.01 * terrain->scaling / (double)renderer->render_quality; - maxstep = 1.0 * terrain->scaling; + minstep = 1.0 * terrain->scaling / (double)(renderer->render_quality * renderer->render_quality); + maxstep = 10.0 * terrain->scaling / (double)renderer->render_quality; } -bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, double escaping_factor, double max_length, TerrainHitResult &result) +static inline Vector3 _getShiftAxis(const Vector3 &direction) +{ + if (fabs(direction.y) > 0.99) + { + // When the ray is vertical, we choose an arbitrary shift axis + return VECTOR_NORTH; + } + else + { + return VECTOR_UP.crossProduct(direction); + } +} + +bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result) { TerrainRenderer* terrain_renderer = renderer->getTerrainRenderer(); TexturesRenderer* textures_renderer = renderer->getTexturesRenderer(); TerrainRenderer::TerrainResult terrain_result; - Vector3 end, displaced; - bool hit; + Vector3 cursor, displaced; double diff; + Matrix4 shift_matrix; + double shift_step = 0.0; + Vector3 previous_cursor = start; + bool hit = false; double step_length = minstep; double walked_length = 0.0; + result.escape_angle = 0.0; + if (escape_angle != 0.0) + { + // Prepare escape + shift_step = escape_angle / (double)(renderer->render_quality * renderer->render_quality); + shift_matrix = Matrix4::newRotateAxis(-shift_step, _getShiftAxis(direction)); + } + do { // Perform a step - end = start.add(direction.scale(step_length)); + cursor = previous_cursor.add(direction.scale(step_length)); // Get the terrain info at end (without textures displacement) - terrain_result = terrain_renderer->getResult(end.x, end.z, true, false); - diff = end.y - terrain_result.location.y; + terrain_result = terrain_renderer->getResult(cursor.x, cursor.z, true, false); + diff = cursor.y - terrain_result.location.y; // If we are very under the terrain, consider a hit if (diff < ydispmin) @@ -61,33 +86,42 @@ bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, dou else if (diff < ydispmax) { displaced = textures_renderer->displaceTerrain(terrain_result); - diff = end.y - displaced.y; - hit = (diff < 0.0); + diff = cursor.y - displaced.y; + hit = diff < 0.0; } if (hit) { - // Refine the hit with dichotomy at high quality + // TODO Refine the hit with dichotomy at high quality /*if (renderer->render_quality > 7) { - end = refineHit(start, end, step_length); + cursor = refineHit(previous_cursor, cursor, step_length); }*/ - // Find an escape - /*if (escaping_factor != 0.0) + // Shift ray to escape terrain + if (escape_angle != 0.0) { - result.escape_length = findEscape(end, walked_length, escaping_factor, max_length); - }*/ + result.escape_angle += shift_step; + if (result.escape_angle > escape_angle) + { + // Too much shifted to escape, make it a hit + result.escape_angle = 0.0; + return true; + } + hit = false; + direction = shift_matrix.multPoint(direction); + previous_cursor = start.add(shift_matrix.multPoint(previous_cursor.sub(start))); + } - result.hit_location = end; + result.hit_location = cursor; } else { // Prepare next step - start = end; + previous_cursor = cursor; walked_length += step_length; - step_length = diff / (double)renderer->render_quality; + step_length = diff * 3.0 / (double)renderer->render_quality; if (step_length < minstep) { step_length = minstep; @@ -97,7 +131,7 @@ bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, dou step_length = maxstep; } } - } while (not hit and start.y < ymax and walked_length < max_length); + } while (not hit and cursor.y < ymax and walked_length < max_length); - return hit; + return hit or result.escape_angle > 0.0; } diff --git a/src/render/software/TerrainRayWalker.h b/src/render/software/TerrainRayWalker.h index c8c9dd6..09ea712 100644 --- a/src/render/software/TerrainRayWalker.h +++ b/src/render/software/TerrainRayWalker.h @@ -20,7 +20,7 @@ class SOFTWARESHARED_EXPORT TerrainRayWalker public: typedef struct { Vector3 hit_location; // Location of the hit - double escape_length; // Length walked before terrain was escaped after hit (0.0 if no escape) + double escape_angle; // Angle used to shift the ray to escape the terrain (0.0 if no escape was possible) } TerrainHitResult; public: @@ -36,12 +36,12 @@ public: * * \param start Point of origin of the ray * \param direction Ray direction (normalized vector) - * \param escaping_factor Factor used to escape the terrain on hit (mainly for shadows computing) + * \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(Vector3 start, const Vector3 &direction, double escaping_factor, double max_length, TerrainHitResult &result); + bool startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result); private: SoftwareRenderer* renderer; diff --git a/src/render/software/TerrainRenderer.cpp b/src/render/software/TerrainRenderer.cpp index 6627a1c..5326e1f 100644 --- a/src/render/software/TerrainRenderer.cpp +++ b/src/render/software/TerrainRenderer.cpp @@ -151,16 +151,16 @@ RayCastingResult TerrainRenderer::castRay(const Vector3 &start, const Vector3 &d bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at) { TerrainDefinition* definition = parent->getScenery()->getTerrain(); - Vector3 inc_vector, direction_to_light, cursor; - double inc_value, inc_base, inc_factor, height, diff, light_factor, smoothing, length; + TerrainRayWalker::TerrainHitResult walk_result; + // If location is above terrain, don't bother if (at.y > definition->getHeightInfo().max_height) { - // Location is above terrain, don't bother return true; } - direction_to_light = light.direction.scale(-1.0); + // Handle sun below horizon + Vector3 direction_to_light = light.direction.scale(-1.0); if (direction_to_light.y < -0.05) { light.color = COLOR_BLACK; @@ -173,60 +173,31 @@ bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at) light.color.b *= (0.05 + direction_to_light.y) / 0.05; } - cursor = at; - inc_factor = (double)parent->render_quality; - inc_base = definition->height / definition->scaling; - inc_value = inc_base / inc_factor; - smoothing = definition->shadow_smoothing; - - light_factor = 1.0; - length = 0.0; - diff = 0.0; - do + // Walk to find an intersection + double escape_angle = definition->shadow_smoothing; + if (walker->startWalking(at, direction_to_light, escape_angle, 100.0, walk_result)) { - inc_vector = direction_to_light.scale(inc_value); - length += inc_vector.getNorm(); - cursor = cursor.add(inc_vector); - height = parent->getTerrainRenderer()->getResult(cursor.x, cursor.z, 1, 1).location.y; - diff = cursor.y - height; - if (diff < 0.0) + if (walk_result.escape_angle == 0.0) { - if (length * smoothing > 0.000001) - { - light_factor += diff * inc_vector.getNorm() / (length * smoothing); - } - else - { - light_factor = 0.0; - } - } - - if (diff < inc_base / inc_factor) - { - inc_value = inc_base / inc_factor; - } - else if (diff > inc_base) - { - inc_value = inc_base; + // Hit, with no escape, cancelling the light + light.color = COLOR_BLACK; + return false; } else { - inc_value = diff; - } - } - while (light_factor > 0.0 && length < (10.0 * inc_factor) && cursor.y <= definition->_max_height); + // Hit, with an escape + double light_factor = 1.0 - (walk_result.escape_angle / escape_angle); - if (light_factor <= 0.0) - { - light.color = COLOR_BLACK; - return false; + light.color.r *= light_factor; + light.color.g *= light_factor; + light.color.b *= light_factor; + + return true; + } } else { - light.color.r *= light_factor; - light.color.g *= light_factor; - light.color.b *= light_factor; - + // No hit, leave light alone return true; } }