]> git.lizzy.rs Git - minetest.git/commitdiff
Shadow mapping render pass (#11244)
authorLiso <anlismon@gmail.com>
Sun, 6 Jun 2021 16:51:21 +0000 (18:51 +0200)
committerGitHub <noreply@github.com>
Sun, 6 Jun 2021 16:51:21 +0000 (18:51 +0200)
Co-authored-by: x2048 <codeforsmile@gmail.com>
35 files changed:
builtin/mainmenu/tab_settings.lua
builtin/settingtypes.txt
client/shaders/nodes_shader/opengl_fragment.glsl
client/shaders/nodes_shader/opengl_vertex.glsl
client/shaders/object_shader/opengl_fragment.glsl
client/shaders/object_shader/opengl_vertex.glsl
client/shaders/shadow_shaders/pass1_fragment.glsl [new file with mode: 0644]
client/shaders/shadow_shaders/pass1_trans_fragment.glsl [new file with mode: 0644]
client/shaders/shadow_shaders/pass1_trans_vertex.glsl [new file with mode: 0644]
client/shaders/shadow_shaders/pass1_vertex.glsl [new file with mode: 0644]
client/shaders/shadow_shaders/pass2_fragment.glsl [new file with mode: 0644]
client/shaders/shadow_shaders/pass2_vertex.glsl [new file with mode: 0644]
src/client/CMakeLists.txt
src/client/clientmap.cpp
src/client/clientmap.h
src/client/content_cao.cpp
src/client/game.cpp
src/client/mapblock_mesh.cpp
src/client/render/core.cpp
src/client/render/core.h
src/client/renderingengine.h
src/client/shader.cpp
src/client/shadows/dynamicshadows.cpp [new file with mode: 0644]
src/client/shadows/dynamicshadows.h [new file with mode: 0644]
src/client/shadows/dynamicshadowsrender.cpp [new file with mode: 0644]
src/client/shadows/dynamicshadowsrender.h [new file with mode: 0644]
src/client/shadows/shadowsScreenQuad.cpp [new file with mode: 0644]
src/client/shadows/shadowsScreenQuad.h [new file with mode: 0644]
src/client/shadows/shadowsshadercallbacks.cpp [new file with mode: 0644]
src/client/shadows/shadowsshadercallbacks.h [new file with mode: 0644]
src/client/sky.cpp
src/client/sky.h
src/client/wieldmesh.cpp
src/client/wieldmesh.h
src/defaultsettings.cpp

index 29744048aab7ca854f558d2fa4619c20638f2730..8bc5bf8b6c894fc94507a74ecfd39995361bdc74 100644 (file)
@@ -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
 
index d13bac91bb4619e1bd9d99f07afc0fc53c5aa235..ae421de2cd45f8c8fd42744b1612d26ceb025974 100644 (file)
@@ -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
index b580950633da4a45a17cb94968abd8a960c2d59e..43a8b1f25a48c485ee0b429f1fc6d71e650cba5d 100644 (file)
@@ -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;
 }
index 1a4840d35850668e3cb87a16590cca0e1990427d..d316930b20e665ab6d9f747a7366b1c12bd17290 100644 (file)
@@ -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
+
 }
index 9a81d8185c9572b0b8e264d8be485ee33c3e31aa..8d6f57a44383323333cacd900a7c9dcf7cc0b004 100644 (file)
@@ -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
index f26224e82b6fc9ca1141c70a715d3204a26e1670..f135ab9dc6919f42236f1b0b5e06e0afe782f225 100644 (file)
@@ -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 (file)
index 0000000..2105def
--- /dev/null
@@ -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 (file)
index 0000000..9f9e5be
--- /dev/null
@@ -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 (file)
index 0000000..ca59f27
--- /dev/null
@@ -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 (file)
index 0000000..a6d8b3d
--- /dev/null
@@ -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 (file)
index 0000000..00b4f9f
--- /dev/null
@@ -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 (file)
index 0000000..ac445c9
--- /dev/null
@@ -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);
+}
index 140814911f47aa603ad9767fe5e7a7293dc7e24b..8d058852a90ca758781815e2b908a6fc966174b6 100644 (file)
@@ -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
 )
index 6dc535898f05b22323da1e2645663f4524e7bdb0..8b09eade1400c6ce5eafc1bbbfe7422976392231 100644 (file)
@@ -72,8 +72,15 @@ ClientMap::ClientMap(
        scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
                rendering_engine->get_scene_manager(), id),
        m_client(client),
+       m_rendering_engine(rendering_engine),
        m_control(control)
 {
+
+       /*
+        * @Liso: Sadly C++ doesn't have introspection, so the only way we have to know
+        * the class is whith a name ;) Name property cames from ISceneNode base class.
+        */
+       Name = "ClientMap";
        m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
                        BS*1000000,BS*1000000,BS*1000000);
 
@@ -115,12 +122,21 @@ void ClientMap::OnRegisterSceneNode()
        }
 
        ISceneNode::OnRegisterSceneNode();
+
+       if (!m_added_to_shadow_renderer) {
+               m_added_to_shadow_renderer = true;
+               if (auto shadows = m_rendering_engine->get_shadow_renderer())
+                       shadows->addNodeToShadowList(this);
+       }
 }
 
 void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
-               v3s16 *p_blocks_min, v3s16 *p_blocks_max)
+               v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range)
 {
-       v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1);
+       if (range <= 0.0f)
+               range = m_control.wanted_range;
+
+       v3s16 box_nodes_d = range * v3s16(1, 1, 1);
        // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
        // can exceed the range of v3s16 when a large view range is used near the
        // world edges.
