156 changes: 127 additions & 29 deletions src/client/shadows/dynamicshadowsrender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/shader.h"
#include "client/client.h"
#include "client/clientmap.h"
#include "profiler.h"

ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
m_device(device), m_smgr(device->getSceneManager()),
m_driver(device->getVideoDriver()), m_client(client)
m_driver(device->getVideoDriver()), m_client(client), m_current_frame(0)
{
m_shadows_enabled = true;

Expand All @@ -43,7 +44,7 @@ ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
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");
m_map_shadow_update_frames = g_settings->getS16("shadow_update_frames");
}

ShadowRenderer::~ShadowRenderer()
Expand All @@ -66,6 +67,9 @@ ShadowRenderer::~ShadowRenderer()

if (shadowMapClientMap)
m_driver->removeTexture(shadowMapClientMap);

if (shadowMapClientMapFuture)
m_driver->removeTexture(shadowMapClientMapFuture);
}

void ShadowRenderer::initialize()
Expand Down Expand Up @@ -93,11 +97,6 @@ void ShadowRenderer::initialize()
}


float ShadowRenderer::getUpdateDelta() const
{
return m_update_delta;
}

size_t ShadowRenderer::addDirectionalLight()
{
m_light_list.emplace_back(m_shadow_map_texture_size,
Expand Down Expand Up @@ -152,10 +151,9 @@ void ShadowRenderer::setClearColor(video::SColor ClearColor)
m_clear_color = ClearColor;
}

void ShadowRenderer::update(video::ITexture *outputTarget)
void ShadowRenderer::updateSMTextures()
{
if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
m_smgr->drawAll();
return;
}

Expand All @@ -174,6 +172,13 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
true);
}

if (!shadowMapClientMapFuture && m_map_shadow_update_frames > 1) {
shadowMapClientMapFuture = getSMTexture(
std::string("shadow_clientmap_bb_") + 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),
Expand Down Expand Up @@ -201,7 +206,22 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
}

if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
// for every directional light:
bool reset_sm_texture = false;

// detect if SM should be regenerated
for (DirectionalLight &light : m_light_list) {
if (light.should_update_map_shadow) {
light.should_update_map_shadow = false;
m_current_frame = 0;
reset_sm_texture = true;
}
}

video::ITexture* shadowMapTargetTexture = shadowMapClientMapFuture;
if (shadowMapTargetTexture == nullptr)
shadowMapTargetTexture = shadowMapClientMap;

// Update SM incrementally:
for (DirectionalLight &light : m_light_list) {
// Static shader values.
m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
Expand All @@ -212,22 +232,60 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
// 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,
if (m_current_frame < m_map_shadow_update_frames) {
m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, 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(shadowMapTargetTexture, light);

// Render transparent part in one pass.
// This is also handled in ClientMap.
if (m_current_frame == m_map_shadow_update_frames - 1) {
if (m_shadow_map_colored) {
m_driver->setRenderTarget(shadowMapTextureColors,
true, false, video::SColor(255, 255, 255, 255));
}
renderShadowMap(shadowMapTextureColors, light,
scene::ESNRP_TRANSPARENT);
}
renderShadowMap(shadowMapTextureColors, light,
scene::ESNRP_TRANSPARENT);
m_driver->setRenderTarget(0, false, false);
}

reset_sm_texture = false;
} // end for lights

// move to the next section
if (m_current_frame <= m_map_shadow_update_frames)
++m_current_frame;

// pass finished, swap textures and commit light changes
if (m_current_frame == m_map_shadow_update_frames) {
if (shadowMapClientMapFuture != nullptr)
std::swap(shadowMapClientMapFuture, shadowMapClientMap);

// Let all lights know that maps are updated
for (DirectionalLight &light : m_light_list)
light.commitFrustum();
}
}
}

void ShadowRenderer::update(video::ITexture *outputTarget)
{
if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
m_smgr->drawAll();
return;
}

updateSMTextures();

if (!m_shadow_node_array.empty() && !m_light_list.empty()) {

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;

// render shadows for the n0n-map objects.
m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
true, video::SColor(255, 255, 255, 255));
Expand Down Expand Up @@ -299,8 +357,8 @@ video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name
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());
m_driver->setTransform(video::ETS_VIEW, light.getFutureViewMatrix());
m_driver->setTransform(video::ETS_PROJECTION, light.getFutureProjectionMatrix());

