From f30dcdb504adb02724e3a5faa30a951eb907b33f Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 29 May 2021 19:08:16 +0200 Subject: Fix procession ordering issue in content_cao --- src/client/content_cao.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src/client/content_cao.cpp') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 6c7559364..2e58e19cf 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -346,18 +346,6 @@ void GenericCAO::initialize(const std::string &data) infostream<<"GenericCAO: Got init data"<getLocalPlayer(); - if (player && strcmp(player->getName(), m_name.c_str()) == 0) { - m_is_local_player = true; - m_is_visible = false; - player->setCAO(this); - - m_prop.show_on_minimap = false; - } - } - m_enable_shaders = g_settings->getBool("enable_shaders"); } @@ -380,6 +368,16 @@ void GenericCAO::processInitData(const std::string &data) m_rotation = readV3F32(is); m_hp = readU16(is); + if (m_is_player) { + // Check if it's the current player + LocalPlayer *player = m_env->getLocalPlayer(); + if (player && strcmp(player->getName(), m_name.c_str()) == 0) { + m_is_local_player = true; + m_is_visible = false; + player->setCAO(this); + } + } + const u8 num_messages = readU8(is); for (int i = 0; i < num_messages; i++) { -- cgit v1.2.3 From c47313db65f968559711ac1b505ef341a9872017 Mon Sep 17 00:00:00 2001 From: Liso Date: Sun, 6 Jun 2021 18:51:21 +0200 Subject: Shadow mapping render pass (#11244) Co-authored-by: x2048 --- builtin/mainmenu/tab_settings.lua | 58 ++- builtin/settingtypes.txt | 52 ++ client/shaders/nodes_shader/opengl_fragment.glsl | 431 +++++++++++++++- client/shaders/nodes_shader/opengl_vertex.glsl | 59 ++- client/shaders/object_shader/opengl_fragment.glsl | 297 +++++++++++- client/shaders/object_shader/opengl_vertex.glsl | 46 ++ client/shaders/shadow_shaders/pass1_fragment.glsl | 13 + .../shadow_shaders/pass1_trans_fragment.glsl | 38 ++ .../shaders/shadow_shaders/pass1_trans_vertex.glsl | 26 + client/shaders/shadow_shaders/pass1_vertex.glsl | 26 + client/shaders/shadow_shaders/pass2_fragment.glsl | 23 + client/shaders/shadow_shaders/pass2_vertex.glsl | 9 + src/client/CMakeLists.txt | 4 + src/client/clientmap.cpp | 218 ++++++++- src/client/clientmap.h | 11 +- src/client/content_cao.cpp | 13 +- src/client/game.cpp | 37 +- src/client/mapblock_mesh.cpp | 6 +- src/client/render/core.cpp | 21 +- src/client/render/core.h | 5 + src/client/renderingengine.h | 11 + src/client/shader.cpp | 64 +++ src/client/shadows/dynamicshadows.cpp | 145 ++++++ src/client/shadows/dynamicshadows.h | 102 ++++ src/client/shadows/dynamicshadowsrender.cpp | 539 +++++++++++++++++++++ src/client/shadows/dynamicshadowsrender.h | 146 ++++++ src/client/shadows/shadowsScreenQuad.cpp | 67 +++ src/client/shadows/shadowsScreenQuad.h | 45 ++ src/client/shadows/shadowsshadercallbacks.cpp | 44 ++ src/client/shadows/shadowsshadercallbacks.h | 34 ++ src/client/sky.cpp | 38 +- src/client/sky.h | 7 + src/client/wieldmesh.cpp | 12 + src/client/wieldmesh.h | 3 + src/defaultsettings.cpp | 12 + 35 files changed, 2624 insertions(+), 38 deletions(-) create mode 100644 client/shaders/shadow_shaders/pass1_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass1_trans_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass1_trans_vertex.glsl create mode 100644 client/shaders/shadow_shaders/pass1_vertex.glsl create mode 100644 client/shaders/shadow_shaders/pass2_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass2_vertex.glsl create mode 100644 src/client/shadows/dynamicshadows.cpp create mode 100644 src/client/shadows/dynamicshadows.h create mode 100644 src/client/shadows/dynamicshadowsrender.cpp create mode 100644 src/client/shadows/dynamicshadowsrender.h create mode 100644 src/client/shadows/shadowsScreenQuad.cpp create mode 100644 src/client/shadows/shadowsScreenQuad.h create mode 100644 src/client/shadows/shadowsshadercallbacks.cpp create mode 100644 src/client/shadows/shadowsshadercallbacks.h (limited to 'src/client/content_cao.cpp') diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 29744048a..8bc5bf8b6 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -43,6 +43,14 @@ local labels = { fgettext("2x"), fgettext("4x"), fgettext("8x") + }, + shadow_levels = { + fgettext("Disabled"), + fgettext("Very Low"), + fgettext("Low"), + fgettext("Medium"), + fgettext("High"), + fgettext("Ultra High") } } @@ -66,6 +74,10 @@ local dd_options = { antialiasing = { table.concat(labels.antialiasing, ","), {"0", "2", "4", "8"} + }, + shadow_levels = { + table.concat(labels.shadow_levels, ","), + { "0", "1", "2", "3", "4", "5" } } } @@ -110,6 +122,15 @@ local getSettingIndex = { end end return 1 + end, + ShadowMapping = function() + local shadow_setting = core.settings:get("shadow_levels") + for i = 1, #dd_options.shadow_levels[2] do + if shadow_setting == dd_options.shadow_levels[2][i] then + return i + end + end + return 1 end } @@ -197,7 +218,10 @@ local function formspec(tabview, name, tabdata) "checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" - .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" + .. dump(core.settings:get_bool("enable_waving_plants")) .. "]".. + "label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" .. + "dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" + .. getSettingIndex.ShadowMapping() .. "]" else tab_string = tab_string .. "label[8.38,0.7;" .. core.colorize("#888888", @@ -207,7 +231,9 @@ local function formspec(tabview, name, tabdata) "label[8.38,1.7;" .. core.colorize("#888888", fgettext("Waving Leaves")) .. "]" .. "label[8.38,2.2;" .. core.colorize("#888888", - fgettext("Waving Plants")) .. "]" + fgettext("Waving Plants")) .. "]".. + "label[8.38,2.7;" .. core.colorize("#888888", + fgettext("Dynamic shadows")) .. "]" end return tab_string @@ -333,6 +359,34 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) ddhandled = true end + for i = 1, #labels.shadow_levels do + if fields["dd_shadows"] == labels.shadow_levels[i] then + core.settings:set("shadow_levels", dd_options.shadow_levels[2][i]) + ddhandled = true + end + end + + if fields["dd_shadows"] == labels.shadow_levels[1] then + core.settings:set("enable_dynamic_shadows", "false") + else + core.settings:set("enable_dynamic_shadows", "true") + local shadow_presets = { + [2] = { 80, 512, "true", 0, "false" }, + [3] = { 120, 1024, "true", 1, "false" }, + [4] = { 350, 2048, "true", 1, "false" }, + [5] = { 350, 2048, "true", 2, "true" }, + [6] = { 450, 4096, "true", 2, "true" }, + } + local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])] + if s then + core.settings:set("shadow_map_max_distance", s[1]) + core.settings:set("shadow_map_texture_size", s[2]) + core.settings:set("shadow_map_texture_32bit", s[3]) + core.settings:set("shadow_filters", s[4]) + core.settings:set("shadow_map_color", s[5]) + end + end + return ddhandled end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index d13bac91b..ae421de2c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -582,6 +582,58 @@ enable_waving_leaves (Waving leaves) bool false # Requires shaders to be enabled. enable_waving_plants (Waving plants) bool false +[***Dynamic shadows] + +# Set to true to enable Shadow Mapping. +# Requires shaders to be enabled. +enable_dynamic_shadows (Dynamic shadows) bool false + +# Set the shadow strength. +# Lower value means lighter shadows, higher value means darker shadows. +shadow_strength (Shadow strength) float 0.2 0.05 1.0 + +# Maximum distance to render shadows. +shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0 + +# Texture size to render the shadow map on. +# This must be a power of two. +# Bigger numbers create better shadowsbut it is also more expensive. +shadow_map_texture_size (Shadow map texture size) int 1024 128 8192 + +# Sets shadow texture quality to 32 bits. +# On false, 16 bits texture will be used. +# This can cause much more artifacts in the shadow. +shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true + +# Enable poisson disk filtering. +# On true uses poisson disk to make "soft shadows". Otherwise uses PCF filtering. +shadow_poisson_filter (Poisson filtering) bool true + +# Define shadow filtering quality +# This simulates the soft shadows effect by applying a PCF or poisson disk +# but also uses more resources. +shadow_filters (Shadow filter quality) enum 1 0,1,2 + +# Enable colored shadows. +# On true translucent nodes cast colored shadows. This is expensive. +shadow_map_color (Colored shadows) bool false + + +# Set the shadow update time. +# Lower value means shadows and map updates faster, but it consume more resources. +# Minimun value 0.001 seconds max value 0.2 seconds +shadow_update_time (Map update time) float 0.2 0.001 0.2 + +# Set the soft shadow radius size. +# Lower values mean sharper shadows bigger values softer. +# Minimun value 1.0 and max value 10.0 +shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0 + +# Set the tilt of Sun/Moon orbit in degrees +# Value of 0 means no tilt / vertical orbit. +# Minimun value 0.0 and max value 60.0 +shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0 + [**Advanced] # Arm inertia, gives a more realistic movement of diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index b58095063..43a8b1f25 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -7,7 +7,22 @@ uniform vec3 eyePosition; // The cameraOffset is the current center of the visible world. uniform vec3 cameraOffset; uniform float animationTimer; +#ifdef ENABLE_DYNAMIC_SHADOWS + // shadow texture + uniform sampler2D ShadowMapSampler; + // shadow uniforms + uniform vec3 v_LightDirection; + uniform float f_textureresolution; + uniform mat4 m_ShadowViewProj; + uniform float f_shadowfar; + varying float normalOffsetScale; + varying float adj_shadow_strength; + varying float cosLight; + varying float f_normal_length; +#endif + +varying vec3 vNormal; varying vec3 vPosition; // World position in the visible world (i.e. relative to the cameraOffset.) // This can be used for many shader effects without loss of precision. @@ -22,10 +37,388 @@ varying mediump vec2 varTexCoord; centroid varying vec2 varTexCoord; #endif varying vec3 eyeVec; +varying float nightRatio; const float fogStart = FOG_START; const float fogShadingParameter = 1.0 / ( 1.0 - fogStart); + + +#ifdef ENABLE_DYNAMIC_SHADOWS +const float bias0 = 0.9; +const float zPersFactor = 0.5; +const float bias1 = 1.0 - bias0 + 1e-6; + +vec4 getPerspectiveFactor(in vec4 shadowPosition) +{ + + float pDistance = length(shadowPosition.xy); + float pFactor = pDistance * bias0 + bias1; + + shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor); + + return shadowPosition; +} + +// assuming near is always 1.0 +float getLinearDepth() +{ + return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0)); +} + +vec3 getLightSpacePosition() +{ + vec4 pLightSpace; + // some drawtypes have zero normals, so we need to handle it :( + #if DRAW_TYPE == NDT_PLANTLIKE + pLightSpace = m_ShadowViewProj * vec4(worldPosition, 1.0); + #else + float offsetScale = (0.0057 * getLinearDepth() + normalOffsetScale); + pLightSpace = m_ShadowViewProj * vec4(worldPosition + offsetScale * normalize(vNormal), 1.0); + #endif + pLightSpace = getPerspectiveFactor(pLightSpace); + return pLightSpace.xyz * 0.5 + 0.5; +} +// custom smoothstep implementation because it's not defined in glsl1.2 +// https://docs.gl/sl4/smoothstep +float mtsmoothstep(in float edge0, in float edge1, in float x) +{ + float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +#ifdef COLORED_SHADOWS + +// c_precision of 128 fits within 7 base-10 digits +const float c_precision = 128.0; +const float c_precisionp1 = c_precision + 1.0; + +float packColor(vec3 color) +{ + return floor(color.b * c_precision + 0.5) + + floor(color.g * c_precision + 0.5) * c_precisionp1 + + floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1; +} + +vec3 unpackColor(float value) +{ + vec3 color; + color.b = mod(value, c_precisionp1) / c_precision; + color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision; + color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision; + return color; +} + +vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba; + + float visibility = step(0.0, realDistance - texDepth.r); + vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g)); + if (visibility < 0.1) { + visibility = step(0.0, realDistance - texDepth.b); + result = vec4(visibility, unpackColor(texDepth.a)); + } + return result; +} + +#else + +float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + float texDepth = texture2D(shadowsampler, smTexCoord.xy).r; + float visibility = step(0.0, realDistance - texDepth); + return visibility; +} + +#endif + + +#if SHADOW_FILTER == 2 + #define PCFBOUND 3.5 + #define PCFSAMPLES 64.0 +#elif SHADOW_FILTER == 1 + #define PCFBOUND 1.5 + #if defined(POISSON_FILTER) + #define PCFSAMPLES 32.0 + #else + #define PCFSAMPLES 16.0 + #endif +#else + #define PCFBOUND 0.0 + #if defined(POISSON_FILTER) + #define PCFSAMPLES 4.0 + #else + #define PCFSAMPLES 1.0 + #endif +#endif +#ifdef COLORED_SHADOWS +float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy); + float depth = max(realDistance - texDepth.r, realDistance - texDepth.b); + return depth; +} +#else +float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + float texDepth = texture2D(shadowsampler, smTexCoord.xy).r; + float depth = realDistance - texDepth; + return depth; +} +#endif + +float getBaseLength(vec2 smTexCoord) +{ + float l = length(2.0 * smTexCoord.xy - 1.0); // length in texture coords + return bias1 / (1.0 / l - bias0); // return to undistorted coords +} + +float getDeltaPerspectiveFactor(float l) +{ + return 0.1 / (bias0 * l + bias1); // original distortion factor, divided by 10 +} + +float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier) +{ + // Return fast if sharp shadows are requested + if (SOFTSHADOWRADIUS <= 1.0) + return SOFTSHADOWRADIUS; + + vec2 clampedpos; + float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5); + float y, x; + float depth = 0.0; + float pointDepth; + float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; + + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + int n = 0; + + for (y = -bound; y <= bound; y += 1.0) + for (x = -bound; x <= bound; x += 1.0) { + clampedpos = vec2(x,y); + perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius); + clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy; + + pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance); + if (pointDepth > -0.01) { + depth += pointDepth; + n += 1; + } + } + + depth = depth / n; + + depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001; + return max(0.5, depth * maxRadius); +} + +#ifdef POISSON_FILTER +const vec2[64] poissonDisk = vec2[64]( + vec2(0.170019, -0.040254), + vec2(-0.299417, 0.791925), + vec2(0.645680, 0.493210), + vec2(-0.651784, 0.717887), + vec2(0.421003, 0.027070), + vec2(-0.817194, -0.271096), + vec2(-0.705374, -0.668203), + vec2(0.977050, -0.108615), + vec2(0.063326, 0.142369), + vec2(0.203528, 0.214331), + vec2(-0.667531, 0.326090), + vec2(-0.098422, -0.295755), + vec2(-0.885922, 0.215369), + vec2(0.566637, 0.605213), + vec2(0.039766, -0.396100), + vec2(0.751946, 0.453352), + vec2(0.078707, -0.715323), + vec2(-0.075838, -0.529344), + vec2(0.724479, -0.580798), + vec2(0.222999, -0.215125), + vec2(-0.467574, -0.405438), + vec2(-0.248268, -0.814753), + vec2(0.354411, -0.887570), + vec2(0.175817, 0.382366), + vec2(0.487472, -0.063082), + vec2(0.355476, 0.025357), + vec2(-0.084078, 0.898312), + vec2(0.488876, -0.783441), + vec2(0.470016, 0.217933), + vec2(-0.696890, -0.549791), + vec2(-0.149693, 0.605762), + vec2(0.034211, 0.979980), + vec2(0.503098, -0.308878), + vec2(-0.016205, -0.872921), + vec2(0.385784, -0.393902), + vec2(-0.146886, -0.859249), + vec2(0.643361, 0.164098), + vec2(0.634388, -0.049471), + vec2(-0.688894, 0.007843), + vec2(0.464034, -0.188818), + vec2(-0.440840, 0.137486), + vec2(0.364483, 0.511704), + vec2(0.034028, 0.325968), + vec2(0.099094, -0.308023), + vec2(0.693960, -0.366253), + vec2(0.678884, -0.204688), + vec2(0.001801, 0.780328), + vec2(0.145177, -0.898984), + vec2(0.062655, -0.611866), + vec2(0.315226, -0.604297), + vec2(-0.780145, 0.486251), + vec2(-0.371868, 0.882138), + vec2(0.200476, 0.494430), + vec2(-0.494552, -0.711051), + vec2(0.612476, 0.705252), + vec2(-0.578845, -0.768792), + vec2(-0.772454, -0.090976), + vec2(0.504440, 0.372295), + vec2(0.155736, 0.065157), + vec2(0.391522, 0.849605), + vec2(-0.620106, -0.328104), + vec2(0.789239, -0.419965), + vec2(-0.545396, 0.538133), + vec2(-0.178564, -0.596057) +); + +#ifdef COLORED_SHADOWS + +vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + if (radius < 0.1) { + // we are in the middle of even brightness, no need for filtering + return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); + } + + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES)); + int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); + int end_offset = int(samples) + init_offset; + + for (int x = init_offset; x < end_offset; x++) { + clampedpos = poissonDisk[x]; + perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); + clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / samples; +} + +#else + +float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + float visibility = 0.0; + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + if (radius < 0.1) { + // we are in the middle of even brightness, no need for filtering + return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); + } + + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES)); + int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); + int end_offset = int(samples) + init_offset; + + for (int x = init_offset; x < end_offset; x++) { + clampedpos = poissonDisk[x]; + perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); + clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / samples; +} + +#endif + +#else +/* poisson filter disabled */ + +#ifdef COLORED_SHADOWS + +vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + if (radius < 0.1) { + // we are in the middle of even brightness, no need for filtering + return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); + } + + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + float y, x; + float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + int n = 0; + + // basic PCF filter + for (y = -bound; y <= bound; y += 1.0) + for (x = -bound; x <= bound; x += 1.0) { + clampedpos = vec2(x,y); // screen offset + perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); + clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); + n += 1; + } + + return visibility / n; +} + +#else +float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + float visibility = 0.0; + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + if (radius < 0.1) { + // we are in the middle of even brightness, no need for filtering + return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); + } + + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + float y, x; + float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + int n = 0; + + // basic PCF filter + for (y = -bound; y <= bound; y += 1.0) + for (x = -bound; x <= bound; x += 1.0) { + clampedpos = vec2(x,y); // screen offset + perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); + clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); + n += 1; + } + + return visibility / n; +} + +#endif + +#endif +#endif + #if ENABLE_TONE_MAPPING /* Hable's UC2 Tone mapping parameters @@ -58,6 +451,8 @@ vec4 applyToneMapping(vec4 color) } #endif + + void main(void) { vec3 color; @@ -74,9 +469,41 @@ void main(void) #endif color = base.rgb; - vec4 col = vec4(color.rgb * varColor.rgb, 1.0); +#ifdef ENABLE_DYNAMIC_SHADOWS + float shadow_int = 0.0; + vec3 shadow_color = vec3(0.0, 0.0, 0.0); + vec3 posLightSpace = getLightSpacePosition(); + + float distance_rate = (1 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 20.0)); + float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z ),0.0); + + if (distance_rate > 1e-7) { + +#ifdef COLORED_SHADOWS + vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); + shadow_int = visibility.r; + shadow_color = visibility.gba; +#else + shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); +#endif + shadow_int *= distance_rate; + shadow_int *= 1.0 - nightRatio; + + + } + + if (f_normal_length != 0 && cosLight < 0.0) { + shadow_int = clamp(1.0-nightRatio, 0.0, 1.0); + } + + shadow_int = 1.0 - (shadow_int * f_adj_shadow_strength); + + col.rgb = mix(shadow_color,col.rgb,shadow_int)*shadow_int; + // col.r = 0.5 * clamp(getPenumbraRadius(ShadowMapSampler, posLightSpace.xy, posLightSpace.z, 1.0) / SOFTSHADOWRADIUS, 0.0, 1.0) + 0.5 * col.r; +#endif + #if ENABLE_TONE_MAPPING col = applyToneMapping(col); #endif @@ -94,6 +521,6 @@ void main(void) - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); col = mix(skyBgColor, col, clarity); col = vec4(col.rgb, base.a); - + gl_FragColor = col; } diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 1a4840d35..d316930b2 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,5 +1,4 @@ uniform mat4 mWorld; - // Color of the light emitted by the sun. uniform vec3 dayLight; uniform vec3 eyePosition; @@ -8,6 +7,7 @@ uniform vec3 eyePosition; uniform vec3 cameraOffset; uniform float animationTimer; +varying vec3 vNormal; varying vec3 vPosition; // World position in the visible world (i.e. relative to the cameraOffset.) // This can be used for many shader effects without loss of precision. @@ -24,13 +24,38 @@ varying mediump vec2 varTexCoord; #else centroid varying vec2 varTexCoord; #endif -varying vec3 eyeVec; +#ifdef ENABLE_DYNAMIC_SHADOWS + // shadow uniforms + uniform vec3 v_LightDirection; + uniform float f_textureresolution; + uniform mat4 m_ShadowViewProj; + uniform float f_shadowfar; + uniform float f_shadow_strength; + uniform float f_timeofday; + varying float cosLight; + varying float normalOffsetScale; + varying float adj_shadow_strength; + varying float f_normal_length; +#endif + +varying vec3 eyeVec; +varying float nightRatio; // Color of the light emitted by the light sources. const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; +#ifdef ENABLE_DYNAMIC_SHADOWS +// custom smoothstep implementation because it's not defined in glsl1.2 +// https://docs.gl/sl4/smoothstep +float mtsmoothstep(in float edge0, in float edge1, in float x) +{ + float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} +#endif + float smoothCurve(float x) { @@ -86,6 +111,9 @@ float snoise(vec3 p) #endif + + + void main(void) { varTexCoord = inTexCoord0.st; @@ -136,10 +164,9 @@ void main(void) gl_Position = mWorldViewProj * inVertexPosition; #endif - vPosition = gl_Position.xyz; - eyeVec = -(mWorldView * inVertexPosition).xyz; + vNormal = inVertexNormal; // Calculate color. // Red, green and blue components are pre-multiplied with @@ -152,7 +179,7 @@ void main(void) vec4 color = inVertexColor; #endif // The alpha gives the ratio of sunlight in the incoming light. - float nightRatio = 1.0 - color.a; + nightRatio = 1.0 - color.a; color.rgb = color.rgb * (color.a * dayLight.rgb + nightRatio * artificialLight.rgb) * 2.0; color.a = 1.0; @@ -164,4 +191,26 @@ void main(void) 0.07 * brightness); varColor = clamp(color, 0.0, 1.0); + +#ifdef ENABLE_DYNAMIC_SHADOWS + vec3 nNormal = normalize(vNormal); + cosLight = dot(nNormal, -v_LightDirection); + float texelSize = 767.0 / f_textureresolution; + float slopeScale = clamp(1.0 - abs(cosLight), 0.0, 1.0); + normalOffsetScale = texelSize * slopeScale; + + if (f_timeofday < 0.2) { + adj_shadow_strength = f_shadow_strength * 0.5 * + (1.0 - mtsmoothstep(0.18, 0.2, f_timeofday)); + } else if (f_timeofday >= 0.8) { + adj_shadow_strength = f_shadow_strength * 0.5 * + mtsmoothstep(0.8, 0.83, f_timeofday); + } else { + adj_shadow_strength = f_shadow_strength * + mtsmoothstep(0.20, 0.25, f_timeofday) * + (1.0 - mtsmoothstep(0.7, 0.8, f_timeofday)); + } + f_normal_length = length(vNormal); +#endif + } diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 9a81d8185..8d6f57a44 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -23,8 +23,22 @@ const float BS = 10.0; const float fogStart = FOG_START; const float fogShadingParameter = 1.0 / (1.0 - fogStart); -#if ENABLE_TONE_MAPPING +#ifdef ENABLE_DYNAMIC_SHADOWS + // shadow texture + uniform sampler2D ShadowMapSampler; + // shadow uniforms + uniform vec3 v_LightDirection; + uniform float f_textureresolution; + uniform mat4 m_ShadowViewProj; + uniform float f_shadowfar; + uniform float f_timeofday; + varying float normalOffsetScale; + varying float adj_shadow_strength; + varying float cosLight; + varying float f_normal_length; +#endif +#if ENABLE_TONE_MAPPING /* Hable's UC2 Tone mapping parameters A = 0.22; B = 0.30; @@ -55,11 +69,263 @@ vec4 applyToneMapping(vec4 color) } #endif +#ifdef ENABLE_DYNAMIC_SHADOWS +const float bias0 = 0.9; +const float zPersFactor = 0.5; +const float bias1 = 1.0 - bias0; + +vec4 getPerspectiveFactor(in vec4 shadowPosition) +{ + float pDistance = length(shadowPosition.xy); + float pFactor = pDistance * bias0 + bias1; + shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor); + + return shadowPosition; +} + +// assuming near is always 1.0 +float getLinearDepth() +{ + return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0)); +} + +vec3 getLightSpacePosition() +{ + vec4 pLightSpace; + float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale; + pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0); + pLightSpace = getPerspectiveFactor(pLightSpace); + return pLightSpace.xyz * 0.5 + 0.5; +} + +#ifdef COLORED_SHADOWS + +// c_precision of 128 fits within 7 base-10 digits +const float c_precision = 128.0; +const float c_precisionp1 = c_precision + 1.0; + +float packColor(vec3 color) +{ + return floor(color.b * c_precision + 0.5) + + floor(color.g * c_precision + 0.5) * c_precisionp1 + + floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1; +} + +vec3 unpackColor(float value) +{ + vec3 color; + color.b = mod(value, c_precisionp1) / c_precision; + color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision; + color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision; + return color; +} + +vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba; + + float visibility = step(0.0, (realDistance-2e-5) - texDepth.r); + vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g)); + if (visibility < 0.1) { + visibility = step(0.0, (realDistance-2e-5) - texDepth.r); + result = vec4(visibility, unpackColor(texDepth.a)); + } + return result; +} + +#else + +float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + float texDepth = texture2D(shadowsampler, smTexCoord.xy).r; + float visibility = step(0.0, (realDistance-2e-5) - texDepth); + + return visibility; +} + +#endif + +#if SHADOW_FILTER == 2 + #define PCFBOUND 3.5 + #define PCFSAMPLES 64.0 +#elif SHADOW_FILTER == 1 + #define PCFBOUND 1.5 + #if defined(POISSON_FILTER) + #define PCFSAMPLES 32.0 + #else + #define PCFSAMPLES 16.0 + #endif +#else + #define PCFBOUND 0.0 + #if defined(POISSON_FILTER) + #define PCFSAMPLES 4.0 + #else + #define PCFSAMPLES 1.0 + #endif +#endif + +#ifdef POISSON_FILTER +const vec2[64] poissonDisk = vec2[64]( + vec2(0.170019, -0.040254), + vec2(-0.299417, 0.791925), + vec2(0.645680, 0.493210), + vec2(-0.651784, 0.717887), + vec2(0.421003, 0.027070), + vec2(-0.817194, -0.271096), + vec2(-0.705374, -0.668203), + vec2(0.977050, -0.108615), + vec2(0.063326, 0.142369), + vec2(0.203528, 0.214331), + vec2(-0.667531, 0.326090), + vec2(-0.098422, -0.295755), + vec2(-0.885922, 0.215369), + vec2(0.566637, 0.605213), + vec2(0.039766, -0.396100), + vec2(0.751946, 0.453352), + vec2(0.078707, -0.715323), + vec2(-0.075838, -0.529344), + vec2(0.724479, -0.580798), + vec2(0.222999, -0.215125), + vec2(-0.467574, -0.405438), + vec2(-0.248268, -0.814753), + vec2(0.354411, -0.887570), + vec2(0.175817, 0.382366), + vec2(0.487472, -0.063082), + vec2(0.355476, 0.025357), + vec2(-0.084078, 0.898312), + vec2(0.488876, -0.783441), + vec2(0.470016, 0.217933), + vec2(-0.696890, -0.549791), + vec2(-0.149693, 0.605762), + vec2(0.034211, 0.979980), + vec2(0.503098, -0.308878), + vec2(-0.016205, -0.872921), + vec2(0.385784, -0.393902), + vec2(-0.146886, -0.859249), + vec2(0.643361, 0.164098), + vec2(0.634388, -0.049471), + vec2(-0.688894, 0.007843), + vec2(0.464034, -0.188818), + vec2(-0.440840, 0.137486), + vec2(0.364483, 0.511704), + vec2(0.034028, 0.325968), + vec2(0.099094, -0.308023), + vec2(0.693960, -0.366253), + vec2(0.678884, -0.204688), + vec2(0.001801, 0.780328), + vec2(0.145177, -0.898984), + vec2(0.062655, -0.611866), + vec2(0.315226, -0.604297), + vec2(-0.780145, 0.486251), + vec2(-0.371868, 0.882138), + vec2(0.200476, 0.494430), + vec2(-0.494552, -0.711051), + vec2(0.612476, 0.705252), + vec2(-0.578845, -0.768792), + vec2(-0.772454, -0.090976), + vec2(0.504440, 0.372295), + vec2(0.155736, 0.065157), + vec2(0.391522, 0.849605), + vec2(-0.620106, -0.328104), + vec2(0.789239, -0.419965), + vec2(-0.545396, 0.538133), + vec2(-0.178564, -0.596057) +); + +#ifdef COLORED_SHADOWS + +vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + vec4 visibility = vec4(0.0); + + float texture_size = 1.0 / (f_textureresolution * 0.5); + int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES))); + int end_offset = int(PCFSAMPLES) + init_offset; + + for (int x = init_offset; x < end_offset; x++) { + clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy; + visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / PCFSAMPLES; +} + +#else + +float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + float visibility = 0.0; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES))); + int end_offset = int(PCFSAMPLES) + init_offset; + + for (int x = init_offset; x < end_offset; x++) { + clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy; + visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / PCFSAMPLES; +} + +#endif + +#else +/* poisson filter disabled */ + +#ifdef COLORED_SHADOWS + +vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float sradius=0.0; + if( PCFBOUND>0) + sradius = SOFTSHADOWRADIUS / PCFBOUND; + float texture_size = 1.0 / (f_textureresolution * 0.5); + float y, x; + // basic PCF filter + for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0) + for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) { + clampedpos = vec2(x,y) * texture_size* sradius + smTexCoord.xy; + visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / PCFSAMPLES; +} + +#else +float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) +{ + vec2 clampedpos; + float visibility = 0.0; + float sradius=0.0; + if( PCFBOUND>0) + sradius = SOFTSHADOWRADIUS / PCFBOUND; + + float texture_size = 1.0 / (f_textureresolution * 0.5); + float y, x; + // basic PCF filter + for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0) + for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) { + clampedpos = vec2(x,y) * texture_size * sradius + smTexCoord.xy; + visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); + } + + return visibility / PCFSAMPLES; +} + +#endif + +#endif +#endif + void main(void) { vec3 color; vec2 uv = varTexCoord.st; - vec4 base = texture2D(baseTexture, uv).rgba; #ifdef USE_DISCARD @@ -72,13 +338,34 @@ void main(void) #endif color = base.rgb; - vec4 col = vec4(color.rgb, base.a); - col.rgb *= varColor.rgb; - col.rgb *= emissiveColor.rgb * vIDiff; +#ifdef ENABLE_DYNAMIC_SHADOWS + float shadow_int = 0.0; + vec3 shadow_color = vec3(0.0, 0.0, 0.0); + vec3 posLightSpace = getLightSpacePosition(); + +#ifdef COLORED_SHADOWS + vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); + shadow_int = visibility.r; + shadow_color = visibility.gba; +#else + shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); +#endif + + if (f_normal_length != 0 && cosLight <= 0.001) { + shadow_int = clamp(shadow_int + 0.5 * abs(cosLight), 0.0, 1.0); + } + + shadow_int = 1.0 - (shadow_int * adj_shadow_strength); + + col.rgb = mix(shadow_color, col.rgb, shadow_int) * shadow_int; +#endif + + + #if ENABLE_TONE_MAPPING col = applyToneMapping(col); #endif diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index f26224e82..f135ab9dc 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -13,12 +13,37 @@ varying mediump vec2 varTexCoord; centroid varying vec2 varTexCoord; #endif +#ifdef ENABLE_DYNAMIC_SHADOWS + // shadow uniforms + uniform vec3 v_LightDirection; + uniform float f_textureresolution; + uniform mat4 m_ShadowViewProj; + uniform float f_shadowfar; + uniform float f_shadow_strength; + uniform float f_timeofday; + varying float cosLight; + varying float normalOffsetScale; + varying float adj_shadow_strength; + varying float f_normal_length; +#endif + varying vec3 eyeVec; varying float vIDiff; const float e = 2.718281828459; const float BS = 10.0; +#ifdef ENABLE_DYNAMIC_SHADOWS +// custom smoothstep implementation because it's not defined in glsl1.2 +// https://docs.gl/sl4/smoothstep +float mtsmoothstep(in float edge0, in float edge1, in float x) +{ + float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} +#endif + + float directional_ambient(vec3 normal) { vec3 v = normal * normal; @@ -54,4 +79,25 @@ void main(void) #else varColor = inVertexColor; #endif + +#ifdef ENABLE_DYNAMIC_SHADOWS + + cosLight = max(0.0, dot(vNormal, -v_LightDirection)); + float texelSize = 0.51; + float slopeScale = clamp(1.0 - cosLight, 0.0, 1.0); + normalOffsetScale = texelSize * slopeScale; + if (f_timeofday < 0.2) { + adj_shadow_strength = f_shadow_strength * 0.5 * + (1.0 - mtsmoothstep(0.18, 0.2, f_timeofday)); + } else if (f_timeofday >= 0.8) { + adj_shadow_strength = f_shadow_strength * 0.5 * + mtsmoothstep(0.8, 0.83, f_timeofday); + } else { + adj_shadow_strength = f_shadow_strength * + mtsmoothstep(0.20, 0.25, f_timeofday) * + (1.0 - mtsmoothstep(0.7, 0.8, f_timeofday)); + } + f_normal_length = length(vNormal); + +#endif } diff --git a/client/shaders/shadow_shaders/pass1_fragment.glsl b/client/shaders/shadow_shaders/pass1_fragment.glsl new file mode 100644 index 000000000..2105def96 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_fragment.glsl @@ -0,0 +1,13 @@ +uniform sampler2D ColorMapSampler; +varying vec4 tPos; + +void main() +{ + vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st); + + if (col.a < 0.70) + discard; + + float depth = 0.5 + tPos.z * 0.5; + gl_FragColor = vec4(depth, 0.0, 0.0, 1.0); +} diff --git a/client/shaders/shadow_shaders/pass1_trans_fragment.glsl b/client/shaders/shadow_shaders/pass1_trans_fragment.glsl new file mode 100644 index 000000000..9f9e5be8c --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_trans_fragment.glsl @@ -0,0 +1,38 @@ +uniform sampler2D ColorMapSampler; +varying vec4 tPos; + +#ifdef COLORED_SHADOWS +// c_precision of 128 fits within 7 base-10 digits +const float c_precision = 128.0; +const float c_precisionp1 = c_precision + 1.0; + +float packColor(vec3 color) +{ + return floor(color.b * c_precision + 0.5) + + floor(color.g * c_precision + 0.5) * c_precisionp1 + + floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1; +} + +const vec3 black = vec3(0.0); +#endif + +void main() +{ + vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st); +#ifndef COLORED_SHADOWS + if (col.a < 0.5) + discard; +#endif + + float depth = 0.5 + tPos.z * 0.5; + // ToDo: Liso: Apply movement on waving plants + // depth in [0, 1] for texture + + //col.rgb = col.a == 1.0 ? vec3(1.0) : col.rgb; +#ifdef COLORED_SHADOWS + float packedColor = packColor(mix(col.rgb, black, col.a)); + gl_FragColor = vec4(depth, packedColor, 0.0,1.0); +#else + gl_FragColor = vec4(depth, 0.0, 0.0, 1.0); +#endif +} diff --git a/client/shaders/shadow_shaders/pass1_trans_vertex.glsl b/client/shaders/shadow_shaders/pass1_trans_vertex.glsl new file mode 100644 index 000000000..ca59f2796 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_trans_vertex.glsl @@ -0,0 +1,26 @@ +uniform mat4 LightMVP; // world matrix +varying vec4 tPos; + +const float bias0 = 0.9; +const float zPersFactor = 0.5; +const float bias1 = 1.0 - bias0 + 1e-6; + +vec4 getPerspectiveFactor(in vec4 shadowPosition) +{ + float pDistance = length(shadowPosition.xy); + float pFactor = pDistance * bias0 + bias1; + shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor); + + return shadowPosition; +} + + +void main() +{ + vec4 pos = LightMVP * gl_Vertex; + + tPos = getPerspectiveFactor(LightMVP * gl_Vertex); + + gl_Position = vec4(tPos.xyz, 1.0); + gl_TexCoord[0].st = gl_MultiTexCoord0.st; +} diff --git a/client/shaders/shadow_shaders/pass1_vertex.glsl b/client/shaders/shadow_shaders/pass1_vertex.glsl new file mode 100644 index 000000000..a6d8b3db8 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_vertex.glsl @@ -0,0 +1,26 @@ +uniform mat4 LightMVP; // world matrix +varying vec4 tPos; + +const float bias0 = 0.9; +const float zPersFactor = 0.5; +const float bias1 = 1.0 - bias0 + 1e-6; + +vec4 getPerspectiveFactor(in vec4 shadowPosition) +{ + float pDistance = length(shadowPosition.xy); + float pFactor = pDistance * bias0 + bias1; + shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor); + + return shadowPosition; +} + + +void main() +{ + vec4 pos = LightMVP * gl_Vertex; + + tPos = getPerspectiveFactor(pos); + + gl_Position = vec4(tPos.xyz, 1.0); + gl_TexCoord[0].st = gl_MultiTexCoord0.st; +} diff --git a/client/shaders/shadow_shaders/pass2_fragment.glsl b/client/shaders/shadow_shaders/pass2_fragment.glsl new file mode 100644 index 000000000..00b4f9f6c --- /dev/null +++ b/client/shaders/shadow_shaders/pass2_fragment.glsl @@ -0,0 +1,23 @@ +uniform sampler2D ShadowMapClientMap; +#ifdef COLORED_SHADOWS +uniform sampler2D ShadowMapClientMapTraslucent; +#endif +uniform sampler2D ShadowMapSamplerdynamic; + +void main() { + +#ifdef COLORED_SHADOWS + vec2 first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).rg; + vec2 depth_splitdynamics = vec2(texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r, 0.0); + if (first_depth.r > depth_splitdynamics.r) + first_depth = depth_splitdynamics; + vec2 depth_color = texture2D(ShadowMapClientMapTraslucent, gl_TexCoord[1].st).rg; + gl_FragColor = vec4(first_depth.r, first_depth.g, depth_color.r, depth_color.g); +#else + float first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).r; + float depth_splitdynamics = texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r; + first_depth = min(first_depth, depth_splitdynamics); + gl_FragColor = vec4(first_depth, 0.0, 0.0, 1.0); +#endif + +} diff --git a/client/shaders/shadow_shaders/pass2_vertex.glsl b/client/shaders/shadow_shaders/pass2_vertex.glsl new file mode 100644 index 000000000..ac445c9c7 --- /dev/null +++ b/client/shaders/shadow_shaders/pass2_vertex.glsl @@ -0,0 +1,9 @@ + +void main() +{ + vec4 uv = vec4(gl_Vertex.xyz, 1.0) * 0.5 + 0.5; + gl_TexCoord[0] = uv; + gl_TexCoord[1] = uv; + gl_TexCoord[2] = uv; + gl_Position = vec4(gl_Vertex.xyz, 1.0); +} 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 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 §or_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 m_drawlist; + std::map m_drawlist_shadow; std::set 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 #include #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 #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> getSupportedVideoModes(); static std::vector 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 m_world_view_proj; CachedVertexShaderSetting m_world; + + // Shadow-related + CachedPixelShaderSetting m_shadow_view_proj; + CachedPixelShaderSetting m_light_direction; + CachedPixelShaderSetting m_texture_res; + CachedPixelShaderSetting m_shadow_strength; + CachedPixelShaderSetting m_time_of_day; + CachedPixelShaderSetting m_shadowfar; + CachedPixelShaderSetting m_shadow_texture; + #if ENABLE_GLES // Modelview matrix CachedVertexShaderSetting 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 + +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 + +#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 + +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 +#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 + +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 +#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(0, 50, 128, 128 + 50), + core::rect({0, 0}, shadowMapTextureFinal->getSize())); + + m_driver->draw2DImage(shadowMapClientMap, + core::rect(0, 50 + 128, 128, 128 + 50 + 128), + core::rect({0, 0}, shadowMapTextureFinal->getSize())); + m_driver->draw2DImage(shadowMapTextureDynamicObjects, + core::rect(0, 128 + 50 + 128, 128, + 128 + 50 + 128 + 128), + core::rect({0, 0}, shadowMapTextureDynamicObjects->getSize())); + + if (m_shadow_map_colored) { + + m_driver->draw2DImage(shadowMapTextureColors, + core::rect(128,128 + 50 + 128 + 128, + 128 + 128, 128 + 50 + 128 + 128 + 128), + core::rect({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(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 BufferMaterialList; + std::vector> 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 ¤t_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 ¤t_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 + +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 +#include +#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 m_light_list; + std::vector 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 + +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 + +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 +#include + +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 + +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 + +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 +#include + +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="< " - <<"wicked_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 #include +#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); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 871290944..38cade80b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -262,6 +262,18 @@ void set_default_settings() settings->setDefault("enable_waving_leaves", "false"); settings->setDefault("enable_waving_plants", "false"); + // Effects Shadows + settings->setDefault("enable_dynamic_shadows", "false"); + settings->setDefault("shadow_strength", "0.2"); + settings->setDefault("shadow_map_max_distance", "200.0"); + settings->setDefault("shadow_map_texture_size", "2048"); + settings->setDefault("shadow_map_texture_32bit", "true"); + 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_soft_radius", "1.0"); + settings->setDefault("shadow_sky_body_orbit_tilt", "0.0"); // Input settings->setDefault("invert_mouse", "false"); -- cgit v1.2.3 From a049e8267fabd101cb5c6528b3270214cb0647f0 Mon Sep 17 00:00:00 2001 From: hecks <42101236+hecktest@users.noreply.github.com> Date: Thu, 22 Jul 2021 00:55:20 +0200 Subject: Remove unused ITextSceneNode header (#11476) Co-authored-by: hecktest <> --- src/client/content_cao.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/client/content_cao.cpp') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 9216f0010..c3ac613a5 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cao.h" #include #include -#include #include #include #include "client/client.h" -- cgit v1.2.3 From 328d9492256e600159d2e04987e493b3a269a067 Mon Sep 17 00:00:00 2001 From: Lean Rada Date: Mon, 16 Aug 2021 23:56:38 +0800 Subject: Start sprite animation at the beginning (#11509) When setting a sprite animation, do not keep the last animation's frame number. Setting a new animation should start the animation at the start of the new animation. --- src/client/content_cao.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/content_cao.cpp') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index c3ac613a5..83c8e15d4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1722,6 +1722,7 @@ void GenericCAO::processMessage(const std::string &data) m_tx_basepos = p; m_anim_num_frames = num_frames; + m_anim_frame = 0; m_anim_framelength = framelength; m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch; -- cgit v1.2.3 From 1d69a23ba48d99b051dcf2a6be225edd7c644c7b Mon Sep 17 00:00:00 2001 From: NeroBurner Date: Fri, 27 Aug 2021 20:24:24 +0200 Subject: Joystick sensitivity for player movement (#11262) This commit deprecates the forward, backward, left, and right binary inputs currently used for player movement in the PlayerControl struct. In their place, it adds the movement_speed and movement_direction values, which represents the player movement is a polar coordinate system. movement_speed is a scalar from 0.0 to 1.0. movement_direction is an angle from 0 to +-Pi: FWD 0 _ LFT / \ RGT -Pi/2 | | +Pi/2 \_/ +-Pi BCK Boolean movement bits will still be set for server telegrams and Lua script invocations to provide full backward compatibility. When generating these values from an analog input, a direction is considered active when it is 22.5 degrees away from either orthogonal axis. Co-authored-by: Markus Koch Co-authored-by: sfan5 --- src/client/content_cao.cpp | 7 ++-- src/client/game.cpp | 77 +++++++++++++++++++----------------- src/client/inputhandler.h | 43 ++++++++++++++++++++ src/client/joystick_controller.cpp | 24 +++++++++-- src/client/joystick_controller.h | 5 ++- src/client/localplayer.cpp | 24 ++--------- src/network/serverpackethandler.cpp | 4 -- src/player.h | 25 ++++-------- src/script/lua_api/l_localplayer.cpp | 20 ++++++---- src/script/lua_api/l_object.cpp | 8 ++-- 10 files changed, 138 insertions(+), 99 deletions(-) (limited to 'src/client/content_cao.cpp') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 83c8e15d4..da78cae7c 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -998,9 +998,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) const PlayerControl &controls = player->getPlayerControl(); bool walking = false; - if (controls.up || controls.down || controls.left || controls.right || - controls.forw_move_joystick_axis != 0.f || - controls.sidew_move_joystick_axis != 0.f) + if (controls.movement_speed > 0.001f) walking = true; f32 new_speed = player->local_animation_speed; @@ -1015,9 +1013,10 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly")))) new_speed *= 1.5; - // slowdown speed if sneeking + // slowdown speed if sneaking if (controls.sneak && walking) new_speed /= 2; + new_speed *= controls.movement_speed; if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; diff --git a/src/client/game.cpp b/src/client/game.cpp index 011875e4a..18df5cc58 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2460,7 +2460,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) if (m_cache_enable_joysticks) { f32 sens_scale = getSensitivityScaleFactor(); - f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime * sens_scale; + f32 c = m_cache_joystick_frustum_sensitivity * dtime * sens_scale; cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c; cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c; } @@ -2471,18 +2471,12 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) void Game::updatePlayerControl(const CameraOrientation &cam) { - //TimeTaker tt("update player control", NULL, PRECISION_NANO); + LocalPlayer *player = client->getEnv().getLocalPlayer(); - // DO NOT use the isKeyDown method for the forward, backward, left, right - // buttons, as the code that uses the controls needs to be able to - // distinguish between the two in order to know when to use joysticks. + //TimeTaker tt("update player control", NULL, PRECISION_NANO); PlayerControl control( - input->isKeyDown(KeyType::FORWARD), - input->isKeyDown(KeyType::BACKWARD), - input->isKeyDown(KeyType::LEFT), - input->isKeyDown(KeyType::RIGHT), - isKeyDown(KeyType::JUMP), + isKeyDown(KeyType::JUMP) || player->getAutojump(), isKeyDown(KeyType::AUX1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), @@ -2490,22 +2484,16 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::PLACE), cam.camera_pitch, cam.camera_yaw, - input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), - input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) + input->getMovementSpeed(), + input->getMovementDirection() ); - u32 keypress_bits = ( - ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | - ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | - ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | - ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | - ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | - ( (u32)(isKeyDown(KeyType::AUX1) & 0x1) << 5) | - ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | - ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | - ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | - ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) - ); + // autoforward if set: move towards pointed position at maximum speed + if (player->getPlayerSettings().continuous_forward && + client->activeObjectsReceived() && !player->isDead()) { + control.movement_speed = 1.0f; + control.movement_direction = 0.0f; + } #ifdef ANDROID /* For Android, simulate holding down AUX1 (fast move) if the user has @@ -2515,23 +2503,38 @@ void Game::updatePlayerControl(const CameraOrientation &cam) */ if (m_cache_hold_aux1) { control.aux1 = control.aux1 ^ true; - keypress_bits ^= ((u32)(1U << 5)); } #endif - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - // autojump if set: simulate "jump" key - if (player->getAutojump()) { - control.jump = true; - keypress_bits |= 1U << 4; - } + u32 keypress_bits = ( + ( (u32)(control.jump & 0x1) << 4) | + ( (u32)(control.aux1 & 0x1) << 5) | + ( (u32)(control.sneak & 0x1) << 6) | + ( (u32)(control.dig & 0x1) << 7) | + ( (u32)(control.place & 0x1) << 8) | + ( (u32)(control.zoom & 0x1) << 9) + ); - // autoforward if set: simulate "up" key - if (player->getPlayerSettings().continuous_forward && - client->activeObjectsReceived() && !player->isDead()) { - control.up = true; - keypress_bits |= 1U << 0; + // Set direction keys to ensure mod compatibility + if (control.movement_speed > 0.001f) { + float absolute_direction; + + // Check in original orientation (absolute value indicates forward / backward) + absolute_direction = abs(control.movement_direction); + if (absolute_direction < (3.0f / 8.0f * M_PI)) + keypress_bits |= (u32)(0x1 << 0); // Forward + if (absolute_direction > (5.0f / 8.0f * M_PI)) + keypress_bits |= (u32)(0x1 << 1); // Backward + + // Rotate entire coordinate system by 90 degrees (absolute value indicates left / right) + absolute_direction = control.movement_direction + M_PI_2; + if (absolute_direction >= M_PI) + absolute_direction -= 2 * M_PI; + absolute_direction = abs(absolute_direction); + if (absolute_direction < (3.0f / 8.0f * M_PI)) + keypress_bits |= (u32)(0x1 << 2); // Left + if (absolute_direction > (5.0f / 8.0f * M_PI)) + keypress_bits |= (u32)(0x1 << 3); // Right } client->setPlayerControl(control); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 1fb4cf0ec..76e3c7b5b 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -240,6 +240,9 @@ public: virtual bool wasKeyReleased(GameKeyType k) = 0; virtual bool cancelPressed() = 0; + virtual float getMovementSpeed() = 0; + virtual float getMovementDirection() = 0; + virtual void clearWasKeyPressed() {} virtual void clearWasKeyReleased() {} @@ -285,6 +288,44 @@ public: { return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k); } + virtual float getMovementSpeed() + { + bool f = m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]), + b = m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]), + l = m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]), + r = m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]); + if (f || b || l || r) + { + // if contradictory keys pressed, stay still + if (f && b && l && r) + return 0.0f; + else if (f && b && !l && !r) + return 0.0f; + else if (!f && !b && l && r) + return 0.0f; + return 1.0f; // If there is a keyboard event, assume maximum speed + } + return joystick.getMovementSpeed(); + } + virtual float getMovementDirection() + { + float x = 0, z = 0; + + /* Check keyboard for input */ + if (m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD])) + z += 1; + if (m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD])) + z -= 1; + if (m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT])) + x += 1; + if (m_receiver->IsKeyDown(keycache.key[KeyType::LEFT])) + x -= 1; + + if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */ + return atan2(x, z); + else + return joystick.getMovementDirection(); + } virtual bool cancelPressed() { return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey); @@ -352,6 +393,8 @@ public: virtual bool wasKeyPressed(GameKeyType k) { return false; } virtual bool wasKeyReleased(GameKeyType k) { return false; } virtual bool cancelPressed() { return false; } + virtual float getMovementSpeed() {return 0.0f;} + virtual float getMovementDirection() {return 0.0f;} virtual v2s32 getMousePos() { return mousepos; } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index 919db5315..630565d8d 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -160,6 +160,7 @@ JoystickController::JoystickController() : for (float &i : m_past_pressed_time) { i = 0; } + m_layout.axes_deadzone = 0; clear(); } @@ -251,10 +252,27 @@ void JoystickController::clear() memset(m_axes_vals, 0, sizeof(m_axes_vals)); } -s16 JoystickController::getAxisWithoutDead(JoystickAxis axis) +float JoystickController::getAxisWithoutDead(JoystickAxis axis) { s16 v = m_axes_vals[axis]; + if (abs(v) < m_layout.axes_deadzone) - return 0; - return v; + return 0.0f; + + v += (v < 0 ? m_layout.axes_deadzone : -m_layout.axes_deadzone); + + return (float)v / ((float)(INT16_MAX - m_layout.axes_deadzone)); +} + +float JoystickController::getMovementDirection() +{ + return atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE), -getAxisWithoutDead(JA_FORWARD_MOVE)); +} + +float JoystickController::getMovementSpeed() +{ + float speed = sqrt(pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) + pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2)); + if (speed > 1.0f) + speed = 1.0f; + return speed; } diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index 3f361e4ef..cbc60886c 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -144,7 +144,10 @@ public: return m_axes_vals[axis]; } - s16 getAxisWithoutDead(JoystickAxis axis); + float getAxisWithoutDead(JoystickAxis axis); + + float getMovementDirection(); + float getMovementSpeed(); f32 doubling_dtime; diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index f3eb1a2dd..2d4f7305a 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -566,23 +566,7 @@ void LocalPlayer::applyControl(float dtime, Environment *env) } } - if (control.up) - speedH += v3f(0.0f, 0.0f, 1.0f); - - if (control.down) - speedH -= v3f(0.0f, 0.0f, 1.0f); - - if (!control.up && !control.down) - speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f); - - if (control.left) - speedH += v3f(-1.0f, 0.0f, 0.0f); - - if (control.right) - speedH += v3f(1.0f, 0.0f, 0.0f); - - if (!control.left && !control.right) - speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f); + speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction)); if (m_autojump) { // release autojump after a given time @@ -639,6 +623,8 @@ void LocalPlayer::applyControl(float dtime, Environment *env) else speedH = speedH.normalize() * movement_speed_walk; + speedH *= control.movement_speed; /* Apply analog input */ + // Acceleration increase f32 incH = 0.0f; // Horizontal (X, Z) f32 incV = 0.0f; // Vertical (Y) @@ -1106,9 +1092,7 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, if (m_autojump) return; - bool control_forward = control.up || - (!control.up && !control.down && - control.forw_move_joystick_axis < -0.05f); + bool control_forward = keyPressed & (1 << 0); bool could_autojump = m_can_jump && !control.jump && !control.sneak && control_forward; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 708ddbf20..dc5864be3 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -510,10 +510,6 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, playersao->setWantedRange(wanted_range); player->keyPressed = keyPressed; - player->control.up = (keyPressed & (0x1 << 0)); - player->control.down = (keyPressed & (0x1 << 1)); - player->control.left = (keyPressed & (0x1 << 2)); - player->control.right = (keyPressed & (0x1 << 3)); player->control.jump = (keyPressed & (0x1 << 4)); player->control.aux1 = (keyPressed & (0x1 << 5)); player->control.sneak = (keyPressed & (0x1 << 6)); diff --git a/src/player.h b/src/player.h index ec068a8b1..3800e1a33 100644 --- a/src/player.h +++ b/src/player.h @@ -49,10 +49,6 @@ struct PlayerControl PlayerControl() = default; PlayerControl( - bool a_up, - bool a_down, - bool a_left, - bool a_right, bool a_jump, bool a_aux1, bool a_sneak, @@ -61,14 +57,10 @@ struct PlayerControl bool a_place, float a_pitch, float a_yaw, - float a_sidew_move_joystick_axis, - float a_forw_move_joystick_axis + float a_movement_speed, + float a_movement_direction ) { - up = a_up; - down = a_down; - left = a_left; - right = a_right; jump = a_jump; aux1 = a_aux1; sneak = a_sneak; @@ -77,13 +69,9 @@ struct PlayerControl place = a_place; pitch = a_pitch; yaw = a_yaw; - sidew_move_joystick_axis = a_sidew_move_joystick_axis; - forw_move_joystick_axis = a_forw_move_joystick_axis; + movement_speed = a_movement_speed; + movement_direction = a_movement_direction; } - bool up = false; - bool down = false; - bool left = false; - bool right = false; bool jump = false; bool aux1 = false; bool sneak = false; @@ -92,8 +80,9 @@ struct PlayerControl bool place = false; float pitch = 0.0f; float yaw = 0.0f; - float sidew_move_joystick_axis = 0.0f; - float forw_move_joystick_axis = 0.0f; + // Note: These two are NOT available on the server + float movement_speed = 0.0f; + float movement_direction = 0.0f; }; struct PlayerSettings diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 77a692f08..9f3569ecc 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -223,16 +223,20 @@ int LuaLocalPlayer::l_get_control(lua_State *L) }; lua_createtable(L, 0, 12); - set("up", c.up); - set("down", c.down); - set("left", c.left); - set("right", c.right); - set("jump", c.jump); - set("aux1", c.aux1); + set("jump", c.jump); + set("aux1", c.aux1); set("sneak", c.sneak); - set("zoom", c.zoom); - set("dig", c.dig); + set("zoom", c.zoom); + set("dig", c.dig); set("place", c.place); + // Player movement in polar coordinates and non-binary speed + set("movement_speed", c.movement_speed); + set("movement_direction", c.movement_direction); + // Provide direction keys to ensure compatibility + set("up", player->keyPressed & (1 << 0)); // Up, down, left, and right were removed in favor of + set("down", player->keyPressed & (1 << 1)); // analog direction indicators and are therefore not + set("left", player->keyPressed & (1 << 2)); // available as booleans anymore. The corresponding values + set("right", player->keyPressed & (1 << 3)); // can still be read from the keyPressed bits though. return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index c915fa9e1..c8fa7d806 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1392,13 +1392,13 @@ int ObjectRef::l_get_player_control(lua_State *L) const PlayerControl &control = player->getPlayerControl(); lua_newtable(L); - lua_pushboolean(L, control.up); + lua_pushboolean(L, player->keyPressed & (1 << 0)); lua_setfield(L, -2, "up"); - lua_pushboolean(L, control.down); + lua_pushboolean(L, player->keyPressed & (1 << 1)); lua_setfield(L, -2, "down"); - lua_pushboolean(L, control.left); + lua_pushboolean(L, player->keyPressed & (1 << 2)); lua_setfield(L, -2, "left"); - lua_pushboolean(L, control.right); + lua_pushboolean(L, player->keyPressed & (1 << 3)); lua_setfield(L, -2, "right"); lua_pushboolean(L, control.jump); lua_setfield(L, -2, "jump"); -- cgit v1.2.3