@@ -321,7 +337,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
                // Mesh animation
                if (pass == scene::ESNRP_SOLID) {
-                       //MutexAutoLock lock(block->mesh_mutex);
                        MapBlockMesh *mapBlockMesh = block->mesh;
                        assert(mapBlockMesh);
                        // Pretty random but this should work somewhat nicely
@@ -342,8 +357,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        Get the meshbuffers of the block
                */
                {
-                       //MutexAutoLock lock(block->mesh_mutex);
-
                        MapBlockMesh *mapBlockMesh = block->mesh;
                        assert(mapBlockMesh);
 
@@ -394,6 +407,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                                "returning." << std::endl;
                                return;
                        }
+
+                       // pass the shadow map texture to the buffer texture
+                       ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
+                       if (shadow && shadow->is_active()) {
+                               auto &layer = list.m.TextureLayer[3];
+                               layer.Texture = shadow->get_texture();
+                               layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+                               layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+                               layer.TrilinearFilter = true;
+                       }
+
                        driver->setMaterial(list.m);
 
                        drawcall_count += list.bufs.size();
@@ -610,3 +634,187 @@ void ClientMap::PrintInfo(std::ostream &out)
 {
        out<<"ClientMap: ";
 }
+
+void ClientMap::renderMapShadows(video::IVideoDriver *driver,
+               const video::SMaterial &material, s32 pass)
+{
+       bool is_transparent_pass = pass != scene::ESNRP_SOLID;
+       std::string prefix;
+       if (is_transparent_pass)
+               prefix = "renderMap(SHADOW TRANS): ";
+       else
+               prefix = "renderMap(SHADOW SOLID): ";
+
+       u32 drawcall_count = 0;
+       u32 vertex_count = 0;
+
+       MeshBufListList drawbufs;
+
+       for (auto &i : m_drawlist_shadow) {
+               v3s16 block_pos = i.first;
+               MapBlock *block = i.second;
+
+               // If the mesh of the block happened to get deleted, ignore it
+               if (!block->mesh)
+                       continue;
+
+               /*
+                       Get the meshbuffers of the block
+               */
+               {
+                       MapBlockMesh *mapBlockMesh = block->mesh;
+                       assert(mapBlockMesh);
+
+                       for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+                               scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
+                               assert(mesh);
+
+                               u32 c = mesh->getMeshBufferCount();
+                               for (u32 i = 0; i < c; i++) {
+                                       scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+
+                                       video::SMaterial &mat = buf->getMaterial();
+                                       auto rnd = driver->getMaterialRenderer(mat.MaterialType);
+                                       bool transparent = rnd && rnd->isTransparent();
+                                       if (transparent == is_transparent_pass)
+                                               drawbufs.add(buf, block_pos, layer);
+                               }
+                       }
+               }
+       }
+
+       TimeTaker draw("Drawing shadow mesh buffers");
+
+       core::matrix4 m; // Model matrix
+       v3f offset = intToFloat(m_camera_offset, BS);
+
+       // Render all layers in order
+       for (auto &lists : drawbufs.lists) {
+               for (MeshBufList &list : lists) {
+                       // Check and abort if the machine is swapping a lot
+                       if (draw.getTimerTime() > 1000) {
+                               infostream << "ClientMap::renderMapShadows(): Rendering "
+                                               "took >1s, returning." << std::endl;
+                               break;
+                       }
+                       for (auto &pair : list.bufs) {
+                               scene::IMeshBuffer *buf = pair.second;
+
+                               // override some material properties
+                               video::SMaterial local_material = buf->getMaterial();
+                               local_material.MaterialType = material.MaterialType;
+                               local_material.BackfaceCulling = material.BackfaceCulling;
+                               local_material.FrontfaceCulling = material.FrontfaceCulling;
+                               local_material.Lighting = false;
+                               driver->setMaterial(local_material);
+
+                               v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
+                               m.setTranslation(block_wpos - offset);
+
+                               driver->setTransform(video::ETS_WORLD, m);
+                               driver->drawMeshBuffer(buf);
+                               vertex_count += buf->getVertexCount();
+                       }
+
+                       drawcall_count += list.bufs.size();
+               }
+       }
+
+       g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
+       g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
+       g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+}
+
+/*
+       Custom update draw list for the pov of shadow light.
+*/
+void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
+{
+       ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
+
+       const v3f camera_position = shadow_light_pos;
+       const v3f camera_direction = shadow_light_dir;
+       // I "fake" fov just to avoid creating a new function to handle orthographic
+       // projection.
+       const f32 camera_fov = m_camera_fov * 1.9f;
+
+       v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
+       v3s16 p_blocks_min;
+       v3s16 p_blocks_max;
+       getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, shadow_range);
+
+       std::vector<v2s16> blocks_in_range;
+
+       for (auto &i : m_drawlist_shadow) {
+               MapBlock *block = i.second;
+               block->refDrop();
+       }
+       m_drawlist_shadow.clear();
+
+       // We need to append the blocks from the camera POV because sometimes
+       // they are not inside the light frustum and it creates glitches.
+       // FIXME: This could be removed if we figure out why they are missing
+       // from the light frustum.
+       for (auto &i : m_drawlist) {
+               i.second->refGrab();
+               m_drawlist_shadow[i.first] = i.second;
+       }
+
+       // Number of blocks currently loaded by the client
+       u32 blocks_loaded = 0;
+       // Number of blocks with mesh in rendering range
+       u32 blocks_in_range_with_mesh = 0;
+       // Number of blocks occlusion culled
+       u32 blocks_occlusion_culled = 0;
+
+       for (auto &sector_it : m_sectors) {
+               MapSector *sector = sector_it.second;
+               if (!sector)
+                       continue;
+               blocks_loaded += sector->size();
+
+               MapBlockVect sectorblocks;
+               sector->getBlocks(sectorblocks);
+
+               /*
+                       Loop through blocks in sector
+               */
+               for (MapBlock *block : sectorblocks) {
+                       if (!block->mesh) {
+                               // Ignore if mesh doesn't exist
+                               continue;
+                       }
+
+                       float range = shadow_range;
+
+                       float d = 0.0;
+                       if (!isBlockInSight(block->getPos(), camera_position,
+                                           camera_direction, camera_fov, range, &d))
+                               continue;
+
+                       blocks_in_range_with_mesh++;
+
+                       /*
+                               Occlusion culling
+                       */
+                       if (isBlockOccluded(block, cam_pos_nodes)) {
+                               blocks_occlusion_culled++;
+                               continue;
+                       }
+
+                       // This block is in range. Reset usage timer.
+                       block->resetUsageTimer();
+
+                       // Add to set
+                       if (m_drawlist_shadow.find(block->getPos()) == m_drawlist_shadow.end()) {
+                               block->refGrab();
+                               m_drawlist_shadow[block->getPos()] = block;
+                       }
+               }
+       }
+
+       g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
+       g_profiler->avg("SHADOW MapBlocks occlusion culled [#]", blocks_occlusion_culled);
+       g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
+       g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
+}
index 80add4a441f71f01adeb51243df0a02b1cad3c8e..93ade4c1520dafc7a5ebff45f486118c6318b915 100644 (file)
@@ -119,10 +119,14 @@ class ClientMap : public Map, public scene::ISceneNode
        }
 
        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 @@ class ClientMap : public Map, public scene::ISceneNode
        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 @@ class ClientMap : public Map, public scene::ISceneNode
        v3s16 m_camera_offset;
 
        std::map<v3s16, MapBlock*> m_drawlist;
+       std::map<v3s16, MapBlock*> m_drawlist_shadow;
 
        std::set<v2s16> m_last_drawn_sectors;
 
        bool m_cache_trilinear_filter;
        bool m_cache_bilinear_filter;
        bool m_cache_anistropic_filter;
+       bool m_added_to_shadow_renderer{false};
 };
index 2e58e19cfb83e0c5bd646f8a805a7fb979ec1d62..9216f0010809112d7b0d4d7166cd41a7502877d9 100644 (file)
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IMeshManipulator.h>
 #include <IAnimatedMeshSceneNode.h>
 #include "client/client.h"
+#include "client/renderingengine.h"
 #include "client/sound.h"
 #include "client/tile.h"
 #include "util/basic_macros.h"
@@ -555,6 +556,9 @@ void GenericCAO::removeFromScene(bool permanent)
                clearParentAttachment();
        }
 
