From 6bedb6de408d531c1843888edc6576040c368695 Mon Sep 17 00:00:00 2001 From: Ben Deutsch Date: Sun, 7 May 2017 18:41:47 +0200 Subject: [PATCH] Fog effect when camera is inside cloud Fixes issue #3576 * Clouds now take camera position as 3D, not 2D * Cloud grid filling extracted to gridFilled method * Clouds detect whether camera is inside cloud * Camera in cloud changes fog by overriding sky colors with cloud color * Sun, moon and stars can be temporarily disabled with setBodiesVisible * Disabling fog also disables all "inside cloud" behaviors --- src/client/clientlauncher.cpp | 2 +- src/clouds.cpp | 57 +++++++++++++++++++++++------------ src/clouds.h | 11 +++++-- src/game.cpp | 24 ++++++++++++--- src/guiEngine.cpp | 2 +- src/sky.cpp | 7 ++++- src/sky.h | 11 +++++-- 7 files changed, 84 insertions(+), 30 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 4fc8fb3ee..718255cad 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -129,7 +129,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) g_menucloudsmgr = RenderingEngine::get_scene_manager()->createNewSceneManager(); if (!g_menuclouds) g_menuclouds = new Clouds(g_menucloudsmgr, -1, rand(), 100); - g_menuclouds->update(v2f(0, 0), video::SColor(255, 200, 200, 255)); + g_menuclouds->update(v3f(0, 0, 0), video::SColor(255, 200, 200, 255)); scene::ICameraSceneNode* camera; camera = g_menucloudsmgr->addCameraSceneNode(0, v3f(0, 0, 0), v3f(0, 60, 100)); diff --git a/src/clouds.cpp b/src/clouds.cpp index dd6a2dcbd..eeac774a9 100644 --- a/src/clouds.cpp +++ b/src/clouds.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "profiler.h" #include "settings.h" +#include // Menu clouds are created later @@ -31,6 +32,9 @@ class Clouds; Clouds *g_menuclouds = NULL; irr::scene::ISceneManager *g_menucloudsmgr = NULL; +// Constant for now +static constexpr const float cloud_size = BS * 64.0f; + static void cloud_3d_setting_changed(const std::string &settingname, void *data) { // TODO: only re-read cloud settings, not height or radius @@ -85,8 +89,6 @@ void Clouds::OnRegisterSceneNode() ISceneNode::OnRegisterSceneNode(); } -#define MYROUND(x) (x > 0.0 ? (int)x : (int)x - 1) - void Clouds::render() { @@ -112,19 +114,19 @@ void Clouds::render() Clouds move from Z+ towards Z- */ - static const float cloud_size = BS * 64.0f; - const float cloud_full_radius = cloud_size * m_cloud_radius_i; + v2f camera_pos_2d(m_camera_pos.X, m_camera_pos.Z); // Position of cloud noise origin from the camera - v2f cloud_origin_from_camera_f = m_origin - m_camera_pos; + v2f cloud_origin_from_camera_f = m_origin - camera_pos_2d; // The center point of drawing in the noise v2f center_of_drawing_in_noise_f = -cloud_origin_from_camera_f; // The integer center point of drawing in the noise v2s16 center_of_drawing_in_noise_i( - MYROUND(center_of_drawing_in_noise_f.X / cloud_size), - MYROUND(center_of_drawing_in_noise_f.Y / cloud_size) + std::floor(center_of_drawing_in_noise_f.X / cloud_size), + std::floor(center_of_drawing_in_noise_f.Y / cloud_size) ); + // The world position of the integer center point of drawing in the noise v2f world_center_of_drawing_in_noise_f = v2f( center_of_drawing_in_noise_i.X * cloud_size, @@ -172,7 +174,6 @@ void Clouds::render() bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2]; - float cloud_size_noise = cloud_size / BS / 200; for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) { u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i; @@ -180,19 +181,10 @@ void Clouds::render() for (s16 xi = -m_cloud_radius_i; xi < m_cloud_radius_i; xi++) { u32 i = si + xi; - v2s16 p_in_noise_i( + grid[i] = gridFilled( xi + center_of_drawing_in_noise_i.X, zi + center_of_drawing_in_noise_i.Y ); - - float noise = noise2d_perlin( - (float)p_in_noise_i.X * cloud_size_noise, - (float)p_in_noise_i.Y * cloud_size_noise, - m_seed, 3, 0.5); - // normalize to 0..1 (given 3 octaves) - static const float noise_bound = 1.0f + 0.5f + 0.25f; - float density = noise / noise_bound * 0.5f + 0.5f; - grid[i] = (density < m_params.density); } } @@ -350,7 +342,7 @@ void Clouds::step(float dtime) m_origin = m_origin + dtime * BS * m_params.speed; } -void Clouds::update(v2f camera_p, video::SColorf color_diffuse) +void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse) { m_camera_pos = camera_p; m_color.r = MYMIN(MYMAX(color_diffuse.r * m_params.color_bright.getRed(), @@ -360,6 +352,20 @@ void Clouds::update(v2f camera_p, video::SColorf color_diffuse) m_color.b = MYMIN(MYMAX(color_diffuse.b * m_params.color_bright.getBlue(), m_params.color_ambient.getBlue()), 255) / 255.0f; m_color.a = m_params.color_bright.getAlpha() / 255.0f; + + // is the camera inside the cloud mesh? + m_camera_inside_cloud = false; // default + if (m_enable_3d) { + float camera_height = camera_p.Y; + if (camera_height >= m_box.MinEdge.Y && + camera_height <= m_box.MaxEdge.Y) { + v2f camera_in_noise; + camera_in_noise.X = floor((camera_p.X - m_origin.X) / cloud_size + 0.5); + camera_in_noise.Y = floor((camera_p.Z - m_origin.Y) / cloud_size + 0.5); + bool filled = gridFilled(camera_in_noise.X, camera_in_noise.Y); + m_camera_inside_cloud = filled; + } + } } void Clouds::readSettings() @@ -369,3 +375,16 @@ void Clouds::readSettings() m_cloud_radius_i = g_settings->getU16("cloud_radius"); m_enable_3d = g_settings->getBool("enable_3d_clouds"); } + +bool Clouds::gridFilled(int x, int y) const +{ + float cloud_size_noise = cloud_size / (BS * 200.f); + float noise = noise2d_perlin( + (float)x * cloud_size_noise, + (float)y * cloud_size_noise, + m_seed, 3, 0.5); + // normalize to 0..1 (given 3 octaves) + static constexpr const float noise_bound = 1.0f + 0.5f + 0.25f; + float density = noise / noise_bound * 0.5f + 0.5f; + return (density < m_params.density); +} diff --git a/src/clouds.h b/src/clouds.h index 52ea930f9..66bd9269c 100644 --- a/src/clouds.h +++ b/src/clouds.h @@ -73,7 +73,7 @@ public: void step(float dtime); - void update(v2f camera_p, video::SColorf color); + void update(const v3f &camera_p, const video::SColorf &color); void updateCameraOffset(v3s16 camera_offset) { @@ -116,6 +116,10 @@ public: updateBox(); } + bool isCameraInsideCloud() const { return m_camera_inside_cloud; } + + const video::SColor getColor() const { return m_color.toSColor(); } + private: void updateBox() { @@ -125,17 +129,20 @@ private: BS * 1000000.0f, height_bs + thickness_bs - BS * m_camera_offset.Y, BS * 1000000.0f); } + bool gridFilled(int x, int y) const; + video::SMaterial m_material; aabb3f m_box; s16 m_passed_cloud_y; u16 m_cloud_radius_i; bool m_enable_3d; u32 m_seed; - v2f m_camera_pos; + v3f m_camera_pos; v2f m_origin; v3s16 m_camera_offset; video::SColorf m_color = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); CloudParams m_params; + bool m_camera_inside_cloud = false; }; diff --git a/src/game.cpp b/src/game.cpp index 9f7b0ca52..b6304f19e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4104,12 +4104,29 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, Update clouds */ if (clouds) { - v3f player_position = player->getPosition(); if (sky->getCloudsVisible()) { clouds->setVisible(true); clouds->step(dtime); - clouds->update(v2f(player_position.X, player_position.Z), - sky->getCloudColor()); + // camera->getPosition is not enough for 3rd person views + v3f camera_node_position = camera->getCameraNode()->getPosition(); + v3s16 camera_offset = camera->getOffset(); + camera_node_position.X = camera_node_position.X + camera_offset.X * BS; + camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS; + camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS; + clouds->update(camera_node_position, + sky->getCloudColor()); + if (clouds->isCameraInsideCloud() && m_cache_enable_fog && + !flags.force_fog_off) { + // if inside clouds, and fog enabled, use that as sky + // color(s) + video::SColor clouds_dark = clouds->getColor() + .getInterpolated(video::SColor(255, 0, 0, 0), 0.9); + sky->overrideColors(clouds_dark, clouds->getColor()); + sky->setBodiesVisible(false); + runData.fog_range = 20.0f * BS; + // do not draw clouds after all + clouds->setVisible(false); + } } else { clouds->setVisible(false); } @@ -4221,7 +4238,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Drawing begins */ - const video::SColor &skycolor = sky->getSkyColor(); TimeTaker tt_draw("mainloop: draw"); diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index 5f5b4bbff..65ef1605a 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -314,7 +314,7 @@ GUIEngine::~GUIEngine() void GUIEngine::cloudInit() { m_cloud.clouds = new Clouds(m_smgr, -1, rand(), 100); - m_cloud.clouds->update(v2f(0, 0), video::SColor(255,200,200,255)); + m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,200,200,255)); m_cloud.camera = m_smgr->addCameraSceneNode(0, v3f(0,0,0), v3f(0, 60, 100)); diff --git a/src/sky.cpp b/src/sky.cpp index 463400194..d92697f30 100644 --- a/src/sky.cpp +++ b/src/sky.cpp @@ -233,6 +233,10 @@ void Sky::render() vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o); driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + // If sun, moon and stars are (temporarily) disabled, abort here + if (!m_bodies_visible) + return; + driver->setMaterial(m_materials[2]); // Draw sunrise/sunset horizon glow texture (textures/base/pack/sunrisebg.png) @@ -412,8 +416,8 @@ void Sky::render() } // Draw stars - driver->setMaterial(m_materials[1]); do { + driver->setMaterial(m_materials[1]); float starbrightness = MYMAX(0, MYMIN(1, (0.285 - fabs(wicked_time_of_day < 0.5 ? wicked_time_of_day : (1.0 - wicked_time_of_day))) * 10)); @@ -501,6 +505,7 @@ void Sky::update(float time_of_day, float time_brightness, m_time_of_day = time_of_day; m_time_brightness = time_brightness; m_sunlight_seen = sunlight_seen; + m_bodies_visible = true; bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.35); diff --git a/src/sky.h b/src/sky.h index 64d2877e5..ed93a9221 100644 --- a/src/sky.h +++ b/src/sky.h @@ -64,8 +64,8 @@ public: return m_visible ? m_skycolor : m_fallback_bg_color; } - bool getCloudsVisible() { return m_clouds_visible && m_clouds_enabled; } - const video::SColorf &getCloudColor() { return m_cloudcolor_f; } + bool getCloudsVisible() const { return m_clouds_visible && m_clouds_enabled; } + const video::SColorf &getCloudColor() const { return m_cloudcolor_f; } void setVisible(bool visible) { m_visible = visible; } // Set only from set_sky API @@ -74,6 +74,12 @@ public: { m_fallback_bg_color = fallback_bg_color; } + void overrideColors(const video::SColor &bgcolor, const video::SColor &skycolor) + { + m_bgcolor = bgcolor; + m_skycolor = skycolor; + } + void setBodiesVisible(bool visible) { m_bodies_visible = visible; } private: aabb3f m_box; @@ -128,6 +134,7 @@ private: bool m_clouds_visible; // Whether clouds are disabled due to player underground bool m_clouds_enabled = true; // Initialised to true, reset only by set_sky API bool m_directional_colored_fog; + bool m_bodies_visible = true; // sun, moon, stars video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_cloudcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);