summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorLiso <anlismon@gmail.com>2021-06-06 18:51:21 +0200
committerGitHub <noreply@github.com>2021-06-06 18:51:21 +0200
commitc47313db65f968559711ac1b505ef341a9872017 (patch)
tree63d9b1b2be512918e2361d96e4fb52ff1ec3f9de /src/client
parent46f42e15c41cf4ab23c5ff4cd8a7d99d94d10d7b (diff)
downloadminetest-c47313db65f968559711ac1b505ef341a9872017.tar.gz
minetest-c47313db65f968559711ac1b505ef341a9872017.tar.bz2
minetest-c47313db65f968559711ac1b505ef341a9872017.zip
Shadow mapping render pass (#11244)
Co-authored-by: x2048 <codeforsmile@gmail.com>
Diffstat (limited to 'src/client')
-rw-r--r--src/client/CMakeLists.txt4
-rw-r--r--src/client/clientmap.cpp218
-rw-r--r--src/client/clientmap.h11
-rw-r--r--src/client/content_cao.cpp13
-rw-r--r--src/client/game.cpp37
-rw-r--r--src/client/mapblock_mesh.cpp6
-rw-r--r--src/client/render/core.cpp21
-rw-r--r--src/client/render/core.h5
-rw-r--r--src/client/renderingengine.h11
-rw-r--r--src/client/shader.cpp64
-rw-r--r--src/client/shadows/dynamicshadows.cpp145
-rw-r--r--src/client/shadows/dynamicshadows.h102
-rw-r--r--src/client/shadows/dynamicshadowsrender.cpp539
-rw-r--r--src/client/shadows/dynamicshadowsrender.h146
-rw-r--r--src/client/shadows/shadowsScreenQuad.cpp67
-rw-r--r--src/client/shadows/shadowsScreenQuad.h45
-rw-r--r--src/client/shadows/shadowsshadercallbacks.cpp44
-rw-r--r--src/client/shadows/shadowsshadercallbacks.h34
-rw-r--r--src/client/sky.cpp38
-rw-r--r--src/client/sky.h7
-rw-r--r--src/client/wieldmesh.cpp12
-rw-r--r--src/client/wieldmesh.h3
22 files changed, 1548 insertions, 24 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index 140814911..8d058852a 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -58,5 +58,9 @@ set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/sky.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/wieldmesh.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/shadows/dynamicshadows.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/shadows/dynamicshadowsrender.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsshadercallbacks.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsScreenQuad.cpp
PARENT_SCOPE
)
diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp
index 6dc535898..8b09eade1 100644
--- a/src/client/clientmap.cpp
+++ b/src/client/clientmap.cpp
@@ -72,8 +72,15 @@ ClientMap::ClientMap(
scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
rendering_engine->get_scene_manager(), id),
m_client(client),
+ m_rendering_engine(rendering_engine),
m_control(control)
{
+
+ /*
+ * @Liso: Sadly C++ doesn't have introspection, so the only way we have to know
+ * the class is whith a name ;) Name property cames from ISceneNode base class.
+ */
+ Name = "ClientMap";
m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000);
@@ -115,12 +122,21 @@ void ClientMap::OnRegisterSceneNode()
}
ISceneNode::OnRegisterSceneNode();
+
+ if (!m_added_to_shadow_renderer) {
+ m_added_to_shadow_renderer = true;
+ if (auto shadows = m_rendering_engine->get_shadow_renderer())
+ shadows->addNodeToShadowList(this);
+ }
}
void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
- v3s16 *p_blocks_min, v3s16 *p_blocks_max)
+ v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range)
{
- v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1);
+ if (range <= 0.0f)
+ range = m_control.wanted_range;
+
+ v3s16 box_nodes_d = range * v3s16(1, 1, 1);
// Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
// can exceed the range of v3s16 when a large view range is used near the
// world edges.
@@ -321,7 +337,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Mesh animation
if (pass == scene::ESNRP_SOLID) {
- //MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
@@ -342,8 +357,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Get the meshbuffers of the block
*/
{
- //MutexAutoLock lock(block->mesh_mutex);
-
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
@@ -394,6 +407,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
"returning." << std::endl;
return;
}
+
+ // pass the shadow map texture to the buffer texture
+ ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
+ if (shadow && shadow->is_active()) {
+ auto &layer = list.m.TextureLayer[3];
+ layer.Texture = shadow->get_texture();
+ layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+ layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+ layer.TrilinearFilter = true;
+ }
+
driver->setMaterial(list.m);
drawcall_count += list.bufs.size();
@@ -610,3 +634,187 @@ void ClientMap::PrintInfo(std::ostream &out)
{
out<<"ClientMap: ";
}
+
+void ClientMap::renderMapShadows(video::IVideoDriver *driver,
+ const video::SMaterial &material, s32 pass)
+{
+ bool is_transparent_pass = pass != scene::ESNRP_SOLID;
+ std::string prefix;
+ if (is_transparent_pass)
+ prefix = "renderMap(SHADOW TRANS): ";
+ else
+ prefix = "renderMap(SHADOW SOLID): ";
+
+ u32 drawcall_count = 0;
+ u32 vertex_count = 0;
+
+ MeshBufListList drawbufs;
+
+ for (auto &i : m_drawlist_shadow) {
+ v3s16 block_pos = i.first;
+ MapBlock *block = i.second;
+
+ // If the mesh of the block happened to get deleted, ignore it
+ if (!block->mesh)
+ continue;
+
+ /*
+ Get the meshbuffers of the block
+ */
+ {
+ MapBlockMesh *mapBlockMesh = block->mesh;
+ assert(mapBlockMesh);
+
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
+ assert(mesh);
+
+ u32 c = mesh->getMeshBufferCount();
+ for (u32 i = 0; i < c; i++) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+
+ video::SMaterial &mat = buf->getMaterial();
+ auto rnd = driver->getMaterialRenderer(mat.MaterialType);
+ bool transparent = rnd && rnd->isTransparent();
+ if (transparent == is_transparent_pass)
+ drawbufs.add(buf, block_pos, layer);
+ }
+ }
+ }
+ }
+
+ TimeTaker draw("Drawing shadow mesh buffers");
+
+ core::matrix4 m; // Model matrix
+ v3f offset = intToFloat(m_camera_offset, BS);
+
+ // Render all layers in order
+ for (auto &lists : drawbufs.lists) {
+ for (MeshBufList &list : lists) {
+ // Check and abort if the machine is swapping a lot
+ if (draw.getTimerTime() > 1000) {
+ infostream << "ClientMap::renderMapShadows(): Rendering "
+ "took >1s, returning." << std::endl;
+ break;
+ }
+ for (auto &pair : list.bufs) {
+ scene::IMeshBuffer *buf = pair.second;
+
+ // override some material properties
+ video::SMaterial local_material = buf->getMaterial();
+ local_material.MaterialType = material.MaterialType;
+ local_material.BackfaceCulling = material.BackfaceCulling;
+ local_material.FrontfaceCulling = material.FrontfaceCulling;
+ local_material.Lighting = false;
+ driver->setMaterial(local_material);
+
+ v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
+ m.setTranslation(block_wpos - offset);
+
+ driver->setTransform(video::ETS_WORLD, m);
+ driver->drawMeshBuffer(buf);
+ vertex_count += buf->getVertexCount();
+ }
+
+ drawcall_count += list.bufs.size();
+ }
+ }
+
+ g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
+ g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
+ g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+}
+
+/*
+ Custom update draw list for the pov of shadow light.
+*/
+void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
+{
+ ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
+
+ const v3f camera_position = shadow_light_pos;
+ const v3f camera_direction = shadow_light_dir;
+ // I "fake" fov just to avoid creating a new function to handle orthographic
+ // projection.
+ const f32 camera_fov = m_camera_fov * 1.9f;
+
+ v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
+ v3s16 p_blocks_min;
+ v3s16 p_blocks_max;
+ getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, shadow_range);
+
+ std::vector<v2s16> blocks_in_range;
+
+ for (auto &i : m_drawlist_shadow) {
+ MapBlock *block = i.second;
+ block->refDrop();
+ }
+ m_drawlist_shadow.clear();
+
+ // We need to append the blocks from the camera POV because sometimes
+ // they are not inside the light frustum and it creates glitches.
+ // FIXME: This could be removed if we figure out why they are missing
+ // from the light frustum.
+ for (auto &i : m_drawlist) {
+ i.second->refGrab();
+ m_drawlist_shadow[i.first] = i.second;
+ }
+
+ // Number of blocks currently loaded by the client
+ u32 blocks_loaded = 0;
+ // Number of blocks with mesh in rendering range
+ u32 blocks_in_range_with_mesh = 0;
+ // Number of blocks occlusion culled
+ u32 blocks_occlusion_culled = 0;
+
+ for (auto &sector_it : m_sectors) {
+ MapSector *sector = sector_it.second;
+ if (!sector)
+ continue;
+ blocks_loaded += sector->size();
+
+ MapBlockVect sectorblocks;
+ sector->getBlocks(sectorblocks);
+
+ /*
+ Loop through blocks in sector
+ */
+ for (MapBlock *block : sectorblocks) {
+ if (!block->mesh) {
+ // Ignore if mesh doesn't exist
+ continue;
+ }
+
+ float range = shadow_range;
+
+ float d = 0.0;
+ if (!isBlockInSight(block->getPos(), camera_position,
+ camera_direction, camera_fov, range, &d))
+ continue;
+
+ blocks_in_range_with_mesh++;
+
+ /*
+ Occlusion culling
+ */
+ if (isBlockOccluded(block, cam_pos_nodes)) {
+ blocks_occlusion_culled++;
+ continue;
+ }
+
+ // This block is in range. Reset usage timer.
+ block->resetUsageTimer();
+
+ // Add to set
+ if (m_drawlist_shadow.find(block->getPos()) == m_drawlist_shadow.end()) {
+ block->refGrab();
+ m_drawlist_shadow[block->getPos()] = block;
+ }
+ }
+ }
+
+ g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
+ g_profiler->avg("SHADOW MapBlocks occlusion culled [#]", blocks_occlusion_culled);
+ g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
+ g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
+}
diff --git a/src/client/clientmap.h b/src/client/clientmap.h
index 80add4a44..93ade4c15 100644
--- a/src/client/clientmap.h
+++ b/src/client/clientmap.h
@@ -119,10 +119,14 @@ public:
}
void getBlocksInViewRange(v3s16 cam_pos_nodes,
- v3s16 *p_blocks_min, v3s16 *p_blocks_max);
+ v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range=-1.0f);
void updateDrawList();
+ void updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range);
void renderMap(video::IVideoDriver* driver, s32 pass);
+ void renderMapShadows(video::IVideoDriver *driver,
+ const video::SMaterial &material, s32 pass);
+
int getBackgroundBrightness(float max_d, u32 daylight_factor,
int oldvalue, bool *sunlight_seen_result);
@@ -132,9 +136,12 @@ public:
virtual void PrintInfo(std::ostream &out);
const MapDrawControl & getControl() const { return m_control; }
+ f32 getWantedRange() const { return m_control.wanted_range; }
f32 getCameraFov() const { return m_camera_fov; }
+
private:
Client *m_client;
+ RenderingEngine *m_rendering_engine;
aabb3f m_box = aabb3f(-BS * 1000000, -BS * 1000000, -BS * 1000000,
BS * 1000000, BS * 1000000, BS * 1000000);
@@ -147,10 +154,12 @@ private:
v3s16 m_camera_offset;
std::map<v3s16, MapBlock*> m_drawlist;
+ std::map<v3s16, MapBlock*> m_drawlist_shadow;
std::set<v2s16> m_last_drawn_sectors;
bool m_cache_trilinear_filter;
bool m_cache_bilinear_filter;
bool m_cache_anistropic_filter;
+ bool m_added_to_shadow_renderer{false};
};
diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp
index 2e58e19cf..9216f0010 100644
--- a/src/client/content_cao.cpp
+++ b/src/client/content_cao.cpp
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IMeshManipulator.h>
#include <IAnimatedMeshSceneNode.h>
#include "client/client.h"
+#include "client/renderingengine.h"
#include "client/sound.h"
#include "client/tile.h"
#include "util/basic_macros.h"
@@ -555,6 +556,9 @@ void GenericCAO::removeFromScene(bool permanent)
clearParentAttachment();
}
+ if (auto shadow = RenderingEngine::get_shadow_renderer())
+ shadow->removeNodeFromShadowList(getSceneNode());
+
if (m_meshnode) {
m_meshnode->remove();
m_meshnode->drop();
@@ -803,10 +807,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
if (m_reset_textures_timer < 0)
updateTextures(m_current_texture_modifier);
- scene::ISceneNode *node = getSceneNode();
+ if (scene::ISceneNode *node = getSceneNode()) {
+ if (m_matrixnode)
+ node->setParent(m_matrixnode);
- if (node && m_matrixnode)
- node->setParent(m_matrixnode);
+ if (auto shadow = RenderingEngine::get_shadow_renderer())
+ shadow->addNodeToShadowList(node);
+ }
updateNametag();
updateMarker();
diff --git a/src/client/game.cpp b/src/client/game.cpp
index eb1dbb5a3..d240ebc0f 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -738,6 +738,7 @@ protected:
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
const CameraOrientation &cam);
+ void updateShadows();
// Misc
void limitFps(FpsControl *fps_timings, f32 *dtime);
@@ -3831,13 +3832,20 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
*/
runData.update_draw_list_timer += dtime;
+ float update_draw_list_delta = 0.2f;
+ if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer())
+ update_draw_list_delta = shadow->getUpdateDelta();
+
v3f camera_direction = camera->getDirection();
- if (runData.update_draw_list_timer >= 0.2
+ if (runData.update_draw_list_timer >= update_draw_list_delta
|| runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
|| m_camera_offset_changed) {
+
runData.update_draw_list_timer = 0;
client->getEnv().getClientMap().updateDrawList();
runData.update_draw_list_last_cam_dir = camera_direction;
+
+ updateShadows();
}
m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
@@ -3968,7 +3976,34 @@ inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
graph->put(values);
}
+/****************************************************************************
+ * Shadows
+ *****************************************************************************/
+void Game::updateShadows()
+{
+ ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer();
+ if (!shadow)
+ return;
+
+ float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
+
+ float timeoftheday = fmod(getWickedTimeOfDay(in_timeofday) + 0.75f, 0.5f) + 0.25f;
+ const float offset_constant = 10000.0f;
+
+ v3f light(0.0f, 0.0f, -1.0f);
+ light.rotateXZBy(90);
+ light.rotateXYBy(timeoftheday * 360 - 90);
+ light.rotateYZBy(sky->getSkyBodyOrbitTilt());
+ v3f sun_pos = light * offset_constant;
+
+ if (shadow->getDirectionalLightCount() == 0)
+ shadow->addDirectionalLight();
+ shadow->getDirectionalLight().setDirection(sun_pos);
+ shadow->setTimeOfDay(in_timeofday);
+
+ shadow->getDirectionalLight().update_frustum(camera, client);
+}
/****************************************************************************
Misc
diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp
index 72e68fe97..402217066 100644
--- a/src/client/mapblock_mesh.cpp
+++ b/src/client/mapblock_mesh.cpp
@@ -860,6 +860,9 @@ static void updateFastFaceRow(
g_settings->getBool("enable_shaders") &&
g_settings->getBool("enable_waving_water");
+ static thread_local const bool force_not_tiling =
+ g_settings->getBool("enable_dynamic_shadows");
+
v3s16 p = startpos;
u16 continuous_tiles_count = 1;
@@ -898,7 +901,8 @@ static void updateFastFaceRow(
waving,
next_tile);
- if (next_makes_face == makes_face
+ if (!force_not_tiling
+ && next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir
&& next_face_dir_corrected == face_dir_corrected
&& memcmp(next_lights, lights, sizeof(lights)) == 0
diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp
index 3c4583623..4a820f583 100644
--- a/src/client/render/core.cpp
+++ b/src/client/render/core.cpp
@@ -24,25 +24,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/clientmap.h"
#include "client/hud.h"
#include "client/minimap.h"
+#include "client/shadows/dynamicshadowsrender.h"
RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud)
: device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()),
guienv(device->getGUIEnvironment()), client(_client), camera(client->getCamera()),
- mapper(client->getMinimap()), hud(_hud)
+ mapper(client->getMinimap()), hud(_hud),
+ shadow_renderer(nullptr)
{
screensize = driver->getScreenSize();
virtual_size = screensize;
+
+ if (g_settings->getBool("enable_shaders") &&
+ g_settings->getBool("enable_dynamic_shadows")) {
+ shadow_renderer = new ShadowRenderer(device, client);
+ }
}
RenderingCore::~RenderingCore()
{
clearTextures();
+ delete shadow_renderer;
}
void RenderingCore::initialize()
{
// have to be called late as the VMT is not ready in the constructor:
initTextures();
+ if (shadow_renderer)
+ shadow_renderer->initialize();
}
void RenderingCore::updateScreenSize()
@@ -72,7 +82,14 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min
void RenderingCore::draw3D()
{
- smgr->drawAll();
+ if (shadow_renderer) {
+ // Shadow renderer will handle the draw stage
+ shadow_renderer->setClearColor(skycolor);
+ shadow_renderer->update();
+ } else {
+ smgr->drawAll();
+ }
+
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
if (!show_hud)
return;
diff --git a/src/client/render/core.h b/src/client/render/core.h
index 52ea8f99f..cabfbbfad 100644
--- a/src/client/render/core.h
+++ b/src/client/render/core.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
+class ShadowRenderer;
class Camera;
class Client;
class Hud;
@@ -47,6 +48,8 @@ protected:
Minimap *mapper;
Hud *hud;
+ ShadowRenderer *shadow_renderer;
+
void updateScreenSize();
virtual void initTextures() {}
virtual void clearTextures() {}
@@ -72,4 +75,6 @@ public:
bool _draw_wield_tool, bool _draw_crosshair);
inline v2u32 getVirtualSize() const { return virtual_size; }
+
+ ShadowRenderer *get_shadow_renderer() { return shadow_renderer; };
};
diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h
index 28ddc8652..6d42221d6 100644
--- a/src/client/renderingengine.h
+++ b/src/client/renderingengine.h
@@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
+#include "client/render/core.h"
+// include the shadow mapper classes too
+#include "client/shadows/dynamicshadowsrender.h"
+
class ITextureSource;
class Camera;
@@ -113,6 +117,13 @@ public:
return m_device->run();
}
+ // FIXME: this is still global when it shouldn't be
+ static ShadowRenderer *get_shadow_renderer()
+ {
+ if (s_singleton && s_singleton->core)
+ return s_singleton->core->get_shadow_renderer();
+ return nullptr;
+ }
static std::vector<core::vector3d<u32>> getSupportedVideoModes();
static std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers();
diff --git a/src/client/shader.cpp b/src/client/shader.cpp
index 58946b90f..355366bd3 100644
--- a/src/client/shader.cpp
+++ b/src/client/shader.cpp
@@ -225,6 +225,16 @@ class MainShaderConstantSetter : public IShaderConstantSetter
{
CachedVertexShaderSetting<float, 16> m_world_view_proj;
CachedVertexShaderSetting<float, 16> m_world;
+
+ // Shadow-related
+ CachedPixelShaderSetting<float, 16> m_shadow_view_proj;
+ CachedPixelShaderSetting<float, 3> m_light_direction;
+ CachedPixelShaderSetting<float> m_texture_res;
+ CachedPixelShaderSetting<float> m_shadow_strength;
+ CachedPixelShaderSetting<float> m_time_of_day;
+ CachedPixelShaderSetting<float> m_shadowfar;
+ CachedPixelShaderSetting<s32> m_shadow_texture;
+
#if ENABLE_GLES
// Modelview matrix
CachedVertexShaderSetting<float, 16> m_world_view;
@@ -243,6 +253,13 @@ public:
, m_texture("mTexture")
, m_normal("mNormal")
#endif
+ , m_shadow_view_proj("m_ShadowViewProj")
+ , m_light_direction("v_LightDirection")
+ , m_texture_res("f_textureresolution")
+ , m_shadow_strength("f_shadow_strength")
+ , m_time_of_day("f_timeofday")
+ , m_shadowfar("f_shadowfar")
+ , m_shadow_texture("ShadowMapSampler")
{}
~MainShaderConstantSetter() = default;
@@ -280,6 +297,36 @@ public:
};
m_normal.set(m, services);
#endif
+
+ // Set uniforms for Shadow shader
+ if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
+ const auto &light = shadow->getDirectionalLight();
+
+ core::matrix4 shadowViewProj = light.getProjectionMatrix();
+ shadowViewProj *= light.getViewMatrix();
+ m_shadow_view_proj.set(shadowViewProj.pointer(), services);
+
+ float v_LightDirection[3];
+ light.getDirection().getAs3Values(v_LightDirection);
+ m_light_direction.set(v_LightDirection, services);
+
+ float TextureResolution = light.getMapResolution();
+ m_texture_res.set(&TextureResolution, services);
+
+ float ShadowStrength = shadow->getShadowStrength();
+ m_shadow_strength.set(&ShadowStrength, services);
+
+ float timeOfDay = shadow->getTimeOfDay();
+ m_time_of_day.set(&timeOfDay, services);
+
+ float shadowFar = shadow->getMaxShadowFar();
+ m_shadowfar.set(&shadowFar, services);
+
+ // I dont like using this hardcoded value. maybe something like
+ // MAX_TEXTURE - 1 or somthing like that??
+ s32 TextureLayerID = 3;
+ m_shadow_texture.set(&TextureLayerID, services);
+ }
}
};
@@ -682,6 +729,23 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n";
+ if (g_settings->getBool("enable_dynamic_shadows")) {
+ shaders_header << "#define ENABLE_DYNAMIC_SHADOWS 1\n";
+ if (g_settings->getBool("shadow_map_color"))
+ shaders_header << "#define COLORED_SHADOWS 1\n";
+
+ if (g_settings->getBool("shadow_poisson_filter"))
+ shaders_header << "#define POISSON_FILTER 1\n";
+
+ s32 shadow_filter = g_settings->getS32("shadow_filters");
+ shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n";
+
+ float shadow_soft_radius = g_settings->getS32("shadow_soft_radius");
+ if (shadow_soft_radius < 1.0f)
+ shadow_soft_radius = 1.0f;
+ shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
+ }
+
std::string common_header = shaders_header.str();
std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp
new file mode 100644
index 000000000..775cdebce
--- /dev/null
+++ b/src/client/shadows/dynamicshadows.cpp
@@ -0,0 +1,145 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <cmath>
+
+#include "client/shadows/dynamicshadows.h"
+#include "client/client.h"
+#include "client/clientenvironment.h"
+#include "client/clientmap.h"
+#include "client/camera.h"
+
+using m4f = core::matrix4;
+
+void DirectionalLight::createSplitMatrices(const Camera *cam)
+{
+ float radius;
+ v3f newCenter;
+ v3f look = cam->getDirection();
+
+ v3f camPos2 = cam->getPosition();
+ v3f camPos = v3f(camPos2.X - cam->getOffset().X * BS,
+ camPos2.Y - cam->getOffset().Y * BS,
+ camPos2.Z - cam->getOffset().Z * BS);
+ camPos += look * shadow_frustum.zNear;
+ camPos2 += look * shadow_frustum.zNear;
+ float end = shadow_frustum.zNear + shadow_frustum.zFar;
+ newCenter = camPos + look * (shadow_frustum.zNear + 0.05f * end);
+ v3f world_center = camPos2 + look * (shadow_frustum.zNear + 0.05f * end);
+ // Create a vector to the frustum far corner
+ // @Liso: move all vars we can outside the loop.
+ float tanFovY = tanf(cam->getFovY() * 0.5f);
+ float tanFovX = tanf(cam->getFovX() * 0.5f);
+
+ const v3f &viewUp = cam->getCameraNode()->getUpVector();
+ // viewUp.normalize();
+
+ v3f viewRight = look.crossProduct(viewUp);
+ // viewRight.normalize();
+
+ v3f farCorner = look + viewRight * tanFovX + viewUp * tanFovY;
+ // Compute the frustumBoundingSphere radius
+ v3f boundVec = (camPos + farCorner * shadow_frustum.zFar) - newCenter;
+ radius = boundVec.getLength() * 2.0f;
+ // boundVec.getLength();
+ float vvolume = radius * 2.0f;
+
+ float texelsPerUnit = getMapResolution() / vvolume;
+ m4f mTexelScaling;
+ mTexelScaling.setScale(texelsPerUnit);
+
+ m4f mLookAt, mLookAtInv;
+
+ mLookAt.buildCameraLookAtMatrixLH(v3f(0.0f, 0.0f, 0.0f), -direction, v3f(0.0f, 1.0f, 0.0f));
+
+ mLookAt *= mTexelScaling;
+ mLookAtInv = mLookAt;
+ mLookAtInv.makeInverse();
+
+ v3f frustumCenter = newCenter;
+ mLookAt.transformVect(frustumCenter);
+ frustumCenter.X = floorf(frustumCenter.X); // clamp to texel increment
+ frustumCenter.Y = floorf(frustumCenter.Y); // clamp to texel increment
+ frustumCenter.Z = floorf(frustumCenter.Z);
+ mLookAtInv.transformVect(frustumCenter);
+ // probar radius multipliacdor en funcion del I, a menor I mas multiplicador
+ v3f eye_displacement = direction * vvolume;
+
+ // we must compute the viewmat with the position - the camera offset
+ // but the shadow_frustum position must be the actual world position
+ v3f eye = frustumCenter - eye_displacement;
+ shadow_frustum.position = world_center - eye_displacement;
+ shadow_frustum.length = vvolume;
+ shadow_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
+ shadow_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(shadow_frustum.length,
+ shadow_frustum.length, -shadow_frustum.length,
+ shadow_frustum.length,false);
+}
+
+DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
+ const v3f &position, video::SColorf lightColor,
+ f32 farValue) :
+ diffuseColor(lightColor),
+ farPlane(farValue), mapRes(shadowMapResolution), pos(position)
+{}
+
+void DirectionalLight::update_frustum(const Camera *cam, Client *client)
+{
+ should_update_map_shadow = true;
+ float zNear = cam->getCameraNode()->getNearValue();
+ float zFar = getMaxFarValue();
+
+ ///////////////////////////////////
+ // update splits near and fars
+ shadow_frustum.zNear = zNear;
+ shadow_frustum.zFar = zFar;
+
+ // update shadow frustum
+ createSplitMatrices(cam);
+ // get the draw list for shadows
+ client->getEnv().getClientMap().updateDrawListShadow(
+ getPosition(), getDirection(), shadow_frustum.length);
+ should_update_map_shadow = true;
+}
+
+void DirectionalLight::setDirection(v3f dir)
+{
+ direction = -dir;
+ direction.normalize();
+}
+
+v3f DirectionalLight::getPosition() const
+{
+ return shadow_frustum.position;
+}
+
+const m4f &DirectionalLight::getViewMatrix() const
+{
+ return shadow_frustum.ViewMat;
+}
+
+const m4f &DirectionalLight::getProjectionMatrix() const
+{
+ return shadow_frustum.ProjOrthMat;
+}
+
+m4f DirectionalLight::getViewProjMatrix()
+{
+ return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
+}
diff --git a/src/client/shadows/dynamicshadows.h b/src/client/shadows/dynamicshadows.h
new file mode 100644
index 000000000..a53612f7c
--- /dev/null
+++ b/src/client/shadows/dynamicshadows.h
@@ -0,0 +1,102 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes_bloated.h"
+#include <matrix4.h>
+#include "util/basic_macros.h"
+
+class Camera;
+class Client;
+
+struct shadowFrustum
+{
+ float zNear{0.0f};
+ float zFar{0.0f};
+ float length{0.0f};
+ core::matrix4 ProjOrthMat;
+ core::matrix4 ViewMat;
+ v3f position;
+};
+
+class DirectionalLight
+{
+public:
+ DirectionalLight(const u32 shadowMapResolution,
+ const v3f &position,
+ video::SColorf lightColor = video::SColor(0xffffffff),
+ f32 farValue = 100.0f);
+ ~DirectionalLight() = default;
+
+ //DISABLE_CLASS_COPY(DirectionalLight)
+
+ void update_frustum(const Camera *cam, Client *client);
+
+ // when set direction is updated to negative normalized(direction)
+ void setDirection(v3f dir);
+ v3f getDirection() const{
+ return direction;
+ };
+ v3f getPosition() const;
+
+ /// Gets the light's matrices.
+ const core::matrix4 &getViewMatrix() const;
+ const core::matrix4 &getProjectionMatrix() const;
+ core::matrix4 getViewProjMatrix();
+
+ /// Gets the light's far value.
+ f32 getMaxFarValue() const
+ {
+ return farPlane;
+ }
+
+
+ /// Gets the light's color.
+ const video::SColorf &getLightColor() const
+ {
+ return diffuseColor;
+ }
+
+ /// Sets the light's color.
+ void setLightColor(const video::SColorf &lightColor)
+ {
+ diffuseColor = lightColor;
+ }
+
+ /// Gets the shadow map resolution for this light.
+ u32 getMapResolution() const
+ {
+ return mapRes;
+ }
+
+ bool should_update_map_shadow{true};
+
+private:
+ void createSplitMatrices(const Camera *cam);
+
+ video::SColorf diffuseColor;
+
+ f32 farPlane;
+ u32 mapRes;
+
+ v3f pos;
+ v3f direction{0};
+ shadowFrustum shadow_frustum;
+};
diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp
new file mode 100644
index 000000000..135c9f895
--- /dev/null
+++ b/src/client/shadows/dynamicshadowsrender.cpp
@@ -0,0 +1,539 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <cstring>
+#include "client/shadows/dynamicshadowsrender.h"
+#include "client/shadows/shadowsScreenQuad.h"
+#include "client/shadows/shadowsshadercallbacks.h"
+#include "settings.h"
+#include "filesys.h"
+#include "util/string.h"
+#include "client/shader.h"
+#include "client/client.h"
+#include "client/clientmap.h"
+
+ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
+ m_device(device), m_smgr(device->getSceneManager()),
+ m_driver(device->getVideoDriver()), m_client(client)
+{
+ m_shadows_enabled = true;
+
+ m_shadow_strength = g_settings->getFloat("shadow_strength");
+
+ m_shadow_map_max_distance = g_settings->getFloat("shadow_map_max_distance");
+
+ m_shadow_map_texture_size = g_settings->getFloat("shadow_map_texture_size");
+
+ m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
+ m_shadow_map_colored = g_settings->getBool("shadow_map_color");
+ m_shadow_samples = g_settings->getS32("shadow_filters");
+ m_update_delta = g_settings->getFloat("shadow_update_time");
+}
+
+ShadowRenderer::~ShadowRenderer()
+{
+ if (m_shadow_depth_cb)
+ delete m_shadow_depth_cb;
+ if (m_shadow_mix_cb)
+ delete m_shadow_mix_cb;
+ m_shadow_node_array.clear();
+ m_light_list.clear();
+
+ if (shadowMapTextureDynamicObjects)
+ m_driver->removeTexture(shadowMapTextureDynamicObjects);
+
+ if (shadowMapTextureFinal)
+ m_driver->removeTexture(shadowMapTextureFinal);
+
+ if (shadowMapTextureColors)
+ m_driver->removeTexture(shadowMapTextureColors);
+
+ if (shadowMapClientMap)
+ m_driver->removeTexture(shadowMapClientMap);
+}
+
+void ShadowRenderer::initialize()
+{
+ auto *gpu = m_driver->getGPUProgrammingServices();
+
+ // we need glsl
+ if (m_shadows_enabled && gpu && m_driver->queryFeature(video::EVDF_ARB_GLSL)) {
+ createShaders();
+ } else {
+ m_shadows_enabled = false;
+
+ warningstream << "Shadows: GLSL Shader not supported on this system."
+ << std::endl;
+ return;
+ }
+
+ m_texture_format = m_shadow_map_texture_32bit
+ ? video::ECOLOR_FORMAT::ECF_R32F
+ : video::ECOLOR_FORMAT::ECF_R16F;
+
+ m_texture_format_color = m_shadow_map_texture_32bit
+ ? video::ECOLOR_FORMAT::ECF_G32R32F
+ : video::ECOLOR_FORMAT::ECF_G16R16F;
+}
+
+
+float ShadowRenderer::getUpdateDelta() const
+{
+ return m_update_delta;
+}
+
+size_t ShadowRenderer::addDirectionalLight()
+{
+ m_light_list.emplace_back(m_shadow_map_texture_size,
+ v3f(0.f, 0.f, 0.f),
+ video::SColor(255, 255, 255, 255), m_shadow_map_max_distance);
+ return m_light_list.size() - 1;
+}
+
+DirectionalLight &ShadowRenderer::getDirectionalLight(u32 index)
+{
+ return m_light_list[index];
+}
+
+size_t ShadowRenderer::getDirectionalLightCount() const
+{
+ return m_light_list.size();
+}
+
+f32 ShadowRenderer::getMaxShadowFar() const
+{
+ if (!m_light_list.empty()) {
+ float wanted_range = m_client->getEnv().getClientMap().getWantedRange();
+
+ float zMax = m_light_list[0].getMaxFarValue() > wanted_range
+ ? wanted_range
+ : m_light_list[0].getMaxFarValue();
+ return zMax * MAP_BLOCKSIZE;
+ }
+ return 0.0f;
+}
+
+void ShadowRenderer::addNodeToShadowList(
+ scene::ISceneNode *node, E_SHADOW_MODE shadowMode)
+{
+ m_shadow_node_array.emplace_back(NodeToApply(node, shadowMode));
+}
+
+void ShadowRenderer::removeNodeFromShadowList(scene::ISceneNode *node)
+{
+ for (auto it = m_shadow_node_array.begin(); it != m_shadow_node_array.end();) {
+ if (it->node == node) {
+ it = m_shadow_node_array.erase(it);
+ break;
+ } else {
+ ++it;
+ }
+ }
+}
+
+void ShadowRenderer::setClearColor(video::SColor ClearColor)
+{
+ m_clear_color = ClearColor;
+}
+
+void ShadowRenderer::update(video::ITexture *outputTarget)
+{
+ if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
+ m_smgr->drawAll();
+ return;
+ }
+
+ if (!shadowMapTextureDynamicObjects) {
+
+ shadowMapTextureDynamicObjects = getSMTexture(
+ std::string("shadow_dynamic_") + itos(m_shadow_map_texture_size),
+ m_texture_format, true);
+ }
+
+ if (!shadowMapClientMap) {
+
+ shadowMapClientMap = getSMTexture(
+ std::string("shadow_clientmap_") + itos(m_shadow_map_texture_size),
+ m_shadow_map_colored ? m_texture_format_color : m_texture_format,
+ true);
+ }
+
+ if (m_shadow_map_colored && !shadowMapTextureColors) {
+ shadowMapTextureColors = getSMTexture(
+ std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
+ m_shadow_map_colored ? m_texture_format_color : m_texture_format,
+ true);
+ }
+
+ // The merge all shadowmaps texture
+ if (!shadowMapTextureFinal) {
+ video::ECOLOR_FORMAT frt;
+ if (m_shadow_map_texture_32bit) {
+ if (m_shadow_map_colored)
+ frt = video::ECOLOR_FORMAT::ECF_A32B32G32R32F;
+ else
+ frt = video::ECOLOR_FORMAT::ECF_R32F;
+ } else {
+ if (m_shadow_map_colored)
+ frt = video::ECOLOR_FORMAT::ECF_A16B16G16R16F;
+ else
+ frt = video::ECOLOR_FORMAT::ECF_R16F;
+ }
+ shadowMapTextureFinal = getSMTexture(
+ std::string("shadowmap_final_") + itos(m_shadow_map_texture_size),
+ frt, true);
+ }
+
+ if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
+ // for every directional light:
+ for (DirectionalLight &light : m_light_list) {
+ // Static shader values.
+ m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
+ m_shadow_depth_cb->MaxFar = (f32)m_shadow_map_max_distance * BS;
+
+ // set the Render Target
+ // right now we can only render in usual RTT, not
+ // Depth texture is available in irrlicth maybe we
+ // should put some gl* fn here
+
+ if (light.should_update_map_shadow) {
+ light.should_update_map_shadow = false;
+
+ m_driver->setRenderTarget(shadowMapClientMap, true, true,
+ video::SColor(255, 255, 255, 255));
+ renderShadowMap(shadowMapClientMap, light);
+
+ if (m_shadow_map_colored) {
+ m_driver->setRenderTarget(shadowMapTextureColors,
+ true, false, video::SColor(255, 255, 255, 255));
+ }
+ renderShadowMap(shadowMapTextureColors, light,
+ scene::ESNRP_TRANSPARENT);
+ m_driver->setRenderTarget(0, false, false);
+ }
+
+ // render shadows for the n0n-map objects.
+ m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
+ true, video::SColor(255, 255, 255, 255));
+ renderShadowObjects(shadowMapTextureDynamicObjects, light);
+ // clear the Render Target
+ m_driver->setRenderTarget(0, false, false);
+
+ // in order to avoid too many map shadow renders,
+ // we should make a second pass to mix clientmap shadows and
+ // entities shadows :(
+ m_screen_quad->getMaterial().setTexture(0, shadowMapClientMap);
+ // dynamic objs shadow texture.
+ if (m_shadow_map_colored)
+ m_screen_quad->getMaterial().setTexture(1, shadowMapTextureColors);
+ m_screen_quad->getMaterial().setTexture(2, shadowMapTextureDynamicObjects);
+
+ m_driver->setRenderTarget(shadowMapTextureFinal, false, false,
+ video::SColor(255, 255, 255, 255));
+ m_screen_quad->render(m_driver);
+ m_driver->setRenderTarget(0, false, false);
+
+ } // end for lights
+
+ // now render the actual MT render pass
+ m_driver->setRenderTarget(outputTarget, true, true, m_clear_color);
+ m_smgr->drawAll();
+
+ /* this code just shows shadows textures in screen and in ONLY for debugging*/
+ #if 0
+ // this is debug, ignore for now.
+ m_driver->draw2DImage(shadowMapTextureFinal,
+ core::rect<s32>(0, 50, 128, 128 + 50),
+ core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
+
+ m_driver->draw2DImage(shadowMapClientMap,
+ core::rect<s32>(0, 50 + 128, 128, 128 + 50 + 128),
+ core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
+ m_driver->draw2DImage(shadowMapTextureDynamicObjects,
+ core::rect<s32>(0, 128 + 50 + 128, 128,
+ 128 + 50 + 128 + 128),
+ core::rect<s32>({0, 0}, shadowMapTextureDynamicObjects->getSize()));
+
+ if (m_shadow_map_colored) {
+
+ m_driver->draw2DImage(shadowMapTextureColors,
+ core::rect<s32>(128,128 + 50 + 128 + 128,
+ 128 + 128, 128 + 50 + 128 + 128 + 128),
+ core::rect<s32>({0, 0}, shadowMapTextureColors->getSize()));
+ }
+ #endif
+ m_driver->setRenderTarget(0, false, false);
+ }
+}
+
+
+video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name,
+ video::ECOLOR_FORMAT texture_format, bool force_creation)
+{
+ if (force_creation) {
+ return m_driver->addRenderTargetTexture(
+ core::dimension2du(m_shadow_map_texture_size,
+ m_shadow_map_texture_size),
+ shadow_map_name.c_str(), texture_format);
+ }
+
+ return m_driver->getTexture(shadow_map_name.c_str());
+}
+
+void ShadowRenderer::renderShadowMap(video::ITexture *target,
+ DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
+{
+ m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
+ m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
+
+ // Operate on the client map
+ for (const auto &shadow_node : m_shadow_node_array) {
+ if (strcmp(shadow_node.node->getName(), "ClientMap") != 0)
+ continue;
+
+ ClientMap *map_node = static_cast<ClientMap *>(shadow_node.node);
+
+ video::SMaterial material;
+ if (map_node->getMaterialCount() > 0) {
+ // we only want the first material, which is the one with the albedo info
+ material = map_node->getMaterial(0);
+ }
+
+ material.BackfaceCulling = false;
+ material.FrontfaceCulling = true;
+ material.PolygonOffsetFactor = 4.0f;
+ material.PolygonOffsetDirection = video::EPO_BACK;
+ //material.PolygonOffsetDepthBias = 1.0f/4.0f;
+ //material.PolygonOffsetSlopeScale = -1.f;
+
+ if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
+ material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
+ else
+ material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
+
+ // FIXME: I don't think this is needed here
+ map_node->OnAnimate(m_device->getTimer()->getTime());
+
+ m_driver->setTransform(video::ETS_WORLD,
+ map_node->getAbsoluteTransformation());
+
+ map_node->renderMapShadows(m_driver, material, pass);
+ break;
+ }
+}
+
+void ShadowRenderer::renderShadowObjects(
+ video::ITexture *target, DirectionalLight &light)
+{
+ m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
+ m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
+
+ for (const auto &shadow_node : m_shadow_node_array) {
+ // we only take care of the shadow casters
+ if (shadow_node.shadowMode == ESM_RECEIVE ||
+ strcmp(shadow_node.node->getName(), "ClientMap") == 0)
+ continue;
+
+ // render other objects
+ u32 n_node_materials = shadow_node.node->getMaterialCount();
+ std::vector<s32> BufferMaterialList;
+ std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
+ BufferMaterialList.reserve(n_node_materials);
+ BufferMaterialCullingList.reserve(n_node_materials);
+
+ // backup materialtype for each material
+ // (aka shader)
+ // and replace it by our "depth" shader
+ for (u32 m = 0; m < n_node_materials; m++) {
+ auto &current_mat = shadow_node.node->getMaterial(m);
+
+ BufferMaterialList.push_back(current_mat.MaterialType);
+ current_mat.MaterialType =
+ (video::E_MATERIAL_TYPE)depth_shader;
+
+ current_mat.setTexture(3, shadowMapTextureFinal);
+
+ BufferMaterialCullingList.emplace_back(
+ (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
+
+ current_mat.BackfaceCulling = true;
+ current_mat.FrontfaceCulling = false;
+ current_mat.PolygonOffsetFactor = 1.0f/2048.0f;
+ current_mat.PolygonOffsetDirection = video::EPO_BACK;
+ //current_mat.PolygonOffsetDepthBias = 1.0 * 2.8e-6;
+ //current_mat.PolygonOffsetSlopeScale = -1.f;
+ }
+
+ m_driver->setTransform(video::ETS_WORLD,
+ shadow_node.node->getAbsoluteTransformation());
+ shadow_node.node->render();
+
+ // restore the material.
+
+ for (u32 m = 0; m < n_node_materials; m++) {
+ auto &current_mat = shadow_node.node->getMaterial(m);
+
+ current_mat.MaterialType = (video::E_MATERIAL_TYPE) BufferMaterialList[m];
+
+ current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
+ current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
+ }
+
+ } // end for caster shadow nodes
+}
+
+void ShadowRenderer::mixShadowsQuad()
+{
+}
+
+/*
+ * @Liso's disclaimer ;) This function loads the Shadow Mapping Shaders.
+ * I used a custom loader because I couldn't figure out how to use the base
+ * Shaders system with custom IShaderConstantSetCallBack without messing up the
+ * code too much. If anyone knows how to integrate this with the standard MT
+ * shaders, please feel free to change it.
+ */
+
+void ShadowRenderer::createShaders()
+{
+ video::IGPUProgrammingServices *gpu = m_driver->getGPUProgrammingServices();
+
+ if (depth_shader == -1) {
+ std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
+ if (depth_shader_vs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error shadow mapping vs shader not found." << std::endl;
+ return;
+ }
+ std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
+ if (depth_shader_fs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error shadow mapping fs shader not found." << std::endl;
+ return;
+ }
+ m_shadow_depth_cb = new ShadowDepthShaderCB();
+
+ depth_shader = gpu->addHighLevelShaderMaterial(
+ readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+ video::EVST_VS_1_1,
+ readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+ video::EPST_PS_1_2, m_shadow_depth_cb);
+
+ if (depth_shader == -1) {
+ // upsi, something went wrong loading shader.
+ delete m_shadow_depth_cb;
+ m_shadows_enabled = false;
+ errorstream << "Error compiling shadow mapping shader." << std::endl;
+ return;
+ }
+
+ // HACK, TODO: investigate this better
+ // Grab the material renderer once more so minetest doesn't crash
+ // on exit
+ m_driver->getMaterialRenderer(depth_shader)->grab();
+ }
+
+ if (mixcsm_shader == -1) {
+ std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
+ if (depth_shader_vs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
+ return;
+ }
+
+ std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass2_fragment.glsl");
+ if (depth_shader_fs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
+ return;
+ }
+ m_shadow_mix_cb = new shadowScreenQuadCB();
+ m_screen_quad = new shadowScreenQuad();
+ mixcsm_shader = gpu->addHighLevelShaderMaterial(
+ readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+ video::EVST_VS_1_1,
+ readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+ video::EPST_PS_1_2, m_shadow_mix_cb);
+
+ m_screen_quad->getMaterial().MaterialType =
+ (video::E_MATERIAL_TYPE)mixcsm_shader;
+
+ if (mixcsm_shader == -1) {
+ // upsi, something went wrong loading shader.
+ delete m_shadow_mix_cb;
+ delete m_screen_quad;
+ m_shadows_enabled = false;
+ errorstream << "Error compiling cascade shadow mapping shader." << std::endl;
+ return;
+ }
+
+ // HACK, TODO: investigate this better
+ // Grab the material renderer once more so minetest doesn't crash
+ // on exit
+ m_driver->getMaterialRenderer(mixcsm_shader)->grab();
+ }
+
+ if (m_shadow_map_colored && depth_shader_trans == -1) {
+ std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_trans_vertex.glsl");
+ if (depth_shader_vs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error shadow mapping vs shader not found." << std::endl;
+ return;
+ }
+ std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_trans_fragment.glsl");
+ if (depth_shader_fs.empty()) {
+ m_shadows_enabled = false;
+ errorstream << "Error shadow mapping fs shader not found." << std::endl;
+ return;
+ }
+ m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
+
+ depth_shader_trans = gpu->addHighLevelShaderMaterial(
+ readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+ video::EVST_VS_1_1,
+ readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+ video::EPST_PS_1_2, m_shadow_depth_trans_cb);
+
+ if (depth_shader_trans == -1) {
+ // upsi, something went wrong loading shader.
+ delete m_shadow_depth_trans_cb;
+ m_shadow_map_colored = false;
+ m_shadows_enabled = false;
+ errorstream << "Error compiling colored shadow mapping shader." << std::endl;
+ return;
+ }
+
+ // HACK, TODO: investigate this better
+ // Grab the material renderer once more so minetest doesn't crash
+ // on exit
+ m_driver->getMaterialRenderer(depth_shader_trans)->grab();
+ }
+}
+
+std::string ShadowRenderer::readShaderFile(const std::string &path)
+{
+ std::string prefix;
+ if (m_shadow_map_colored)
+ prefix.append("#define COLORED_SHADOWS 1\n");
+
+ std::string content;
+ fs::ReadFile(path, content);
+
+ return prefix + content;
+}
diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h
new file mode 100644
index 000000000..e633bd4f7
--- /dev/null
+++ b/src/client/shadows/dynamicshadowsrender.h
@@ -0,0 +1,146 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "irrlichttypes_extrabloated.h"
+#include "client/shadows/dynamicshadows.h"
+
+class ShadowDepthShaderCB;
+class shadowScreenQuad;
+class shadowScreenQuadCB;
+
+enum E_SHADOW_MODE : u8
+{
+ ESM_RECEIVE = 0,
+ ESM_BOTH,
+};
+
+struct NodeToApply
+{
+ NodeToApply(scene::ISceneNode *n,
+ E_SHADOW_MODE m = E_SHADOW_MODE::ESM_BOTH) :
+ node(n),
+ shadowMode(m){};
+ bool operator<(const NodeToApply &other) const { return node < other.node; };
+
+ scene::ISceneNode *node;
+
+ E_SHADOW_MODE shadowMode{E_SHADOW_MODE::ESM_BOTH};
+ bool dirty{false};
+};
+
+class ShadowRenderer
+{
+public:
+ ShadowRenderer(IrrlichtDevice *device, Client *client);
+
+ ~ShadowRenderer();
+
+ void initialize();
+
+ /// Adds a directional light shadow map (Usually just one (the sun) except in
+ /// Tattoine ).
+ size_t addDirectionalLight();
+ DirectionalLight &getDirectionalLight(u32 index = 0);
+ size_t getDirectionalLightCount() const;
+ f32 getMaxShadowFar() const;
+
+ float getUpdateDelta() const;
+ /// Adds a shadow to the scene node.
+ /// The shadow mode can be ESM_BOTH, or ESM_RECEIVE.
+ /// ESM_BOTH casts and receives shadows
+ /// ESM_RECEIVE only receives but does not cast shadows.
+ ///
+ void addNodeToShadowList(scene::ISceneNode *node,
+ E_SHADOW_MODE shadowMode = ESM_BOTH);
+ void removeNodeFromShadowList(scene::ISceneNode *node);
+
+ void setClearColor(video::SColor ClearColor);
+
+ void update(video::ITexture *outputTarget = nullptr);
+
+ video::ITexture *get_texture()
+ {
+ return shadowMapTextureFinal;
+ }
+
+
+ bool is_active() const { return m_shadows_enabled; }
+ void setTimeOfDay(float isDay) { m_time_day = isDay; };
+
+ s32 getShadowSamples() const { return m_shadow_samples; }
+ float getShadowStrength() const { return m_shadow_strength; }
+ float getTimeOfDay() const { return m_time_day; }
+
+private:
+ video::ITexture *getSMTexture(const std::string &shadow_map_name,
+ video::ECOLOR_FORMAT texture_format,
+ bool force_creation = false);
+
+ void renderShadowMap(video::ITexture *target, DirectionalLight &light,
+ scene::E_SCENE_NODE_RENDER_PASS pass =
+ scene::ESNRP_SOLID);
+ void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
+ void mixShadowsQuad();
+
+ // a bunch of variables
+ IrrlichtDevice *m_device{nullptr};
+ scene::ISceneManager *m_smgr{nullptr};
+ video::IVideoDriver *m_driver{nullptr};
+ Client *m_client{nullptr};
+ video::ITexture *shadowMapClientMap{nullptr};
+ video::ITexture *shadowMapTextureFinal{nullptr};
+ video::ITexture *shadowMapTextureDynamicObjects{nullptr};
+ video::ITexture *shadowMapTextureColors{nullptr};
+ video::SColor m_clear_color{0x0};
+
+ std::vector<DirectionalLight> m_light_list;
+ std::vector<NodeToApply> m_shadow_node_array;
+
+ float m_shadow_strength;
+ float m_shadow_map_max_distance;
+ float m_shadow_map_texture_size;
+ float m_time_day{0.0f};
+ float m_update_delta;
+ int m_shadow_samples;
+ bool m_shadow_map_texture_32bit;
+ bool m_shadows_enabled;
+ bool m_shadow_map_colored;
+
+ video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F};
+ video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
+
+ // Shadow Shader stuff
+
+ void createShaders();
+ std::string readShaderFile(const std::string &path);
+
+ s32 depth_shader{-1};
+ s32 depth_shader_trans{-1};
+ s32 mixcsm_shader{-1};
+
+ ShadowDepthShaderCB *m_shadow_depth_cb{nullptr};
+ ShadowDepthShaderCB *m_shadow_depth_trans_cb{nullptr};
+
+ shadowScreenQuad *m_screen_quad{nullptr};
+ shadowScreenQuadCB *m_shadow_mix_cb{nullptr};
+};
diff --git a/src/client/shadows/shadowsScreenQuad.cpp b/src/client/shadows/shadowsScreenQuad.cpp
new file mode 100644
index 000000000..c36ee0d60
--- /dev/null
+++ b/src/client/shadows/shadowsScreenQuad.cpp
@@ -0,0 +1,67 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "shadowsScreenQuad.h"
+
+shadowScreenQuad::shadowScreenQuad()
+{
+ Material.Wireframe = false;
+ Material.Lighting = false;
+
+ video::SColor color(0x0);
+ Vertices[0] = video::S3DVertex(
+ -1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
+ Vertices[1] = video::S3DVertex(
+ -1.0f, 1.0f, 0.0f, 0, 0, 1, color, 0.0f, 0.0f);
+ Vertices[2] = video::S3DVertex(
+ 1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
+ Vertices[3] = video::S3DVertex(
+ 1.0f, -1.0f, 0.0f, 0, 0, 1, color, 1.0f, 1.0f);
+ Vertices[4] = video::S3DVertex(
+ -1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
+ Vertices[5] = video::S3DVertex(
+ 1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
+}
+
+void shadowScreenQuad::render(video::IVideoDriver *driver)
+{
+ u16 indices[6] = {0, 1, 2, 3, 4, 5};
+ driver->setMaterial(Material);
+ driver->setTransform(video::ETS_WORLD, core::matrix4());
+ driver->drawIndexedTriangleList(&Vertices[0], 6, &indices[0], 2);
+}
+
+void shadowScreenQuadCB::OnSetConstants(
+ video::IMaterialRendererServices *services, s32 userData)
+{
+ s32 TextureId = 0;
+ services->setPixelShaderConstant(
+ services->getPixelShaderConstantID("ShadowMapClientMap"),
+ &TextureId, 1);
+
+ TextureId = 1;
+ services->setPixelShaderConstant(
+ services->getPixelShaderConstantID("ShadowMapClientMapTraslucent"),
+ &TextureId, 1);
+
+ TextureId = 2;
+ services->setPixelShaderConstant(
+ services->getPixelShaderConstantID("ShadowMapSamplerdynamic"),
+ &TextureId, 1);
+}
diff --git a/src/client/shadows/shadowsScreenQuad.h b/src/client/shadows/shadowsScreenQuad.h
new file mode 100644
index 000000000..e6cc95957
--- /dev/null
+++ b/src/client/shadows/shadowsScreenQuad.h
@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include "irrlichttypes_extrabloated.h"
+#include <IMaterialRendererServices.h>
+#include <IShaderConstantSetCallBack.h>
+
+class shadowScreenQuad
+{
+public:
+ shadowScreenQuad();
+
+ void render(video::IVideoDriver *driver);
+ video::SMaterial &getMaterial() { return Material; }
+
+private:
+ video::S3DVertex Vertices[6];
+ video::SMaterial Material;
+};
+
+class shadowScreenQuadCB : public video::IShaderConstantSetCallBack
+{
+public:
+ shadowScreenQuadCB(){};
+
+ virtual void OnSetConstants(video::IMaterialRendererServices *services,
+ s32 userData);
+};
diff --git a/src/client/shadows/shadowsshadercallbacks.cpp b/src/client/shadows/shadowsshadercallbacks.cpp
new file mode 100644
index 000000000..2f5797084
--- /dev/null
+++ b/src/client/shadows/shadowsshadercallbacks.cpp
@@ -0,0 +1,44 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "client/shadows/shadowsshadercallbacks.h"
+
+void ShadowDepthShaderCB::OnSetConstants(
+ video::IMaterialRendererServices *services, s32 userData)
+{
+ video::IVideoDriver *driver = services->getVideoDriver();
+
+ core::matrix4 lightMVP = driver->getTransform(video::ETS_PROJECTION);
+ lightMVP *= driver->getTransform(video::ETS_VIEW);
+ lightMVP *= driver->getTransform(video::ETS_WORLD);
+
+ services->setVertexShaderConstant(
+ services->getPixelShaderConstantID("LightMVP"),
+ lightMVP.pointer(), 16);
+
+ services->setVertexShaderConstant(
+ services->getPixelShaderConstantID("MapResolution"), &MapRes, 1);
+ services->setVertexShaderConstant(
+ services->getPixelShaderConstantID("MaxFar"), &MaxFar, 1);
+
+ s32 TextureId = 0;
+ services->setPixelShaderConstant(
+ services->getPixelShaderConstantID("ColorMapSampler"), &TextureId,
+ 1);
+}
diff --git a/src/client/shadows/shadowsshadercallbacks.h b/src/client/shadows/shadowsshadercallbacks.h
new file mode 100644
index 000000000..43ad489f2
--- /dev/null
+++ b/src/client/shadows/shadowsshadercallbacks.h
@@ -0,0 +1,34 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include "irrlichttypes_extrabloated.h"
+#include <IMaterialRendererServices.h>
+#include <IShaderConstantSetCallBack.h>
+
+class ShadowDepthShaderCB : public video::IShaderConstantSetCallBack
+{
+public:
+ void OnSetMaterial(const video::SMaterial &material) override {}
+
+ void OnSetConstants(video::IMaterialRendererServices *services,
+ s32 userData) override;
+
+ f32 MaxFar{2048.0f}, MapRes{1024.0f};
+};
diff --git a/src/client/sky.cpp b/src/client/sky.cpp
index 47296a7a5..1cf9a4afc 100644
--- a/src/client/sky.cpp
+++ b/src/client/sky.cpp
@@ -122,7 +122,14 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
m_materials[i].Lighting = true;
m_materials[i].MaterialType = video::EMT_SOLID;
}
+
m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
+
+ if (g_settings->getBool("enable_dynamic_shadows")) {
+ float val = g_settings->getFloat("shadow_sky_body_orbit_tilt");
+ m_sky_body_orbit_tilt = rangelim(val, 0.0f, 60.0f);
+ }
+
setStarCount(1000, true);
}
@@ -175,17 +182,7 @@ void Sky::render()
video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
- float nightlength = 0.415;
- float wn = nightlength / 2;
- float wicked_time_of_day = 0;
- if (m_time_of_day > wn && m_time_of_day < 1.0 - wn)
- wicked_time_of_day = (m_time_of_day - wn) / (1.0 - wn * 2) * 0.5 + 0.25;
- else if (m_time_of_day < 0.5)
- wicked_time_of_day = m_time_of_day / wn * 0.25;
- else
- wicked_time_of_day = 1.0 - ((1.0 - m_time_of_day) / wn * 0.25);
- /*std::cerr<<"time_of_day="<<m_time_of_day<<" -> "
- <<"wicked_time_of_day="<<wicked_time_of_day<<std::endl;*/
+ float wicked_time_of_day = getWickedTimeOfDay(m_time_of_day);
video::SColor suncolor = suncolor_f.toSColor();
video::SColor suncolor2 = suncolor2_f.toSColor();
@@ -739,10 +736,15 @@ void Sky::place_sky_body(
* day_position: turn the body around the Z axis, to place it depending of the time of the day
*/
{
+ v3f centrum(0, 0, -1);
+ centrum.rotateXZBy(horizon_position);
+ centrum.rotateXYBy(day_position);
+ centrum.rotateYZBy(m_sky_body_orbit_tilt);
for (video::S3DVertex &vertex : vertices) {
// Body is directed to -Z (south) by default
vertex.Pos.rotateXZBy(horizon_position);
vertex.Pos.rotateXYBy(day_position);
+ vertex.Pos.Z += centrum.Z;
}
}
@@ -931,3 +933,17 @@ void Sky::setSkyDefaults()
m_moon_params = sky_defaults.getMoonDefaults();
m_star_params = sky_defaults.getStarDefaults();
}
+
+float getWickedTimeOfDay(float time_of_day)
+{
+ float nightlength = 0.415f;
+ float wn = nightlength / 2;
+ float wicked_time_of_day = 0;
+ if (time_of_day > wn && time_of_day < 1.0f - wn)
+ wicked_time_of_day = (time_of_day - wn) / (1.0f - wn * 2) * 0.5f + 0.25f;
+ else if (time_of_day < 0.5f)
+ wicked_time_of_day = time_of_day / wn * 0.25f;
+ else
+ wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f);
+ return wicked_time_of_day;
+}
diff --git a/src/client/sky.h b/src/client/sky.h
index 121a16bb7..83106453b 100644
--- a/src/client/sky.h
+++ b/src/client/sky.h
@@ -105,6 +105,8 @@ public:
ITextureSource *tsrc);
const video::SColorf &getCurrentStarColor() const { return m_star_color; }
+ float getSkyBodyOrbitTilt() const { return m_sky_body_orbit_tilt; }
+
private:
aabb3f m_box;
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
@@ -159,6 +161,7 @@ private:
bool m_directional_colored_fog;
bool m_in_clouds = true; // Prevent duplicating bools to remember old values
bool m_enable_shaders = false;
+ float m_sky_body_orbit_tilt = 0.0f;
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);
@@ -205,3 +208,7 @@ private:
float horizon_position, float day_position);
void setSkyDefaults();
};
+
+// calculates value for sky body positions for the given observed time of day
+// this is used to draw both Sun/Moon and shadows
+float getWickedTimeOfDay(float time_of_day);
diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp
index 08fd49fc0..7597aaa88 100644
--- a/src/client/wieldmesh.cpp
+++ b/src/client/wieldmesh.cpp
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include <map>
#include <IMeshManipulator.h>
+#include "client/renderingengine.h"
#define WIELD_SCALE_FACTOR 30.0
#define WIELD_SCALE_FACTOR_EXTRUDED 40.0
@@ -220,11 +221,18 @@ WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool l
m_meshnode->setReadOnlyMaterials(false);
m_meshnode->setVisible(false);
dummymesh->drop(); // m_meshnode grabbed it
+
+ m_shadow = RenderingEngine::get_shadow_renderer();
}
WieldMeshSceneNode::~WieldMeshSceneNode()
{
sanity_check(g_extrusion_mesh_cache);
+
+ // Remove node from shadow casters
+ if (m_shadow)
+ m_shadow->removeNodeFromShadowList(m_meshnode);
+
if (g_extrusion_mesh_cache->drop())
g_extrusion_mesh_cache = nullptr;
}
@@ -527,6 +535,10 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
// need to normalize normals when lighting is enabled (because of setScale())
m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
m_meshnode->setVisible(true);
+
+ // Add mesh to shadow caster
+ if (m_shadow)
+ m_shadow->addNodeToShadowList(m_meshnode);
}
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h
index 933097230..d1eeb64f5 100644
--- a/src/client/wieldmesh.h
+++ b/src/client/wieldmesh.h
@@ -27,6 +27,7 @@ struct ItemStack;
class Client;
class ITextureSource;
struct ContentFeatures;
+class ShadowRenderer;
/*!
* Holds color information of an item mesh's buffer.
@@ -124,6 +125,8 @@ private:
// so this variable is just required so we can implement
// getBoundingBox() and is set to an empty box.
aabb3f m_bounding_box;
+
+ ShadowRenderer *m_shadow;
};
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);