+       if (auto shadow = RenderingEngine::get_shadow_renderer())
+               shadow->removeNodeFromShadowList(getSceneNode());
+
        if (m_meshnode) {
                m_meshnode->remove();
                m_meshnode->drop();
@@ -803,10 +807,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
        if (m_reset_textures_timer < 0)
                updateTextures(m_current_texture_modifier);
 
-       scene::ISceneNode *node = getSceneNode();
+       if (scene::ISceneNode *node = getSceneNode()) {
+               if (m_matrixnode)
+                       node->setParent(m_matrixnode);
 
-       if (node && m_matrixnode)
-               node->setParent(m_matrixnode);
+               if (auto shadow = RenderingEngine::get_shadow_renderer())
+                       shadow->addNodeToShadowList(node);
+       }
 
        updateNametag();
        updateMarker();
index eb1dbb5a3254bc7c80d894bfe27f741fed6b9c96..d240ebc0fd49d81947953768b11021f2a425d48b 100644 (file)
@@ -738,6 +738,7 @@ class Game {
                        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
index 72e68fe97df84a23c9a4b7e42ecb08890564f3eb..402217066c6023d2067074458353b0b4d59d04ea 100644 (file)
@@ -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
index 3c4583623250e7a7d3ba6592deaeb1751a24eb5b..4a820f58394e43de66b206673867db0a60a01675 100644 (file)
@@ -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;
index 52ea8f99f71c7b8bc335ec472a91e33593a88667..cabfbbfad95181f3b7fffbff2cabcd214ab5d0af 100644 (file)
@@ -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 @@ class RenderingCore
        Minimap *mapper;
        Hud *hud;
 
+       ShadowRenderer *shadow_renderer;
+
        void updateScreenSize();
        virtual void initTextures() {}
        virtual void clearTextures() {}
@@ -72,4 +75,6 @@ class RenderingCore
                        bool _draw_wield_tool, bool _draw_crosshair);
 
        inline v2u32 getVirtualSize() const { return virtual_size; }
+
+       ShadowRenderer *get_shadow_renderer() { return shadow_renderer; };
 };
index 28ddc86528d6cfeec77a77244d116e023c94dd63..6d42221d60819948dcc4e6f5c9d542d0c1378dfc 100644 (file)
@@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include "irrlichttypes_extrabloated.h"
 #include "debug.h"
+#include "client/render/core.h"
+// include the shadow mapper classes too
+#include "client/shadows/dynamicshadowsrender.h"
+
 
 class ITextureSource;
 class Camera;
@@ -113,6 +117,13 @@ class RenderingEngine
                return m_device->run();
        }
 
+       // FIXME: this is still global when it shouldn't be
+       static ShadowRenderer *get_shadow_renderer()
+       {
+               if (s_singleton && s_singleton->core)
+                       return s_singleton->core->get_shadow_renderer();
+               return nullptr;
+       }
        static std::vector<core::vector3d<u32>> getSupportedVideoModes();
        static std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers();
 
index 58946b90f5d79f4f617b19fea7453364c449ecce..355366bd388e0528f9e108881d1f92f541724d3e 100644 (file)
@@ -225,6 +225,16 @@ class MainShaderConstantSetter : public IShaderConstantSetter
 {
        CachedVertexShaderSetting<float, 16> m_world_view_proj;
        CachedVertexShaderSetting<float, 16> m_world;
+
+       // Shadow-related
+       CachedPixelShaderSetting<float, 16> m_shadow_view_proj;
+       CachedPixelShaderSetting<float, 3> m_light_direction;
+       CachedPixelShaderSetting<float> m_texture_res;
+       CachedPixelShaderSetting<float> m_shadow_strength;
+       CachedPixelShaderSetting<float> m_time_of_day;
+       CachedPixelShaderSetting<float> m_shadowfar;
+       CachedPixelShaderSetting<s32> m_shadow_texture;
+
 #if ENABLE_GLES
        // Modelview matrix
        CachedVertexShaderSetting<float, 16> m_world_view;
@@ -243,6 +253,13 @@ class MainShaderConstantSetter : public IShaderConstantSetter
                , 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 @@ class MainShaderConstantSetter : public IShaderConstantSetter
                };
                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 (file)
