From f392a656a7494ae3dd3b540eb3b83bac7c09701f Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sat, 14 Mar 2026 10:18:20 +0800 Subject: [PATCH] feat: add outline for block that is looked --- CMakeLists.txt | 26 ++++---- assets/texture/status/0.png | Bin 0 -> 105 bytes include/Cubed/config.hpp | 19 +++++- include/Cubed/gameplay/player.hpp | 5 +- include/Cubed/gameplay/world.hpp | 4 ++ include/Cubed/texture_manager.hpp | 7 ++- include/Cubed/tools/shader_tools.hpp | 3 +- src/gameplay/player.cpp | 25 +++++++- src/gameplay/world.cpp | 57 ++++++++++++++++- src/main.cpp | 88 +++++++++++---------------- src/shaders/outline_f_shader.glsl | 7 +++ src/shaders/outline_v_shader.glsl | 10 +++ src/texture_manager.cpp | 50 ++++++++++++++- src/tools/shader_tools.cpp | 52 +++++++++++++++- 14 files changed, 276 insertions(+), 77 deletions(-) create mode 100644 assets/texture/status/0.png create mode 100644 src/shaders/outline_f_shader.glsl create mode 100644 src/shaders/outline_v_shader.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c0d6b1..4880aa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,19 +56,19 @@ add_executable(${PROJECT_NAME} src/tools/log.cpp ) -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Building with AddressSanitizer enabled for target: ${PROJECT_NAME}") - - target_compile_options(${PROJECT_NAME} PRIVATE - -fsanitize=address - -fno-omit-frame-pointer - -g - ) - - target_link_options(${PROJECT_NAME} PRIVATE - -fsanitize=address - ) -endif() +#if(CMAKE_BUILD_TYPE STREQUAL "Debug") +# message(STATUS "Building with AddressSanitizer enabled for target: ${PROJECT_NAME}") +# +# target_compile_options(${PROJECT_NAME} PRIVATE +# -fsanitize=address +# -fno-omit-frame-pointer +# -g +# ) +# +# target_link_options(${PROJECT_NAME} PRIVATE +# -fsanitize=address +# ) +#endif() target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIR}) diff --git a/assets/texture/status/0.png b/assets/texture/status/0.png new file mode 100644 index 0000000000000000000000000000000000000000..147266f5eee780039616174d811f17ec29c87849 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|ls#P>Lo9le zQxXz>oOe)MaHQeC&y-tsSqD}yE@pFJP+oddlHsZ`i{ui+zng)|89ZJ6T-G@yGywoz CYaa9f literal 0 HcmV?d00001 diff --git a/include/Cubed/config.hpp b/include/Cubed/config.hpp index f32c3b0..7c9fdeb 100644 --- a/include/Cubed/config.hpp +++ b/include/Cubed/config.hpp @@ -5,7 +5,7 @@ constexpr int WORLD_SIZE_Y = 16; constexpr int MAX_BLOCK_NUM = 2; constexpr int CHUCK_SIZE = 16; constexpr int DISTANCE = 8; - +constexpr int MAX_BLOCK_STATUS = 1; constexpr float VERTICES_POS[6][6][3] = { // ===== front (z = +1) ===== @@ -60,4 +60,21 @@ constexpr float TEX_COORDS[6][6][2] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, + }; + +constexpr float CUBE_VER[24] = { + -0.5, -0.5, -0.5, + 0.5, -0.5, -0.5, + 0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + -0.5, 0.5, 0.5 + }; + +constexpr int OUTLINE_CUBE_INDICES[24] = { + 0,1, 1,2, 2,3, 3,0, + 4,5, 5,6, 6,7, 7,4, + 0,4, 1,5, 2,6, 3,7 }; \ No newline at end of file diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp index a4e6e65..ac24ddd 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/player.hpp @@ -31,11 +31,12 @@ private: MoveState m_move_state; std::string m_name; - const World& m_world; + World& m_world; + bool ray_cast(const glm::vec3& start, const glm::vec3& dir, glm::ivec3& block_pos, float distance = 4.0f); public: - Player(const World& world, const std::string& name); + Player(World& world, const std::string& name); ~Player(); const glm::vec3& get_front() const; const glm::vec3& get_player_pos() const; diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index 30a5ef5..a974a29 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -8,6 +8,7 @@ class Player; class World { private: BlockRenderData m_block_render_data; + glm::ivec3 last_block_pos = glm::ivec3(0, 0, 0); std::unordered_map m_chunks; std::unordered_map m_players; public: @@ -16,8 +17,11 @@ public: ~World(); const BlockRenderData& get_block_render_data(int x, int y ,int z); + const glm::ivec3& get_last_block_pos() const; Player& get_player(const std::string& name); void init_world(); + bool is_block(const glm::ivec3& block_pos) const; + void mark_looked_block(const glm::ivec3& block_pos); void render(); void update(float delta_time); diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index 7af1397..2fc44f2 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -7,15 +7,18 @@ class TextureManager { private: GLuint m_texture_array; - + GLuint m_block_status_array; + void load_block_status(int status_id); void load_block_texture(unsigned block_id); - + public: TextureManager(); ~TextureManager(); void delet_texture(); + GLuint get_block_status_array(); GLuint get_texture_array(); + // Must call after MapTable::init_map() and glfwMakeContextCurrent(window); void init_texture(); }; \ No newline at end of file diff --git a/include/Cubed/tools/shader_tools.hpp b/include/Cubed/tools/shader_tools.hpp index 63a0f42..d92e64a 100644 --- a/include/Cubed/tools/shader_tools.hpp +++ b/include/Cubed/tools/shader_tools.hpp @@ -5,10 +5,11 @@ #include namespace Shader { + GLuint create_shader_program(const std::string& v_shader_path, const std::string& f_shader_path); void print_shader_log(GLuint shader); void print_program_info(int prog); bool check_opengl_error(); - std::string read_shader_source(const char* file_path); + std::string read_shader_source(const std::string& file_path); void delete_image_data(unsigned char* data); unsigned char* load_image_data(const std::string& tex_image_path); diff --git a/src/gameplay/player.cpp b/src/gameplay/player.cpp index 152f384..5a2aca0 100644 --- a/src/gameplay/player.cpp +++ b/src/gameplay/player.cpp @@ -1,8 +1,10 @@ +#include +#include +#include #include -#include -Player::Player(const World& world, const std::string& name) : +Player::Player(World& world, const std::string& name) : m_world(world), m_name(name) { @@ -23,6 +25,20 @@ const MoveState& Player::get_move_state() const { return m_move_state; } +bool Player::ray_cast(const glm::vec3& start, const glm::vec3& dir,glm::ivec3& block_pos, float distance) { + float step = 0.1f; + glm::ivec3 cur = glm::floor(start); + for (float t = 0.0f; t < distance; t += step) { + glm::vec3 point = start + dir * t; + block_pos = glm::floor(point); + if (m_world.is_block(block_pos)) { + return true; + } + } + return false; +} + + void Player::set_player_pos(const glm::vec3& pos) { m_player_pos = pos; } @@ -49,6 +65,11 @@ void Player::update(float delta_time) { if (m_move_state.down) { m_player_pos -= glm::vec3(0.0f, 1.0f, 0.0f) * speed; } + // calculate the block that is looked + glm::ivec3 block_pos; + if(ray_cast(glm::vec3(m_player_pos.x + 0.5f, (m_player_pos.y + 1.0f), m_player_pos.z + 0.5f), m_front, block_pos)) { + m_world.mark_looked_block(block_pos); + } } diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 529b759..279d33f 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -11,7 +11,7 @@ World::~World() { } const BlockRenderData& World::get_block_render_data(int world_x, int world_y ,int world_z) { - static int chunk_x, chunk_z; + int chunk_x, chunk_z; if (world_x < 0) { chunk_x = (world_x + 1) / CHUCK_SIZE - 1; } @@ -211,6 +211,10 @@ const BlockRenderData& World::get_block_render_data(int world_x, int world_y ,in return m_block_render_data; } +const glm::ivec3& World::get_last_block_pos() const { + return last_block_pos; +} + Player& World::get_player(const std::string& name){ auto it = m_players.find(HASH::str(name)); if (it == m_players.end()) { @@ -251,6 +255,13 @@ void World::init_world() { m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer")); } +void World::mark_looked_block(const glm::ivec3& block_pos) { + if (last_block_pos == block_pos) { + return; + } + last_block_pos = block_pos; +} + void World::render() { for (const auto& chunk_map : m_chunks) { const auto& [pos, chunk] = chunk_map; @@ -266,7 +277,49 @@ void World::render() { glDrawArrays(GL_TRIANGLES, 0, chunk.get_vertex_data().size() * 3); glBindBuffer(GL_ARRAY_BUFFER, 0); - //LOG::info("Chunk {} {} render finished", pos.x, pos.z); + + } +} + +bool World::is_block(const glm::ivec3& block_pos) const{ + int chunk_x, chunk_z; + int world_x, world_y, world_z; + world_x = block_pos.x; + world_y = block_pos.y; + world_z = block_pos.z; + + if (world_x < 0) { + chunk_x = (world_x + 1) / CHUCK_SIZE - 1; + } + if (world_x >= 0) { + chunk_x = world_x / CHUCK_SIZE; + } + if (world_z < 0) { + chunk_z = (world_z + 1) / CHUCK_SIZE - 1; + } + if (world_z >= 0) { + chunk_z = world_z / CHUCK_SIZE; + } + + auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); + + if (it == m_chunks.end()) { + return false; + } + + const auto& chunk_blocks = it->second.get_chunk_blocks(); + int x, y, z; + y = world_y; + x = world_x - chunk_x * CHUCK_SIZE; + z = world_z - chunk_z * CHUCK_SIZE; + if (x < 0 || y < 0 || z < 0 || x >= CHUCK_SIZE || y >= CHUCK_SIZE || z >= CHUCK_SIZE) { + return false; + } + auto id = chunk_blocks[Chunk::get_index(x, y, z)]; + if (id == 0) { + return false; + } else { + return true; } } diff --git a/src/main.cpp b/src/main.cpp index 6f9005f..e51c913 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,7 @@ constexpr int NUM_VAO = 1; -GLuint rendering_program; +GLuint rendering_program, outline_program; GLuint vao[NUM_VAO]; GLuint mv_loc, proj_loc; int width ,height; @@ -34,67 +34,29 @@ Camera camera; TextureManager texture_manager; World world; +GLuint outline_vbo, outline_indices_vbo; void setup_vertices(void) { glGenVertexArrays(NUM_VAO, vao); glBindVertexArray(vao[0]); glBindVertexArray(0); - + glGenBuffers(1, &outline_vbo); + glBindBuffer(GL_ARRAY_BUFFER, outline_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(CUBE_VER), CUBE_VER, GL_STATIC_DRAW); + glGenBuffers(1, &outline_indices_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, outline_indices_vbo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(OUTLINE_CUBE_INDICES), OUTLINE_CUBE_INDICES, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); } - -GLuint create_shader_program() { - std::string v_shader_str = Shader::read_shader_source("shaders/vShader.glsl"); - std::string f_shader_str = Shader::read_shader_source("shaders/fShader.glsl"); - const char *v_shader_source = v_shader_str.c_str(); - const char *f_shader_source = f_shader_str.c_str(); - - GLuint v_shader = glCreateShader(GL_VERTEX_SHADER); - GLuint f_shader = glCreateShader(GL_FRAGMENT_SHADER); - - GLint vc, fc; - glShaderSource(v_shader, 1, &v_shader_source, NULL); - glShaderSource(f_shader, 1, &f_shader_source, NULL); - glCompileShader(v_shader); - Shader::check_opengl_error(); - glGetShaderiv(v_shader, GL_COMPILE_STATUS, &vc); - if (vc != 1) { - LOG::error("vertex compilation failed"); - Shader::print_shader_log(v_shader); - } - glCompileShader(f_shader); - Shader::check_opengl_error(); - glGetShaderiv(f_shader, GL_COMPILE_STATUS, &fc); - if (fc != 1) { - LOG::error("vertex compilation failed"); - Shader::print_shader_log(f_shader); - } - GLuint vf_program = glCreateProgram(); - glAttachShader(vf_program, v_shader); - glAttachShader(vf_program, f_shader); - glLinkProgram(vf_program); - - GLint linked; - Shader::check_opengl_error(); - glGetProgramiv(vf_program, GL_LINK_STATUS, &linked); - if (linked != 1) { - LOG::error("linking failed"); - Shader::print_program_info(vf_program); - } - glDeleteShader(v_shader); - glDeleteShader(f_shader); - return vf_program; -} - - void init(GLFWwindow* window) { - rendering_program = create_shader_program(); + rendering_program = Shader::create_shader_program("shaders/vShader.glsl", "shaders/fShader.glsl"); + outline_program = Shader::create_shader_program("shaders/outline_v_shader.glsl", "shaders/outline_f_shader.glsl"); - mv_loc = glGetUniformLocation(rendering_program, "mv_matrix"); - proj_loc = glGetUniformLocation(rendering_program, "proj_matrix"); camera.camera_init(&world.get_player("TestPlayer")); glfwGetFramebufferSize(window, &width, &height); @@ -145,7 +107,8 @@ void display(GLFWwindow* window, double current_time) { glUseProgram(rendering_program); glBindVertexArray(vao[0]); - + mv_loc = glGetUniformLocation(rendering_program, "mv_matrix"); + proj_loc = glGetUniformLocation(rendering_program, "proj_matrix"); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array); m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); @@ -156,6 +119,26 @@ void display(GLFWwindow* window, double current_time) { world.render(); + glUseProgram(outline_program); + mv_loc = glGetUniformLocation(outline_program, "mv_matrix"); + proj_loc = glGetUniformLocation(outline_program, "proj_matrix"); + + m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(world.get_last_block_pos())); + mv_mat = v_mat * m_mat; + glUniformMatrix4fv(mv_loc, 1, GL_FALSE, glm::value_ptr(mv_mat)); + glUniformMatrix4fv(proj_loc, 1 ,GL_FALSE, glm::value_ptr(p_mat)); + + glBindBuffer(GL_ARRAY_BUFFER, outline_vbo); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, outline_indices_vbo); + glLineWidth(5.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0); + + } @@ -218,9 +201,12 @@ int main() { glfwPollEvents(); } glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &outline_vbo); + glDeleteBuffers(1, &outline_indices_vbo); glBindVertexArray(0); glDeleteVertexArrays(NUM_VAO, vao); glDeleteProgram(rendering_program); + glDeleteProgram(outline_program); glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); diff --git a/src/shaders/outline_f_shader.glsl b/src/shaders/outline_f_shader.glsl new file mode 100644 index 0000000..315e818 --- /dev/null +++ b/src/shaders/outline_f_shader.glsl @@ -0,0 +1,7 @@ +#version 460 + +out vec4 frag_color; + +void main(void) { + frag_color = vec4(0.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/src/shaders/outline_v_shader.glsl b/src/shaders/outline_v_shader.glsl new file mode 100644 index 0000000..cb69d7f --- /dev/null +++ b/src/shaders/outline_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/src/texture_manager.cpp b/src/texture_manager.cpp index 5ea544b..3fa16c7 100644 --- a/src/texture_manager.cpp +++ b/src/texture_manager.cpp @@ -13,13 +13,32 @@ TextureManager::~TextureManager() { void TextureManager::delet_texture() { glDeleteTextures(1, &m_texture_array); + glDeleteTextures(1, &m_block_status_array); LOG::info("Successfully delete all texture"); } +GLuint TextureManager::get_block_status_array() { + return m_block_status_array; +} + GLuint TextureManager::get_texture_array() { return m_texture_array; } +void TextureManager::load_block_status(int id) { + CUBED_ASSERT_MSG(id < MAX_BLOCK_STATUS, "Exceed the max status sum limit"); + std::string path = "assets/texture/status/" + std::to_string(id) + ".png"; + unsigned char* image_data = nullptr; + image_data = (Shader::load_image_data(path)); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, + 0 ,0, id, + 16, 16, 1, + GL_RGBA, GL_UNSIGNED_BYTE, + image_data + ); + Shader::delete_image_data(image_data); +} void TextureManager::load_block_texture(unsigned id) { CUBED_ASSERT_MSG(id < MAX_BLOCK_NUM, "Exceed the max block sum limit"); const std::string& name = MapTable::get_name_from_id(id); @@ -27,7 +46,7 @@ void TextureManager::load_block_texture(unsigned id) { if (id == 0) { return; } - unsigned char * image_data[6]; + unsigned char* image_data[6]; std::string block_texture_path = "assets/texture/block/" + name; image_data[0] = (Shader::load_image_data(block_texture_path + "/front.png")); @@ -87,4 +106,33 @@ void TextureManager::init_texture() { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, max_aniso); } + glGenTextures(1, &m_block_status_array); + Shader::check_opengl_error(); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); + Shader::check_opengl_error(); + glTexImage3D(GL_TEXTURE_2D_ARRAY, + 0, GL_RGBA, + 16, 16, + MAX_BLOCK_STATUS, + 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + Shader::check_opengl_error(); + for (int i = 0; i < MAX_BLOCK_STATUS; i++) { + load_block_status(i); + } + + glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); + Shader::check_opengl_error(); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + Shader::check_opengl_error(); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + Shader::check_opengl_error(); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + Shader::check_opengl_error(); + + if (max_aniso > 0.0f) { + LOG::info("Support anisotropic filtering max_aniso is {}", max_aniso); + glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, max_aniso); + } + } \ No newline at end of file diff --git a/src/tools/shader_tools.cpp b/src/tools/shader_tools.cpp index 622de95..1485b8c 100644 --- a/src/tools/shader_tools.cpp +++ b/src/tools/shader_tools.cpp @@ -7,6 +7,54 @@ namespace Shader { + + GLuint create_shader_program(const std::string& v_shader_path, const std::string& f_shader_path) { + std::string v_shader_str = Shader::read_shader_source(v_shader_path); + std::string f_shader_str = Shader::read_shader_source(f_shader_path); + const char *v_shader_source = v_shader_str.c_str(); + const char *f_shader_source = f_shader_str.c_str(); + + GLuint v_shader = glCreateShader(GL_VERTEX_SHADER); + GLuint f_shader = glCreateShader(GL_FRAGMENT_SHADER); + + GLint vc, fc; + glShaderSource(v_shader, 1, &v_shader_source, NULL); + glShaderSource(f_shader, 1, &f_shader_source, NULL); + glCompileShader(v_shader); + Shader::check_opengl_error(); + glGetShaderiv(v_shader, GL_COMPILE_STATUS, &vc); + if (vc != 1) { + LOG::error("vertex compilation failed"); + Shader::print_shader_log(v_shader); + CUBED_ASSERT(0); + } + glCompileShader(f_shader); + Shader::check_opengl_error(); + glGetShaderiv(f_shader, GL_COMPILE_STATUS, &fc); + if (fc != 1) { + LOG::error("vertex compilation failed"); + Shader::print_shader_log(f_shader); + CUBED_ASSERT(0); + } + GLuint vf_program = glCreateProgram(); + glAttachShader(vf_program, v_shader); + glAttachShader(vf_program, f_shader); + glLinkProgram(vf_program); + + GLint linked; + Shader::check_opengl_error(); + glGetProgramiv(vf_program, GL_LINK_STATUS, &linked); + if (linked != 1) { + LOG::error("linking failed"); + Shader::print_program_info(vf_program); + CUBED_ASSERT(0); + } + glDeleteShader(v_shader); + glDeleteShader(f_shader); + return vf_program; + } + + void print_shader_log(GLuint shader) { int len = 0; int ch_written = 0; @@ -48,7 +96,7 @@ namespace Shader { } - std::string read_shader_source(const char* file_path) { + std::string read_shader_source(const std::string& file_path) { std::string content; std::ifstream file_stream(file_path, std::ios::in); @@ -71,7 +119,7 @@ namespace Shader { } unsigned char* load_image_data(const std::string& tex_image_path) { - unsigned char* data; + unsigned char* data = nullptr; int width, height, channels; data = SOIL_load_image(tex_image_path.c_str(), &width, &height, &channels, SOIL_LOAD_AUTO); std::string error_info = "Could not load texture " + tex_image_path;