// Operate on the client map
for (const auto &shadow_node : m_shadow_node_array) {
Expand All @@ -322,18 +380,21 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
//material.PolygonOffsetDepthBias = 1.0f/4.0f;
//material.PolygonOffsetSlopeScale = -1.f;

if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
if (m_shadow_map_colored && pass != scene::ESNRP_SOLID) {
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
else
}
else {
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
material.BlendOperation = video::EBO_MIN;
}

// 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);
map_node->renderMapShadows(m_driver, material, pass, m_current_frame, m_map_shadow_update_frames);
break;
}
}
Expand All @@ -354,8 +415,10 @@ void ShadowRenderer::renderShadowObjects(
u32 n_node_materials = shadow_node.node->getMaterialCount();
std::vector<s32> BufferMaterialList;
std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
std::vector<video::E_BLEND_OPERATION> BufferBlendOperationList;
BufferMaterialList.reserve(n_node_materials);
BufferMaterialCullingList.reserve(n_node_materials);
BufferBlendOperationList.reserve(n_node_materials);

// backup materialtype for each material
// (aka shader)
Expand All @@ -365,12 +428,11 @@ void ShadowRenderer::renderShadowObjects(

BufferMaterialList.push_back(current_mat.MaterialType);
current_mat.MaterialType =
(video::E_MATERIAL_TYPE)depth_shader;

current_mat.setTexture(3, shadowMapTextureFinal);
(video::E_MATERIAL_TYPE)depth_shader_entities;

BufferMaterialCullingList.emplace_back(
(bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
BufferBlendOperationList.push_back(current_mat.BlendOperation);

current_mat.BackfaceCulling = true;
current_mat.FrontfaceCulling = false;
Expand All @@ -393,6 +455,7 @@ void ShadowRenderer::renderShadowObjects(

current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
current_mat.BlendOperation = BufferBlendOperationList[m];
}

} // end for caster shadow nodes
Expand Down Expand Up @@ -433,7 +496,7 @@ void ShadowRenderer::createShaders()
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);
video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);

if (depth_shader == -1) {
// upsi, something went wrong loading shader.
Expand All @@ -449,6 +512,41 @@ void ShadowRenderer::createShaders()
m_driver->getMaterialRenderer(depth_shader)->grab();
}

// This creates a clone of depth_shader with base material set to EMT_SOLID,
// because entities won't render shadows with base material EMP_ONETEXTURE_BLEND
if (depth_shader_entities == -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;
}

depth_shader_entities = 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_entities == -1) {
// upsi, something went wrong loading shader.
m_shadows_enabled = false;
errorstream << "Error compiling shadow mapping shader (dynamic)." << 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_entities)->grab();
}

if (mixcsm_shader == -1) {
std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
if (depth_shader_vs.empty()) {
Expand Down
7 changes: 5 additions & 2 deletions src/client/shadows/dynamicshadowsrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ class ShadowRenderer
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
Expand Down Expand Up @@ -101,13 +100,15 @@ class ShadowRenderer
scene::ESNRP_SOLID);
void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
void mixShadowsQuad();
void updateSMTextures();

// 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 *shadowMapClientMapFuture{nullptr};
video::ITexture *shadowMapTextureFinal{nullptr};
video::ITexture *shadowMapTextureDynamicObjects{nullptr};
video::ITexture *shadowMapTextureColors{nullptr};
Expand All @@ -120,11 +121,12 @@ class ShadowRenderer
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;
u8 m_map_shadow_update_frames; /* Use this number of frames to update map shaodw */
u8 m_current_frame{0}; /* Current frame */

video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F};
video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
Expand All @@ -135,6 +137,7 @@ class ShadowRenderer
std::string readShaderFile(const std::string &path);

s32 depth_shader{-1};
s32 depth_shader_entities{-1};
s32 depth_shader_trans{-1};
s32 mixcsm_shader{-1};

Expand Down
2 changes: 1 addition & 1 deletion src/defaultsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ void set_default_settings()
settings->setDefault("shadow_map_color", "false");
settings->setDefault("shadow_filters", "1");
settings->setDefault("shadow_poisson_filter", "true");
settings->setDefault("shadow_update_time", "0.2");
settings->setDefault("shadow_update_frames", "8");
settings->setDefault("shadow_soft_radius", "1.0");
settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");

Expand Down