index 0000000..775cdeb
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <cmath>
+
+#include "client/shadows/dynamicshadows.h"
+#include "client/client.h"
+#include "client/clientenvironment.h"
+#include "client/clientmap.h"
+#include "client/camera.h"
+
+using m4f = core::matrix4;
+
+void DirectionalLight::createSplitMatrices(const Camera *cam)
+{
+       float radius;
+       v3f newCenter;
+       v3f look = cam->getDirection();
+
+       v3f camPos2 = cam->getPosition();
+       v3f camPos = v3f(camPos2.X - cam->getOffset().X * BS,
+                       camPos2.Y - cam->getOffset().Y * BS,
+                       camPos2.Z - cam->getOffset().Z * BS);
+       camPos += look * shadow_frustum.zNear;
+       camPos2 += look * shadow_frustum.zNear;
+       float end = shadow_frustum.zNear + shadow_frustum.zFar;
+       newCenter = camPos + look * (shadow_frustum.zNear + 0.05f * end);
+       v3f world_center = camPos2 + look * (shadow_frustum.zNear + 0.05f * end);
+       // Create a vector to the frustum far corner
+       // @Liso: move all vars we can outside the loop.
+       float tanFovY = tanf(cam->getFovY() * 0.5f);
+       float tanFovX = tanf(cam->getFovX() * 0.5f);
+
+       const v3f &viewUp = cam->getCameraNode()->getUpVector();
+       // viewUp.normalize();
+
+       v3f viewRight = look.crossProduct(viewUp);
+       // viewRight.normalize();
+
+       v3f farCorner = look + viewRight * tanFovX + viewUp * tanFovY;
+       // Compute the frustumBoundingSphere radius
+       v3f boundVec = (camPos + farCorner * shadow_frustum.zFar) - newCenter;
+       radius = boundVec.getLength() * 2.0f;
+       // boundVec.getLength();
+       float vvolume = radius * 2.0f;
+
+       float texelsPerUnit = getMapResolution() / vvolume;
+       m4f mTexelScaling;
+       mTexelScaling.setScale(texelsPerUnit);
+
+       m4f mLookAt, mLookAtInv;
+
+       mLookAt.buildCameraLookAtMatrixLH(v3f(0.0f, 0.0f, 0.0f), -direction, v3f(0.0f, 1.0f, 0.0f));
+
+       mLookAt *= mTexelScaling;
+       mLookAtInv = mLookAt;
+       mLookAtInv.makeInverse();
+
+       v3f frustumCenter = newCenter;
+       mLookAt.transformVect(frustumCenter);
+       frustumCenter.X = floorf(frustumCenter.X); // clamp to texel increment
+       frustumCenter.Y = floorf(frustumCenter.Y); // clamp to texel increment
+       frustumCenter.Z = floorf(frustumCenter.Z);
+       mLookAtInv.transformVect(frustumCenter);
+       // probar radius multipliacdor en funcion del I, a menor I mas multiplicador
+       v3f eye_displacement = direction * vvolume;
+
+       // we must compute the viewmat with the position - the camera offset
+       // but the shadow_frustum position must be the actual world position
+       v3f eye = frustumCenter - eye_displacement;
+       shadow_frustum.position = world_center - eye_displacement;
+       shadow_frustum.length = vvolume;
+       shadow_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
+       shadow_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(shadow_frustum.length,
+                       shadow_frustum.length, -shadow_frustum.length,
+                       shadow_frustum.length,false);
+}
+
+DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
+               const v3f &position, video::SColorf lightColor,
+               f32 farValue) :
+               diffuseColor(lightColor),
+               farPlane(farValue), mapRes(shadowMapResolution), pos(position)
+{}
+
+void DirectionalLight::update_frustum(const Camera *cam, Client *client)
+{
+       should_update_map_shadow = true;
+       float zNear = cam->getCameraNode()->getNearValue();
+       float zFar = getMaxFarValue();
+
+       ///////////////////////////////////
+       // update splits near and fars
+       shadow_frustum.zNear = zNear;
+       shadow_frustum.zFar = zFar;
+
+       // update shadow frustum
+       createSplitMatrices(cam);
+       // get the draw list for shadows
+       client->getEnv().getClientMap().updateDrawListShadow(
+                       getPosition(), getDirection(), shadow_frustum.length);
+       should_update_map_shadow = true;
+}
+
+void DirectionalLight::setDirection(v3f dir)
+{
+       direction = -dir;
+       direction.normalize();
+}
+
+v3f DirectionalLight::getPosition() const
+{
+       return shadow_frustum.position;
+}
+
+const m4f &DirectionalLight::getViewMatrix() const
+{
+       return shadow_frustum.ViewMat;
+}
+
+const m4f &DirectionalLight::getProjectionMatrix() const
+{
+       return shadow_frustum.ProjOrthMat;
+}
+
+m4f DirectionalLight::getViewProjMatrix()
+{
+       return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
+}
diff --git a/src/client/shadows/dynamicshadows.h b/src/client/shadows/dynamicshadows.h
new file mode 100644 (file)
index 0000000..a53612f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes_bloated.h"
+#include <matrix4.h>
+#include "util/basic_macros.h"
+
+class Camera;
+class Client;
+
+struct shadowFrustum
+{
+       float zNear{0.0f};
+       float zFar{0.0f};
+       float length{0.0f};
+       core::matrix4 ProjOrthMat;
+       core::matrix4 ViewMat;
+       v3f position;
+};
+
+class DirectionalLight
+{
+public:
+       DirectionalLight(const u32 shadowMapResolution,
+                       const v3f &position,
+                       video::SColorf lightColor = video::SColor(0xffffffff),
+                       f32 farValue = 100.0f);
+       ~DirectionalLight() = default;
+
+       //DISABLE_CLASS_COPY(DirectionalLight)
+
+       void update_frustum(const Camera *cam, Client *client);
+
+       // when set direction is updated to negative normalized(direction)
+       void setDirection(v3f dir);
+       v3f getDirection() const{
+               return direction;
+       };
+       v3f getPosition() const;
+
+       /// Gets the light's matrices.
+       const core::matrix4 &getViewMatrix() const;
+       const core::matrix4 &getProjectionMatrix() const;
+       core::matrix4 getViewProjMatrix();
+
+       /// Gets the light's far value.
+       f32 getMaxFarValue() const
+       {
+               return farPlane;
+       }
+
+
+       /// Gets the light's color.
+       const video::SColorf &getLightColor() const
+       {
+               return diffuseColor;
+       }
+
+       /// Sets the light's color.
+       void setLightColor(const video::SColorf &lightColor)
+       {
+               diffuseColor = lightColor;
+       }
+
+       /// Gets the shadow map resolution for this light.
+       u32 getMapResolution() const
+       {
+               return mapRes;
+       }
+
+       bool should_update_map_shadow{true};
+
+private:
+       void createSplitMatrices(const Camera *cam);
+
+       video::SColorf diffuseColor;
+
+       f32 farPlane;
+       u32 mapRes;
+
+       v3f pos;
+       v3f direction{0};
+       shadowFrustum shadow_frustum;
+};
diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp
new file mode 100644 (file)
index 0000000..135c9f8
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <cstring>
+#include "client/shadows/dynamicshadowsrender.h"
+#include "client/shadows/shadowsScreenQuad.h"
+#include "client/shadows/shadowsshadercallbacks.h"
+#include "settings.h"
+#include "filesys.h"
+#include "util/string.h"
+#include "client/shader.h"
+#include "client/client.h"
+#include "client/clientmap.h"
+
+ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
+               m_device(device), m_smgr(device->getSceneManager()),
+               m_driver(device->getVideoDriver()), m_client(client)
+{
+       m_shadows_enabled = true;
+
+       m_shadow_strength = g_settings->getFloat("shadow_strength");
+
+       m_shadow_map_max_distance = g_settings->getFloat("shadow_map_max_distance");
+
+       m_shadow_map_texture_size = g_settings->getFloat("shadow_map_texture_size");
+
+       m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
+       m_shadow_map_colored = g_settings->getBool("shadow_map_color");
+       m_shadow_samples = g_settings->getS32("shadow_filters");
+       m_update_delta = g_settings->getFloat("shadow_update_time");
+}
+
+ShadowRenderer::~ShadowRenderer()
+{
+       if (m_shadow_depth_cb)
+               delete m_shadow_depth_cb;
+       if (m_shadow_mix_cb)
+               delete m_shadow_mix_cb;
+       m_shadow_node_array.clear();
+       m_light_list.clear();
+
+       if (shadowMapTextureDynamicObjects)
+               m_driver->removeTexture(shadowMapTextureDynamicObjects);
+
+       if (shadowMapTextureFinal)
+               m_driver->removeTexture(shadowMapTextureFinal);
+
+       if (shadowMapTextureColors)
+               m_driver->removeTexture(shadowMapTextureColors);
+
+       if (shadowMapClientMap)
+               m_driver->removeTexture(shadowMapClientMap);
+}
+
+void ShadowRenderer::initialize()
+{
+       auto *gpu = m_driver->getGPUProgrammingServices();
+
+       // we need glsl
+       if (m_shadows_enabled && gpu && m_driver->queryFeature(video::EVDF_ARB_GLSL)) {
+               createShaders();
+       } else {
+               m_shadows_enabled = false;
+
+               warningstream << "Shadows: GLSL Shader not supported on this system."
+                       << std::endl;
+               return;
+       }
+
+       m_texture_format = m_shadow_map_texture_32bit
+                                          ? video::ECOLOR_FORMAT::ECF_R32F
+                                          : video::ECOLOR_FORMAT::ECF_R16F;
+
+       m_texture_format_color = m_shadow_map_texture_32bit
+                                                ? video::ECOLOR_FORMAT::ECF_G32R32F
+                                                : video::ECOLOR_FORMAT::ECF_G16R16F;
+}
+
+
+float ShadowRenderer::getUpdateDelta() const
+{
+       return m_update_delta;
+}
+
+size_t ShadowRenderer::addDirectionalLight()
+{
+       m_light_list.emplace_back(m_shadow_map_texture_size,
+                       v3f(0.f, 0.f, 0.f),
+                       video::SColor(255, 255, 255, 255), m_shadow_map_max_distance);
+       return m_light_list.size() - 1;
+}
+
+DirectionalLight &ShadowRenderer::getDirectionalLight(u32 index)
+{
+       return m_light_list[index];
+}
+
+size_t ShadowRenderer::getDirectionalLightCount() const
+{
+       return m_light_list.size();
+}
+
+f32 ShadowRenderer::getMaxShadowFar() const
+{
+       if (!m_light_list.empty()) {
+               float wanted_range = m_client->getEnv().getClientMap().getWantedRange();
+
+               float zMax = m_light_list[0].getMaxFarValue() > wanted_range
+                                            ? wanted_range
+                                            : m_light_list[0].getMaxFarValue();
+               return zMax * MAP_BLOCKSIZE;
+       }
+       return 0.0f;
+}
+
+void ShadowRenderer::addNodeToShadowList(
+               scene::ISceneNode *node, E_SHADOW_MODE shadowMode)
+{
+       m_shadow_node_array.emplace_back(NodeToApply(node, shadowMode));
+}
+
+void ShadowRenderer::removeNodeFromShadowList(scene::ISceneNode *node)
+{
+       for (auto it = m_shadow_node_array.begin(); it != m_shadow_node_array.end();) {
+               if (it->node == node) {
+                       it = m_shadow_node_array.erase(it);
+                       break;
+               } else {
+                       ++it;
+               }
+       }
+}
+
+void ShadowRenderer::setClearColor(video::SColor ClearColor)
+{
+       m_clear_color = ClearColor;
+}
+
+void ShadowRenderer::update(video::ITexture *outputTarget)
+{
+       if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
+               m_smgr->drawAll();
+               return;
+       }
+
+       if (!shadowMapTextureDynamicObjects) {
+
+               shadowMapTextureDynamicObjects = getSMTexture(
+                       std::string("shadow_dynamic_") + itos(m_shadow_map_texture_size),
+                       m_texture_format, true);
+       }
+
+       if (!shadowMapClientMap) {
+
+               shadowMapClientMap = getSMTexture(
+                       std::string("shadow_clientmap_") + itos(m_shadow_map_texture_size),
+                       m_shadow_map_colored ? m_texture_format_color : m_texture_format,
+                       true);
+       }
+
+       if (m_shadow_map_colored && !shadowMapTextureColors) {
+               shadowMapTextureColors = getSMTexture(
+                       std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
+                       m_shadow_map_colored ? m_texture_format_color : m_texture_format,
+                       true);
+       }
+
+       // The merge all shadowmaps texture
+       if (!shadowMapTextureFinal) {
+               video::ECOLOR_FORMAT frt;
+               if (m_shadow_map_texture_32bit) {
+                       if (m_shadow_map_colored)
+                               frt = video::ECOLOR_FORMAT::ECF_A32B32G32R32F;
+                       else
+                               frt = video::ECOLOR_FORMAT::ECF_R32F;
+               } else {
+                       if (m_shadow_map_colored)
+                               frt = video::ECOLOR_FORMAT::ECF_A16B16G16R16F;
+                       else
+                               frt = video::ECOLOR_FORMAT::ECF_R16F;
+               }
+               shadowMapTextureFinal = getSMTexture(
+                       std::string("shadowmap_final_") + itos(m_shadow_map_texture_size),
+                       frt, true);
+       }
+
+       if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
+               // for every directional light:
+               for (DirectionalLight &light : m_light_list) {
+                       // Static shader values.
+                       m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
+                       m_shadow_depth_cb->MaxFar = (f32)m_shadow_map_max_distance * BS;
+
+                       // set the Render Target
+                       // right now we can only render in usual RTT, not
+                       // Depth texture is available in irrlicth maybe we
+                       // should put some gl* fn here
+
+                       if (light.should_update_map_shadow) {
+                               light.should_update_map_shadow = false;
+
+                               m_driver->setRenderTarget(shadowMapClientMap, true, true,
+                                               video::SColor(255, 255, 255, 255));
+                               renderShadowMap(shadowMapClientMap, light);
+
+                               if (m_shadow_map_colored) {
+                                       m_driver->setRenderTarget(shadowMapTextureColors,
+                                                       true, false, video::SColor(255, 255, 255, 255));
+                               }
+                               renderShadowMap(shadowMapTextureColors, light,
+                                               scene::ESNRP_TRANSPARENT);
+                               m_driver->setRenderTarget(0, false, false);
+                       }
+
+                       // render shadows for the n0n-map objects.
+                       m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
+                                       true, video::SColor(255, 255, 255, 255));
+                       renderShadowObjects(shadowMapTextureDynamicObjects, light);
+                       // clear the Render Target
+                       m_driver->setRenderTarget(0, false, false);
+
+                       // in order to avoid too many map shadow renders,
+                       // we should make a second pass to mix clientmap shadows and
+                       // entities shadows :(
+                       m_screen_quad->getMaterial().setTexture(0, shadowMapClientMap);
+                       // dynamic objs shadow texture.
+                       if (m_shadow_map_colored)
+                               m_screen_quad->getMaterial().setTexture(1, shadowMapTextureColors);
+                       m_screen_quad->getMaterial().setTexture(2, shadowMapTextureDynamicObjects);
+
+                       m_driver->setRenderTarget(shadowMapTextureFinal, false, false,
+                                       video::SColor(255, 255, 255, 255));
+                       m_screen_quad->render(m_driver);
+                       m_driver->setRenderTarget(0, false, false);
+
+               } // end for lights
+
+               // now render the actual MT render pass
+               m_driver->setRenderTarget(outputTarget, true, true, m_clear_color);
+               m_smgr->drawAll();
+
+               /* this code just shows shadows textures in screen and in ONLY for debugging*/
+               #if 0
+               // this is debug, ignore for now.
+               m_driver->draw2DImage(shadowMapTextureFinal,
+                               core::rect<s32>(0, 50, 128, 128 + 50),
+                               core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
+
+               m_driver->draw2DImage(shadowMapClientMap,
+                               core::rect<s32>(0, 50 + 128, 128, 128 + 50 + 128),
+                               core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
+               m_driver->draw2DImage(shadowMapTextureDynamicObjects,
+                               core::rect<s32>(0, 128 + 50 + 128, 128,
+                                               128 + 50 + 128 + 128),
+                               core::rect<s32>({0, 0}, shadowMapTextureDynamicObjects->getSize()));
+
+               if (m_shadow_map_colored) {
+
+                       m_driver->draw2DImage(shadowMapTextureColors,
+                                       core::rect<s32>(128,128 + 50 + 128 + 128,
+                                                       128 + 128, 128 + 50 + 128 + 128 + 128),
+                                       core::rect<s32>({0, 0}, shadowMapTextureColors->getSize()));
+               }
+               #endif
+               m_driver->setRenderTarget(0, false, false);
+       }
+}
+
+
+video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name,
+               video::ECOLOR_FORMAT texture_format, bool force_creation)
+{
+       if (force_creation) {
+               return m_driver->addRenderTargetTexture(
+                               core::dimension2du(m_shadow_map_texture_size,
+                                               m_shadow_map_texture_size),
+                               shadow_map_name.c_str(), texture_format);
+       }
+
+       return m_driver->getTexture(shadow_map_name.c_str());
+}
+
+void ShadowRenderer::renderShadowMap(video::ITexture *target,
+               DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
+{
+       m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
+       m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
+
+       // Operate on the client map
+       for (const auto &shadow_node : m_shadow_node_array) {
+               if (strcmp(shadow_node.node->getName(), "ClientMap") != 0)
+                       continue;
+
+               ClientMap *map_node = static_cast<ClientMap *>(shadow_node.node);
+
+               video::SMaterial material;
+               if (map_node->getMaterialCount() > 0) {
+                       // we only want the first material, which is the one with the albedo info
+                       material = map_node->getMaterial(0);
+               }
+
+               material.BackfaceCulling = false;
+               material.FrontfaceCulling = true;
+               material.PolygonOffsetFactor = 4.0f;
+               material.PolygonOffsetDirection = video::EPO_BACK;
+               //material.PolygonOffsetDepthBias = 1.0f/4.0f;
+               //material.PolygonOffsetSlopeScale = -1.f;
+
+               if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
+                       material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
+               else
+                       material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
+
+               // FIXME: I don't think this is needed here
+               map_node->OnAnimate(m_device->getTimer()->getTime());
+
+               m_driver->setTransform(video::ETS_WORLD,
+                               map_node->getAbsoluteTransformation());
+
+               map_node->renderMapShadows(m_driver, material, pass);
+               break;
+       }
+}
+
+void ShadowRenderer::renderShadowObjects(
+               video::ITexture *target, DirectionalLight &light)
+{
+       m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
+       m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
+
+       for (const auto &shadow_node : m_shadow_node_array) {
+               // we only take care of the shadow casters
+               if (shadow_node.shadowMode == ESM_RECEIVE ||
+                               strcmp(shadow_node.node->getName(), "ClientMap") == 0)
+                       continue;
+
+               // render other objects
+               u32 n_node_materials = shadow_node.node->getMaterialCount();
+               std::vector<s32> BufferMaterialList;
+               std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
+               BufferMaterialList.reserve(n_node_materials);
+               BufferMaterialCullingList.reserve(n_node_materials);
+
+               // backup materialtype for each material
+               // (aka shader)
+               // and replace it by our "depth" shader
+               for (u32 m = 0; m < n_node_materials; m++) {
+                       auto &current_mat = shadow_node.node->getMaterial(m);
+
+                       BufferMaterialList.push_back(current_mat.MaterialType);
+                       current_mat.MaterialType =
+                                       (video::E_MATERIAL_TYPE)depth_shader;
+
+                       current_mat.setTexture(3, shadowMapTextureFinal);
+
+                       BufferMaterialCullingList.emplace_back(
+                               (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
+
+                       current_mat.BackfaceCulling = true;
+                       current_mat.FrontfaceCulling = false;
+                       current_mat.PolygonOffsetFactor = 1.0f/2048.0f;
+                       current_mat.PolygonOffsetDirection = video::EPO_BACK;
+                       //current_mat.PolygonOffsetDepthBias = 1.0 * 2.8e-6;
+                       //current_mat.PolygonOffsetSlopeScale = -1.f;
+               }
+
+               m_driver->setTransform(video::ETS_WORLD,
+                               shadow_node.node->getAbsoluteTransformation());
+               shadow_node.node->render();
+
+               // restore the material.
+
+               for (u32 m = 0; m < n_node_materials; m++) {
+                       auto &current_mat = shadow_node.node->getMaterial(m);
+
+                       current_mat.MaterialType = (video::E_MATERIAL_TYPE) BufferMaterialList[m];
+
+                       current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
+                       current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
+               }
+
+       } // end for caster shadow nodes
+}
+
+void ShadowRenderer::mixShadowsQuad()
+{
+}
+
+/*
+ * @Liso's disclaimer ;) This function loads the Shadow Mapping Shaders.
+ * I used a custom loader because I couldn't figure out how to use the base
+ * Shaders system with custom IShaderConstantSetCallBack without messing up the
+ * code too much. If anyone knows how to integrate this with the standard MT
+ * shaders, please feel free to change it.
+ */
+
+void ShadowRenderer::createShaders()
+{
+       video::IGPUProgrammingServices *gpu = m_driver->getGPUProgrammingServices();
+
+       if (depth_shader == -1) {
+               std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
+               if (depth_shader_vs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping vs shader not found." << std::endl;
+                       return;
+               }
+               std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
+               if (depth_shader_fs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping fs shader not found." << std::endl;
+                       return;
+               }
+               m_shadow_depth_cb = new ShadowDepthShaderCB();
+
+               depth_shader = gpu->addHighLevelShaderMaterial(
+                               readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+                               video::EVST_VS_1_1,
+                               readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+                               video::EPST_PS_1_2, m_shadow_depth_cb);
+
+               if (depth_shader == -1) {
+                       // upsi, something went wrong loading shader.
+                       delete m_shadow_depth_cb;
+                       m_shadows_enabled = false;
+                       errorstream << "Error compiling shadow mapping shader." << std::endl;
+                       return;
+               }
+
+               // HACK, TODO: investigate this better
+               // Grab the material renderer once more so minetest doesn't crash
+               // on exit
+               m_driver->getMaterialRenderer(depth_shader)->grab();
+       }
+
+       if (mixcsm_shader == -1) {
+               std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
+               if (depth_shader_vs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
+                       return;
+               }
+
+               std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass2_fragment.glsl");
+               if (depth_shader_fs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
+                       return;
+               }
+               m_shadow_mix_cb = new shadowScreenQuadCB();
+               m_screen_quad = new shadowScreenQuad();
+               mixcsm_shader = gpu->addHighLevelShaderMaterial(
+                               readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+                               video::EVST_VS_1_1,
+                               readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+                               video::EPST_PS_1_2, m_shadow_mix_cb);
+
+               m_screen_quad->getMaterial().MaterialType =
+                               (video::E_MATERIAL_TYPE)mixcsm_shader;
+
+               if (mixcsm_shader == -1) {
+                       // upsi, something went wrong loading shader.
+                       delete m_shadow_mix_cb;
+                       delete m_screen_quad;
+                       m_shadows_enabled = false;
+                       errorstream << "Error compiling cascade shadow mapping shader." << std::endl;
+                       return;
+               }
+
+               // HACK, TODO: investigate this better
+               // Grab the material renderer once more so minetest doesn't crash
+               // on exit
+               m_driver->getMaterialRenderer(mixcsm_shader)->grab();
+       }
+
+       if (m_shadow_map_colored && depth_shader_trans == -1) {
+               std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_trans_vertex.glsl");
+               if (depth_shader_vs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping vs shader not found." << std::endl;
+                       return;
+               }
+               std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_trans_fragment.glsl");
+               if (depth_shader_fs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping fs shader not found." << std::endl;
+                       return;
+               }
+               m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
+
+               depth_shader_trans = gpu->addHighLevelShaderMaterial(
+                               readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+                               video::EVST_VS_1_1,
+                               readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+                               video::EPST_PS_1_2, m_shadow_depth_trans_cb);
+
+               if (depth_shader_trans == -1) {
+                       // upsi, something went wrong loading shader.
+                       delete m_shadow_depth_trans_cb;
+                       m_shadow_map_colored = false;
+                       m_shadows_enabled = false;
+                       errorstream << "Error compiling colored shadow mapping shader." << std::endl;
+                       return;
+               }
+
+               // HACK, TODO: investigate this better
+               // Grab the material renderer once more so minetest doesn't crash
+               // on exit
+               m_driver->getMaterialRenderer(depth_shader_trans)->grab();
+       }
+}
+
+std::string ShadowRenderer::readShaderFile(const std::string &path)
+{
+       std::string prefix;
+       if (m_shadow_map_colored)
+               prefix.append("#define COLORED_SHADOWS 1\n");
+
+       std::string content;
+       fs::ReadFile(path, content);
+
+       return prefix + content;
+}
diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h
new file mode 100644 (file)
index 0000000..e633bd4
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "irrlichttypes_extrabloated.h"
+#include "client/shadows/dynamicshadows.h"
+
+class ShadowDepthShaderCB;
+class shadowScreenQuad;
+class shadowScreenQuadCB;
+
+enum E_SHADOW_MODE : u8
+{
+       ESM_RECEIVE = 0,
+       ESM_BOTH,
+};
+
+struct NodeToApply
+{
+       NodeToApply(scene::ISceneNode *n,
+                       E_SHADOW_MODE m = E_SHADOW_MODE::ESM_BOTH) :
+                       node(n),
+                       shadowMode(m){};
+       bool operator<(const NodeToApply &other) const { return node < other.node; };
+
+       scene::ISceneNode *node;
+
+       E_SHADOW_MODE shadowMode{E_SHADOW_MODE::ESM_BOTH};
+       bool dirty{false};
+};
+
+class ShadowRenderer
+{
+public:
+       ShadowRenderer(IrrlichtDevice *device, Client *client);
+
+       ~ShadowRenderer();
+
+       void initialize();
+
+       /// Adds a directional light shadow map (Usually just one (the sun) except in
+       /// Tattoine ).
+       size_t addDirectionalLight();
+       DirectionalLight &getDirectionalLight(u32 index = 0);
+       size_t getDirectionalLightCount() const;
+       f32 getMaxShadowFar() const;
+
+       float getUpdateDelta() const;
+       /// Adds a shadow to the scene node.
+       /// The shadow mode can be ESM_BOTH, or ESM_RECEIVE.
+       /// ESM_BOTH casts and receives shadows
+       /// ESM_RECEIVE only receives but does not cast shadows.
+       ///
+       void addNodeToShadowList(scene::ISceneNode *node,
+                       E_SHADOW_MODE shadowMode = ESM_BOTH);
+       void removeNodeFromShadowList(scene::ISceneNode *node);
+
+       void setClearColor(video::SColor ClearColor);
+
+       void update(video::ITexture *outputTarget = nullptr);
+
+       video::ITexture *get_texture()
+       {
+               return shadowMapTextureFinal;
+       }
+
+
+       bool is_active() const { return m_shadows_enabled; }
+       void setTimeOfDay(float isDay) { m_time_day = isDay; };
+
+       s32 getShadowSamples() const { return m_shadow_samples; }
+       float getShadowStrength() const { return m_shadow_strength; }
+       float getTimeOfDay() const { return m_time_day; }
+
+private:
+       video::ITexture *getSMTexture(const std::string &shadow_map_name,
+                       video::ECOLOR_FORMAT texture_format,
+                       bool force_creation = false);
+
+       void renderShadowMap(video::ITexture *target, DirectionalLight &light,
+                       scene::E_SCENE_NODE_RENDER_PASS pass =
+                                       scene::ESNRP_SOLID);
+       void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
+       void mixShadowsQuad();
+
+       // a bunch of variables
+       IrrlichtDevice *m_device{nullptr};
+       scene::ISceneManager *m_smgr{nullptr};
+       video::IVideoDriver *m_driver{nullptr};
+       Client *m_client{nullptr};
+       video::ITexture *shadowMapClientMap{nullptr};
+       video::ITexture *shadowMapTextureFinal{nullptr};
+       video::ITexture *shadowMapTextureDynamicObjects{nullptr};
+       video::ITexture *shadowMapTextureColors{nullptr};
+       video::SColor m_clear_color{0x0};
+
+       std::vector<DirectionalLight> m_light_list;
+       std::vector<NodeToApply> m_shadow_node_array;
+
+       float m_shadow_strength;
+       float m_shadow_map_max_distance;
+       float m_shadow_map_texture_size;
+       float m_time_day{0.0f};
+       float m_update_delta;
+       int m_shadow_samples;
+       bool m_shadow_map_texture_32bit;
+       bool m_shadows_enabled;
+       bool m_shadow_map_colored;
+
+       video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F};
+       video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
+
+       // Shadow Shader stuff
+
+       void createShaders();
+       std::string readShaderFile(const std::string &path);
+
+       s32 depth_shader{-1};
+       s32 depth_shader_trans{-1};
+       s32 mixcsm_shader{-1};
+
+       ShadowDepthShaderCB *m_shadow_depth_cb{nullptr};
+       ShadowDepthShaderCB *m_shadow_depth_trans_cb{nullptr};
+
+       shadowScreenQuad *m_screen_quad{nullptr};
+       shadowScreenQuadCB *m_shadow_mix_cb{nullptr};
+};
diff --git a/src/client/shadows/shadowsScreenQuad.cpp b/src/client/shadows/shadowsScreenQuad.cpp
new file mode 100644 (file)
index 0000000..c36ee0d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "shadowsScreenQuad.h"
+
+shadowScreenQuad::shadowScreenQuad()
+{
+       Material.Wireframe = false;
+       Material.Lighting = false;
+
+       video::SColor color(0x0);
+       Vertices[0] = video::S3DVertex(
+                       -1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
+       Vertices[1] = video::S3DVertex(
+                       -1.0f, 1.0f, 0.0f, 0, 0, 1, color, 0.0f, 0.0f);
+       Vertices[2] = video::S3DVertex(
+                       1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
+       Vertices[3] = video::S3DVertex(
+                       1.0f, -1.0f, 0.0f, 0, 0, 1, color, 1.0f, 1.0f);
+       Vertices[4] = video::S3DVertex(
+                       -1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
+       Vertices[5] = video::S3DVertex(
+                       1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
+}
+
+void shadowScreenQuad::render(video::IVideoDriver *driver)
+{
+       u16 indices[6] = {0, 1, 2, 3, 4, 5};
+       driver->setMaterial(Material);
+       driver->setTransform(video::ETS_WORLD, core::matrix4());
+       driver->drawIndexedTriangleList(&Vertices[0], 6, &indices[0], 2);
+}
+
+void shadowScreenQuadCB::OnSetConstants(
+               video::IMaterialRendererServices *services, s32 userData)
+{
+       s32 TextureId = 0;
+       services->setPixelShaderConstant(
+               services->getPixelShaderConstantID("ShadowMapClientMap"),
+               &TextureId, 1);
+
+       TextureId = 1;
+       services->setPixelShaderConstant(
+               services->getPixelShaderConstantID("ShadowMapClientMapTraslucent"),
+               &TextureId, 1);
+
+       TextureId = 2;
+       services->setPixelShaderConstant(
+               services->getPixelShaderConstantID("ShadowMapSamplerdynamic"),
+               &TextureId, 1);
+}
diff --git a/src/client/shadows/shadowsScreenQuad.h b/src/client/shadows/shadowsScreenQuad.h
new file mode 100644 (file)
index 0000000..e6cc959
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include "irrlichttypes_extrabloated.h"
+#include <IMaterialRendererServices.h>
+#include <IShaderConstantSetCallBack.h>
+
+class shadowScreenQuad
+{
+public:
+       shadowScreenQuad();
+
+       void render(video::IVideoDriver *driver);
+       video::SMaterial &getMaterial() { return Material; }
+
+private:
+       video::S3DVertex Vertices[6];
+       video::SMaterial Material;
+};
+
+class shadowScreenQuadCB : public video::IShaderConstantSetCallBack
+{
+public:
+       shadowScreenQuadCB(){};
+
+       virtual void OnSetConstants(video::IMaterialRendererServices *services,
+                       s32 userData);
+};
diff --git a/src/client/shadows/shadowsshadercallbacks.cpp b/src/client/shadows/shadowsshadercallbacks.cpp
new file mode 100644 (file)
index 0000000..2f57970
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "client/shadows/shadowsshadercallbacks.h"
+
+void ShadowDepthShaderCB::OnSetConstants(
+               video::IMaterialRendererServices *services, s32 userData)
+{
+       video::IVideoDriver *driver = services->getVideoDriver();
+
+       core::matrix4 lightMVP = driver->getTransform(video::ETS_PROJECTION);
+       lightMVP *= driver->getTransform(video::ETS_VIEW);
+       lightMVP *= driver->getTransform(video::ETS_WORLD);
+
+       services->setVertexShaderConstant(
+               services->getPixelShaderConstantID("LightMVP"),
+               lightMVP.pointer(), 16);
+
+       services->setVertexShaderConstant(
+               services->getPixelShaderConstantID("MapResolution"), &MapRes, 1);
+       services->setVertexShaderConstant(
+               services->getPixelShaderConstantID("MaxFar"), &MaxFar, 1);
+
+       s32 TextureId = 0;
+       services->setPixelShaderConstant(
+               services->getPixelShaderConstantID("ColorMapSampler"), &TextureId,
+               1);
+}
diff --git a/src/client/shadows/shadowsshadercallbacks.h b/src/client/shadows/shadowsshadercallbacks.h
new file mode 100644 (file)
index 0000000..43ad489
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+Minetest
+Copyright (C) 2021 Liso <anlismon@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include "irrlichttypes_extrabloated.h"
+#include <IMaterialRendererServices.h>
+#include <IShaderConstantSetCallBack.h>
+
+class ShadowDepthShaderCB : public video::IShaderConstantSetCallBack
+{
+public:
+       void OnSetMaterial(const video::SMaterial &material) override {}
+
+       void OnSetConstants(video::IMaterialRendererServices *services,
+                       s32 userData) override;
+
+       f32 MaxFar{2048.0f}, MapRes{1024.0f};
+};
index 47296a7a51add082b22df394f42d2cd199faa19c..1cf9a4afca3b43cc442582d435e62f9bd4604905 100644 (file)
@@ -122,7 +122,14 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
                m_materials[i].Lighting = true;
                m_materials[i].MaterialType = video::EMT_SOLID;
        }
+
        m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
+
+       if (g_settings->getBool("enable_dynamic_shadows")) {
+               float val = g_settings->getFloat("shadow_sky_body_orbit_tilt");
+               m_sky_body_orbit_tilt = rangelim(val, 0.0f, 60.0f);
+       }
+
        setStarCount(1000, true);
 }
 
@@ -175,17 +182,7 @@ void Sky::render()
                video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
                video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
 
-               float nightlength = 0.415;
-               float wn = nightlength / 2;
-               float wicked_time_of_day = 0;
-               if (m_time_of_day > wn && m_time_of_day < 1.0 - wn)
-                       wicked_time_of_day = (m_time_of_day - wn) / (1.0 - wn * 2) * 0.5 + 0.25;
-               else if (m_time_of_day < 0.5)
-                       wicked_time_of_day = m_time_of_day / wn * 0.25;
-               else
-                       wicked_time_of_day = 1.0 - ((1.0 - m_time_of_day) / wn * 0.25);
-               /*std::cerr<<"time_of_day="<<m_time_of_day<<" -> "
-                               <<"wicked_time_of_day="<<wicked_time_of_day<<std::endl;*/
+               float wicked_time_of_day = getWickedTimeOfDay(m_time_of_day);
 
                video::SColor suncolor = suncolor_f.toSColor();
                video::SColor suncolor2 = suncolor2_f.toSColor();
@@ -739,10 +736,15 @@ void Sky::place_sky_body(
        * day_position: turn the body around the Z axis, to place it depending of the time of the day
        */
 {
+       v3f centrum(0, 0, -1);
+       centrum.rotateXZBy(horizon_position);
+       centrum.rotateXYBy(day_position);
+       centrum.rotateYZBy(m_sky_body_orbit_tilt);
        for (video::S3DVertex &vertex : vertices) {
                // Body is directed to -Z (south) by default
                vertex.Pos.rotateXZBy(horizon_position);
                vertex.Pos.rotateXYBy(day_position);
+               vertex.Pos.Z += centrum.Z;
        }
 }
 
@@ -931,3 +933,17 @@ void Sky::setSkyDefaults()
        m_moon_params = sky_defaults.getMoonDefaults();
        m_star_params = sky_defaults.getStarDefaults();
 }
+
+float getWickedTimeOfDay(float time_of_day)
+{
+       float nightlength = 0.415f;
+       float wn = nightlength / 2;
+       float wicked_time_of_day = 0;
+       if (time_of_day > wn && time_of_day < 1.0f - wn)
+               wicked_time_of_day = (time_of_day - wn) / (1.0f - wn * 2) * 0.5f + 0.25f;
+       else if (time_of_day < 0.5f)
+               wicked_time_of_day = time_of_day / wn * 0.25f;
+       else
+               wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f);
+       return wicked_time_of_day;
+}
index 121a16bb727e0bc57fe0307303bad6476837a5c8..83106453b4af309d64ba58d7542912b23b8ea8b5 100644 (file)
@@ -105,6 +105,8 @@ class Sky : public scene::ISceneNode
                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 @@ class Sky : public scene::ISceneNode
        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 @@ class Sky : public scene::ISceneNode
                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);
index 08fd49fc0c7dfd46bd39615a3c5ed30fad7ea1a0..7597aaa88223c63dc7653c1f01c344803a3bb66c 100644 (file)
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/numeric.h"
 #include <map>
 #include <IMeshManipulator.h>
+#include "client/renderingengine.h"
 
 #define WIELD_SCALE_FACTOR 30.0
 #define WIELD_SCALE_FACTOR_EXTRUDED 40.0
@@ -220,11 +221,18 @@ WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool l
        m_meshnode->setReadOnlyMaterials(false);
        m_meshnode->setVisible(false);
        dummymesh->drop(); // m_meshnode grabbed it
+
+       m_shadow = RenderingEngine::get_shadow_renderer();
 }
 
 WieldMeshSceneNode::~WieldMeshSceneNode()
 {
        sanity_check(g_extrusion_mesh_cache);
+
+       // Remove node from shadow casters
+       if (m_shadow)
+               m_shadow->removeNodeFromShadowList(m_meshnode);
+
        if (g_extrusion_mesh_cache->drop())
                g_extrusion_mesh_cache = nullptr;
 }
@@ -527,6 +535,10 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
        // need to normalize normals when lighting is enabled (because of setScale())
        m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
        m_meshnode->setVisible(true);
+
+       // Add mesh to shadow caster
+       if (m_shadow)
+               m_shadow->addNodeToShadowList(m_meshnode);
 }
 
 void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
index 93309723088385460f9348af97aa532cc89c7eff..d1eeb64f5799d34f465afa9eab63f78475fad7f3 100644 (file)
@@ -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 @@ class WieldMeshSceneNode : public scene::ISceneNode
        // 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);
index 871290944069d3e961c578c7993456ef145e172c..38cade80b5394bf0396bb1c372f65a0bfcd598c7 100644 (file)
@@ -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");