From 290610659736771fc2b47a8655ae6e0807374ea7 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 30 May 2026 15:11:40 +0800 Subject: [PATCH] feat: water rendering (#13) * refactor: update water texture * feat(gameplay): implement transparent block rendering with depth sorting * fix(world): use camera pos for distance calculations, make water passable * refactor(player): use ivec3 for block pass check * feat(camera): add underwater detection * feat(renderer): add underwater effect with framebuffer post-processing * feat(block): add gas property and refactor solid block check * fix(assets): set leaf block transparency to false * fix(block): set leaf as transparent --- assets/data/block/air.toml | 1 + assets/data/block/dirt.toml | 1 + assets/data/block/grass.toml | 1 + assets/data/block/grass_block.toml | 1 + assets/data/block/leaf.toml | 1 + assets/data/block/log.toml | 1 + assets/data/block/sand.toml | 1 + assets/data/block/snowy_grass_block.toml | 1 + assets/data/block/stone.toml | 1 + assets/data/block/template.toml | 1 + assets/data/block/water.toml | 5 +- assets/shaders/under_water_f_shader.glsl | 35 ++++++ assets/shaders/under_water_v_shader.glsl | 11 ++ assets/texture/block/water/back.png | Bin 262 -> 261 bytes assets/texture/block/water/base.png | Bin 262 -> 261 bytes assets/texture/block/water/front.png | Bin 262 -> 261 bytes assets/texture/block/water/left.png | Bin 262 -> 261 bytes assets/texture/block/water/right.png | Bin 262 -> 261 bytes assets/texture/block/water/top.png | Bin 262 -> 261 bytes include/Cubed/camera.hpp | 3 + include/Cubed/gameplay/block.hpp | 11 +- include/Cubed/gameplay/chunk.hpp | 7 ++ include/Cubed/gameplay/player.hpp | 1 + include/Cubed/gameplay/world.hpp | 9 +- include/Cubed/primitive_data.hpp | 7 ++ include/Cubed/renderer.hpp | 29 +++-- include/Cubed/shader.hpp | 3 +- src/app.cpp | 1 + src/block.cpp | 18 ++- src/camera.cpp | 10 ++ src/gameplay/chunk.cpp | 36 +++++- src/gameplay/player.cpp | 14 +-- src/gameplay/world.cpp | 138 +++++++++++++++++------ src/renderer.cpp | 97 +++++++++++++++- src/shader.cpp | 13 ++- src/tools/shader_tools.cpp | 2 +- src/window.cpp | 2 +- 37 files changed, 393 insertions(+), 69 deletions(-) create mode 100644 assets/shaders/under_water_f_shader.glsl create mode 100644 assets/shaders/under_water_v_shader.glsl diff --git a/assets/data/block/air.toml b/assets/data/block/air.toml index e5c8784..0a15eea 100644 --- a/assets/data/block/air.toml +++ b/assets/data/block/air.toml @@ -1,5 +1,6 @@ id = 0 is_cross_plane = false +is_gas = true is_liquid = false is_passable = true is_transparent = true diff --git a/assets/data/block/dirt.toml b/assets/data/block/dirt.toml index 82888e0..8c1af59 100644 --- a/assets/data/block/dirt.toml +++ b/assets/data/block/dirt.toml @@ -1,5 +1,6 @@ id = 2 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/grass.toml b/assets/data/block/grass.toml index 39a9f18..05a911b 100644 --- a/assets/data/block/grass.toml +++ b/assets/data/block/grass.toml @@ -1,5 +1,6 @@ id = 9 is_cross_plane = true +is_gas = false is_liquid = false is_passable = true is_transparent = true diff --git a/assets/data/block/grass_block.toml b/assets/data/block/grass_block.toml index b871f4d..266c892 100644 --- a/assets/data/block/grass_block.toml +++ b/assets/data/block/grass_block.toml @@ -1,5 +1,6 @@ id = 1 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/leaf.toml b/assets/data/block/leaf.toml index 8015824..3fc5943 100644 --- a/assets/data/block/leaf.toml +++ b/assets/data/block/leaf.toml @@ -1,5 +1,6 @@ id = 6 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = true diff --git a/assets/data/block/log.toml b/assets/data/block/log.toml index c67e269..40eb1a5 100644 --- a/assets/data/block/log.toml +++ b/assets/data/block/log.toml @@ -1,5 +1,6 @@ id = 5 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/sand.toml b/assets/data/block/sand.toml index 18c69b6..0ec5867 100644 --- a/assets/data/block/sand.toml +++ b/assets/data/block/sand.toml @@ -1,5 +1,6 @@ id = 4 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/snowy_grass_block.toml b/assets/data/block/snowy_grass_block.toml index ecf6b4a..94f6e36 100644 --- a/assets/data/block/snowy_grass_block.toml +++ b/assets/data/block/snowy_grass_block.toml @@ -1,5 +1,6 @@ id = 8 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/stone.toml b/assets/data/block/stone.toml index 4f27b08..df5b889 100644 --- a/assets/data/block/stone.toml +++ b/assets/data/block/stone.toml @@ -1,5 +1,6 @@ id = 3 is_cross_plane = false +is_gas = false is_liquid = false is_passable = false is_transparent = false diff --git a/assets/data/block/template.toml b/assets/data/block/template.toml index 8c7cbe8..8f4e951 100644 --- a/assets/data/block/template.toml +++ b/assets/data/block/template.toml @@ -1,6 +1,7 @@ name = "template" id = 0 is_liquid = false +is_gas = false is_passable = false is_cross_plane = false is_transparent = false diff --git a/assets/data/block/water.toml b/assets/data/block/water.toml index a2910ff..710ec0a 100644 --- a/assets/data/block/water.toml +++ b/assets/data/block/water.toml @@ -1,6 +1,7 @@ id = 7 is_cross_plane = false +is_gas = false is_liquid = true -is_passable = false -is_transparent = false +is_passable = true +is_transparent = true name = 'water' \ No newline at end of file diff --git a/assets/shaders/under_water_f_shader.glsl b/assets/shaders/under_water_f_shader.glsl new file mode 100644 index 0000000..1d78c11 --- /dev/null +++ b/assets/shaders/under_water_f_shader.glsl @@ -0,0 +1,35 @@ +#version 460 + +in vec2 TexCoord; + +out vec4 FragColor; + +uniform sampler2D u_sceneTexture; +uniform float u_time; +uniform bool u_underwater; +uniform vec3 u_waterColor; +uniform float u_fogDensity; + +void main() { + vec4 original = texture(u_sceneTexture, TexCoord); + + if (!u_underwater) { + FragColor = original; + return; + } + + vec2 distoredUV = TexCoord; + float strength = 0.003; + distoredUV.x += sin(TexCoord.y * 15.0 + u_time * 5.0) * strength; + distoredUV.y += cos(TexCoord.x * 15.0 + u_time * 4.3) * strength; + distoredUV = clamp(distoredUV, 0.001, 0.999); + vec4 distorted = texture(u_sceneTexture, distoredUV); + + float caustic = 0.9 + 0.1 * sin(TexCoord.x * 20.0 + u_time) * cos(TexCoord.y * 20.0 + u_time * 1.2); + vec3 causticLight = vec3(caustic, caustic * 0.95, caustic * 0.9); + //vec3 causticLight = vec3(1.0); + float fogFactor = clamp(1.0 - (TexCoord.y * u_fogDensity * 10.0), 0.0, 1.0); + vec3 mixed = mix(u_waterColor, distorted.rgb * causticLight, fogFactor); + + FragColor = vec4(mixed, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/under_water_v_shader.glsl b/assets/shaders/under_water_v_shader.glsl new file mode 100644 index 0000000..e0863a8 --- /dev/null +++ b/assets/shaders/under_water_v_shader.glsl @@ -0,0 +1,11 @@ +#version 460 + +layout (location = 0) in vec2 pos; +layout (location = 1) in vec2 texCoord; + +out vec2 TexCoord; + +void main() { + gl_Position = vec4(pos.x, pos.y, 0.0, 1.0); + TexCoord = texCoord; +} \ No newline at end of file diff --git a/assets/texture/block/water/back.png b/assets/texture/block/water/back.png index ddec4463e14d87c1c23048bcda6d2913aeea9510..21d9835f6e92c00059a449bbd192726dbdb6dd13 100644 GIT binary patch delta 220 zcmV<203-i~0)+yQF@L{FL_t(Ijg?Zn5rZ%cHFvL|rErrw*dQyU%odr2-B=+TBo`VA zS_%pp+eq&uA8{qg`n{*uJYT;tq!+tA$k*P7VsGzHv6mIa#?>Mjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TOMjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TOMjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TOMjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TOMjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TOMjk0Op*k(?HtX0VD> zVcJmo8*d=H=1+!}ea-_1yoR2Se+!u_)bK4ULYQU+hq(dcl~oBAf&JsAwg8`y$>RYG z1)f-Haiv7*cw*tHawAyyxtWT0ySj|hK_-aT?X+pM56o+9f*fgPNgt`!!{{!6x$fm+1(Y*%aqpM><7r~r1=s>m)=mn=6^zNV|Nlo8AXXKy;7Tm`#8FZrG6z>; zL3bIlmszm+AKfq%alt<*F#`{SAD16tjWS%(2m_3;+=%QFViOmN7qF!Tbf+T%5TO m_is_on_gen_vertex_data{false}; std::atomic m_normal_vertices_sum = 0; std::atomic m_cross_vertices_sum = 0; + std::atomic m_transparent_vertices_sum = 0; std::atomic m_biome = BiomeType::PLAIN; std::mutex m_vertexs_data_mutex; @@ -35,8 +36,10 @@ private: std::vector m_blocks; GLuint m_normal_vbo = 0; GLuint m_cross_plane_vbo = 0; + GLuint m_transparent_normal_vbo = 0; std::vector m_normal_vertices; std::vector m_cross_plane_vertices; + std::vector m_transparent_normal_vertices; float frequency = 0.01f; float height = 80; unsigned m_seed = 0; @@ -100,9 +103,13 @@ public: GLuint get_normal_vbo() const; size_t get_normal_vertices_sum() const; + GLuint get_cross_vbo() const; size_t get_cross_vertices_sum() const; + GLuint get_transparent_vbo() const; + size_t get_transparent_vertices_sum() const; + bool is_dirty() const; void mark_dirty(); diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp index 81262b3..b3a9322 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/player.hpp @@ -104,6 +104,7 @@ public: Gait& gait(); GameMode& game_mode(); + const World& get_world() const; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index eea6e57..b676856 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -19,6 +19,8 @@ struct ChunkRenderSnapshot { size_t normal_vertices_count; GLuint cross_vbo; size_t cross_vertices_count; + GLuint transparent_vbo; + size_t transparent_vertices_count; glm::vec3 center; glm::vec3 half_extents; }; @@ -92,14 +94,15 @@ public: const glm::vec3& half_extents); int get_block(const glm::ivec3& block_pos) const; - bool is_block(const glm::ivec3& block_pos) const; + bool is_solid(const glm::ivec3& block_pos) const; bool can_pass_block(const glm::ivec3& block_pos) const; - + BlockType get_block_tpye(const glm::ivec3& block_pos) const; static ChunkPos chunk_pos(int world_x, int world_z); void need_gen(); void render(const glm::mat4& mvp_matrix, - const TextureManager& texture_manager); + const TextureManager& texture_manager, + const glm::vec3& camera_pos); void set_block(const glm::ivec3& pos, unsigned id); void update(float delta_time); diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 3e11b91..2d3336f 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -149,6 +149,13 @@ constexpr float CROSS_TEX_COORDS[2][6][2] = { {0.0f, 1.0f}}, // bottom left }; #pragma endregion + +constexpr float QUAD_VERTICES[] = { + // postion // texcoorlds + -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, + + -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 { float x = 0.0f, y = 0.0f, z = 0.0f; float s = 0.0f, t = 0.0f; diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 18d2682..7225b71 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -15,7 +15,7 @@ class World; class DevPanel; class Renderer { public: - constexpr static int NUM_VAO = 5; + constexpr static int NUM_VAO = 6; Renderer(const Camera& camera, World& world, const TextureManager& texture_manager, DevPanel& dev_panel); @@ -24,8 +24,10 @@ public: void init(); const Shader& get_shader(const std::string& name) const; void render(); + void update(float delta_time); void update_fov(float fov); void update_proj_matrix(float aspect, float width, float height); + void updata_framebuffer(int width, int height); private: const Camera& m_camera; @@ -35,16 +37,25 @@ private: float m_aspect = 0.0f; float m_fov = DEFAULT_FOV; + + float m_delta_time = 0.0f; + glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat; - GLuint m_mv_loc; - GLuint m_proj_loc; + GLuint m_mv_loc = 0; + GLuint m_proj_loc = 0; - GLuint m_sky_vbo; - GLuint m_text_vbo; - GLuint m_outline_indices_vbo; - GLuint m_outline_vbo; - GLuint m_ui_vbo; + GLuint m_sky_vbo = 0; + GLuint m_text_vbo = 0; + GLuint m_outline_indices_vbo = 0; + GLuint m_outline_vbo = 0; + GLuint m_ui_vbo = 0; + + GLuint m_fbo = 0; + GLuint m_screen_texture = 0; + GLuint m_depth_render_buffer = 0; + + GLuint m_quad_vbo = 0; glm::mat4 m_ui_proj; glm::mat4 m_ui_m_matrix; @@ -52,6 +63,7 @@ private: std::vector m_vao; std::vector m_ui; + void init_underwater(); void init_text(); void render_outline(); @@ -59,6 +71,7 @@ private: void render_text(); void render_ui(); void render_world(); + void render_underwater(); void render_dev_panel(); }; diff --git a/include/Cubed/shader.hpp b/include/Cubed/shader.hpp index 8c5b445..e8a5f88 100644 --- a/include/Cubed/shader.hpp +++ b/include/Cubed/shader.hpp @@ -1,7 +1,7 @@ #pragma once #include #include - +#include namespace Cubed { class Shader { @@ -26,6 +26,7 @@ private: GLuint m_program = 0; std::size_t m_hash = 0; std::string m_name = "-1"; + mutable std::unordered_map m_uniform_cache; }; } // namespace Cubed \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index 736b2aa..ad0b45c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -257,6 +257,7 @@ void App::update() { m_renderer.update_fov(fov + 5.0f); } } + m_renderer.update(delta_time); } int App::start_cubed_application(int argc, char** argv) { diff --git a/src/block.cpp b/src/block.cpp index 273fdf5..6074a12 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -52,6 +52,21 @@ const std::string& BlockManager::name_form_id(BlockType id) { return m_datas[id].name; } +bool BlockManager::is_gas(BlockType id) { + if (id >= sums()) { + Logger::error("Id {}, is Over The Max Id", id, sums() - 1); + return m_datas[0].is_gas; + } + return m_datas[id].is_gas; +} +bool BlockManager::is_liquid(BlockType id) { + if (id >= sums()) { + Logger::error("Id {}, is Over The Max Id", id, sums() - 1); + return m_datas[0].is_liquid; + } + return m_datas[id].is_liquid; +} + bool BlockManager::is_cross_plane(BlockType id) { if (id >= sums()) { Logger::error("Id {}, is Over The Max Id", id, sums() - 1); @@ -109,8 +124,9 @@ void BlockManager::init() { auto is_passable = safe_get_value(block, "is_passable", false); auto is_cross_plane = safe_get_value(block, "is_cross_plane", false); auto is_transparent = safe_get_value(block, "is_transparent", false); + auto is_gas = safe_get_value(block, "is_gas", false); m_datas.emplace_back(*id, *name, *is_liquid, *is_passable, - *is_cross_plane, *is_transparent); + *is_cross_plane, *is_transparent, *is_gas); } std::sort( m_datas.begin(), m_datas.end(), diff --git a/src/camera.cpp b/src/camera.cpp index bca6810..224f91e 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -1,6 +1,7 @@ #include "Cubed/camera.hpp" #include "Cubed/gameplay/player.hpp" +#include "Cubed/gameplay/world.hpp" #include "Cubed/tools/cubed_assert.hpp" namespace Cubed { @@ -12,6 +13,13 @@ void Camera::update_move_camera() { auto pos = m_player->get_player_pos(); // pos.y need to add 1.6f to center m_camera_pos = glm::vec3(pos.x, pos.y + 1.6f, pos.z); + glm::ivec3 block_pos = glm::floor(m_camera_pos); + auto& world = m_player->get_world(); + if (world.get_block_tpye(block_pos) == 7) { + m_under_water = true; + } else { + m_under_water = false; + } } void Camera::camera_init(Player* player) { @@ -50,4 +58,6 @@ const glm::mat4 Camera::get_camera_lookat() const { const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; } +bool Camera::is_under_water() const { return m_under_water; } + } // namespace Cubed diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 6be9699..6f71f58 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -18,6 +18,9 @@ Chunk::~Chunk() { if (m_cross_plane_vbo != 0) { m_world.push_delete_vbo(m_cross_plane_vbo); } + if (m_transparent_normal_vbo != 0) { + m_world.push_delete_vbo(m_transparent_normal_vbo); + } } Chunk::Chunk(Chunk&& other) noexcept @@ -25,15 +28,20 @@ Chunk::Chunk(Chunk&& other) noexcept m_is_on_gen_vertex_data(other.m_is_on_gen_vertex_data.load()), m_normal_vertices_sum(other.m_normal_vertices_sum.load()), m_cross_vertices_sum(other.m_cross_vertices_sum.load()), + m_transparent_vertices_sum(other.m_transparent_vertices_sum.load()), m_biome(other.m_biome.load()), m_chunk_pos(std::move(other.m_chunk_pos)), m_world(other.m_world), m_heightmap(std::move(other.m_heightmap)), m_blocks(std::move(other.m_blocks)), m_normal_vbo(other.m_normal_vbo), m_cross_plane_vbo(other.m_cross_plane_vbo), + m_transparent_normal_vbo(other.m_transparent_normal_vbo), m_normal_vertices(std::move(other.m_normal_vertices)), m_cross_plane_vertices(std::move(other.m_cross_plane_vertices)), + m_transparent_normal_vertices( + std::move(other.m_transparent_normal_vertices)), m_seed(other.m_seed), m_conditions(other.m_conditions) { other.m_normal_vbo = 0; other.m_cross_plane_vbo = 0; + other.m_transparent_normal_vbo = 0; } Chunk& Chunk::operator=(Chunk&& other) noexcept { @@ -43,6 +51,8 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { m_normal_vbo = other.m_normal_vbo; other.m_normal_vbo = 0; m_cross_plane_vbo = other.m_cross_plane_vbo; + m_transparent_normal_vbo = other.m_transparent_normal_vbo; + other.m_transparent_normal_vbo = 0; other.m_cross_plane_vbo = 0; m_chunk_pos = std::move(other.m_chunk_pos); m_heightmap = std::move(other.m_heightmap); @@ -50,11 +60,14 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { m_dirty = other.is_dirty(); m_normal_vertices = std::move(other.m_normal_vertices); m_cross_plane_vertices = std::move(other.m_cross_plane_vertices); + m_transparent_normal_vertices = + std::move(other.m_transparent_normal_vertices); m_biome = other.m_biome.load(); m_is_on_gen_vertex_data = other.m_is_on_gen_vertex_data.load(); m_need_upload = other.m_need_upload.load(); m_normal_vertices_sum = other.m_normal_vertices_sum.load(); m_cross_vertices_sum = other.m_cross_vertices_sum.load(); + m_transparent_vertices_sum = other.m_transparent_vertices_sum.load(); m_seed = other.m_seed; m_conditions = other.m_conditions; return *this; @@ -146,6 +159,11 @@ size_t Chunk::get_cross_vertices_sum() const { return m_cross_vertices_sum.load(); } +GLuint Chunk::get_transparent_vbo() const { return m_transparent_normal_vbo; } +size_t Chunk::get_transparent_vertices_sum() const { + return m_transparent_vertices_sum.load(); +} + void Chunk::gen_phase_one() { m_generator = std::make_unique(*this); if (!m_generator) { @@ -235,6 +253,15 @@ void Chunk::upload_to_gpu() { m_cross_plane_vertices.size() * sizeof(Vertex), m_cross_plane_vertices.data(), GL_DYNAMIC_DRAW); } + if (m_transparent_normal_vertices.size() != 0) { + if (m_transparent_normal_vbo == 0) { + glGenBuffers(1, &m_transparent_normal_vbo); + } + glBindBuffer(GL_ARRAY_BUFFER, m_transparent_normal_vbo); + glBufferData(GL_ARRAY_BUFFER, + m_transparent_normal_vertices.size() * sizeof(Vertex), + m_transparent_normal_vertices.data(), GL_DYNAMIC_DRAW); + } // after fininshed it, can use clear_dirty(); m_need_upload = false; @@ -276,7 +303,7 @@ BiomeConditions& Chunk::conditions() { return m_conditions; } void Chunk::gen_normal_vertices( const std::array*, 4>& neighbor_block) { m_normal_vertices.clear(); - + m_transparent_normal_vertices.clear(); static const glm::ivec3 DIR[6] = {{0, 0, 1}, {1, 0, 0}, {0, 0, -1}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}}; @@ -382,13 +409,18 @@ void Chunk::gen_normal_vertices( static_cast(cur_id * 6 + face) }; - m_normal_vertices.emplace_back(vex); + if (BlockManager::is_transparent(cur_id)) { + m_transparent_normal_vertices.emplace_back(vex); + } else { + m_normal_vertices.emplace_back(vex); + } } } } } } m_normal_vertices_sum = m_normal_vertices.size(); + m_transparent_vertices_sum = m_transparent_normal_vertices.size(); } void Chunk::gen_cross_plane_vertices() { diff --git a/src/gameplay/player.cpp b/src/gameplay/player.cpp index 523f3c4..c785f80 100644 --- a/src/gameplay/player.cpp +++ b/src/gameplay/player.cpp @@ -88,7 +88,7 @@ bool Player::ray_cast(const glm::vec3& start, const glm::vec3& front, float t = 0.0f; normal = glm::vec3(0.0f, 0.0f, 0.0f); while (t <= distance) { - if (m_world.is_block(glm::ivec3(ix, iy, iz))) { + if (m_world.is_solid(glm::ivec3(ix, iy, iz))) { block_pos = glm::ivec3(ix, iy, iz); return true; } @@ -300,14 +300,14 @@ void Player::update_lookup_block() { if (m_look_block != std::nullopt) { if (Input::get_input_state().mouse_state.left) { - if (m_world.is_block(m_look_block->pos)) { + if (m_world.is_solid(m_look_block->pos)) { m_world.set_block(m_look_block->pos, 0); } Input::get_input_state().mouse_state.left = false; } if (Input::get_input_state().mouse_state.right) { glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; - if (!m_world.is_block(near_pos)) { + if (!m_world.is_solid(near_pos)) { auto x = near_pos.x; auto y = near_pos.y; auto z = near_pos.z; @@ -421,7 +421,7 @@ void Player::update_x_move() { for (int x = minx; x <= maxx; ++x) { for (int y = miny; y <= maxy; ++y) { for (int z = minz; z <= maxz; ++z) { - if (!m_world.can_pass_block(glm::vec3{x, y, z})) { + if (!m_world.can_pass_block(glm::ivec3{x, y, z})) { AABB block_box = {glm::vec3{static_cast(x), static_cast(y), static_cast(z)}, @@ -455,7 +455,7 @@ void Player::update_y_move() { for (int x = minx; x <= maxx; ++x) { for (int y = miny; y <= maxy; ++y) { for (int z = minz; z <= maxz; ++z) { - if (!m_world.can_pass_block(glm::vec3{x, y, z})) { + if (!m_world.can_pass_block(glm::ivec3{x, y, z})) { AABB block_box = {glm::vec3{static_cast(x), static_cast(y), static_cast(z)}, @@ -493,7 +493,7 @@ void Player::update_z_move() { for (int x = minx; x <= maxx; ++x) { for (int y = miny; y <= maxy; ++y) { for (int z = minz; z <= maxz; ++z) { - if (!m_world.can_pass_block(glm::vec3{x, y, z})) { + if (!m_world.can_pass_block(glm::ivec3{x, y, z})) { AABB block_box = {glm::vec3{static_cast(x), static_cast(y), static_cast(z)}, @@ -547,5 +547,5 @@ float& Player::g() { return m_g; } unsigned Player::place_block() const { return m_place_block; }; Gait& Player::gait() { return m_gait; } GameMode& Player::game_mode() { return m_game_mode; } - +const World& Player::get_world() const { return m_world; } } // namespace Cubed diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 6a8e8f7..6c7e8e6 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -292,11 +292,10 @@ void World::init_chunks() { } */ void World::render(const glm::mat4& mvp_matrix, - const TextureManager& texture_manager) { + const TextureManager& texture_manager, + const glm::vec3& camera_pos) { Math::extract_frustum_planes(mvp_matrix, m_planes); int rendered_sum = 0; - auto player_pos = get_player("TestPlayer").get_player_pos(); - glm::vec2 player_pos_xz{player_pos.x, player_pos.z}; for (const auto& snapshot : m_render_snapshots) { if (is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) { @@ -316,38 +315,88 @@ void World::render(const glm::mat4& mvp_matrix, glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_vertices_count); glBindBuffer(GL_ARRAY_BUFFER, 0); - if (snapshot.cross_vertices_count != 0) { - glm::vec2 center_xz{snapshot.center.x, snapshot.center.z}; - float dist = glm::distance(player_pos_xz, center_xz); - if (dist <= CROSS_PLANE_DISTANCE * 16) { - - glDepthMask(GL_FALSE); - glBindTexture(GL_TEXTURE_2D_ARRAY, - texture_manager.get_cross_plane_array()); - glBindBuffer(GL_ARRAY_BUFFER, snapshot.cross_vbo); - 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)); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - - glDrawArrays(GL_TRIANGLES, 0, - snapshot.cross_vertices_count); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDepthMask(GL_TRUE); - } - } rendered_sum++; } } + glDepthMask(GL_FALSE); + + struct SortableSnapshot { + const ChunkRenderSnapshot* snapshot; + float distance; + }; + + std::vector cross_list; + std::vector transparent_list; + + for (const auto& snapshot : m_render_snapshots) { + + if (!is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) { + continue; + } + + float dist = glm::distance(camera_pos, snapshot.center); + 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) { + cross_list.push_back({&snapshot, dist}); + } + } + if (snapshot.transparent_vertices_count != 0) { + transparent_list.push_back({&snapshot, dist}); + } + } + std::sort(transparent_list.begin(), transparent_list.end(), + [](const SortableSnapshot& a, const SortableSnapshot& b) { + return a.distance > b.distance; + }); + std::sort(cross_list.begin(), cross_list.end(), + [](const SortableSnapshot& a, const SortableSnapshot& b) { + return a.distance > b.distance; + }); + + for (const auto& item : cross_list) { + const auto& snapshot = *item.snapshot; + glBindTexture(GL_TEXTURE_2D_ARRAY, + texture_manager.get_cross_plane_array()); + glBindBuffer(GL_ARRAY_BUFFER, snapshot.cross_vbo); + 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)); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + for (const auto& item : transparent_list) { + const auto& snapshot = *item.snapshot; + glBindTexture(GL_TEXTURE_2D_ARRAY, texture_manager.get_texture_array()); + glBindBuffer(GL_ARRAY_BUFFER, snapshot.transparent_vbo); + 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)); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.transparent_vertices_count); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + glDepthMask(GL_TRUE); DebugCollector::get().report( "rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum)); } @@ -758,7 +807,7 @@ int World::get_block(const glm::ivec3& block_pos) const { return chunk_blocks[Chunk::index(x, y, z)]; } -bool World::is_block(const glm::ivec3& block_pos) const { +bool World::is_solid(const glm::ivec3& block_pos) const { auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); @@ -773,7 +822,7 @@ bool World::is_block(const glm::ivec3& block_pos) const { return false; } auto id = chunk_blocks[Chunk::index(x, y, z)]; - if (id == 0) { + if (BlockManager::is_gas(id) || BlockManager::is_liquid(id)) { return false; } else { return true; @@ -798,6 +847,27 @@ bool World::can_pass_block(const glm::ivec3& block_pos) const { return BlockManager::is_passable(id); } +BlockType World::get_block_tpye(const glm::ivec3& block_pos) const { + auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); + std::lock_guard lk(m_chunks_mutex); + auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); + + if (it == m_chunks.end()) { + Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, + block_pos.z); + return 0; + } + const auto& chunk_blocks = it->second.get_chunk_blocks(); + auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); + if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || + z >= CHUNK_SIZE) { + Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, + block_pos.z); + return 0; + } + return chunk_blocks[Chunk::index(x, y, z)]; +} + void World::set_block(const glm::ivec3& block_pos, unsigned id) { int world_x, world_y, world_z; @@ -896,6 +966,8 @@ void World::update(float delta_time) { m_render_snapshots.push_back( {chunk.get_normal_vbo(), chunk.get_normal_vertices_sum(), chunk.get_cross_vbo(), chunk.get_cross_vertices_sum(), + chunk.get_transparent_vbo(), + chunk.get_transparent_vertices_sum(), glm::vec3(static_cast(pos.x * CHUNK_SIZE) + static_cast(CHUNK_SIZE / 2), static_cast(WORLD_SIZE_Y / 2), diff --git a/src/renderer.cpp b/src/renderer.cpp index d22589b..ac6c7cd 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -33,6 +33,9 @@ Renderer::~Renderer() { glDeleteBuffers(1, &m_text_vbo); glBindVertexArray(0); glDeleteVertexArrays(NUM_VAO, m_vao.data()); + glDeleteFramebuffers(1, &m_fbo); + glDeleteTextures(1, &m_screen_texture); + glDeleteRenderbuffers(1, &m_depth_render_buffer); } void Renderer::hot_reload() { @@ -59,12 +62,17 @@ void Renderer::init() { "shaders/ui_f_shader.glsl"}; Shader text_shdaer{"text", "shaders/text_v_shader.glsl", "shaders/text_f_shader.glsl"}; + Shader under_water_shader{"under_water", + "shaders/under_water_v_shader.glsl", + "shaders/under_water_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)}); m_shaders.insert({ui_shdaer.hash(), std::move(ui_shdaer)}); m_shaders.insert({text_shdaer.hash(), std::move(text_shdaer)}); + m_shaders.insert( + {under_water_shader.hash(), std::move(under_water_shader)}); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -116,7 +124,7 @@ void Renderer::init() { glBindBuffer(GL_ARRAY_BUFFER, m_text_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); - + init_underwater(); init_text(); hot_reload(); } @@ -127,6 +135,14 @@ const Shader& Renderer::get_shader(const std::string& name) const { return it->second; } +void Renderer::init_underwater() { + glGenBuffers(1, &m_quad_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_quad_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(QUAD_VERTICES), QUAD_VERTICES, + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void Renderer::init_text() { const auto& shader = get_shader("text"); Text::set_loc(shader); @@ -134,9 +150,9 @@ void Renderer::init_text() { } void Renderer::render() { + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - glClear(GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindVertexArray(m_vao[0]); render_sky(); @@ -144,9 +160,19 @@ void Renderer::render() { render_world(); glBindVertexArray(m_vao[2]); render_outline(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_DEPTH_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glBindVertexArray(m_vao[3]); - render_ui(); + render_underwater(); + glEnable(GL_DEPTH_TEST); + glBindVertexArray(m_vao[4]); + render_ui(); + glBindVertexArray(m_vao[5]); render_text(); glBindVertexArray(0); render_dev_panel(); @@ -256,6 +282,32 @@ void Renderer::render_ui() { glEnable(GL_DEPTH_TEST); } +void Renderer::render_underwater() { + const auto& shader = get_shader("under_water"); + shader.use(); + glBindBuffer(GL_ARRAY_BUFFER, m_quad_vbo); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + (void*)(2 * sizeof(float))); + + glUniform1i(shader.loc("u_sceneTexture"), 0); + glUniform1f(shader.loc("u_time"), glfwGetTime()); + glUniform1i(shader.loc("u_underwater"), m_camera.is_under_water()); + glUniform3f(shader.loc("u_waterColor"), 0.1f, 0.25f, 0.35f); + glUniform1f(shader.loc("u_fogDensity"), 0.08f); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_screen_texture); + + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); +} + +void Renderer::update(float delta_time) { m_delta_time = delta_time; } + void Renderer::update_fov(float fov) { m_fov = fov; m_p_mat = glm::perspective(glm::radians(fov), m_aspect, 0.1f, 1000.0f); @@ -272,6 +324,41 @@ void Renderer::update_proj_matrix(float aspect, float width, float height) { glm::scale(glm::mat4(1.0f), glm::vec3(50.0f, 50.0f, 1.0f)); } +void Renderer::updata_framebuffer(int width, int height) { + if (width <= 0 || height <= 0) + return; + if (m_fbo == 0) { + glGenFramebuffers(1, &m_fbo); + } + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glDeleteTextures(1, &m_screen_texture); + glDeleteRenderbuffers(1, &m_depth_render_buffer); + + glGenTextures(1, &m_screen_texture); + glBindTexture(GL_TEXTURE_2D, m_screen_texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, + GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + m_screen_texture, 0); + + glGenRenderbuffers(1, &m_depth_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, m_depth_render_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, m_depth_render_buffer); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("FBO incomplete after resize!"); + } else { + Logger::info("Frame Buffer Complete!"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + void Renderer::render_world() { const auto& shader = get_shader("world"); shader.use(); @@ -286,7 +373,7 @@ void Renderer::render_world() { 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)); m_mvp_mat = m_p_mat * m_mv_mat; - m_world.render(m_mvp_mat, m_texture_manager); + m_world.render(m_mvp_mat, m_texture_manager, m_camera.get_camera_pos()); } void Renderer::render_dev_panel() { diff --git a/src/shader.cpp b/src/shader.cpp index 3c107f0..21293ce 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -18,7 +18,8 @@ Shader::Shader(const std::string& name, const std::string& v_shader_path, Shader::Shader(Shader&& shader) noexcept : m_program(shader.m_program), m_hash(shader.m_hash), - m_name(std::move(shader.m_name)) { + m_name(std::move(shader.m_name)), + m_uniform_cache(std::move(shader.m_uniform_cache)) { shader.m_hash = 0; shader.m_program = 0; } @@ -33,6 +34,7 @@ Shader& Shader::operator=(Shader&& shader) noexcept { m_hash = shader.m_hash; m_name = std::move(shader.m_name); m_program = shader.m_program; + m_uniform_cache = std::move(shader.m_uniform_cache); shader.m_hash = 0; shader.m_program = 0; return *this; @@ -59,11 +61,18 @@ std::size_t Shader::hash() const { GLuint Shader::loc(const std::string& loc) const { ASSERT_MSG(m_program != 0, "Shader program not created"); + + auto it = m_uniform_cache.find(loc); + if (it != m_uniform_cache.end()) { + return it->second; + } + GLint pos = glGetUniformLocation(m_program, loc.c_str()); if (pos == -1) { - Logger::info("Shader name {}, loc name {}, pos {}", m_name, loc, pos); + Logger::error("Shader name {}, loc name {}, pos {}", m_name, loc, pos); ASSERT_MSG(pos == -1, "Can't find UniformLocation"); } + m_uniform_cache[loc] = pos; return static_cast(pos); } diff --git a/src/tools/shader_tools.cpp b/src/tools/shader_tools.cpp index 68ceaab..0fdf0fc 100644 --- a/src/tools/shader_tools.cpp +++ b/src/tools/shader_tools.cpp @@ -40,7 +40,7 @@ GLuint create_shader_program(const std::string& v_shader_path, Tools::check_opengl_error(); glGetShaderiv(f_shader, GL_COMPILE_STATUS, &fc); if (fc != 1) { - Logger::error("vertex compilation failed"); + Logger::error("fragment compilation failed"); Tools::print_shader_log(f_shader); ASSERT(0); } diff --git a/src/window.cpp b/src/window.cpp index 09dafea..a30069a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -42,8 +42,8 @@ void Window::update_viewport() { m_aspect = (float)m_width / (float)m_height; glViewport(0, 0, m_width, m_height); m_renderer.update_proj_matrix(m_aspect, m_width, m_height); + m_renderer.updata_framebuffer(m_width, m_height); auto& config = Config::get(); - config.set("window.width", windowed_width); config.set("window.height", windowed_height); }