diff --git a/assets/data/block/air.toml b/assets/data/block/air.toml index 47a8c02..3b833c2 100644 --- a/assets/data/block/air.toml +++ b/assets/data/block/air.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = true is_transitional = false is_transparent = true -name = 'air' \ No newline at end of file +name = 'air' +roughness = 1.0 \ No newline at end of file diff --git a/assets/data/block/dirt.toml b/assets/data/block/dirt.toml index 6e15751..d7f3c5b 100644 --- a/assets/data/block/dirt.toml +++ b/assets/data/block/dirt.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = true is_transparent = false -name = 'dirt' \ No newline at end of file +name = 'dirt' +roughness = 1.0 \ No newline at end of file diff --git a/assets/data/block/grass.toml b/assets/data/block/grass.toml index 0cc428e..3d09cac 100644 --- a/assets/data/block/grass.toml +++ b/assets/data/block/grass.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = true is_transitional = false is_transparent = true -name = 'grass' \ No newline at end of file +name = 'grass' +roughness = 0.9 \ No newline at end of file diff --git a/assets/data/block/grass_block.toml b/assets/data/block/grass_block.toml index 13db505..4c72c22 100644 --- a/assets/data/block/grass_block.toml +++ b/assets/data/block/grass_block.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = true is_transparent = false -name = 'grass_block' \ No newline at end of file +name = 'grass_block' +roughness = 0.9 \ No newline at end of file diff --git a/assets/data/block/leaf.toml b/assets/data/block/leaf.toml index 07b0c99..ddb2e12 100644 --- a/assets/data/block/leaf.toml +++ b/assets/data/block/leaf.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = false is_transparent = true -name = 'leaf' \ No newline at end of file +name = 'leaf' +roughness = 0.7 \ No newline at end of file diff --git a/assets/data/block/log.toml b/assets/data/block/log.toml index a4b4bb9..8bc499e 100644 --- a/assets/data/block/log.toml +++ b/assets/data/block/log.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = false is_transparent = false -name = 'log' \ No newline at end of file +name = 'log' +roughness = 0.7 \ No newline at end of file diff --git a/assets/data/block/sand.toml b/assets/data/block/sand.toml index 793ba67..bef0425 100644 --- a/assets/data/block/sand.toml +++ b/assets/data/block/sand.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = true is_transparent = false -name = 'sand' \ No newline at end of file +name = 'sand' +roughness = 0.8 \ No newline at end of file diff --git a/assets/data/block/snowy_grass_block.toml b/assets/data/block/snowy_grass_block.toml index d52ecd0..48fcd44 100644 --- a/assets/data/block/snowy_grass_block.toml +++ b/assets/data/block/snowy_grass_block.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = true is_transparent = false -name = 'snowy_grass_block' \ No newline at end of file +name = 'snowy_grass_block' +roughness = 0.9 \ No newline at end of file diff --git a/assets/data/block/stone.toml b/assets/data/block/stone.toml index 9724776..c0c7016 100644 --- a/assets/data/block/stone.toml +++ b/assets/data/block/stone.toml @@ -7,4 +7,5 @@ is_liquid = false is_passable = false is_transitional = true is_transparent = false -name = 'stone' \ No newline at end of file +name = 'stone' +roughness = 0.75 \ No newline at end of file diff --git a/assets/data/block/template.toml b/assets/data/block/template.toml index 6be41a5..f7d095e 100644 --- a/assets/data/block/template.toml +++ b/assets/data/block/template.toml @@ -8,3 +8,4 @@ is_transparent = false is_discard = false is_blend = false is_transitional = false +roughness = 1.0 \ No newline at end of file diff --git a/assets/data/block/water.toml b/assets/data/block/water.toml index d0efcf2..0cd0e4e 100644 --- a/assets/data/block/water.toml +++ b/assets/data/block/water.toml @@ -7,4 +7,5 @@ is_liquid = true is_passable = true is_transitional = false is_transparent = true -name = 'water' \ No newline at end of file +name = 'water' +roughness = 0.02 \ No newline at end of file diff --git a/assets/shaders/billboard_f_shader.glsl b/assets/shaders/billboard_f_shader.glsl new file mode 100644 index 0000000..859a6dd --- /dev/null +++ b/assets/shaders/billboard_f_shader.glsl @@ -0,0 +1,11 @@ +#version 460 + +out vec4 frag_color; + +uniform vec3 color; + +void main(void) { + + frag_color = vec4(color, 1.0); + +} \ No newline at end of file diff --git a/assets/shaders/billboard_v_shader.glsl b/assets/shaders/billboard_v_shader.glsl new file mode 100644 index 0000000..cb69d7f --- /dev/null +++ b/assets/shaders/billboard_v_shader.glsl @@ -0,0 +1,10 @@ +#version 460 + +layout (location = 0) in vec3 vertices_pos; + +uniform mat4 mv_matrix; +uniform mat4 proj_matrix; + +void main(void) { + gl_Position = proj_matrix * mv_matrix * vec4(vertices_pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/block_f_shader.glsl b/assets/shaders/block_f_shader.glsl index 2523ed7..e9544aa 100644 --- a/assets/shaders/block_f_shader.glsl +++ b/assets/shaders/block_f_shader.glsl @@ -1,15 +1,329 @@ #version 460 in vec2 tc; +in vec3 normal; +in vec3 vert_pos; +in vec4 FragPosLightSpace; +in float roughness; flat in int tex_layer; out vec4 color; +layout (binding = 0) uniform sampler2D shadowMap; +layout (binding = 1) uniform sampler2DArray samp; + +uniform float ambientStrength; +uniform vec3 sunlightColor; +uniform vec3 ambientColor; +uniform vec3 sunlightDir; +uniform vec3 cameraPos; +uniform bool shader_on; +uniform int shadowMode; +uniform float specularStrength; +uniform float lightSizeUV; +uniform float minRadius; +uniform float maxRadius; +const vec2 poissonDisk32[32] = vec2[]( + vec2(-0.975402, -0.071138), + vec2(-0.920347, -0.411420), + vec2(-0.883908, 0.217872), + vec2(-0.815442, -0.879125), + vec2(-0.775043, 0.543896), + vec2(-0.698126, -0.227570), + vec2(-0.682433, 0.801894), + vec2(-0.563905, 0.021517), + vec2(-0.443233, -0.975116), + vec2(-0.412231, 0.361307), + vec2(-0.264969, -0.418930), + vec2(-0.241888, 0.997065), + vec2(-0.094184, -0.929389), + vec2(-0.019101, 0.680997), + vec2( 0.143832, -0.141008), + vec2( 0.199841, 0.786414), + + vec2( 0.344959, 0.293878), + vec2( 0.443233, -0.475115), + vec2( 0.537430, -0.473734), + vec2( 0.589349, 0.569135), + vec2( 0.674281, -0.178897), + vec2( 0.791975, 0.190902), + vec2( 0.815442, 0.879125), + vec2( 0.896420, -0.613392), + vec2( 0.945586, -0.768907), + vec2( 0.974844, 0.756484), + vec2(-0.814100, 0.914376), + vec2(-0.382775, 0.276768), + vec2(-0.915886, 0.457714), + vec2( 0.537800, 0.912200), + vec2(-0.620000, -0.650000), + vec2( 0.120000, -0.780000) +); + +const vec2 poissonDisk16[16] = vec2[]( + vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), + vec2(-0.09418410, -0.92938870), vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), vec2(0.14383161, -0.14100790) +); +const vec2 poissonDisk8[8] = vec2[]( + vec2( 0.1440, 0.7659), vec2(-0.5761, 0.4479), + vec2(-0.3220, -0.6058), vec2( 0.5693, -0.4048), + vec2(-0.1276, 0.1657), vec2(-0.0649, -0.0165), + vec2( 0.2773, -0.0305), vec2(-0.1134, -0.2122) +); +uniform int samples; +float random(vec3 seed) { + return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453); +} + +float FindBlocker(vec2 uv, + float zReceiver, + vec2 texelSize, + float bias, + float lightSizeUV) +{ + float avgDepth = 0.0; + int blockers = 0; + + float searchRadius = lightSizeUV * 0.5; + + for(int i = 0; i < samples; i++) + { + vec2 offset; + if (samples == 32) { + offset = + poissonDisk32[i] + * searchRadius + * texelSize; + } else if (samples == 16) { + offset = + poissonDisk16[i] + * searchRadius + * texelSize; + } else if (samples == 8) { + offset = + poissonDisk8[i] + * searchRadius + * texelSize; + } else { + offset = + poissonDisk32[i] + * searchRadius + * texelSize; + } + float depth = + texture(shadowMap, uv + offset).r; + + if(depth < zReceiver - bias) + { + avgDepth += depth; + blockers++; + } + } + + if(blockers == 0) + return -1.0; + + return avgDepth / blockers; +} + +float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir) +{ + + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + projCoords = projCoords * 0.5 + 0.5; + if (projCoords.x < 0.0 || projCoords.x > 1.0 || + projCoords.y < 0.0 || projCoords.y > 1.0 || + projCoords.z < 0.0 || projCoords.z > 1.0) { + return 0.0; + } + float currentDepth = projCoords.z; + vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); + float shadow = 0.0; + + float bias = + clamp( + 0.001 * (1.0 - dot(norm, lightDir)), + 0.0003, + 0.003 + ); + + if (shadowMode == 0) { + vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; + float angle = random(seed) * 6.2831853;; // 2*PI + float s = sin(angle), c = cos(angle); + mat2 rot = mat2(c, -s, s, c); + //float radius = 0.7; + float radius = mix(1.0, 4.0, currentDepth); + + for (int i = 0; i < samples; ++i) { + vec2 offset; + if (samples == 32) { + offset = rot * poissonDisk32[i] * radius * texelSize; + } else if (samples == 16) { + offset = rot * poissonDisk16[i] * radius * texelSize; + } else if (samples == 8) { + offset = rot * poissonDisk8[i] * radius * texelSize; + } else { + offset = rot * poissonDisk32[i] * radius * texelSize; + } + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + shadow /= float(samples); + } else if (shadowMode == 1) { + for (int x = -1; x <= 1; ++x) { + for (int y = -1; y <= 1; ++y) { + vec2 offset = vec2(x, y) * texelSize; + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + } + shadow /= 9.0; + } else if (shadowMode == 2) { + // pcf off + float pcfDepth = + texture(shadowMap, projCoords.xy).r; + + shadow = + currentDepth - bias > pcfDepth + ? 1.0 + : 0.0; + } else if (shadowMode == 3) { + float avgBlockerDepth = + FindBlocker( + projCoords.xy, + currentDepth, + texelSize, + bias, + lightSizeUV + ); + + if(avgBlockerDepth < 0.0) + { + return 0.0; + } + + vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; + float angle = random(seed) * 6.2831853;; // 2*PI + float s = sin(angle), c = cos(angle); + mat2 rot = mat2(c, -s, s, c); + /* + float penumbraRatio = (currentDepth - avgBlockerDepth); + float radius = clamp( + penumbraRatio * lightSizeUV, + minRadius, + maxRadius + ); + */ + float radius = + mix( + minRadius, + maxRadius, + smoothstep( + 0.0, + 0.05, + currentDepth - avgBlockerDepth + ) + ); + for (int i = 0; i < samples; ++i) { + vec2 offset; + if (samples == 32) { + offset = rot * poissonDisk32[i] * radius * texelSize; + } else if (samples == 16) { + offset = rot * poissonDisk16[i] * radius * texelSize; + } else if (samples == 8) { + offset = rot * poissonDisk8[i] * radius * texelSize; + } else { + offset = rot * poissonDisk32[i] * radius * texelSize; + } + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + shadow /= float(samples); + + } else { + float pcfDepth = + texture(shadowMap, projCoords.xy).r; + + shadow = + currentDepth - bias > pcfDepth + ? 1.0 + : 0.0; + } + + + return shadow; +} -layout (binding = 0) uniform sampler2DArray samp; void main(void) { - color = texture(samp, vec3(tc, tex_layer)); - if (color.a < 0.8) { + vec4 objectColor = texture(samp, vec3(tc, tex_layer)); + + if (objectColor.a < 0.8) { discard; } + if (!shader_on) { + color = objectColor; + return; + } + + + vec3 lightDir = normalize(-sunlightDir); + + vec3 norm = normalize(normal); + + vec3 V = + normalize(cameraPos - vert_pos); + + vec3 H = + normalize(lightDir + V); + + vec3 ambient = ambientStrength * ambientColor; + + float diff = max(dot(norm, lightDir), 0.0); + + vec3 diffuse = diff * sunlightColor; + + float r = + clamp(roughness, 0.0, 1.0); + + float shininess = + mix( + 512.0, + 4.0, + r + ); + float ks = + mix( + 0.8, + 0.02, + r + ); + + float spec = 0.0; + + if(diff > 0.0) + { + spec = + ks * + pow( + max(dot(norm,H),0.0), + shininess + ); + } + + + + + vec3 specular = spec * sunlightColor * specularStrength; + + float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); + + color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular, objectColor.a); + //color = varyingColor; } diff --git a/assets/shaders/block_v_shader.glsl b/assets/shaders/block_v_shader.glsl index 317b55e..d22fcad 100644 --- a/assets/shaders/block_v_shader.glsl +++ b/assets/shaders/block_v_shader.glsl @@ -3,8 +3,15 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec2 texCoord; layout (location = 2) in float layer; +layout (location = 3) in vec3 aNormal; +layout (location = 4) in float Roughness; + out vec2 tc; +out vec3 normal; +out vec3 vert_pos; flat out int tex_layer; +out vec4 FragPosLightSpace; +out float roughness; mat4 buildRotateX(float rad); mat4 buildRotateY(float rad); @@ -13,13 +20,21 @@ mat4 buildTranslate(float x, float y, float z); uniform mat4 mv_matrix; uniform mat4 proj_matrix; - - +uniform mat4 norm_matrix; +uniform mat4 lightSpaceMatrix; void main(void) { - gl_Position = proj_matrix * mv_matrix * vec4(pos, 1.0); + vec4 viewPos = mv_matrix * vec4(pos, 1.0); + + vert_pos = pos; + tc = texCoord; + tex_layer = int(layer); + roughness = Roughness; + normal = mat3(norm_matrix) * aNormal; + FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0); + gl_Position = proj_matrix * viewPos; } mat4 buildTranslate(float x, float y, float z) { diff --git a/assets/shaders/depth_fragment_shader.glsl b/assets/shaders/depth_fragment_shader.glsl new file mode 100644 index 0000000..9976e10 --- /dev/null +++ b/assets/shaders/depth_fragment_shader.glsl @@ -0,0 +1,16 @@ +#version 460 + +in vec2 tc; +flat in int tex_layer; +layout (binding = 1) uniform sampler2DArray samp; + +uniform bool is_discard_tranparent; + +void main() { + if (is_discard_tranparent) { + vec4 texColor = texture(samp, vec3(tc, tex_layer)); + if (texColor.a < 0.8) + discard; + } + //gl_FragDepth = gl_FragCoord.z; +} \ No newline at end of file diff --git a/assets/shaders/depth_shader.glsl b/assets/shaders/depth_shader.glsl new file mode 100644 index 0000000..5aab56a --- /dev/null +++ b/assets/shaders/depth_shader.glsl @@ -0,0 +1,12 @@ +#version 460 +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in float layer; +uniform mat4 lightSpaceMatrix; +out vec2 tc; +flat out int tex_layer; +void main() { + tc = texCoord; + tex_layer = int(layer); + gl_Position = lightSpaceMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/sky_f_shader.glsl b/assets/shaders/sky_f_shader.glsl index e52005f..0c9e0e6 100644 --- a/assets/shaders/sky_f_shader.glsl +++ b/assets/shaders/sky_f_shader.glsl @@ -1,9 +1,95 @@ #version 460 +in vec3 dir; + out vec4 frag_color; -void main(void) { - - frag_color = vec4(0.529, 0.808, 0.922, 1.0); +uniform vec3 skyTop; +uniform vec3 skyBottom; + +uniform vec3 sunDir; +uniform vec3 sunColor; + +uniform float horizonSharpness; +uniform float cloudWhiteMix; + +uniform float cloudThresholdLow; +uniform float cloudThresholdHigh; + +uniform float time; + + +float hash(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123); +} + +float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash(i); + float b = hash(i + vec2(1.0, 0.0)); + float c = hash(i + vec2(0.0, 1.0)); + float d = hash(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p) { + float v = 0.0; + float amp = 0.5; + for (int i = 0; i < 5; i++) { + v += amp * noise(p); + p *= 2.0; + amp *= 0.5; + } + return v; +} + +void main(void) { + vec3 sund = normalize(sunDir); + + float t = + clamp( + dir.y * 0.5 + 0.5, + 0.0, + 1.0 + ); + + + vec3 sky = + mix( + skyBottom, + skyTop, + pow(t, horizonSharpness) + ); + + // cloud + if (dir.y > 0.0) { + vec2 cloud_uv = dir.xz / (dir.y + 0.15) * 0.5 + vec2(time * 0.005, time * 0.002); + float cloud_density = fbm(cloud_uv * 2.0); + float safeLow = cloudThresholdLow; + float safeHigh = max(cloudThresholdHigh, cloudThresholdLow + 0.001); + cloud_density = smoothstep(safeLow,safeHigh, cloud_density); + + + float fade = smoothstep(0.0, 0.3, dir.y) * (1.0 - smoothstep(0.85, 1.0, dir.y)); + cloud_density *= fade; + + vec3 cloud_color = mix(skyBottom, vec3(1.0), cloudWhiteMix); + sky = mix(sky, cloud_color, cloud_density * 0.6); + } + + float sunAmount = max(dot(dir, sund), 0.0); + + //float glow = pow(sunAmount, 8.0) * 0.15; + + float glow = pow(sunAmount, 8.0) * 0.15 + pow(sunAmount, 32.0) * 0.3; + + sky += glow * sunColor; + + frag_color = vec4(sky, 1.0); + //frag_color = vec4(vec3(sunAmount), 1.0); + //frag_color = vec4(t,0,0,1); + } \ No newline at end of file diff --git a/assets/shaders/sky_v_shader.glsl b/assets/shaders/sky_v_shader.glsl index cb69d7f..20b8ea2 100644 --- a/assets/shaders/sky_v_shader.glsl +++ b/assets/shaders/sky_v_shader.glsl @@ -5,6 +5,12 @@ layout (location = 0) in vec3 vertices_pos; uniform mat4 mv_matrix; uniform mat4 proj_matrix; +out vec3 dir; + void main(void) { + // Our skybox mesh uses [0,1] coordinates instead of the usual [-1,1]. + // Shift to the cube center before normalizing to obtain the correct + // view direction for atmospheric calculations. + dir = normalize(vertices_pos - vec3(0.5)); gl_Position = proj_matrix * mv_matrix * vec4(vertices_pos, 1.0); } \ No newline at end of file diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index 2abb3fc..d831a13 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -32,6 +32,7 @@ public: const glm::vec3& get_camera_pos() const; bool is_under_water() const; + glm::vec3 get_camera_front() const; }; } // namespace Cubed diff --git a/include/Cubed/constants.hpp b/include/Cubed/constants.hpp index ccfd053..a8a3151 100644 --- a/include/Cubed/constants.hpp +++ b/include/Cubed/constants.hpp @@ -23,9 +23,9 @@ constexpr float DEFAULT_MAX_RUN_SPEED = 7.0f; constexpr float DEFAULT_ACCELERATION = 10.0f; constexpr float DEFAULT_DECELERATION = 15.0f; constexpr float DEFAULT_G = 22.5f; -static constexpr int SIZE_X = CHUNK_SIZE; -static constexpr int SIZE_Y = WORLD_SIZE_Y; -static constexpr int SIZE_Z = CHUNK_SIZE; +constexpr int SIZE_X = CHUNK_SIZE; +constexpr int SIZE_Y = WORLD_SIZE_Y; +constexpr int SIZE_Z = CHUNK_SIZE; constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index 4756805..7993746 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -44,14 +44,20 @@ private: bool m_need_save_config = false; bool m_gen_thread_running = true; int m_theme = 0; + int m_pre_set_day_tick = 0; + int m_pre_set_tick_speed = 1; + bool m_tick_frezze = false; + int m_samples_idx = 1; void show_about_table_bar(); void show_biome_table_bar(); + void show_time_table_bar(); void show_cave_table_bar(); void show_river_table_bar(); void show_settings_tab_item(); void show_world_tab_item(); void show_player_tab_item(); void show_items_tab_item(); + void show_shader_tab_item(); void update_config_view(); void update_player_profile(); diff --git a/include/Cubed/gameplay/block.hpp b/include/Cubed/gameplay/block.hpp index 87fa841..d859711 100644 --- a/include/Cubed/gameplay/block.hpp +++ b/include/Cubed/gameplay/block.hpp @@ -51,13 +51,14 @@ struct BlockData { bool is_discard = false; bool is_blend = false; bool is_transitional = false; + float roughness = 1.0f; BlockData(BlockType b_id, std::string_view b_name, bool liquid, bool passable, bool cross_plane, bool transparent, bool gas, - bool discard, bool blend, bool transitional) + bool discard, bool blend, bool transitional, float r) : name(b_name), id(b_id), is_liquid(liquid), is_gas(gas), is_passable(passable), is_cross_plane(cross_plane), is_transparent(transparent), is_discard(discard), is_blend(blend), - is_transitional(transitional) {} + is_transitional(transitional), roughness(r) {} }; class BlockManager { @@ -79,6 +80,7 @@ public: static bool is_discard(BlockType id); static bool is_blend(BlockType id); static bool is_transitional(BlockType id); + static float roughness(BlockType id); static BlockType cross_plane_index(BlockType id); private: diff --git a/include/Cubed/gameplay/game_time.hpp b/include/Cubed/gameplay/game_time.hpp new file mode 100644 index 0000000..5cb08b7 --- /dev/null +++ b/include/Cubed/gameplay/game_time.hpp @@ -0,0 +1,10 @@ +#pragma once + +// Prevent unsigned underflow issues in subtraction +using TickType = long long; + +constexpr int DEFAULT_PER_TICK_TIME = 50; + +constexpr TickType DAY_TIME = 24000; + +constexpr TickType PER_HOUR = 1000; \ No newline at end of file diff --git a/include/Cubed/gameplay/vertex_data.hpp b/include/Cubed/gameplay/vertex_data.hpp index aafed9d..b3ae5bb 100644 --- a/include/Cubed/gameplay/vertex_data.hpp +++ b/include/Cubed/gameplay/vertex_data.hpp @@ -7,7 +7,7 @@ namespace Cubed { class World; struct VertexData { - std::vector m_vertices; + std::vector m_vertices; GLuint m_vbo = 0; GLuint m_vao = 0; std::atomic m_sum{0}; diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index fec1ad5..21fe48c 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -2,6 +2,7 @@ #include "Cubed/AABB.hpp" #include "Cubed/gameplay/cave_carver.hpp" #include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/river_worm.hpp" #include @@ -39,12 +40,21 @@ private: std::unordered_map; using ChunkPosSet = std::unordered_set; using ChunkHashMap = std::unordered_map; + glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f}; ChunkHashMap m_chunks; std::unordered_map m_players; std::vector m_planes; std::thread m_gen_thread; + std::thread m_server_thread; + + std::stop_source m_server_stop_source; + + std::atomic m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms + + std::atomic m_day_tick = 6000; + mutable std::mutex m_chunks_mutex; std::mutex m_gen_signal_mutex; std::mutex m_new_chunk_queue_mutex; @@ -59,8 +69,12 @@ private: std::atomic m_is_rebuilding{false}; std::atomic m_chunk_gen_finished{false}; std::atomic m_could_gen{true}; + std::atomic m_tick_running{true}; std::atomic m_rendering_distance{24}; std::atomic m_chunk_gen_fraction{0.0f}; + + std::atomic m_game_ticks{0}; + std::vector m_dirty_queue; std::vector m_render_snapshots; std::vector> m_new_chunk; @@ -119,12 +133,25 @@ public: int rendering_distance() const; void rendering_distance(int rendering_distance); void start_gen_thread(); + void start_server_thread(); void stop_gen_thread(); + void stop_server_thread(); + void serever_run(std::stop_token stoken); CaveCarver& cave_carcer(); RiverWorm& river_worm(); std::vector& planes(); std::vector& render_snapshots(); + + glm::vec3 sunlight_dir() const; + TickType game_tick() const; + TickType day_tick() const; + void day_tick(TickType tick); + int per_tick_time() const; + void per_tick_time(int ms); + + bool is_tick_running() const; + void tick_running(bool run); }; } // namespace Cubed diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 2d3336f..725d931 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -91,6 +91,51 @@ constexpr float TEX_COORDS[6][6][2] = { {0.0f, 1.0f}, // back left {0.0f, 0.0f}} // front left }; + +constexpr float NORMALS[6][6][3] = { + // ===== front (z = +1) ===== + {{0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}}, + // ===== right (x = +1) ===== + {{1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}}, + // ===== back (z = -1) ===== + {{0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}}, + // ===== left (x = -1) ===== + {{-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}}, + // ===== top (y = +1) ===== + {{0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}}, + // ===== bottom (y = -1) ===== + {{0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}}}; + #pragma endregion constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, @@ -148,6 +193,24 @@ constexpr float CROSS_TEX_COORDS[2][6][2] = { {1.0f, 1.0f}, // bottom right {0.0f, 1.0f}}, // bottom left }; + +constexpr float CROSS_NORMALS[2][6][3] = { + // ===== Plane 1: upward ===== + {{0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}}, + + // ===== Plane 2: upward ===== + {{0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}}}; + #pragma endregion constexpr float QUAD_VERTICES[] = { @@ -156,10 +219,12 @@ constexpr float QUAD_VERTICES[] = { -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; -struct Vertex { +struct Vertex3D { float x = 0.0f, y = 0.0f, z = 0.0f; float s = 0.0f, t = 0.0f; float layer = 0.0f; + float nx = 0.0f, ny = 0.0f, nz = 0.0f; + float roughness = 1.0f; }; struct Vertex2D { diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index d8d1265..0a187cb 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -28,22 +28,71 @@ public: void update_fov(float fov); void update_proj_matrix(float aspect, float width, float height); void updata_framebuffer(int width, int height); + float& ambient_strength(); + + bool& discard_transparent(); + bool& shader_on(); + int& shadow_mode(); + int& light_cull_face(); + int& light_size_uv(); + float& min_radius(); + float& max_radius(); + int& samples(); + float& specular_strength(); + float& cloud_speed(); + float& cloud_threshold_low(); + float& cloud_threshold_high(); private: + struct ParallelLight { + glm::vec3 sundir; + glm::vec3 lightdir; + float sun_height = 0.0f; + float day_light = 0.0f; + float day_factor = 0.0f; + glm::vec3 sun_color; + glm::vec3 directional_light_color; + glm::vec3 finnal_ambient_color; + }; + + static constexpr glm::vec3 SUN_COLOR{1.00f, 0.95f, 0.80f}; + static constexpr glm::vec3 MOON_COLOR{0.75f, 0.80f, 1.00f}; + + static constexpr glm::vec3 SUNSET_SUNLIGHT_COLOR{1.00f, 0.45f, 0.15f}; + static constexpr glm::vec3 NOON_SUNLIGHT_COLOR{1.00f, 0.90f, 0.65f}; + static constexpr glm::vec3 SUNSET_AMBIENT_COLOR{0.18f, 0.12f, 0.35f}; + static constexpr glm::vec3 NOON_AMBIENT_COLOR{0.35f, 0.50f, 0.85f}; + static constexpr glm::vec3 MOONLIGHT_COLOR{0.55f, 0.70f, 1.00f}; + static constexpr glm::vec3 NIGHT_AMBIENT_COLOR{0.08f, 0.10f, 0.18f}; + static constexpr float FAR_PLANE = 1000.0f; + static constexpr float NEAR_PLANE = 0.1f; + static constexpr float SUN_SIZE = 50.0f; + static constexpr float MOON_SIZE = 50.0f; + static constexpr float DEPTH_MAP_SIZE = 4096.0f; + static constexpr float ANGLE_STEP_DEG = 0.5f; + float m_ambient_strength = 0.1f; + const Camera& m_camera; DevPanel& m_dev_panel; const TextureManager& m_texture_manager; World& m_world; + bool m_discard_tranparent = true; + bool m_shader_on = true; + int m_shadow_mode = 0; + int m_light_cull_face = 0; float m_aspect = 0.0f; float m_fov = DEFAULT_FOV; float m_delta_time = 0.0f; + float m_cloud_time = 0.0f; + float m_cloud_speed = 5.0f; + float m_width = 0.0f; float m_height = 0.0f; - glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat; + glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat, m_norm_mat; GLuint m_mv_loc = 0; GLuint m_proj_loc = 0; @@ -62,18 +111,43 @@ private: GLuint m_accum_texture = 0; GLuint m_reveal_texture = 0; GLuint m_oit_depth_render_buffer = 0; + + GLuint m_depth_map_fbo = 0; + GLuint m_depth_map_texture = 0; + GLuint m_quad_vbo = 0; glm::mat4 m_ui_proj; glm::mat4 m_ui_m_matrix; std::unordered_map m_shaders; + glm::vec3 m_blend_from_lightdir; + glm::vec3 m_blend_to_lightdir; + float m_blend_t = 1.0f; + bool m_blend_initialized = false; + static constexpr float BLEND_DURATION = 0.15f; + int m_light_size_uv = 20; + + float m_min_radius = 2.0f; + float m_max_radius = 20.0f; + int m_samples = 16; + + float m_specular_strength = 0.5f; + + float moon_intensity = 0.3f; + float sun_intensity = 1.00f; + + float m_cloud_threshold_low = 0.5f; + float m_cloud_threshold_high = 0.75f; + + ParallelLight m_parallel_light; /* 0 - quad vao 1 - sky vao 2 - outline vao 3 - ui vao 4 - text vao + */ std::vector m_vao; std::vector m_ui; @@ -81,6 +155,8 @@ private: void init_quad(); void init_text(); + void day_night_calculation(); + void render_outline(); void render_sky(); void render_text(); @@ -88,6 +164,11 @@ private: void render_world(); void render_underwater(); void render_dev_panel(); + + glm::vec3 quantize_sun_direction(const glm::vec3& sundir, + float angle_step_deg) const; + glm::vec3 get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_sundir, + float dt); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/math_tools.hpp b/include/Cubed/tools/math_tools.hpp index 5bb4795..4ee8b1c 100644 --- a/include/Cubed/tools/math_tools.hpp +++ b/include/Cubed/tools/math_tools.hpp @@ -12,6 +12,8 @@ float smootherstep(float edge0, float edge1, float x); bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents, const std::vector& planes); float deterministic_random(int x, int z, uint64_t seed); +glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t); + } // namespace Math } // namespace Cubed \ No newline at end of file diff --git a/src/block.cpp b/src/block.cpp index d3ab2f1..9be8c94 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -111,6 +111,15 @@ bool BlockManager::is_transitional(BlockType id) { } return m_datas[id].is_transitional; } + +float BlockManager::roughness(BlockType id) { + if (id >= sums()) { + Logger::error("Id {}, is Over The Max Id", id, sums() - 1); + return m_datas[0].roughness; + } + return m_datas[id].roughness; +} + void BlockManager::init() { fs::path data_path{block_data_dir}; @@ -149,9 +158,11 @@ void BlockManager::init() { auto is_discard = safe_get_value(block, "is_discard", false); auto is_blend = safe_get_value(block, "is_blend", false); auto is_transitional = safe_get_value(block, "is_transitional", false); + auto roughness = safe_get_value(block, "roughness", 1.0); m_datas.emplace_back(*id, *name, *is_liquid, *is_passable, *is_cross_plane, *is_transparent, *is_gas, - *is_discard, *is_blend, *is_transitional); + *is_discard, *is_blend, *is_transitional, + static_cast(*roughness)); } std::sort( m_datas.begin(), m_datas.end(), diff --git a/src/camera.cpp b/src/camera.cpp index 224f91e..22562a2 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -60,4 +60,6 @@ const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; } bool Camera::is_under_water() const { return m_under_water; } +glm::vec3 Camera::get_camera_front() const { return m_player->get_front(); } + } // namespace Cubed diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index d2902e2..ca45c53 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -84,6 +84,7 @@ void DevPanel::render() { show_world_tab_item(); show_player_tab_item(); show_items_tab_item(); + show_shader_tab_item(); show_about_table_bar(); ImGui::EndTabBar(); @@ -107,6 +108,7 @@ void DevPanel::show_about_table_bar() { ImGui::Text("FreeType"); ImGui::Text("toml++"); ImGui::Text("Dear ImGui"); + ImGui::Text("Tbb"); ImGui::Separator(); ImGui::Text("Special Thanks"); ImGui::Text("TANGERIME"); @@ -263,6 +265,31 @@ void DevPanel::show_biome_table_bar() { } } +void DevPanel::show_time_table_bar() { + World& world = m_app.world(); + ImGui::Text("Game Tick %llu", world.game_tick()); + ImGui::SameLine(); + ImGui::Text("Day Tick %llu", world.day_tick()); + m_tick_frezze = !world.is_tick_running(); + ImGui::SameLine(); + if (ImGui::Checkbox("Tick Frezze", &m_tick_frezze)) { + world.tick_running(!m_tick_frezze); + } + if (ImGui::SliderInt("SetDayTick", &m_pre_set_day_tick, 0, DAY_TIME)) { + } + ImGui::SameLine(); + if (ImGui::Button("Set##DayTick")) { + world.day_tick(static_cast(m_pre_set_day_tick)); + } + ImGui::Text("MSPT %d", world.per_tick_time()); + if (ImGui::SliderInt("SetMSPT", &m_pre_set_tick_speed, 1, 200)) { + } + ImGui::SameLine(); + if (ImGui::Button("Set##MSPT")) { + world.per_tick_time(m_pre_set_tick_speed); + } +} + void DevPanel::show_cave_table_bar() { auto& cave_carcer = m_app.world().cave_carcer(); @@ -336,6 +363,7 @@ void DevPanel::show_settings_tab_item() { static_cast(m_config.mouse_sensitivity)); m_player->hot_reload(); } + if (ImGui::SliderInt("Distance", &m_config.rendering_distance, 2, 128)) { Config::get().set("world.rendering_distance", @@ -457,6 +485,10 @@ void DevPanel::show_world_tab_item() { ImGui::Text("Chunk Build Progress\n"); ImGui::ProgressBar(m_app.world().chunk_gen_fraction()); if (ImGui::BeginTabBar("World Settings")) { + if (ImGui::BeginTabItem("Time")) { + show_time_table_bar(); + ImGui::EndTabItem(); + } if (ImGui::BeginTabItem("Cave")) { show_cave_table_bar(); ImGui::EndTabItem(); @@ -577,6 +609,58 @@ void DevPanel::show_items_tab_item() { } } +void DevPanel::show_shader_tab_item() { + + static const char* shader_mode[] = { + "Rotated Poisson Disk PCF", "3x3 Square Grid PCF", "PCF off", "PCSS"}; + static const char* cull_face_mode[] = {"Front", "Back"}; + static const char* samples[] = {"8", "16", "32"}; + if (ImGui::BeginTabItem("shader")) { + ImGui::Checkbox("Shader", &m_app.renderer().shader_on()); + if (ImGui::SliderFloat("AmbientStrength", + &m_app.renderer().ambient_strength(), 0.0f, + 0.35f)) + ; + ImGui::SliderFloat("SpecularStrength", + &m_app.renderer().specular_strength(), 0.0f, 2.0f); + ImGui::Checkbox("Discard Transparent", + &m_app.renderer().discard_transparent()); + ImGui::Combo("ShaderMode", &m_app.renderer().shadow_mode(), shader_mode, + IM_ARRAYSIZE(shader_mode)); + ImGui::Combo("LightCullFaceMode", &m_app.renderer().light_cull_face(), + cull_face_mode, IM_ARRAYSIZE(cull_face_mode)); + if (ImGui::Combo("samples", &m_samples_idx, samples, + IM_ARRAYSIZE(samples))) { + if (m_samples_idx == 0) { + m_app.renderer().samples() = 8; + } else if (m_samples_idx == 1) { + m_app.renderer().samples() = 16; + } else if (m_samples_idx == 2) { + m_app.renderer().samples() = 32; + } else { + Logger::warn("Samples Index {} is invaild", m_samples_idx); + m_app.renderer().samples() = 16; + } + } + if (m_app.renderer().shadow_mode() == 3) { + ImGui::SliderInt("LightSizeUV", &m_app.renderer().light_size_uv(), + 0, 800); + ImGui::SliderFloat("MinRaduis", &m_app.renderer().min_radius(), + 0.0f, 20.0f); + ImGui::SliderFloat("MaxRadius", &m_app.renderer().max_radius(), + 0.0f, 100.0f); + } + ImGui::SliderFloat("Cloud Speed", &m_app.renderer().cloud_speed(), 1.0f, + 100.0f); + ImGui::SliderFloat("Cloud Threshold Low", + &m_app.renderer().cloud_threshold_low(), 0.0f, 1.0f); + ImGui::SliderFloat("Cloud Threshold High", + &m_app.renderer().cloud_threshold_high(), 0.0f, + 1.0f); + ImGui::EndTabItem(); + } +} + void DevPanel::update_config_view() { auto config = Config::get(); m_config.fov = diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 2fc9ae9..d9d3289 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -366,13 +366,19 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) { cur_id); } for (int i = 0; i < 6; i++) { - Vertex vex = { + Vertex3D vex = { VERTICES_POS[face][i][0] + (float)world_x * 1.0f, VERTICES_POS[face][i][1] + (float)world_y * 1.0f, VERTICES_POS[face][i][2] + (float)world_z * 1.0f, TEX_COORDS[face][i][0], TEX_COORDS[face][i][1], - static_cast(cur_id * 6 + face) + + static_cast(cur_id * 6 + face), + + NORMALS[face][i][0], + NORMALS[face][i][1], + NORMALS[face][i][2], + BlockManager::roughness(cur_id) }; if (BlockManager::is_transparent(cur_id)) { @@ -413,13 +419,17 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z, } for (int face = 0; face < 2; face++) { for (int i = 0; i < 6; i++) { - Vertex vex = { + Vertex3D vex = { CROSS_VERTICES_POS[face][i][0] + (float)world_x * 1.0f, CROSS_VERTICES_POS[face][i][1] + (float)world_y * 1.0f, CROSS_VERTICES_POS[face][i][2] + (float)world_z * 1.0f, CROSS_TEX_COORDS[face][i][0], CROSS_TEX_COORDS[face][i][1], - static_cast(BlockManager::cross_plane_index(id)) + static_cast(BlockManager::cross_plane_index(id)), + CROSS_NORMALS[face][i][0], + CROSS_NORMALS[face][i][1], + CROSS_NORMALS[face][i][2], + BlockManager::roughness(id) }; m_vertex_data[1].m_vertices.emplace_back(vex); diff --git a/src/gameplay/vertex_data.cpp b/src/gameplay/vertex_data.cpp index ea1805a..87ae597 100644 --- a/src/gameplay/vertex_data.cpp +++ b/src/gameplay/vertex_data.cpp @@ -41,18 +41,23 @@ void VertexData::upload() { } glBindVertexArray(m_vao); glBindBuffer(GL_ARRAY_BUFFER, m_vbo); - glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex), + glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex3D), m_vertices.data(), GL_DYNAMIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void*)offsetof(Vertex, s)); - glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void*)offsetof(Vertex, layer)); - + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (void*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, s)); + glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, layer)); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, nx)); + glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, roughness)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 21b337e..0c319cb 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -6,6 +6,9 @@ #include "Cubed/tools/cubed_hash.hpp" #include +#include +#include +using namespace std::chrono; namespace Cubed { @@ -18,6 +21,7 @@ World::World() {} World::~World() { stop_gen_thread(); + stop_server_thread(); m_chunks.clear(); { std::lock_guard lk(m_delete_vbo_mutex); @@ -86,6 +90,8 @@ void World::init_world() { auto d = std::chrono::duration_cast(t2 - t1); Logger::info("Chunk Block Init Finish, Time Consuming: {}", d); + start_server_thread(); + Logger::info("TestPlayer Create Finish"); } void World::init_chunks() { @@ -727,6 +733,11 @@ void World::start_gen_thread() { }); } +void World::start_server_thread() { + m_server_thread = std::thread( + [this]() { serever_run(m_server_stop_source.get_token()); }); +} + void World::stop_gen_thread() { m_gen_running = false; m_gen_cv.notify_all(); @@ -736,6 +747,25 @@ void World::stop_gen_thread() { Logger::info("Gen Thread Stopped"); } +void World::stop_server_thread() { + m_server_stop_source.request_stop(); + if (m_server_thread.joinable()) { + m_server_thread.join(); + } +} + +void World::serever_run(std::stop_token stoken) { + Logger::info("Server Thread Started!"); + while (!stoken.stop_requested()) { + std::this_thread::sleep_for(milliseconds(m_per_tick_time)); + if (m_tick_running) { + ++m_game_ticks; + m_day_tick = (m_day_tick + 1) % DAY_TIME; + } + } + Logger::info("Server Thread Stopped!"); +} + void World::need_gen() { if (!m_could_gen) { Logger::warn("It is generating or consuming new chunks"); @@ -1006,4 +1036,49 @@ std::vector& World::planes() { return m_planes; } std::vector& World::render_snapshots() { return m_render_snapshots; }; +/* +glm::vec3 World::sunlight_dir() const { + float t = static_cast(m_day_tick) / DAY_TIME; + + float azimuth = glm::radians(90.0f - t * 360.0f); + + float altitude = + glm::half_pi() * sin((t - 0.25f) * glm::two_pi()); + + glm::vec3 dir{cos(altitude) * cos(azimuth), sin(altitude), + cos(altitude) * sin(azimuth)}; + + return glm::normalize(-dir); +} +*/ + +glm::vec3 World::sunlight_dir() const { + float altitude = sin((m_day_tick - 6 * PER_HOUR) / + static_cast(DAY_TIME / 2) * std::numbers::pi) * + 90.0f; + + float t = static_cast(m_day_tick) / DAY_TIME; + float azimuth = 90.0f - 360.0f * (t - 0.25f); + + float alt = glm::radians(altitude); + float az = glm::radians(azimuth); + glm::vec3 dir; + dir.x = cos(alt) * sin(az); + dir.y = sin(alt); + dir.z = cos(alt) * cos(az); + + return glm::normalize(-dir); +} + +TickType World::game_tick() const { return m_game_ticks.load(); } +TickType World::day_tick() const { return m_day_tick.load(); } +void World::day_tick(TickType tick) { + tick %= DAY_TIME; + m_day_tick = tick; +} +int World::per_tick_time() const { return m_per_tick_time.load(); } +void World::per_tick_time(int ms) { m_per_tick_time = ms; } + +bool World::is_tick_running() const { return m_tick_running.load(); } +void World::tick_running(bool run) { m_tick_running = run; } } // namespace Cubed \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index a1c06e8..a3d44ec 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -18,6 +18,7 @@ #include #include #include + namespace Cubed { Renderer::Renderer(const Camera& camera, World& world, @@ -42,6 +43,9 @@ Renderer::~Renderer() { glDeleteTextures(1, &m_accum_texture); glDeleteTextures(1, &m_reveal_texture); glDeleteRenderbuffers(1, &m_oit_depth_render_buffer); + + glDeleteFramebuffers(1, &m_depth_map_fbo); + glDeleteTextures(1, &m_depth_map_texture); } void Renderer::hot_reload() { @@ -76,6 +80,10 @@ void Renderer::init() { Shader composite_block_shader{"composite", "shaders/block_composite_v_shader.glsl", "shaders/block_composite_f_shader.glsl"}; + Shader depth_shader{"depth_shader", "shaders/depth_shader.glsl", + "shaders/depth_fragment_shader.glsl"}; + Shader billboard{"billboard", "shaders/billboard_v_shader.glsl", + "shaders/billboard_f_shader.glsl"}; m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)}); @@ -86,6 +94,8 @@ void Renderer::init() { m_shaders.insert({accum_shader.hash(), std::move(accum_shader)}); m_shaders.insert( {composite_block_shader.hash(), std::move(composite_block_shader)}); + m_shaders.insert({depth_shader.hash(), std::move(depth_shader)}); + m_shaders.insert({billboard.hash(), std::move(billboard)}); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -192,27 +202,56 @@ void Renderer::init_text() { } void Renderer::render() { + glDisable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + day_night_calculation(); render_sky(); render_world(); render_outline(); glBindFramebuffer(GL_FRAMEBUFFER, 0); + glEnable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0); glClear(GL_COLOR_BUFFER_BIT); render_underwater(); - + glDisable(GL_FRAMEBUFFER_SRGB); render_ui(); render_text(); render_dev_panel(); } +void Renderer::day_night_calculation() { + m_parallel_light.sundir = glm::normalize(m_world.sunlight_dir()); + m_parallel_light.sun_height = (-m_parallel_light.sundir).y; + m_parallel_light.lightdir = m_parallel_light.sundir; + + m_parallel_light.day_light = + glm::smoothstep(0.15f, 0.3f, m_parallel_light.sun_height); + + m_parallel_light.sun_color = mix(SUNSET_SUNLIGHT_COLOR, NOON_SUNLIGHT_COLOR, + m_parallel_light.day_light); + + glm::vec3 ambient_color = mix(SUNSET_AMBIENT_COLOR, NOON_AMBIENT_COLOR, + m_parallel_light.day_light); + + m_parallel_light.day_factor = + glm::smoothstep(-0.15f, 0.05f, m_parallel_light.sun_height); + auto day_factor = m_parallel_light.day_factor; + float light_intensity = + glm::smoothstep(moon_intensity, sun_intensity, day_factor); + m_parallel_light.directional_light_color = + glm::mix(MOON_COLOR, m_parallel_light.sun_color, day_factor) * + light_intensity; + m_parallel_light.finnal_ambient_color = + glm::mix(NIGHT_AMBIENT_COLOR, ambient_color, day_factor); + + m_ambient_strength = glm::mix(0.45f, 0.25f, day_factor); +} + void Renderer::render_outline() { const auto& shader = get_shader("outline"); shader.use(); @@ -244,26 +283,114 @@ void Renderer::render_outline() { void Renderer::render_sky() { - const auto& shader = get_shader("sky"); + glm::vec3 zenith = {0.20f, 0.45f, 0.95f}; - shader.use(); - m_mv_loc = shader.loc("mv_matrix"); - m_proj_loc = shader.loc("proj_matrix"); + glm::vec3 horizon = {0.55f, 0.75f, 1.00f}; + + glm::vec3 sunset_zenith = {0.05f, 0.10f, 0.25f}; + + glm::vec3 sunset_horizon = {1.0f, 0.35f, 0.10f}; + + glm::vec3 night_zenith = {0.018f, 0.023f, 0.048f}; + glm::vec3 night_horizon = {0.022f, 0.027f, 0.052f}; + + constexpr float NIGHT_SHARPNESS = 0.35f; + constexpr float SUNSET_SHARPNESS = 0.6f; + constexpr float NOON_SHARPNESS = 0.35f; + + constexpr float NIGHT_CLOUD_MIX = 0.3f; + constexpr float SUNSET_CLOUD_MIX = 0.4f; + constexpr float NOON_CLOUD_MIX = 0.7; + + glm::vec3 day_top = mix(sunset_zenith, zenith, m_parallel_light.day_light); + glm::vec3 day_bottom = + mix(sunset_horizon, horizon, m_parallel_light.day_light); + + glm::vec3 sky_top = mix(night_zenith, day_top, m_parallel_light.day_factor); + glm::vec3 sky_bottom = + mix(night_horizon, day_bottom, m_parallel_light.day_factor); + + float day_sharpness = + glm::mix(SUNSET_SHARPNESS, NOON_SHARPNESS, m_parallel_light.day_light); + + float horizon_sharpness = + glm::mix(NIGHT_SHARPNESS, day_sharpness, m_parallel_light.day_factor); + + float day_cloud_mix = + glm::mix(SUNSET_CLOUD_MIX, NOON_CLOUD_MIX, m_parallel_light.day_light); + float cloud_white_mix = + glm::mix(NIGHT_CLOUD_MIX, day_cloud_mix, m_parallel_light.day_factor); + + m_cloud_time += m_delta_time * m_cloud_speed; + + const auto& sky_shader = get_shader("sky"); + + sky_shader.use(); + m_mv_loc = sky_shader.loc("mv_matrix"); + m_proj_loc = sky_shader.loc("proj_matrix"); m_m_mat = glm::translate(glm::mat4(1.0f), m_camera.get_camera_pos() - glm::vec3(0.5f, 0.5f, 0.5f)); m_v_mat = m_camera.get_camera_lookat(); m_mv_mat = m_v_mat * m_m_mat; + glm::vec3 sun_dir_view = (-m_parallel_light.sundir); + glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - + glUniform3fv(sky_shader.loc("skyTop"), 1, glm::value_ptr(sky_top)); + glUniform3fv(sky_shader.loc("skyBottom"), 1, glm::value_ptr(sky_bottom)); + glUniform3fv(sky_shader.loc("sunDir"), 1, glm::value_ptr(sun_dir_view)); + glUniform3fv(sky_shader.loc("sunColor"), 1, + glm::value_ptr(m_parallel_light.directional_light_color)); + glUniform1f(sky_shader.loc("horizonSharpness"), horizon_sharpness); + glUniform1f(sky_shader.loc("time"), m_cloud_time); + glUniform1f(sky_shader.loc("cloudWhiteMix"), cloud_white_mix); + glUniform1f(sky_shader.loc("cloudThresholdLow"), m_cloud_threshold_low); + glUniform1f(sky_shader.loc("cloudThresholdHigh"), m_cloud_threshold_high); glBindVertexArray(m_vao[1]); glDisable(GL_DEPTH_TEST); glDrawArrays(GL_TRIANGLES, 0, 36); glEnable(GL_DEPTH_TEST); + + // draw sun and moon + const auto& billboard = get_shader("billboard"); + billboard.use(); + glDepthMask(GL_FALSE); + + glBindVertexArray(m_vao[0]); + // draw sun + glm::vec3 sun_pos = m_camera.get_camera_pos() + + normalize(-m_world.sunlight_dir()) * (FAR_PLANE * 0.9f); + glm::vec3 sun_view_pos = glm::vec3(m_v_mat * glm::vec4(sun_pos, 1.0f)); + m_mv_mat = glm::translate(glm::mat4(1.0f), sun_view_pos) * + glm::scale(glm::mat4(1.0f), glm::vec3(SUN_SIZE)) * + glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f)); + + m_mv_loc = billboard.loc("mv_matrix"); + m_proj_loc = billboard.loc("proj_matrix"); + + glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); + glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); + glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(SUN_COLOR)); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glm::vec3 moon_pos = m_camera.get_camera_pos() + + normalize(m_world.sunlight_dir()) * (FAR_PLANE * 0.9f); + glm::vec3 moon_view_pos = glm::vec3(m_v_mat * glm::vec4(moon_pos, 1.0f)); + m_mv_mat = glm::translate(glm::mat4(1.0f), moon_view_pos) * + glm::scale(glm::mat4(1.0f), glm::vec3(MOON_SIZE)) * + glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f)); + glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); + glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); + glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(MOON_COLOR)); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDepthMask(GL_TRUE); } void Renderer::render_text() { @@ -343,7 +470,8 @@ void Renderer::update_fov(float fov) { void Renderer::update_proj_matrix(float aspect, float width, float height) { m_aspect = aspect; - m_p_mat = glm::perspective(glm::radians(m_fov), aspect, 0.1f, 1000.0f); + m_p_mat = + glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE); m_ui_proj = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f); // scale and then translate m_ui_m_matrix = @@ -423,11 +551,150 @@ void Renderer::updata_framebuffer(int width, int height) { } glBindFramebuffer(GL_FRAMEBUFFER, 0); + // depth map fbo + if (m_depth_map_fbo == 0) { + glGenFramebuffers(1, &m_depth_map_fbo); + } + glDeleteTextures(1, &m_depth_map_texture); + glGenTextures(1, &m_depth_map_texture); + + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, DEPTH_MAP_SIZE, + DEPTH_MAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + float border_color[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); + // Manually compare shadows + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, + // GL_COMPARE_REF_TO_TEXTURE); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + + glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + m_depth_map_texture, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("FBO incomplete after resize!"); + } else { + Logger::info("Frame Buffer Complete!"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); m_width = width; m_height = height; } - +#pragma region render_world void Renderer::render_world() { + // shader map + glm::mat4 light_space_matrix; + auto& m_render_snapshots = m_world.render_snapshots(); + auto& camera_pos = m_camera.get_camera_pos(); + float texels_per_unit = 0.0f; + + const auto& lightdir = m_parallel_light.lightdir; + + if (m_shader_on) { + const auto& depth_shader = get_shader("depth_shader"); + depth_shader.use(); + + glm::vec3 cam_pos = m_camera.get_camera_pos(); + glm::vec3 cam_fwd = m_camera.get_camera_front(); + float half_extent = 128.0f; + + glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f); + + glm::vec3 raw_shadow_lightdir = + quantize_sun_direction(lightdir, ANGLE_STEP_DEG); + glm::vec3 shadow_lightdir = + get_smoothed_shadow_lightdir(raw_shadow_lightdir, m_delta_time); + glm::vec3 up = fabs(shadow_lightdir.y) > 0.99f ? glm::vec3(0, 0, 1) + : glm::vec3(0, 1, 0); + + glm::mat4 light_basis = + glm::lookAt(glm::vec3(0.0f), shadow_lightdir, up); + texels_per_unit = DEPTH_MAP_SIZE / (half_extent * 2.0f); + glm::vec3 ls_center = glm::vec3(light_basis * glm::vec4(center, 1.0f)); + ls_center.x = + std::round(ls_center.x * texels_per_unit) / texels_per_unit; + ls_center.y = + std::round(ls_center.y * texels_per_unit) / texels_per_unit; + glm::vec3 snapped_center = + glm::vec3(glm::inverse(light_basis) * glm::vec4(ls_center, 1.0f)); + + float distance = half_extent * 1.5f; + float near_plane = 1.0f; + float far_plane = distance + half_extent * 2.0f; + glm::vec3 light_pos = snapped_center - shadow_lightdir * distance; + glm::mat4 light_view = glm::lookAt(light_pos, snapped_center, up); + glm::mat4 light_projection = + glm::ortho(-half_extent, half_extent, -half_extent, half_extent, + near_plane, far_plane); + + light_space_matrix = light_projection * light_view; + glUniformMatrix4fv(depth_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, + glm::value_ptr(light_space_matrix)); + glUniform1i(depth_shader.loc("is_discard_tranparent"), + m_discard_tranparent); + glViewport(0, 0, DEPTH_MAP_SIZE, DEPTH_MAP_SIZE); + if (m_light_cull_face == 0) { + glCullFace(GL_FRONT); + } else if (m_light_cull_face == 1) { + glCullFace(GL_BACK); + } else { + Logger::warn("Light Cull Face {} Over The Max Selection", + m_light_cull_face); + glCullFace(GL_BACK); + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); + glClear(GL_DEPTH_BUFFER_BIT); + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_DEPTH_TEST); + for (const auto& snapshot : m_render_snapshots) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.normal_vao); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_vertices_count); + } + + // cross_plane and discard + + for (const auto& snapshot : m_render_snapshots) { + + glm::vec2 camera_pos_xz{camera_pos.x, camera_pos.z}; + if (snapshot.cross_vertices_count != 0) { + glm::vec2 center_xz{snapshot.center.x, snapshot.center.z}; + float dist2d = glm::distance(camera_pos_xz, center_xz); + if (dist2d <= CROSS_PLANE_DISTANCE * 16) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.cross_vao); + + glDrawArrays(GL_TRIANGLES, 0, + snapshot.cross_vertices_count); + } + } + if (snapshot.normal_discard_vertices_count != 0) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.normal_discard_vao); + + glDrawArrays(GL_TRIANGLES, 0, + snapshot.normal_discard_vertices_count); + } + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glCullFace(GL_BACK); + glViewport(0, 0, m_width, m_height); const auto& normal_block_shader = get_shader("normal_block"); normal_block_shader.use(); @@ -435,17 +702,43 @@ void Renderer::render_world() { m_proj_loc = normal_block_shader.loc("proj_matrix"); glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + glActiveTexture(GL_TEXTURE1); m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); m_v_mat = m_camera.get_camera_lookat(); m_mv_mat = m_v_mat * m_m_mat; + m_norm_mat = glm::transpose(glm::inverse(m_mv_mat)); + glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir); + glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); + glUniformMatrix4fv(normal_block_shader.loc("norm_matrix"), 1, GL_FALSE, + glm::value_ptr(m_norm_mat)); + glUniformMatrix4fv(normal_block_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, + glm::value_ptr(light_space_matrix)); + glUniform1f(normal_block_shader.loc("ambientStrength"), m_ambient_strength); + glUniform3fv(normal_block_shader.loc("sunlightColor"), 1, + glm::value_ptr(m_parallel_light.directional_light_color)); + glUniform3fv(normal_block_shader.loc("ambientColor"), 1, + glm::value_ptr(m_parallel_light.finnal_ambient_color)); + glUniform3fv(normal_block_shader.loc("sunlightDir"), 1, + glm::value_ptr(light_dir_view)); + glUniform1i(normal_block_shader.loc("shadowMode"), m_shadow_mode); + glUniform1i(normal_block_shader.loc("shader_on"), m_shader_on); + glUniform1f(normal_block_shader.loc("texelsPerUnit"), texels_per_unit); + glUniform1f(normal_block_shader.loc("lightSizeUV"), + static_cast(m_light_size_uv)); + glUniform1f(normal_block_shader.loc("minRadius"), m_min_radius); + glUniform1f(normal_block_shader.loc("maxRadius"), m_max_radius); + glUniform1i(normal_block_shader.loc("samples"), m_samples); + glUniform1f(normal_block_shader.loc("specularStrength"), + m_specular_strength); + glUniform3fv(normal_block_shader.loc("cameraPos"), 1, + glm::value_ptr(m_camera.get_camera_pos())); m_mvp_mat = m_p_mat * m_mv_mat; - auto& camera_pos = m_camera.get_camera_pos(); auto& m_planes = m_world.planes(); - auto& m_render_snapshots = m_world.render_snapshots(); Math::extract_frustum_planes(m_mvp_mat, m_planes); @@ -526,6 +819,7 @@ void Renderer::render_world() { glBlendFunci(0, GL_ONE, GL_ONE); glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glActiveTexture(GL_TEXTURE0); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) { @@ -564,11 +858,71 @@ void Renderer::render_world() { DebugCollector::get().report( "rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum)); } - +#pragma endregion void Renderer::render_dev_panel() { glDisable(GL_DEPTH_TEST); m_dev_panel.render(); glEnable(GL_DEPTH_TEST); } +glm::vec3 Renderer::quantize_sun_direction(const glm::vec3& lightdir, + float angle_step_deg) const { + float elevation = std::asin(glm::clamp(lightdir.y, -1.0f, 1.0f)); + float azimuth = std::atan2(lightdir.z, lightdir.x); + + float step = glm::radians(angle_step_deg); + + float quantized_elevation = std::round(elevation / step) * step; + float quantized_azimuth = std::round(azimuth / step) * step; + + glm::vec3 quantized_dir; + quantized_dir.x = + std::cos(quantized_elevation) * std::cos(quantized_azimuth); + quantized_dir.z = + std::cos(quantized_elevation) * std::sin(quantized_azimuth); + quantized_dir.y = std::sin(quantized_elevation); + + return glm::normalize(quantized_dir); +} + +glm::vec3 +Renderer::get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_lightdir, + float dt) { + if (!m_blend_initialized) { + + m_blend_from_lightdir = raw_shadow_lightdir; + m_blend_to_lightdir = raw_shadow_lightdir; + m_blend_t = 1.0f; + m_blend_initialized = true; + return raw_shadow_lightdir; + } + + if (raw_shadow_lightdir != m_blend_to_lightdir) { + glm::vec3 current_displayed = glm::normalize( + Math::slerp(m_blend_from_lightdir, m_blend_to_lightdir, m_blend_t)); + + m_blend_from_lightdir = current_displayed; + m_blend_to_lightdir = raw_shadow_lightdir; + m_blend_t = 0.0f; + } + + m_blend_t = glm::min(m_blend_t + dt / BLEND_DURATION, 1.0f); + + return glm::normalize( + Math::slerp(m_blend_from_lightdir, m_blend_to_lightdir, m_blend_t)); +} + +float& Renderer::ambient_strength() { return m_ambient_strength; } +bool& Renderer::discard_transparent() { return m_discard_tranparent; } +bool& Renderer::shader_on() { return m_shader_on; } +int& Renderer::shadow_mode() { return m_shadow_mode; } +int& Renderer::light_cull_face() { return m_light_cull_face; } +int& Renderer::light_size_uv() { return m_light_size_uv; } +float& Renderer::min_radius() { return m_min_radius; } +float& Renderer::max_radius() { return m_max_radius; } +int& Renderer::samples() { return m_samples; } +float& Renderer::specular_strength() { return m_specular_strength; } +float& Renderer::cloud_speed() { return m_cloud_speed; } +float& Renderer::cloud_threshold_low() { return m_cloud_threshold_low; } +float& Renderer::cloud_threshold_high() { return m_cloud_threshold_high; } } // namespace Cubed \ No newline at end of file diff --git a/src/texture_manager.cpp b/src/texture_manager.cpp index 26a0bda..915d946 100644 --- a/src/texture_manager.cpp +++ b/src/texture_manager.cpp @@ -164,6 +164,8 @@ void TextureManager::init_block() { glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, diff --git a/src/tools/math_tools.cpp b/src/tools/math_tools.cpp index 6f29107..4c1621b 100644 --- a/src/tools/math_tools.cpp +++ b/src/tools/math_tools.cpp @@ -1,7 +1,9 @@ #include "Cubed/tools/math_tools.hpp" #include +#include #include + namespace Cubed { namespace Math { @@ -65,6 +67,39 @@ float deterministic_random(int x, int z, uint64_t seed) { h = h * 6364136223846793005ULL + (uint64_t)z; return (float)(h >> 40) / (float)(1 << 24); } + +glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t) { + + float cos_theta = glm::clamp(glm::dot(from, to), -1.0f, 1.0f); + + if (cos_theta > 0.9995f) { + return glm::normalize(glm::mix(from, to, t)); + } + + if (cos_theta < -0.9995f) { + + glm::vec3 axis = (std::fabs(from.x) < 0.9f) + ? glm::vec3(1.0f, 0.0f, 0.0f) + : glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 ortho = glm::normalize(glm::cross(from, axis)); + + float angle = glm::pi() * t; + + glm::vec3 rotated = + from * std::cos(angle) + glm::cross(ortho, from) * std::sin(angle); + + return glm::normalize(rotated); + } + + float theta = std::acos(cos_theta); + float sin_theta = std::sin(theta); + + float a = std::sin((1.0f - t) * theta) / sin_theta; + float b = std::sin(t * theta) / sin_theta; + + return glm::normalize(a * from + b * to); +} + } // namespace Math } // namespace Cubed \ No newline at end of file