diff --git a/assets/shaders/image_f_shader.glsl b/assets/shaders/image_f_shader.glsl new file mode 100644 index 0000000..376eded --- /dev/null +++ b/assets/shaders/image_f_shader.glsl @@ -0,0 +1,11 @@ +#version 460 + +in vec2 tc; + +out vec4 color; + +layout (binding = 0) uniform sampler2D samp; + +void main(void) { + color = texture(samp, tc); +} \ No newline at end of file diff --git a/assets/shaders/image_v_shader.glsl b/assets/shaders/image_v_shader.glsl new file mode 100644 index 0000000..dbd4b7f --- /dev/null +++ b/assets/shaders/image_v_shader.glsl @@ -0,0 +1,14 @@ +#version 460 + +layout (location = 0) in vec2 pos; +layout (location = 1) in vec2 texCoord; + +out vec2 tc; + +uniform mat4 model_matrix; +uniform mat4 proj_matrix; + +void main(void) { + gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0); + tc = texCoord; +} diff --git a/assets/shaders/rect_f_shader.glsl b/assets/shaders/rect_f_shader.glsl new file mode 100644 index 0000000..cf009cf --- /dev/null +++ b/assets/shaders/rect_f_shader.glsl @@ -0,0 +1,9 @@ +#version 460 + +out vec4 color; + +uniform vec4 inColor; + +void main(void) { + color = inColor; +} \ No newline at end of file diff --git a/assets/shaders/rect_v_shader.glsl b/assets/shaders/rect_v_shader.glsl new file mode 100644 index 0000000..217484a --- /dev/null +++ b/assets/shaders/rect_v_shader.glsl @@ -0,0 +1,11 @@ +#version 460 + +layout (location = 0) in vec2 pos; + + +uniform mat4 model_matrix; +uniform mat4 proj_matrix; + +void main(void) { + gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0); +} diff --git a/assets/shaders/ui_f_shader.glsl b/assets/shaders/ui_f_shader.glsl deleted file mode 100644 index eee4617..0000000 --- a/assets/shaders/ui_f_shader.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version 460 - -in vec2 tc; -flat in int tex_layer; - -out vec4 color; - -layout (binding = 0) uniform sampler2DArray samp; - -void main(void) { - color = texture(samp, vec3(tc, tex_layer)); -} \ No newline at end of file diff --git a/assets/shaders/ui_v_shader.glsl b/assets/shaders/ui_v_shader.glsl deleted file mode 100644 index ae600c3..0000000 --- a/assets/shaders/ui_v_shader.glsl +++ /dev/null @@ -1,18 +0,0 @@ -#version 460 - -layout (location = 0) in vec2 pos; -layout (location = 1) in vec2 texCoord; -layout (location = 2) in float layer; - -out vec2 tc; - -flat out int tex_layer; - -uniform mat4 m_matrix; -uniform mat4 proj_matrix; - -void main(void) { - gl_Position = proj_matrix * m_matrix * vec4(pos, 0.0, 1.0); - tc = texCoord; - tex_layer = int(layer); -} diff --git a/assets/texture/ui/background.png b/assets/texture/ui/background.png new file mode 100644 index 0000000..1e5f3df Binary files /dev/null and b/assets/texture/ui/background.png differ diff --git a/assets/texture/ui/button001.png b/assets/texture/ui/button001.png new file mode 100644 index 0000000..0a8bd3e Binary files /dev/null and b/assets/texture/ui/button001.png differ diff --git a/assets/texture/ui/slider_thumb001.png b/assets/texture/ui/slider_thumb001.png new file mode 100644 index 0000000..b2ccf54 Binary files /dev/null and b/assets/texture/ui/slider_thumb001.png differ diff --git a/assets/texture/ui/slider_track001.png b/assets/texture/ui/slider_track001.png new file mode 100644 index 0000000..7b5b956 Binary files /dev/null and b/assets/texture/ui/slider_track001.png differ diff --git a/assets/texture/ui/textfield001.png b/assets/texture/ui/textfield001.png new file mode 100644 index 0000000..7b5b956 Binary files /dev/null and b/assets/texture/ui/textfield001.png differ diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index bde0f9c..2aef875 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -1,27 +1,17 @@ #pragma once #include "Cubed/audio/audio_engine.hpp" -#include "Cubed/gameplay/client_world.hpp" -#include "Cubed/gameplay/network_server.hpp" -#include "Cubed/gameplay/server_world.hpp" #define GLFW_INCLUDE_NONE -#include "Cubed/camera.hpp" +#include "Cubed/argument.hpp" #include "Cubed/config.hpp" #include "Cubed/dev_panel.hpp" #include "Cubed/render/renderer.hpp" +#include "Cubed/scene/scene_manager.hpp" #include "Cubed/texture_manager.hpp" #include "Cubed/window.hpp" namespace Cubed { class App { public: - struct Argument { - bool is_client = false; - int port = 25530; - std::string ip{"127.0.0.1"}; - std::string player{"Unknown"}; - bool debug_on = true; - }; - App(); ~App(); static void cursor_position_callback(GLFWwindow* window, double xpos, @@ -33,6 +23,8 @@ public: static void window_focus_callback(GLFWwindow* window, int focused); static void window_reshape_callback(GLFWwindow* window, int new_width, int new_height); + static void framebuffer_size_callback(GLFWwindow* window, int new_width, + int new_height); static void mouse_scroll_callback(GLFWwindow* window, double xoffset, double yoffset); static void cursor_enter_callback(GLFWwindow* window, int entered); @@ -43,31 +35,29 @@ public: static float delta_time(); static float get_fps(); - Camera& camera(); - DevPanel& dev_panel(); Renderer& renderer(); TextureManager& texture_manager(); Window& window(); - ClientWorld& client_world(); - ServerWorld& server_world(); + Config& config(); const Argument& argument() const; AudioEngine& audio(); + const char* get_clipboard_text(); + private: Config m_game_config; - Camera m_camera; - TextureManager m_texture_manager; - NetworkServer m_server; - std::shared_ptr m_client; - AudioEngine m_audio; - ClientWorld m_client_world; - DevPanel m_dev_panel; + TextureManager m_texture_manager; + + AudioEngine m_audio; + Renderer m_renderer; Window m_window; + SceneManager m_scene_manager; + inline static double last_time = glfwGetTime(); inline static double current_time = glfwGetTime(); inline static double dt = 0.0f; @@ -86,6 +76,8 @@ private: void render(); void run(); void update(); + + void dispatch_event(const Event& e); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/argument.hpp b/include/Cubed/argument.hpp new file mode 100644 index 0000000..88a1ee0 --- /dev/null +++ b/include/Cubed/argument.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +namespace Cubed { +struct Argument { + bool is_client = false; + int port = 25530; + std::string ip{"127.0.0.1"}; + std::string player{"Unknown"}; + bool debug_on = true; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_engine.hpp b/include/Cubed/audio/audio_engine.hpp index 56a2b97..c800800 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -27,6 +27,7 @@ public: void init(); void play_bgm(); + void stop_bgm(); void change_bgm(const std::string& sound); void play_3d(const std::string& sound, const glm::vec3& pos, bool check = false); diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index aa1a649..dfe6149 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Cubed/input/event.hpp" #define GLFW_INCLUDE_NONE #include #include @@ -28,6 +29,9 @@ private: glm::vec3 camera_collision(glm::vec3 start, glm::vec3 end, float radius = 0.2f); + bool handle_key_event(const KeyEvent& e); + bool handle_mouse_move_event(const MouseMoveEvent& e); + public: Camera(); @@ -45,6 +49,7 @@ public: glm::vec3 get_camera_front() const; void change_perspective(); bool is_first_person() const; + bool handle_event(const Event& e); }; } // namespace Cubed diff --git a/include/Cubed/config.hpp b/include/Cubed/config.hpp index 9acb689..0abf0dc 100644 --- a/include/Cubed/config.hpp +++ b/include/Cubed/config.hpp @@ -25,7 +25,6 @@ public: } set(key, default_value); - save_to_file(); return default_value; } diff --git a/include/Cubed/debug_collector.hpp b/include/Cubed/debug_collector.hpp index 6601c94..5aeaca7 100644 --- a/include/Cubed/debug_collector.hpp +++ b/include/Cubed/debug_collector.hpp @@ -1,6 +1,8 @@ #pragma once -#include "Cubed/ui/text.hpp" +#include "Cubed/ui/column_layout.hpp" +#include "Cubed/ui/label.hpp" +#include "Cubed/ui/widget.hpp" #include @@ -11,14 +13,14 @@ public: static DebugCollector& get(); DebugCollector(); - std::unordered_map& all_texts(); - - Text& text(std::string_view name); - void report(std::string_view name, std::string_view content); - void init_text(); + void report(const std::string& name, std::string_view content); + void init(int width, int height); + Widget& get_widget(); + bool handle_event(const Event& e); private: - std::unordered_map m_texts; + ColumnLayout m_widget; + std::unordered_map m_component; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index 1f927fd..e7eded5 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -6,8 +6,9 @@ namespace Cubed { -class App; +class WorldScene; class ClientPlayer; +class App; class DevPanel { struct ConfigView { float fov = 70.0f; @@ -32,12 +33,13 @@ class DevPanel { }; public: - DevPanel(App& app); + DevPanel(WorldScene& app); void init(); void render(); private: App& m_app; + WorldScene& m_world_scene; Config& m_config; ConfigView m_config_view; ClientPlayer* m_player; @@ -51,7 +53,6 @@ private: int m_samples_idx = 1; int m_threads = 1; int m_chunk_style = 0; - void show_about_table_bar(); void show_biome_table_bar(); void show_time_table_bar(); void show_cave_table_bar(); diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 2fce476..27faca9 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -18,6 +18,7 @@ public: ChunkGenerator(ServerChunk& chunk); static void init(); + static void init(unsigned seed); static void reload(); static const unsigned& seed(); static void seed(unsigned s); diff --git a/include/Cubed/gameplay/client_player.hpp b/include/Cubed/gameplay/client_player.hpp index 9baaecc..9ec1387 100644 --- a/include/Cubed/gameplay/client_player.hpp +++ b/include/Cubed/gameplay/client_player.hpp @@ -6,7 +6,8 @@ #include "Cubed/gameplay/game_mode.hpp" #include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/player.hpp" -#include "Cubed/input.hpp" +#include "Cubed/input/event.hpp" +#include "Cubed/input/input.hpp" #include #include @@ -23,6 +24,14 @@ public: ClientPlayer(ClientWorld& world); ~ClientPlayer(); + bool handle_mouse_button_event(const MouseButtonEvent& e); + bool handle_key_event(const KeyEvent& e); + bool handle_mouse_wheel_event(const MouseWheelEvent& e); + + void update_front_vec(float offset_x, float offset_y); + bool update_player_move_state(Key key, KeyAction action); + bool update_scroll(float yoffset); + void update_chunk_set(const ChunkPosSet& set); const ChunkPosSet& get_chunk_pos_set() const; ChunkPosSet get_chunk_pos_set(); @@ -36,13 +45,10 @@ public: const MoveState& get_move_state() const; void change_mode(GameMode mode); - void hot_reload(); + void reload_config(); void set_player_pos(const glm::vec3& pos); void set_place_block(unsigned id); void update(float delta_time); - void update_front_vec(float offset_x, float offset_y); - void update_player_move_state(int key, int action); - void update_scroll(double yoffset); float& max_walk_speed(); float& max_run_speed(); @@ -52,7 +58,7 @@ public: float& g(); float& fly_y_speed(); - unsigned place_block() const; + unsigned get_current_block() const; void set_gait(Gait gait); GameMode& game_mode(); @@ -62,7 +68,7 @@ public: void set_uuid(std::string_view uuid); std::string get_uuid() const; const std::string& get_name() const; - + void reset_key_status(); void init(std::string_view name); float yaw() const; @@ -74,6 +80,7 @@ public: float distance = 4.0f); bool is_underwater() const; void set_underwater(bool u); + void place_block(float dt); private: using enum GameMode; @@ -83,7 +90,8 @@ private: float m_deceleration = DEFAULT_DECELERATION; float m_g = DEFAULT_G; constexpr static float MAX_SPACE_ON_TIME = 0.3f; - + constexpr static float PLACE_BLOCK_INTERVAL = 0.2f; + float m_place_time = PLACE_BLOCK_INTERVAL; std::atomic m_yaw = 0.0f; std::atomic m_pitch = 0.0f; @@ -119,6 +127,7 @@ private: std::atomic m_gait = Gait::STOP; MoveState m_move_state{}; + MouseState m_mouse_state{}; GameMode m_game_mode = CREATIVE; std::optional m_look_block = std::nullopt; std::string m_name{}; @@ -137,11 +146,15 @@ private: void update_direction(); void update_lookup_block(); + void update_move(float delta_time); + void update_x_move(glm::vec3& player_pos); void update_y_move(glm::vec3& player_pos); void update_z_move(glm::vec3& player_pos); + void update_player_chunk(); + void play_walk_sound(float dt); Gait compute_gait() const; }; diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 2ec0596..4e0dd32 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -7,6 +7,7 @@ #include "Cubed/gameplay/client_player.hpp" #include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/network_client.hpp" +#include "Cubed/input/event.hpp" #include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/priority_thread_pool.hpp" @@ -41,14 +42,15 @@ struct PlayerRenderData { Gait gait; float angle; }; - +class WorldScene; class ClientWorld { public: - ClientWorld(AudioEngine& auido, Config& config); + ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene); ~ClientWorld(); void init(std::string_view player_name, std::shared_ptr client); void update(float delta_time); + bool handle_event(const Event& e); const std::optional& get_look_block_pos() const; ClientPlayer& get_player(); const ClientPlayer& get_player() const; @@ -81,8 +83,9 @@ public: void start_thread_pool(); void stop_thread_pool(); void change_pool_threads(int threads); - void hot_reload(); + void reload_config(bool chunk_build = true); void request_chunk(); + void reset_key_status(); std::vector& planes(); const std::vector& render_snapshots() const; const std::vector& render_player_data() const; @@ -97,6 +100,7 @@ public: static AABB get_block_aabb(const glm::ivec3& pos); AudioEngine& get_audio(); Config& get_config(); + WorldScene& world_scene(); template void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) { m_ticktimers.emplace( @@ -133,6 +137,7 @@ private: ChunkHashMap m_chunks; AudioEngine& m_audio; Config& m_config; + WorldScene& m_world_scene; std::vector m_planes; std::jthread m_client_thread; diff --git a/include/Cubed/gameplay/network_client.hpp b/include/Cubed/gameplay/network_client.hpp index 6f18dc9..a59bcd3 100644 --- a/include/Cubed/gameplay/network_client.hpp +++ b/include/Cubed/gameplay/network_client.hpp @@ -19,6 +19,8 @@ public: void start(std::string ip, int port = 25530); bool is_connected() const; bool is_connect_error() const; + std::string get_error_string() const; + void clear_error(); private: struct Task { @@ -53,6 +55,8 @@ private: std::atomic m_closed{false}; std::atomic m_connected{false}; std::atomic m_connect_error{false}; + mutable std::mutex m_error_string_mutex; + std::string m_error_string; // ClientWorld is managed by App ClientWorld& m_world; std::atomic_uint64_t m_sequence{0}; @@ -61,5 +65,6 @@ private: asio::awaitable read_loop(); void do_write(); + void set_error(std::string_view error); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/input/event.hpp b/include/Cubed/input/event.hpp new file mode 100644 index 0000000..13b8535 --- /dev/null +++ b/include/Cubed/input/event.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "Cubed/input/key.hpp" +#include "Cubed/input/mouse.hpp" + +#include +#include + +namespace Cubed { + +struct MouseMoveEvent { + float xpos; + float ypos; + + MouseMoveEvent(float x, float y) : xpos(x), ypos(y) {} +}; + +struct MouseButtonEvent { + MouseKey key; + KeyAction action; + + MouseButtonEvent(MouseKey k, KeyAction a) : key(k), action(a) {} +}; + +struct MouseWheelEvent { + float offset; + + MouseWheelEvent(float o) : offset(o) {} +}; + +struct KeyEvent { + Key key; + KeyAction action; + + KeyEvent(Key k, KeyAction a) : key(k), action(a) {} +}; + +struct TextInputEvent { + std::string text; + + TextInputEvent(std::string t) : text(std::move(t)) {} +}; + +struct WindowResizeEvent { + int width; + int height; +}; +struct FrameBufferResizeEvent { + int width; + int height; +}; +using Event = + std::variant; + +template struct Overloaded : T... { + using T::operator()...; +}; +template Overloaded(T...) -> Overloaded; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/input.hpp b/include/Cubed/input/input.hpp similarity index 61% rename from include/Cubed/input.hpp rename to include/Cubed/input/input.hpp index 7183328..51d65a0 100644 --- a/include/Cubed/input.hpp +++ b/include/Cubed/input/input.hpp @@ -18,19 +18,4 @@ struct MouseState { bool right = false; }; -struct KeyState { - bool r = false; -}; - -struct InputState { - MoveState move_state; - MouseState mouse_state; - KeyState key_state; -}; - -namespace Input { -InputState& get_input_state(); - -} - } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/input/key.hpp b/include/Cubed/input/key.hpp new file mode 100644 index 0000000..fca79e1 --- /dev/null +++ b/include/Cubed/input/key.hpp @@ -0,0 +1,129 @@ +#pragma once +#ifdef DELETE +#undef DELETE +#endif +namespace Cubed { +enum class Key { + // Letter keys + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + + // Digit keys (main keyboard area) + DIGIT_0, + DIGIT_1, + DIGIT_2, + DIGIT_3, + DIGIT_4, + DIGIT_5, + DIGIT_6, + DIGIT_7, + DIGIT_8, + DIGIT_9, + + // Function keys + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + + // Control keys + BACKSPACE, + TAB, + ENTER, + ESCAPE, + SPACE, + CAPS_LOCK, + NUM_LOCK, + SCROLL_LOCK, + + // Modifier keys + LEFT_SHIFT, + RIGHT_SHIFT, + LEFT_CTRL, + RIGHT_CTRL, + LEFT_ALT, + RIGHT_ALT, + LEFT_SUPER, + RIGHT_SUPER, // Windows / Command 键 + + // Navigation keys + INSERT, + DELETE, + HOME, + END, + PAGE_UP, + PAGE_DOWN, + LEFT, + RIGHT, + UP, + DOWN, + + // Lock and system keys + PRINT_SCREEN, + PAUSE, + + // Main keyboard area symbol keys + GRAVE_ACCENT, // ` + MINUS, // - + EQUALS, // = + LEFT_BRACKET, // [ + RIGHT_BRACKET, // ] + BACKSLASH, // \ / + SEMICOLON, // ; + APOSTROPHE, // ' + COMMA, // , + PERIOD, // . + SLASH, // / + + // Numpad area + NUMPAD_0, + NUMPAD_1, + NUMPAD_2, + NUMPAD_3, + NUMPAD_4, + NUMPAD_5, + NUMPAD_6, + NUMPAD_7, + NUMPAD_8, + NUMPAD_9, + NUMPAD_ADD, // + + NUMPAD_SUBTRACT, // - + NUMPAD_MULTIPLY, // * + NUMPAD_DIVIDE, // / + NUMPAD_DECIMAL, // . + NUMPAD_ENTER +}; +enum class KeyAction { PRESS, RELEASE, REPEAT }; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/input/mouse.hpp b/include/Cubed/input/mouse.hpp new file mode 100644 index 0000000..1643513 --- /dev/null +++ b/include/Cubed/input/mouse.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace Cubed { + +enum class MouseKey { + LEFT_BUTTON, + RIGHT_BUTTON, + MIDDLE_BUTTON, + + BACK_BUTTON, // Side button back + FORWARD_BUTTON, // Side button forward + + WHEEL_UP, + WHEEL_DOWN, + WHEEL_LEFT, + WHEEL_RIGHT, + + EXTRA_BUTTON_1, // Additional programmable buttons + EXTRA_BUTTON_2, + EXTRA_BUTTON_3, + EXTRA_BUTTON_4, + EXTRA_BUTTON_5 +}; + +} diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 5bee0cc..48659c6 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -188,13 +188,14 @@ constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 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}; -constexpr float SQUARE_VERTICES[6][2] = { - {-0.5f, -0.5f}, // bottom left - {-0.5f, 0.5f}, // top left - {0.5f, 0.5f}, // top right - {0.5f, 0.5f}, // top right - {0.5f, -0.5f}, // bottom right - {-0.5f, -0.5f} // bottom left +constexpr float SQUARE_VERTICES_TOP_LEFT[6][2] = { + {0.0f, 0.0f}, // top left + {0.0f, 1.0f}, // bottom left + {1.0f, 1.0f}, // bottom right + + {1.0f, 1.0f}, // bottom right + {1.0f, 0.0f}, // top right + {0.0f, 0.0f} // top left }; constexpr float SQUARE_TEXTURE_POS[6][2] = { diff --git a/include/Cubed/render/player_renderer.hpp b/include/Cubed/render/player_renderer.hpp index 4d2cc85..4ac66ad 100644 --- a/include/Cubed/render/player_renderer.hpp +++ b/include/Cubed/render/player_renderer.hpp @@ -5,6 +5,7 @@ #include namespace Cubed { +class ClientWorld; class Renderer; class PlayerRenderer { public: @@ -12,8 +13,9 @@ public: PlayerRenderer(Renderer& renderer); ~PlayerRenderer(); void init(); - void render(const Shader& shader); - void shadow_render(const Shader& shader, glm::mat4& light_matrix); + void render(const Shader& shader, ClientWorld& world); + void shadow_render(const Shader& shader, glm::mat4& light_matrix, + ClientWorld& world); private: struct PlayerVertex { diff --git a/include/Cubed/render/renderer.hpp b/include/Cubed/render/renderer.hpp index 38f6949..9043070 100644 --- a/include/Cubed/render/renderer.hpp +++ b/include/Cubed/render/renderer.hpp @@ -2,6 +2,7 @@ #include "Cubed/config.hpp" #include "Cubed/constants.hpp" +#include "Cubed/input/event.hpp" #include "Cubed/primitive_data.hpp" #include "Cubed/render/player_renderer.hpp" #include "Cubed/render/shader_manager.hpp" @@ -9,13 +10,13 @@ #include "Cubed/render/vertex_buffer.hpp" #include "Cubed/render/world_renderer.hpp" #include "Cubed/shader.hpp" -#include "Cubed/ui/text.hpp" +#include "Cubed/ui/image.hpp" +#include "Cubed/ui/label.hpp" +#include "Cubed/ui/rect.hpp" #include #include namespace Cubed { - -class Camera; class TextureManager; class ClientWorld; class DevPanel; @@ -23,18 +24,23 @@ class Renderer { public: constexpr static int NUM_VAO = 7; - Renderer(const Camera& camera, ClientWorld& world, - const TextureManager& texture_manager, DevPanel& dev_panel, - Config& config); + Renderer(TextureManager& texture_manager, Config& config); ~Renderer(); - void hot_reload(); + void reload_config(); void init(bool debug_on); const Shader& get_shader(const std::string& name) const; - void render(); + void begin_frame(); + void end_frame(); + void render_world(ClientWorld& world); + void render_lable(const Label& label); + void render_image(const Image& image); + void render_rect(const Rect& rect); + void begin_render_ui(); + void end_render_ui(); + 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); + float& ambient_strength(); bool& discard_transparent(); @@ -58,24 +64,23 @@ public: float& underwater_fog_density(); float& water_density(); - const Camera& camera() const; - const ClientWorld& world() const; - ClientWorld& world(); const glm::mat4& world_proj_matrix() const; const TextureManager& texture_mamger() const; float delta_time() const; - float height() const; - float width() const; + float window_height() const; + float window_width() const; + float frame_height() const; + float frame_width() const; const glm::mat4& p_mat() const; const std::vector& vao() const; + void render_dev_panel(DevPanel& dev_panel); + + bool handle_event(const Event& e); private: - const Camera& m_camera; - DevPanel& m_dev_panel; - const TextureManager& m_texture_manager; - ClientWorld& m_world; + TextureManager& m_texture_manager; bool m_init = false; @@ -84,8 +89,11 @@ private: float m_delta_time = 0.0f; - float m_width = 0.0f; - float m_height = 0.0f; + float m_frame_width = 0.0f; + float m_frame_height = 0.0f; + + float m_window_width = 0.0f; + float m_window_height = 0.0f; glm::mat4 m_world_proj_matrix; @@ -97,14 +105,13 @@ private: std::unique_ptr m_quad_vbo; glm::mat4 m_ui_proj_matrix; - glm::mat4 m_ui_model_matrix; ShaderManager m_shaders; /* - 0 - quad vao + 0 - quad vao (center) 1 - sky vao 2 - outline vao - 3 - ui vao + 3 - ui vao (top-left) 4 - text vao */ std::vector m_vao; @@ -112,16 +119,12 @@ private: WorldRenderer m_world_renderer; Config& m_config; + + bool handle_window_resize_event(const WindowResizeEvent& e); + bool handle_frame_buffer_resize_event(const FrameBufferResizeEvent& e); + void updata_framebuffer(int width, int height); void init_quad(); void init_text(); - - void day_night_calculation(); - - void render_sky(); - void render_text(); - void render_ui(); - - void render_dev_panel(); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/texture.hpp b/include/Cubed/render/texture.hpp index df975e8..b135261 100644 --- a/include/Cubed/render/texture.hpp +++ b/include/Cubed/render/texture.hpp @@ -35,6 +35,7 @@ enum TextureFormat : GLenum { RGBA16F = GL_RGBA16F, RED = GL_RED, RGBA = GL_RGBA, + R8 = GL_R8, RGB = GL_RGB, RGBA8 = GL_RGBA8, @@ -60,12 +61,12 @@ public: void tex_image_2d(TextureFormat internalformat, TextureFormat format, GLenum type, const void* data, GLsizei width, - GLsizei height, GLint level = 0, GLint border = 0) const; + GLsizei height, GLint level = 0, GLint border = 0); void tex_image_3d(TextureFormat internalformat, TextureFormat format, GLenum type, const void* data, GLsizei width, GLsizei height, GLsizei depth, GLint level = 0, - GLint border = 0) const; + GLint border = 0); void tex_sub_image_3d(TextureFormat format, GLenum type, const void* data, GLint xoffset, GLint yoffset, GLint zoffset, @@ -84,11 +85,14 @@ public: void set_clamp_to_edge(bool r = true, bool s = true, bool t = true) const; TextureType type() const; + float width() const; + float height() const; private: GLuint m_id = 0; + float m_width = 0; + float m_height = 0; const TextureType M_TYPE; - GLenum get_gl_texture_type() const; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/world_renderer.hpp b/include/Cubed/render/world_renderer.hpp index 3a54141..40cd118 100644 --- a/include/Cubed/render/world_renderer.hpp +++ b/include/Cubed/render/world_renderer.hpp @@ -39,7 +39,7 @@ public: WorldRenderer& operator=(const WorldRenderer&) = delete; WorldRenderer& operator=(WorldRenderer&&) = delete; void init(); - void render(); + void render(ClientWorld& world); void updata_framebuffer(int width, int height); float& ambient_strength(); @@ -121,27 +121,26 @@ private: glm::mat4 view_matrix; - ClientWorld& m_world; - const Camera& m_camera; const TextureManager& m_texture_manager; - void day_night_calculation(); + void day_night_calculation(ClientWorld& world); - void render_sky(); + void render_sky(ClientWorld& world); - void render_world(); + void render_world(ClientWorld& world); - void shadow_map_generate(); + void shadow_map_generate(ClientWorld& world); - void render_underwater(); - void render_outline(); - void render_player(); + void render_underwater(ClientWorld& world); + void render_outline(ClientWorld& world); + void render_player(ClientWorld& world); void render_normal_block(const glm::mat4& model_mat, - const glm::mat4& mv_mat, - const glm::mat4& norm_mat); + const glm::mat4& mv_mat, const glm::mat4& norm_mat, + ClientWorld& world); void render_transparent_block(const glm::mat4& mv_mat, - const glm::mat4& norm_mat); + const glm::mat4& norm_mat, + ClientWorld& world); glm::vec3 quantize_sun_direction(const glm::vec3& sundir, float angle_step_deg) const; diff --git a/include/Cubed/scene/credits_scene.hpp b/include/Cubed/scene/credits_scene.hpp new file mode 100644 index 0000000..c5ffaec --- /dev/null +++ b/include/Cubed/scene/credits_scene.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/credits_ui.hpp" +namespace Cubed { +class SceneManager; +class CreditsScene : public Scene { +public: + CreditsScene(SceneManager& scene_manager); + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_re_enter() override; + SceneManager& scene_manager(); + +private: + SceneManager& m_scene_manager; + CreditsUI m_ui; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/host_game_scene.hpp b/include/Cubed/scene/host_game_scene.hpp new file mode 100644 index 0000000..0e16cca --- /dev/null +++ b/include/Cubed/scene/host_game_scene.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/host_game_ui.hpp" +namespace Cubed { +class SceneManager; +class HostGameScene : public Scene { +public: + HostGameScene(SceneManager& scene_manager); + ~HostGameScene(); + + HostGameScene(const HostGameScene&) = delete; + HostGameScene(HostGameScene&&) = delete; + HostGameScene& operator=(const HostGameScene&) = delete; + HostGameScene& operator=(HostGameScene&&) = delete; + + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_leave() override; + void on_re_enter() override; + SceneManager& scene_manager(); + +private: + SceneManager& m_scene_manager; + HostGameUI m_ui; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/join_game_scene.hpp b/include/Cubed/scene/join_game_scene.hpp new file mode 100644 index 0000000..b0e8d83 --- /dev/null +++ b/include/Cubed/scene/join_game_scene.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/join_game_ui.hpp" +namespace Cubed { +class SceneManager; +class JoinGameScene : public Scene { +public: + JoinGameScene(const JoinGameScene&) = delete; + JoinGameScene(JoinGameScene&&) = delete; + JoinGameScene& operator=(const JoinGameScene&) = delete; + JoinGameScene& operator=(JoinGameScene&&) = delete; + + JoinGameScene(SceneManager& scene_manager); + + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_leave() override; + void on_re_enter() override; + SceneManager& scene_manager(); + +private: + SceneManager& m_scene_manager; + JoinGameUI m_ui; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/main_menu_scene.hpp b/include/Cubed/scene/main_menu_scene.hpp new file mode 100644 index 0000000..af84472 --- /dev/null +++ b/include/Cubed/scene/main_menu_scene.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/main_menu_ui_manager.hpp" +namespace Cubed { +class SceneManager; +class MainMenuScene : public Scene { +public: + MainMenuScene(const MainMenuScene&) = delete; + MainMenuScene(MainMenuScene&&) = delete; + MainMenuScene& operator=(const MainMenuScene&) = delete; + MainMenuScene& operator=(MainMenuScene&&) = delete; + MainMenuScene(SceneManager& scene_manager); + ~MainMenuScene(); + + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_leave() override; + void on_re_enter() override; + SceneManager& scene_manager(); + +private: + SceneManager& m_scene_manager; + MainMenuUIManager m_ui_manager; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/scene.hpp b/include/Cubed/scene/scene.hpp new file mode 100644 index 0000000..b11f8c2 --- /dev/null +++ b/include/Cubed/scene/scene.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "Cubed/input/event.hpp" +namespace Cubed { +class Renderer; + +enum class SceneType { + MAIN_MENU, + WORLD, + CREDITS, + SETTINGS, + HOST_GAME, + JOIN_GAME +}; + +class Scene { +public: + Scene() = default; + + Scene(const Scene&) = delete; + Scene(Scene&&) = delete; + Scene& operator=(const Scene&) = delete; + Scene& operator=(Scene&&) = delete; + + virtual ~Scene() = default; + virtual void update(float dt) = 0; + virtual void render(Renderer& renderer) = 0; + virtual bool handle_event(const Event& e) = 0; + virtual void on_enter() {}; + virtual void on_leave() {}; + virtual void on_re_enter() {}; + +protected: + virtual bool handle_mouse_move_event(const MouseMoveEvent&) { + return false; + } + virtual bool handle_mouse_button_event(const MouseButtonEvent&) { + return false; + } + virtual bool handle_window_resize_event(const WindowResizeEvent&) { + return false; + } + virtual bool handle_mouse_wheel_event(const MouseWheelEvent&) { + return false; + } + virtual bool handle_key_event(const KeyEvent&) { return false; } + virtual bool handle_text_input_event(const TextInputEvent&) { + return false; + } +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/scene_manager.hpp b/include/Cubed/scene/scene_manager.hpp new file mode 100644 index 0000000..973d380 --- /dev/null +++ b/include/Cubed/scene/scene_manager.hpp @@ -0,0 +1,59 @@ +#pragma once +#include "Cubed/scene/scene.hpp" + +#include +#include +#include +#include + +namespace Cubed { +class App; + +struct WorldSceneParam { + bool host_game = true; + std::optional seed = std::nullopt; + std::string ip{"127.0.0.1"}; + int port = 25530; +}; + +class SceneManager { +public: + SceneManager(const SceneManager&) = delete; + SceneManager(SceneManager&&) = delete; + SceneManager& operator=(const SceneManager&) = delete; + SceneManager& operator=(SceneManager&&) = delete; + SceneManager(App& app); + ~SceneManager(); + + void update(float dt); + void render(Renderer& renderer); + + bool handle_event(const Event& e); + + void request_change(SceneType type); + void request_push(SceneType type); + void request_pop(); + + App& app(); + WorldSceneParam& world_scene_param(); + +private: + enum class OperationType { PUSH, POP, CHANGE }; + struct SceneOperation { + OperationType type; + std::optional scene; + }; + + App& m_app; + WorldSceneParam m_world_param; + std::vector> m_pending_delete_scene; + std::optional m_operation; + std::stack> m_scenes; + void process_operation(); + void change(SceneType type); + void push(SceneType type); + void pop(bool re_enter = true); + + std::unique_ptr create_scene(SceneType); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/settings_scene.hpp b/include/Cubed/scene/settings_scene.hpp new file mode 100644 index 0000000..ad7aafa --- /dev/null +++ b/include/Cubed/scene/settings_scene.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/settings_ui.hpp" +namespace Cubed { + +struct SliderVariable { + float fov = 70.0f; + float mouse_sensitivity = 0.15f; + float sfx = 1.0f; + float music = 0.5f; + int rendering_distance = 24; +}; + +class SceneManager; +class SettingsScene : public Scene { +public: + SettingsScene(const SettingsScene&) = delete; + SettingsScene(SettingsScene&&) = delete; + SettingsScene& operator=(const SettingsScene&) = delete; + SettingsScene& operator=(SettingsScene&&) = delete; + SettingsScene(SceneManager& scene_manager); + ~SettingsScene(); + + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_leave() override; + void on_re_enter() override; + SceneManager& scene_manager(); + SliderVariable& slider_variable(); + void set_texture_reload(bool reload); + +private: + void save_and_apply(); + SceneManager& m_scene_manager; + SliderVariable m_slider_variable; + SettingsUI m_settings_ui; + bool m_need_texture_reload = false; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/world_scene.hpp b/include/Cubed/scene/world_scene.hpp new file mode 100644 index 0000000..a48e6be --- /dev/null +++ b/include/Cubed/scene/world_scene.hpp @@ -0,0 +1,58 @@ +#pragma once +#include "Cubed/argument.hpp" +#include "Cubed/camera.hpp" +#include "Cubed/dev_panel.hpp" +#include "Cubed/gameplay/client_world.hpp" +#include "Cubed/gameplay/network_server.hpp" +#include "Cubed/scene/scene.hpp" +#include "Cubed/ui/error_ui.hpp" +#include "Cubed/ui/pasue_menu_ui_manager.hpp" +#include "Cubed/ui/world_ui_manager.hpp" +namespace Cubed { +class SceneManager; +class WorldScene : public Scene { +public: + WorldScene(const WorldScene&) = delete; + WorldScene(WorldScene&&) = delete; + WorldScene& operator=(const WorldScene&) = delete; + WorldScene& operator=(WorldScene&&) = delete; + + WorldScene(SceneManager& scene_manager); + ~WorldScene(); + + void update(float dt) override; + void render(Renderer& renderer) override; + bool handle_event(const Event& e) override; + void on_enter() override; + void on_leave() override; + void on_re_enter() override; + Camera& camera(); + SceneManager& scene_manager(); + ClientWorld& client_world(); + ServerWorld& server_world(); + bool pause() const; + void set_pause(bool pause); + void set_error(std::string_view error); + +private: + bool handle_mouse_move_event(const MouseMoveEvent& e) override; + bool handle_mouse_button_event(const MouseButtonEvent& e) override; + bool handle_window_resize_event(const WindowResizeEvent& e) override; + bool handle_mouse_wheel_event(const MouseWheelEvent& e) override; + bool handle_key_event(const KeyEvent& e) override; + + SceneManager& m_scene_manager; + DevPanel m_dev_panel; + Camera m_camera; + NetworkServer m_server; + std::shared_ptr m_client; + ClientWorld m_client_world; + bool m_paused = false; + bool m_show_hud = true; + bool m_show_dev_pannel = true; + PauseMenuUIManager m_pasue_menu; + WorldUIManager m_hud_ui; + ErrorUI m_error_ui; + const Argument& m_argument; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/shader.hpp b/include/Cubed/shader.hpp index c46fd11..e7894d4 100644 --- a/include/Cubed/shader.hpp +++ b/include/Cubed/shader.hpp @@ -40,6 +40,9 @@ public: } else if constexpr (is_same_v) { glUniformMatrix4fv(loc(location), 1, GL_FALSE, glm::value_ptr(value)); + + } else if constexpr (is_same_v) { + glUniform4fv(loc(location), 1, glm::value_ptr(value)); } else { static_assert(always_false
::value, "Unknown Type"); } diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index edd31f2..3c5fbb4 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/config.hpp" #include "Cubed/gameplay/block.hpp" +#include "Cubed/input/event.hpp" #include "Cubed/render/texture.hpp" #include @@ -15,10 +16,10 @@ private: std::unique_ptr m_block_status_array; std::unique_ptr m_texture_array; std::unique_ptr m_cross_plane_array; - std::unique_ptr m_ui_array; std::unique_ptr m_normal_texture_array; std::unique_ptr m_skin; std::vector> m_item_textures; + std::unordered_map> m_ui_map; GLfloat m_max_aniso = 0.0f; Config& m_config; int m_aniso = 1; @@ -27,7 +28,7 @@ private: void load_block_texture(unsigned block_id); void load_block_item_texture(unsigned id); void load_cross_plane_texture(unsigned id); - void load_ui_texture(unsigned id); + const Texture* load_image_texture(const std::string& path); void load_pbr_texture(unsigned id); void init_item(); void init_block(); @@ -35,6 +36,7 @@ private: void init_block_status(); void init_skin(); void hot_reload(); + bool handle_key_event(const KeyEvent& e); public: TextureManager(Config& config); @@ -44,7 +46,7 @@ public: const Texture* get_block_status_array() const; const Texture* get_texture_array() const; const Texture* get_cross_plane_array() const; - const Texture* get_ui_array() const; + const Texture* get_image_texture(const std::string& path); const Texture* get_pbr_texture() const; const std::vector>& item_textures() const; const Texture* get_skin() const; @@ -54,6 +56,7 @@ public: void need_reload(); void update(); int max_aniso() const; + bool handle_event(const Event& e); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/font.hpp b/include/Cubed/tools/font.hpp index 726f436..2566c77 100644 --- a/include/Cubed/tools/font.hpp +++ b/include/Cubed/tools/font.hpp @@ -22,6 +22,16 @@ struct Character { GLuint advance; }; +struct TextMesh { + std::vector vertices; + + float width; + float height; + + float min_x; + float min_y; +}; + class Shader; class Font { @@ -29,9 +39,7 @@ public: Font(); ~Font(); - static std::vector vertices(const std::string& text, - float x = 0.0f, float y = 0.0f, - float scale = 1.0f); + static TextMesh vertices(const std::string& text); static const Texture* text_texture(); static const std::string& font_path(); diff --git a/include/Cubed/tools/shader_tools.hpp b/include/Cubed/tools/shader_tools.hpp index aedb4e3..3162e36 100644 --- a/include/Cubed/tools/shader_tools.hpp +++ b/include/Cubed/tools/shader_tools.hpp @@ -3,9 +3,42 @@ #include #include #include - +#include namespace Cubed { +namespace Tools { +void delete_image_data(unsigned char* data); +} + +struct ImageData { + unsigned char* data = nullptr; + int width = 0; + int height = 0; + int channels = 0; + ImageData(const ImageData&) = delete; + ImageData(ImageData&& o) noexcept + : data(std::exchange(o.data, nullptr)), width(o.width), + height(o.height), channels(o.channels) {} + ImageData& operator=(const ImageData&) = delete; + ImageData& operator=(ImageData&& o) noexcept { + if (this == &o) { + return *this; + } + if (data) { + Tools::delete_image_data(data); + } + data = std::exchange(o.data, nullptr); + width = o.width; + height = o.height; + channels = o.channels; + return *this; + } + ImageData(unsigned char* d, int w, int h, int c) + : data(d), width(w), height(h), channels(c) {} + ImageData() = default; + ~ImageData() { Tools::delete_image_data(data); } +}; + namespace Tools { GLuint create_shader_program(const std::string& v_shader_path, const std::string& f_shader_path); @@ -13,9 +46,9 @@ void print_shader_log(GLuint shader); void print_program_info(int prog); bool check_opengl_error(); 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, - bool check_exist = true); + +ImageData load_image_data(const std::string& tex_image_path, + bool check_exist = true); } // namespace Tools diff --git a/include/Cubed/tools/system_info.hpp b/include/Cubed/tools/system_info.hpp index 52f1e63..851cba2 100644 --- a/include/Cubed/tools/system_info.hpp +++ b/include/Cubed/tools/system_info.hpp @@ -7,7 +7,11 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN // clang-format off +#ifndef NOMINMAX +#define NOMINMAX +#endif #include +#undef DELETE #include // clang-format on typedef LONG(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); diff --git a/include/Cubed/tools/text_tools.hpp b/include/Cubed/tools/text_tools.hpp new file mode 100644 index 0000000..38b6869 --- /dev/null +++ b/include/Cubed/tools/text_tools.hpp @@ -0,0 +1,41 @@ +#pragma once +#include +#include +namespace Cubed { +inline std::string codepoint_to_utf8(uint32_t cp) { + std::string out; + + if (cp <= 0x7F) { + out.push_back(static_cast(cp)); + } else if (cp <= 0x7FF) { + out.push_back(static_cast(0xC0 | (cp >> 6))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp <= 0xFFFF) { + out.push_back(static_cast(0xE0 | (cp >> 12))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else { + out.push_back(static_cast(0xF0 | (cp >> 18))); + out.push_back(static_cast(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } + + return out; +} + +inline void utf8_pop_back(std::string& str) { + if (str.empty()) { + return; + } + + std::size_t i = str.size() - 1; + + while (i > 0 && (static_cast(str[i]) & 0xC0) == 0x80) { + --i; + } + + str.erase(i); +} + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/ui/anchor.hpp b/include/Cubed/ui/anchor.hpp new file mode 100644 index 0000000..95fd298 --- /dev/null +++ b/include/Cubed/ui/anchor.hpp @@ -0,0 +1,15 @@ +#pragma once + +enum class Anchor { + TOP_LEFT, + TOP_CENTER, + TOP_RIGHT, + + CENTER_LEFT, + CENTER, + CENTER_RIGHT, + + BOTTOM_LEFT, + BOTTOM_CENTER, + BOTTOM_RIGHT, +}; \ No newline at end of file diff --git a/include/Cubed/ui/button.hpp b/include/Cubed/ui/button.hpp new file mode 100644 index 0000000..ec2a570 --- /dev/null +++ b/include/Cubed/ui/button.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "Cubed/ui/image.hpp" +#include "Cubed/ui/label.hpp" +#include "Cubed/ui/widget.hpp" + +#include + +namespace Cubed { +class Button : public Widget { +public: + Button(Widget* parent); + + Button(const Button&) = delete; + Button(Button&&) = delete; + Button& operator=(const Button&) = delete; + Button& operator=(Button&&) = delete; + + bool handle_mouse_move_event(const MouseMoveEvent& e) override; + virtual bool handle_mouse_button_event(const MouseButtonEvent& e) override; + Button& set_scale(float scale); + + float width() const override; + float height() const override; + + Button& set_width(float width); + Button& set_height(float height); + Button& set_background_image(const std::string& path, + TextureManager& texture_manager); + Button& set_default_image(TextureManager& texture_manager); + virtual Button& set_text(const std::string& text); + + Button& set_auto_scale(bool auto_scale); + Button& set_enable(bool enable); + float scale() const; + template Button& set_clicked(F&& f) { + m_clicked = std::forward(f); + return *this; + } + +protected: + std::unique_ptr m_background; + std::unique_ptr