mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 09:47:01 +08:00
feature: UI (#31)
* refactor(ui): replace Text with Label/Widget component system Replace the old `Text` class with a new component-based UI system consisting of `Widget` and `Label` classes. Key changes: - Remove `Text` class and its GPU upload logic; introduce `Label` as a `Widget` subclass with dirty-flag vertex update. - Add `UIVertexData` struct to manage per-label vertex buffer/array and memory. - Refactor `DebugCollector` to store labels in a `Widget` tree, removing hash-based lookup. - Simplify `Font::vertices()` signature by removing position/scale parameters; scaling is now applied in the renderer’s model matrix. - Update `Renderer` with `render_label()` and move UI rendering to a `render_ui()` that renders the widget tree. - Add new source files `label.cpp`, `widget.cpp`, `ui_vertex_data.cpp` and update CMakeLists. * refactor(render): replace UI shaders with image shaders and Image widget * refactor(core): add event system and scene management - Introduce Event variant and Overloaded helper for input handling - Move game state (camera, client_world, dev_panel) from App to WorldScene - Add SceneManager with push/pop/change operations for scene stack - Refactor input callbacks to dispatch events through scene hierarchy - Extract Argument struct to separate header - Update Renderer and WorldRenderer to accept world reference - Replace global input state with event-driven processing in ClientPlayer, Camera, etc. * refactor: add framebuffer resize event and separate UI management * fix(font): use correct texture format and clamp to edge for font atlas * feat(input): add KeyAction::REPEAT for key repeat events Add a new REPEAT value to the KeyAction enum and update the input callbacks to map GLFW_REPEAT actions to the new value instead of treating them as press. * feat(client-player): add block cooldown and track mouse state Introduce a time-based cooldown for block placement (PLACE_BLOCK_INTERVAL) to prevent rapid clicking. Replace the global InputState with per-player MouseState tracking in ClientPlayer. Rename place_block() to get_current_block() for clarity. Remove the old src/input.cpp and the Cubed/input.hpp header, moving input structures to Cubed/input/input.hpp. * ui: add main menu scene and interactive button widget - Implement MainMenuScene with a "Start Game" button that pushes the WorldScene. - Extend Button class with background/foreground, hover detection, and size queries. - Add virtual width/height and event handlers to Widget base class. - Update WorldUIManager to propagate mouse move events. - Adjust Window mouse mode based on game running state. * feat(ui): add button scaling and change square vertices to top-left origin * refactor(font): return TextMesh from Font::vertices to include bounding box Replace the plain vertex vector with a TextMesh struct that carries width, height, and min coordinates. Update Label to compute and store these values immediately on text change (removing lazy dirty update). Fix Button's width/height to factor in scale and use concrete widget types for background and foreground. * feat(ui): support scaling of labels and images Modify width() and height() methods to multiply by m_scale for both Label and Image. Rename internal dimensions to m_real_width and m_real_height for clarity. Adjust Button foreground position to vertically center when label is scaled. * refactor(ui): move scale property to subclasses and fix resize event handling Remove scale from base Widget class and add per-type set_scale/scale methods to Button, Image, and Label. Dispatch separate WindowResizeEvent alongside existing FrameBufferResizeEvent. Correct centering calculations from `+` to `-` in main menu and world UI managers. * fix(render): remove duplicate scaling in render_image model matrix * feat(ui): introduce anchor-based widget positioning system Replace set_position with anchor and offset for relative positioning. Add parent pointer to widget hierarchy. Remove manual resize logic in UI managers. * feat(ui): introduce ColumnLayout widget and refactor widget parenting - Add ColumnLayout widget that arranges children vertically with spacing. - Refactor Widget::add_child to automatically pass `this` as parent. - Update DebugCollector to use ColumnLayout for consistent spacing. - Expose children() accessor in Widget for layout management. * feat(main_menu): add exit button and refactor layout with ColumnLayout - Add `should_close_window()` method to force window close from UI. - Replace standalone button with ColumnLayout for proper centering. - Add "Exit" button that calls `should_close_window()`. - Update ESC key handling to only close window when game is not running. - Fix ColumnLayout to respect parent's anchor instead of hardcoded TOP_LEFT. - Remove unused parameter name in Image::update for consistency. * feat(ui): add Rect widget with fill, color, and parent-relative sizing * refactor(ui): extract common UI manager logic into base class * refactor: move server logic to WorldScene and refactor UI methods * feat(scene): implement pause menu with UI manager and escape key toggling Add PauseMenuUIManager class and integrate pause state into WorldScene. Escape key toggles pause in ClientPlayer and PauseMenuUIManager. Refactor Window mouse and imgui control to support pause state. * refactor(scene): dispatch events via typed handlers and move pause toggle to WorldScene * feat(ui): add HUD visibility toggle and image fill features * refactor(app): remove redundant ImGui WantCaptureMouse check in cursor callback * feat(scene): add credits scene and UI * feat(credits): add ESC key handling; remove about tab from dev panel * feat(ui): add Slider widget for value selection * refactor(ui/slider): improve track/thumb initialization and validation Rename track/thumb accessors from set_track/set_thumb to get_track/get_thumb. Add private init methods and a constant THUMB_WIDTH. Add validation to set_width with log and assert. Modify set_height to assume non-null m_thumb. * refactor(ui): simplify Button API with property setters * feat(ui): add label and image support to slider - Add `set_text()`, `set_track_image()`, `set_thumb_image()` methods. - Include label rendering and auto-scaling based on slider dimensions. - Replace static `THUMB_WIDTH` with dynamic thumb sizing. - Add slider thumb and track texture assets. * feat(ui): add int slider support and fix update_text_scale calls * refactor(config): rename hot_reload to reload_config and add settings scene * feat(ui): add auto-scaling option for button and slider text * feat(ui): add button enable/disable and reset on re-enter Prevent double-clicking on buttons that push new scenes by disabling them on click and re-enabling when the UI manager's on_re_enter is called. * fix(input): reset key status on pause * feat(ui): add ComboButton widget and integrate into settings Introduce ComboButton, a button that cycles through options on click. Used in SettingsUI for fullscreen, V-Sync, and anisotropic filtering. Adds texture reload support in SettingsScene and makes Button methods virtual to allow overriding. Also fixes default mouse_enable to true. * feat(ui): add child anchor and content sizing to column layout * feat(ui): add TextField widget with UTF-8 text input support * feat(scene): add host game scene and world parameter handling * feat(scene): add join game scene and UI * feat(ui): add clipboard paste to TextField * feat(ui): add blinking cursor to text field * feat(ui): add error UI for connection failures and network errors Introduce ErrorUI to display error messages in-world when the client encounters connection failures or network errors. The network client now stores error strings thread-safely and exposes them via `get_error_string()`. The world scene suspends normal update/render when an error is active and shows the error overlay, allowing the user to return to the previous scene. Additionally, the client world checks for connection errors during exit to avoid hanging, and the join game UI is fixed to correctly default to client mode. * refactor: move connect error check to WorldScene and add UI titles * feat(ui): add error display in host and join game screens * feat(ui): add configurable child traversal order for event handling Add TraversalOrder enum to Widget, allowing event handlers to iterate children in back-to-front (default) or front-to-back order. Set ColumnLayout to front-to-back order. Change TextField mouse button handler to return false, enabling event propagation. * fix: window build fail * style(ui): remove extra blank line
This commit is contained in:
11
assets/shaders/image_f_shader.glsl
Normal file
11
assets/shaders/image_f_shader.glsl
Normal file
@@ -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);
|
||||
}
|
||||
14
assets/shaders/image_v_shader.glsl
Normal file
14
assets/shaders/image_v_shader.glsl
Normal file
@@ -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;
|
||||
}
|
||||
9
assets/shaders/rect_f_shader.glsl
Normal file
9
assets/shaders/rect_f_shader.glsl
Normal file
@@ -0,0 +1,9 @@
|
||||
#version 460
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform vec4 inColor;
|
||||
|
||||
void main(void) {
|
||||
color = inColor;
|
||||
}
|
||||
11
assets/shaders/rect_v_shader.glsl
Normal file
11
assets/shaders/rect_v_shader.glsl
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
BIN
assets/texture/ui/background.png
Normal file
BIN
assets/texture/ui/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
BIN
assets/texture/ui/button001.png
Normal file
BIN
assets/texture/ui/button001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 215 B |
BIN
assets/texture/ui/slider_thumb001.png
Normal file
BIN
assets/texture/ui/slider_thumb001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 120 B |
BIN
assets/texture/ui/slider_track001.png
Normal file
BIN
assets/texture/ui/slider_track001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
BIN
assets/texture/ui/textfield001.png
Normal file
BIN
assets/texture/ui/textfield001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
@@ -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<NetworkClient> 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
|
||||
11
include/Cubed/argument.hpp
Normal file
11
include/Cubed/argument.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
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
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/input/event.hpp"
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/glm.hpp>
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,6 @@ public:
|
||||
}
|
||||
|
||||
set(key, default_value);
|
||||
save_to_file();
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@@ -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 <unordered_map>
|
||||
|
||||
@@ -11,14 +13,14 @@ public:
|
||||
static DebugCollector& get();
|
||||
DebugCollector();
|
||||
|
||||
std::unordered_map<std::size_t, Text>& 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<std::size_t, Text> m_texts;
|
||||
ColumnLayout m_widget;
|
||||
std::unordered_map<std::string, Label*> m_component;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
@@ -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<float> m_yaw = 0.0f;
|
||||
std::atomic<float> m_pitch = 0.0f;
|
||||
|
||||
@@ -119,6 +127,7 @@ private:
|
||||
|
||||
std::atomic<Gait> m_gait = Gait::STOP;
|
||||
MoveState m_move_state{};
|
||||
MouseState m_mouse_state{};
|
||||
GameMode m_game_mode = CREATIVE;
|
||||
std::optional<LookBlock> 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;
|
||||
};
|
||||
|
||||
@@ -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<NetworkClient> client);
|
||||
void update(float delta_time);
|
||||
bool handle_event(const Event& e);
|
||||
const std::optional<LookBlock>& 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<glm::vec4>& planes();
|
||||
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
|
||||
const std::vector<PlayerRenderData>& 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 <typename Fn>
|
||||
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<glm::vec4> m_planes;
|
||||
std::jthread m_client_thread;
|
||||
|
||||
|
||||
@@ -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<bool> m_closed{false};
|
||||
std::atomic<bool> m_connected{false};
|
||||
std::atomic<bool> 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<void> read_loop();
|
||||
|
||||
void do_write();
|
||||
void set_error(std::string_view error);
|
||||
};
|
||||
} // namespace Cubed
|
||||
61
include/Cubed/input/event.hpp
Normal file
61
include/Cubed/input/event.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/input/key.hpp"
|
||||
#include "Cubed/input/mouse.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
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<MouseMoveEvent, MouseButtonEvent, MouseWheelEvent, KeyEvent,
|
||||
TextInputEvent, WindowResizeEvent, FrameBufferResizeEvent>;
|
||||
|
||||
template <class... T> struct Overloaded : T... {
|
||||
using T::operator()...;
|
||||
};
|
||||
template <class... T> Overloaded(T...) -> Overloaded<T...>;
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -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
|
||||
129
include/Cubed/input/key.hpp
Normal file
129
include/Cubed/input/key.hpp
Normal file
@@ -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
|
||||
25
include/Cubed/input/mouse.hpp
Normal file
25
include/Cubed/input/mouse.hpp
Normal file
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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] = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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 <glm/glm.hpp>
|
||||
#include <vector>
|
||||
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<VertexArray>& 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<VertexBuffer> 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<VertexArray> 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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
21
include/Cubed/scene/credits_scene.hpp
Normal file
21
include/Cubed/scene/credits_scene.hpp
Normal file
@@ -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
|
||||
29
include/Cubed/scene/host_game_scene.hpp
Normal file
29
include/Cubed/scene/host_game_scene.hpp
Normal file
@@ -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
|
||||
28
include/Cubed/scene/join_game_scene.hpp
Normal file
28
include/Cubed/scene/join_game_scene.hpp
Normal file
@@ -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
|
||||
28
include/Cubed/scene/main_menu_scene.hpp
Normal file
28
include/Cubed/scene/main_menu_scene.hpp
Normal file
@@ -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
|
||||
50
include/Cubed/scene/scene.hpp
Normal file
50
include/Cubed/scene/scene.hpp
Normal file
@@ -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
|
||||
59
include/Cubed/scene/scene_manager.hpp
Normal file
59
include/Cubed/scene/scene_manager.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "Cubed/scene/scene.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
namespace Cubed {
|
||||
class App;
|
||||
|
||||
struct WorldSceneParam {
|
||||
bool host_game = true;
|
||||
std::optional<unsigned> 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<SceneType> scene;
|
||||
};
|
||||
|
||||
App& m_app;
|
||||
WorldSceneParam m_world_param;
|
||||
std::vector<std::unique_ptr<Scene>> m_pending_delete_scene;
|
||||
std::optional<SceneOperation> m_operation;
|
||||
std::stack<std::unique_ptr<Scene>> m_scenes;
|
||||
void process_operation();
|
||||
void change(SceneType type);
|
||||
void push(SceneType type);
|
||||
void pop(bool re_enter = true);
|
||||
|
||||
std::unique_ptr<Scene> create_scene(SceneType);
|
||||
};
|
||||
} // namespace Cubed
|
||||
41
include/Cubed/scene/settings_scene.hpp
Normal file
41
include/Cubed/scene/settings_scene.hpp
Normal file
@@ -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
|
||||
58
include/Cubed/scene/world_scene.hpp
Normal file
58
include/Cubed/scene/world_scene.hpp
Normal file
@@ -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<NetworkClient> 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
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
} else if constexpr (is_same_v<dT, glm::mat4>) {
|
||||
glUniformMatrix4fv(loc(location), 1, GL_FALSE,
|
||||
glm::value_ptr(value));
|
||||
|
||||
} else if constexpr (is_same_v<dT, glm::vec4>) {
|
||||
glUniform4fv(loc(location), 1, glm::value_ptr(value));
|
||||
} else {
|
||||
static_assert(always_false<dT>::value, "Unknown Type");
|
||||
}
|
||||
|
||||
@@ -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 <glad/glad.h>
|
||||
@@ -15,10 +16,10 @@ private:
|
||||
std::unique_ptr<Texture> m_block_status_array;
|
||||
std::unique_ptr<Texture> m_texture_array;
|
||||
std::unique_ptr<Texture> m_cross_plane_array;
|
||||
std::unique_ptr<Texture> m_ui_array;
|
||||
std::unique_ptr<Texture> m_normal_texture_array;
|
||||
std::unique_ptr<Texture> m_skin;
|
||||
std::vector<std::unique_ptr<Texture>> m_item_textures;
|
||||
std::unordered_map<std::string, std::unique_ptr<Texture>> 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<std::unique_ptr<Texture>>& 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
|
||||
@@ -22,6 +22,16 @@ struct Character {
|
||||
GLuint advance;
|
||||
};
|
||||
|
||||
struct TextMesh {
|
||||
std::vector<Vertex2D> 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<Vertex2D> 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();
|
||||
|
||||
|
||||
@@ -3,9 +3,42 @@
|
||||
#include <SOIL2.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
|
||||
#include <utility>
|
||||
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
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// clang-format off
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#undef DELETE
|
||||
#include <psapi.h>
|
||||
// clang-format on
|
||||
typedef LONG(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
|
||||
41
include/Cubed/tools/text_tools.hpp
Normal file
41
include/Cubed/tools/text_tools.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
namespace Cubed {
|
||||
inline std::string codepoint_to_utf8(uint32_t cp) {
|
||||
std::string out;
|
||||
|
||||
if (cp <= 0x7F) {
|
||||
out.push_back(static_cast<char>(cp));
|
||||
} else if (cp <= 0x7FF) {
|
||||
out.push_back(static_cast<char>(0xC0 | (cp >> 6)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else if (cp <= 0xFFFF) {
|
||||
out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else {
|
||||
out.push_back(static_cast<char>(0xF0 | (cp >> 18)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(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<unsigned char>(str[i]) & 0xC0) == 0x80) {
|
||||
--i;
|
||||
}
|
||||
|
||||
str.erase(i);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
15
include/Cubed/ui/anchor.hpp
Normal file
15
include/Cubed/ui/anchor.hpp
Normal file
@@ -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,
|
||||
};
|
||||
64
include/Cubed/ui/button.hpp
Normal file
64
include/Cubed/ui/button.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
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 <typename F> Button& set_clicked(F&& f) {
|
||||
m_clicked = std::forward<F>(f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Image> m_background;
|
||||
std::unique_ptr<Label> m_foreground;
|
||||
bool m_hovered = false;
|
||||
bool m_enable = true;
|
||||
void update_text_scale();
|
||||
|
||||
private:
|
||||
static constexpr float PADDING = 5.0f;
|
||||
static constexpr float DEFAULT_SCALE = 3.0f;
|
||||
static constexpr float TEXT_SCALE = 0.6f;
|
||||
|
||||
static constexpr const char* DEFAULT_BUTTON_IMAGE =
|
||||
"texture/ui/button001.png";
|
||||
std::function<void()> m_clicked;
|
||||
|
||||
float m_width = NORMAL_BUTTON_WIDTH;
|
||||
float m_height = NORMAL_BUTTON_HEIGHT;
|
||||
float m_scale = DEFAULT_SCALE;
|
||||
bool m_auto_scale = false;
|
||||
void on_render(Renderer& renderer) override;
|
||||
void on_update(float dt) override;
|
||||
};
|
||||
} // namespace Cubed
|
||||
33
include/Cubed/ui/column_layout.hpp
Normal file
33
include/Cubed/ui/column_layout.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
enum class ColumnLayoutAnchor { LEFT, CENTER, RIGHT };
|
||||
|
||||
class ColumnLayout : public Widget {
|
||||
public:
|
||||
ColumnLayout(const ColumnLayout&) = delete;
|
||||
ColumnLayout(ColumnLayout&&) = delete;
|
||||
ColumnLayout& operator=(const ColumnLayout&) = delete;
|
||||
ColumnLayout& operator=(ColumnLayout&&) = delete;
|
||||
ColumnLayout(Widget* parent);
|
||||
~ColumnLayout();
|
||||
|
||||
void update(float dt) override;
|
||||
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
|
||||
ColumnLayout& set_spacing(int spacing);
|
||||
// No need for parent node pointer; do not modify children's anchors and
|
||||
// scale.
|
||||
ColumnLayout& set_child_anchor(ColumnLayoutAnchor anchor);
|
||||
void layout();
|
||||
|
||||
private:
|
||||
int m_spacing = 0;
|
||||
float m_content_height = 0;
|
||||
float m_content_width = 0;
|
||||
ColumnLayoutAnchor m_layout_anchor = ColumnLayoutAnchor::CENTER;
|
||||
};
|
||||
} // namespace Cubed
|
||||
30
include/Cubed/ui/combo_button.hpp
Normal file
30
include/Cubed/ui/combo_button.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "Cubed/ui/button.hpp"
|
||||
|
||||
#include <span>
|
||||
namespace Cubed {
|
||||
using ComboPair = std::pair<std::string, std::function<void()>>;
|
||||
class ComboButton : public Button {
|
||||
|
||||
public:
|
||||
ComboButton(const ComboButton&) = delete;
|
||||
ComboButton(ComboButton&&) = delete;
|
||||
ComboButton& operator=(const ComboButton&) = delete;
|
||||
ComboButton& operator=(ComboButton&&) = delete;
|
||||
|
||||
ComboButton(Widget* parent);
|
||||
Button& set_text(const std::string& text) override;
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
ComboButton& set_combos(std::span<ComboPair> combos);
|
||||
ComboButton& set_index(int index);
|
||||
|
||||
private:
|
||||
void update_text();
|
||||
|
||||
std::vector<std::string> m_suffix;
|
||||
std::vector<std::function<void()>> m_funs;
|
||||
std::string m_text;
|
||||
int m_index = 0;
|
||||
int m_sum = 0;
|
||||
};
|
||||
} // namespace Cubed
|
||||
17
include/Cubed/ui/credits_ui.hpp
Normal file
17
include/Cubed/ui/credits_ui.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
class CreditsScene;
|
||||
|
||||
class CreditsUI : public UIManager {
|
||||
public:
|
||||
CreditsUI(CreditsScene& m_scene);
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
bool handle_key_event(const KeyEvent& e) override;
|
||||
CreditsScene& m_scene;
|
||||
};
|
||||
} // namespace Cubed
|
||||
24
include/Cubed/ui/error_ui.hpp
Normal file
24
include/Cubed/ui/error_ui.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class WorldScene;
|
||||
class ErrorUI : public UIManager {
|
||||
public:
|
||||
ErrorUI(WorldScene& scene);
|
||||
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
void set_error(std::string_view error);
|
||||
bool has_error() const;
|
||||
void clear_error();
|
||||
|
||||
private:
|
||||
bool handle_key_event(const KeyEvent& e) override;
|
||||
WorldScene& m_scene;
|
||||
bool m_has_error = false;
|
||||
std::string m_error_str;
|
||||
Label* m_error_label = nullptr;
|
||||
};
|
||||
} // namespace Cubed
|
||||
20
include/Cubed/ui/host_game_ui.hpp
Normal file
20
include/Cubed/ui/host_game_ui.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class HostGameScene;
|
||||
class HostGameUI : public UIManager {
|
||||
public:
|
||||
HostGameUI(HostGameScene& scene);
|
||||
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
|
||||
private:
|
||||
HostGameScene& m_scene;
|
||||
Label* m_error_label;
|
||||
void set_error(std::string_view error);
|
||||
void clear_error();
|
||||
};
|
||||
} // namespace Cubed
|
||||
40
include/Cubed/ui/image.hpp
Normal file
40
include/Cubed/ui/image.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/render/texture.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
#include "glm/ext/vector_float2.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
class TextureManager;
|
||||
class Image : public Widget {
|
||||
public:
|
||||
Image(const Image&) = delete;
|
||||
Image(Image&&) = delete;
|
||||
Image& operator=(const Image&) = delete;
|
||||
Image& operator=(Image&&) = delete;
|
||||
Image(Widget* parent);
|
||||
|
||||
Image& set_image(const std::string& path, TextureManager& texture_manager);
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
const Texture* texture() const;
|
||||
Image& set_scale(float scale);
|
||||
float scale() const;
|
||||
|
||||
Image& set_height(float height);
|
||||
Image& set_width(float width);
|
||||
|
||||
Image& set_fill(bool fill);
|
||||
|
||||
private:
|
||||
void on_render(Renderer& renderer) override;
|
||||
void on_update(float dt) override;
|
||||
float m_width = 0.0f;
|
||||
float m_height = 0.0f;
|
||||
bool m_fill = false;
|
||||
|
||||
const Texture* m_texture = nullptr;
|
||||
glm::vec2 m_pos{0.0f, 0.0f};
|
||||
float m_scale = 1.0f;
|
||||
};
|
||||
} // namespace Cubed
|
||||
25
include/Cubed/ui/join_game_ui.hpp
Normal file
25
include/Cubed/ui/join_game_ui.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class JoinGameScene;
|
||||
class JoinGameUI : public UIManager {
|
||||
public:
|
||||
JoinGameUI(const JoinGameUI&) = delete;
|
||||
JoinGameUI(JoinGameUI&&) = delete;
|
||||
JoinGameUI& operator=(const JoinGameUI&) = delete;
|
||||
JoinGameUI& operator=(JoinGameUI&&) = delete;
|
||||
|
||||
JoinGameUI(JoinGameScene& m_scene);
|
||||
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
|
||||
private:
|
||||
JoinGameScene& m_scene;
|
||||
Label* m_error_label;
|
||||
void set_error(std::string_view error);
|
||||
void clear_error();
|
||||
};
|
||||
} // namespace Cubed
|
||||
49
include/Cubed/ui/label.hpp
Normal file
49
include/Cubed/ui/label.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "Cubed/ui/text.hpp"
|
||||
#include "Cubed/ui/ui_vertex_data.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
class Label : public Widget {
|
||||
public:
|
||||
Label(const Label&) = delete;
|
||||
Label(Label&&) = delete;
|
||||
Label& operator=(const Label&) = delete;
|
||||
Label& operator=(Label&&) = delete;
|
||||
|
||||
Label(const std::string& id, Widget* parent);
|
||||
Label(Widget* parent);
|
||||
virtual ~Label() = default;
|
||||
|
||||
Label& set_text(std::string_view text);
|
||||
|
||||
Label& set_color(Color color);
|
||||
Label& set_scale(float scale);
|
||||
|
||||
const UIVertexData& data() const;
|
||||
|
||||
const TextStyle& text_style() const;
|
||||
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
float real_width() const;
|
||||
float real_height() const;
|
||||
float offset_x() const;
|
||||
float offset_y() const;
|
||||
float scale() const;
|
||||
const std::string& text() const;
|
||||
|
||||
protected:
|
||||
virtual void on_update(float dt) override;
|
||||
virtual void on_render(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
TextStyle m_text;
|
||||
UIVertexData m_data;
|
||||
float m_real_width = 0.0f;
|
||||
float m_real_height = 0.0f;
|
||||
float m_offset_x = 0.0f;
|
||||
float m_offset_y = 0.0f;
|
||||
float m_scale = 1.0f;
|
||||
void update_vertices();
|
||||
};
|
||||
} // namespace Cubed
|
||||
25
include/Cubed/ui/main_menu_ui_manager.hpp
Normal file
25
include/Cubed/ui/main_menu_ui_manager.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/button.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
class MainMenuScene;
|
||||
class MainMenuUIManager : public UIManager {
|
||||
public:
|
||||
MainMenuUIManager(MainMenuScene& m_scene);
|
||||
MainMenuUIManager(const MainMenuUIManager&) = delete;
|
||||
MainMenuUIManager(MainMenuUIManager&&) = delete;
|
||||
MainMenuUIManager& operator=(const MainMenuUIManager&) = delete;
|
||||
MainMenuUIManager& operator=(MainMenuUIManager&&) = delete;
|
||||
~MainMenuUIManager();
|
||||
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
|
||||
private:
|
||||
MainMenuScene& m_scene;
|
||||
std::vector<Button*> m_pending_enable;
|
||||
};
|
||||
} // namespace Cubed
|
||||
21
include/Cubed/ui/pasue_menu_ui_manager.hpp
Normal file
21
include/Cubed/ui/pasue_menu_ui_manager.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/button.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class WorldScene;
|
||||
class PauseMenuUIManager : public UIManager {
|
||||
public:
|
||||
PauseMenuUIManager(const PauseMenuUIManager&) = delete;
|
||||
PauseMenuUIManager(PauseMenuUIManager&&) = delete;
|
||||
PauseMenuUIManager& operator=(const PauseMenuUIManager&) = delete;
|
||||
PauseMenuUIManager& operator=(PauseMenuUIManager&&) = delete;
|
||||
PauseMenuUIManager(WorldScene& scene);
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
|
||||
private:
|
||||
WorldScene& m_scene;
|
||||
std::vector<Button*> m_pending_enable;
|
||||
};
|
||||
} // namespace Cubed
|
||||
41
include/Cubed/ui/rect.hpp
Normal file
41
include/Cubed/ui/rect.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/color.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
class Rect : public Widget {
|
||||
public:
|
||||
Rect(const Rect&) = delete;
|
||||
Rect(Rect&&) = delete;
|
||||
Rect& operator=(const Rect&) = delete;
|
||||
Rect& operator=(Rect&&) = delete;
|
||||
Rect(Widget* parent);
|
||||
~Rect();
|
||||
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
float alpha() const;
|
||||
|
||||
Rect& set_width(float width);
|
||||
Rect& set_height(float height);
|
||||
|
||||
Rect& set_fill(bool fill);
|
||||
|
||||
Rect& set_scale(float scale);
|
||||
Rect& set_color(Color color);
|
||||
Rect& set_alpha(float alpha);
|
||||
|
||||
Color color() const;
|
||||
|
||||
private:
|
||||
void on_update(float dt) override;
|
||||
void on_render(Renderer& renderer) override;
|
||||
|
||||
Color m_color = Color::WHITE;
|
||||
float m_width = 0.0f;
|
||||
float m_height = 0.0f;
|
||||
float m_scale = 1.0f;
|
||||
float m_alpha = 1.0f;
|
||||
bool m_fill = false;
|
||||
};
|
||||
} // namespace Cubed
|
||||
16
include/Cubed/ui/settings_ui.hpp
Normal file
16
include/Cubed/ui/settings_ui.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class SettingsScene;
|
||||
class SettingsUI : public UIManager {
|
||||
public:
|
||||
SettingsUI(SettingsScene& scene);
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
bool handle_key_event(const KeyEvent& e) override;
|
||||
SettingsScene& m_scene;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
72
include/Cubed/ui/slider.hpp
Normal file
72
include/Cubed/ui/slider.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
enum class ValueType {
|
||||
FLOAT,
|
||||
INT,
|
||||
};
|
||||
|
||||
class Slider : public Widget {
|
||||
public:
|
||||
Slider(Widget* parent);
|
||||
|
||||
Slider& set_slider(float* value, const float& min, const float& max);
|
||||
Slider& set_slider(int* value, const int& min, const int& max);
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
|
||||
Slider& set_scale(float scale);
|
||||
Slider& set_width(float width);
|
||||
Slider& set_height(float h);
|
||||
Slider& set_text(const std::string& text);
|
||||
Slider& set_track_image(const std::string& path,
|
||||
TextureManager& texture_manager);
|
||||
Slider& set_thumb_image(const std::string& path,
|
||||
TextureManager& texture_manager);
|
||||
|
||||
Slider& set_default_image(TextureManager& texture_manager);
|
||||
Slider& set_auto_scale(bool auto_scale);
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
|
||||
private:
|
||||
static constexpr float PADDING = 5.0f;
|
||||
static constexpr float TEXT_SCALE = 0.6f;
|
||||
static constexpr float DEFAULT_SCALE = 3.0f;
|
||||
static constexpr const char* DEFAULT_TRACK_IMAGE =
|
||||
"texture/ui/slider_track001.png";
|
||||
static constexpr const char* DEFAULT_THUMB_IMAGE =
|
||||
"texture/ui/slider_thumb001.png";
|
||||
std::unique_ptr<Image> m_track;
|
||||
std::unique_ptr<Image> m_thumb;
|
||||
std::unique_ptr<Label> m_label;
|
||||
ValueType m_type = ValueType::FLOAT;
|
||||
float m_scale = DEFAULT_SCALE;
|
||||
float m_width = NORMAL_SLIDER_WIDTH;
|
||||
float m_height = NORMAL_SLIDER_HEIGHT;
|
||||
float m_xpos = 0.0f;
|
||||
bool m_dragging = false;
|
||||
bool m_inside = false;
|
||||
bool m_auto_scale = false;
|
||||
float* m_float_value = nullptr;
|
||||
int* m_int_value = nullptr;
|
||||
float m_min = 0;
|
||||
float m_max = 0;
|
||||
std::string m_text;
|
||||
|
||||
void init_track();
|
||||
void init_thumb();
|
||||
|
||||
void update_value(float mouse_x);
|
||||
|
||||
void on_update(float) override;
|
||||
|
||||
void on_render(Renderer& renderer) override;
|
||||
void update_text_scale();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/vertex_array.hpp"
|
||||
#include "Cubed/render/vertex_buffer.hpp"
|
||||
#include "Cubed/ui/color.hpp"
|
||||
|
||||
#include <glad/glad.h>
|
||||
@@ -10,46 +7,9 @@
|
||||
#include <string>
|
||||
namespace Cubed {
|
||||
|
||||
class Shader;
|
||||
|
||||
class Text {
|
||||
public:
|
||||
explicit Text(std::string_view name);
|
||||
Text(std::string_view name, std::string_view str,
|
||||
glm::vec2 pos = glm::vec2{0.0f, 0.0f}, Color color = Color::BLACK);
|
||||
~Text();
|
||||
Text(const Text&) = delete;
|
||||
Text(Text&&) noexcept;
|
||||
Text& operator=(const Text&) = delete;
|
||||
Text& operator=(Text&&) noexcept = delete;
|
||||
Text& color(Color color);
|
||||
// Text& color(const glm::vec4& color, int pos);
|
||||
Text& position(float x, float y);
|
||||
Text& scale(float s);
|
||||
Text& text(std::string_view str);
|
||||
|
||||
std::size_t uuid() const;
|
||||
void render(const Shader& shader);
|
||||
|
||||
bool operator==(const Text& other) const;
|
||||
|
||||
private:
|
||||
float m_scale = 1.0f;
|
||||
glm::vec2 m_pos{0.0f, 0.0f};
|
||||
|
||||
const std::string NAME;
|
||||
const std::size_t UUID;
|
||||
|
||||
std::string m_text;
|
||||
glm::vec4 m_color{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glm::mat4 m_model_matrix;
|
||||
|
||||
std::vector<Vertex2D> m_vertices;
|
||||
std::unique_ptr<VertexBuffer> m_vbo;
|
||||
std::unique_ptr<VertexArray> m_vao;
|
||||
|
||||
void update_vertices();
|
||||
void upload_to_gpu();
|
||||
struct TextStyle {
|
||||
std::string text;
|
||||
Color color = Color::WHITE;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
73
include/Cubed/ui/text_field.hpp
Normal file
73
include/Cubed/ui/text_field.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/rect.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
class App;
|
||||
class TextField : public Widget {
|
||||
public:
|
||||
TextField(const TextField&) = delete;
|
||||
TextField(TextField&&) = delete;
|
||||
TextField& operator=(const TextField&) = delete;
|
||||
TextField& operator=(TextField&&) = delete;
|
||||
TextField(Widget* parent);
|
||||
|
||||
TextField& set_scale(float scale);
|
||||
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
|
||||
TextField& set_width(float width);
|
||||
TextField& set_height(float height);
|
||||
TextField& set_show_text(const std::string& text);
|
||||
TextField& set_background_image(const std::string& path,
|
||||
TextureManager& texture_manager);
|
||||
TextField& set_default_image(TextureManager& texture_manager);
|
||||
TextField& set_auto_scale(bool auto_scale);
|
||||
TextField& set_app(App* app);
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
bool handle_text_input_event(const TextInputEvent& e) override;
|
||||
bool handle_key_event(const KeyEvent& e) override;
|
||||
|
||||
const std::string& input_text() const;
|
||||
|
||||
template <typename F> TextField& set_on_finish(F&& f) {
|
||||
m_on_finished = std::forward<F>(f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float PADDING = 5.0f;
|
||||
static constexpr float DEFAULT_SCALE = 3.0f;
|
||||
static constexpr float TEXT_SCALE = 0.6f;
|
||||
static constexpr float CURSOR_INTERVAL = 0.5f;
|
||||
static constexpr const char* DEFAULT_TEXT_FIELD_IMAGE =
|
||||
"texture/ui/textfield001.png";
|
||||
|
||||
App* m_app;
|
||||
std::unique_ptr<Image> m_background;
|
||||
std::unique_ptr<Label> m_foreground;
|
||||
std::unique_ptr<Rect> m_cursor;
|
||||
float m_cursor_timer = 0.0f;
|
||||
bool m_cursor_visible = true;
|
||||
bool m_inside = false;
|
||||
bool m_typing = false;
|
||||
bool m_auto_scale = false;
|
||||
bool m_ctrl_press = false;
|
||||
float m_width = NORMAL_TEXTFIELD_WIDTH;
|
||||
float m_height = NORMAL_TEXTFIELD_HEIGHT;
|
||||
float m_scale = DEFAULT_SCALE;
|
||||
|
||||
std::string m_input_text;
|
||||
std::string m_show_text;
|
||||
|
||||
std::function<void()> m_on_finished;
|
||||
void update_text_scale();
|
||||
void on_render(Renderer& renderer) override;
|
||||
void on_update(float dt) override;
|
||||
void update_show_text();
|
||||
};
|
||||
} // namespace Cubed
|
||||
35
include/Cubed/ui/ui_manager.hpp
Normal file
35
include/Cubed/ui/ui_manager.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
|
||||
#include <memory>
|
||||
namespace Cubed {
|
||||
class UIManager {
|
||||
public:
|
||||
UIManager() = default;
|
||||
UIManager(const UIManager&) = delete;
|
||||
UIManager(UIManager&&) = delete;
|
||||
UIManager& operator=(const UIManager&) = delete;
|
||||
UIManager& operator=(UIManager&&) = delete;
|
||||
virtual ~UIManager();
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual void update(float dt);
|
||||
virtual void render(Renderer& renderer);
|
||||
virtual bool handle_event(const Event& e);
|
||||
|
||||
virtual Widget* get_widget(std::string name);
|
||||
|
||||
virtual bool handle_mouse_move_event(const MouseMoveEvent& e);
|
||||
virtual bool handle_mouse_button_event(const MouseButtonEvent& e);
|
||||
virtual bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||
virtual bool handle_mouse_wheel_event(const MouseWheelEvent& e);
|
||||
virtual bool handle_key_event(const KeyEvent& e);
|
||||
virtual bool handle_text_input_event(const TextInputEvent& e);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Widget> m_root_widget;
|
||||
std::unordered_map<std::string, Widget*> m_widgets;
|
||||
};
|
||||
} // namespace Cubed
|
||||
25
include/Cubed/ui/ui_vertex_data.hpp
Normal file
25
include/Cubed/ui/ui_vertex_data.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/vertex_array.hpp"
|
||||
#include "Cubed/render/vertex_buffer.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
struct UIVertexData {
|
||||
std::vector<Vertex2D> m_vertices;
|
||||
std::unique_ptr<VertexBuffer> m_vbo;
|
||||
std::unique_ptr<VertexArray> m_vao;
|
||||
std::atomic<std::size_t> m_sum{0};
|
||||
UIVertexData();
|
||||
~UIVertexData();
|
||||
UIVertexData(const UIVertexData&) = delete;
|
||||
UIVertexData(UIVertexData&&) noexcept;
|
||||
UIVertexData& operator=(const UIVertexData&) = delete;
|
||||
UIVertexData& operator=(UIVertexData&&) noexcept;
|
||||
|
||||
void upload();
|
||||
void update_sum();
|
||||
};
|
||||
} // namespace Cubed
|
||||
82
include/Cubed/ui/widget.hpp
Normal file
82
include/Cubed/ui/widget.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/ui/anchor.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
|
||||
constexpr float NORMAL_BUTTON_WIDTH = 225.0f;
|
||||
constexpr float NORMAL_BUTTON_HEIGHT = 20.0f;
|
||||
constexpr float NORMAL_SLIDER_WIDTH = 225.0f;
|
||||
constexpr float NORMAL_SLIDER_HEIGHT = 20.0f;
|
||||
constexpr float NORMAL_TEXTFIELD_WIDTH = 225.0f;
|
||||
constexpr float NORMAL_TEXTFIELD_HEIGHT = 20.0f;
|
||||
|
||||
enum class TraversalOrder { FRONT_TO_BACK, BACK_TO_FRONT };
|
||||
|
||||
class Widget {
|
||||
|
||||
public:
|
||||
Widget(const Widget&) = delete;
|
||||
Widget(Widget&&) = delete;
|
||||
Widget& operator=(const Widget&) = delete;
|
||||
Widget& operator=(Widget&&) = delete;
|
||||
|
||||
Widget(const std::string& id, Widget* parent);
|
||||
Widget(Widget* parent);
|
||||
virtual ~Widget() = default;
|
||||
virtual void update(float dt);
|
||||
virtual void render(Renderer& renderer);
|
||||
virtual const std::string& id() const;
|
||||
virtual Widget& set_anchor(Anchor anchor);
|
||||
virtual Widget& set_offset(glm::ivec2 offset);
|
||||
virtual Widget& set_window_size(int width, int height);
|
||||
virtual Widget& set_visible(bool visible);
|
||||
// Returns the final display size
|
||||
virtual float width() const = 0;
|
||||
virtual float height() const = 0;
|
||||
virtual glm::vec2 pos() const;
|
||||
|
||||
virtual bool handle_key_event(const KeyEvent& e);
|
||||
virtual bool handle_mouse_button_event(const MouseButtonEvent& e);
|
||||
virtual bool handle_mouse_wheel_event(const MouseWheelEvent& e);
|
||||
virtual bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||
virtual bool handle_mouse_move_event(const MouseMoveEvent& e);
|
||||
virtual bool handle_text_input_event(const TextInputEvent& e);
|
||||
|
||||
void set_order(TraversalOrder order);
|
||||
|
||||
template <typename T, typename... Args> T& add_child(Args&&... args) {
|
||||
auto widget = std::make_unique<T>(std::forward<Args>(args)..., this);
|
||||
T& ref = *widget;
|
||||
m_children.emplace_back(std::move(widget));
|
||||
return ref;
|
||||
};
|
||||
|
||||
protected:
|
||||
Widget* m_parent = nullptr;
|
||||
std::string m_id;
|
||||
float m_window_height = 0;
|
||||
float m_window_width = 0;
|
||||
// Center is at the top-left corner, position is at the top-left corner
|
||||
Anchor m_anchor = Anchor::TOP_LEFT;
|
||||
bool m_visible = true;
|
||||
|
||||
glm::ivec2 m_offset{0, 0};
|
||||
|
||||
std::vector<std::unique_ptr<Widget>>& children();
|
||||
|
||||
const std::vector<std::unique_ptr<Widget>>& children() const;
|
||||
|
||||
virtual void on_update(float dt);
|
||||
virtual void on_render(Renderer& renderer);
|
||||
virtual glm::vec2 compute_position() const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Widget>> m_children;
|
||||
TraversalOrder m_order = TraversalOrder::BACK_TO_FRONT;
|
||||
};
|
||||
} // namespace Cubed
|
||||
24
include/Cubed/ui/world_ui_manager.hpp
Normal file
24
include/Cubed/ui/world_ui_manager.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
class WorldScene;
|
||||
class WorldUIManager : public UIManager {
|
||||
public:
|
||||
WorldUIManager(const WorldUIManager&) = delete;
|
||||
WorldUIManager(WorldUIManager&&) = delete;
|
||||
WorldUIManager& operator=(const WorldUIManager&) = delete;
|
||||
WorldUIManager& operator=(WorldUIManager&&) = delete;
|
||||
|
||||
WorldUIManager(WorldScene& scene);
|
||||
~WorldUIManager();
|
||||
|
||||
void init() override;
|
||||
void render(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
WorldScene& m_scene;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,15 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
namespace Cubed {
|
||||
|
||||
class Camera;
|
||||
class Renderer;
|
||||
class Window {
|
||||
public:
|
||||
Window(Renderer& renderer, Config& config);
|
||||
Window(Config& config);
|
||||
~Window();
|
||||
|
||||
bool is_mouse_enable() const;
|
||||
@@ -17,22 +18,41 @@ public:
|
||||
GLFWwindow* get_glfw_window();
|
||||
void init();
|
||||
void imgui_init();
|
||||
void update_viewport();
|
||||
|
||||
// end of frame to reload!
|
||||
void hot_reload();
|
||||
bool handle_event(const Event& e);
|
||||
|
||||
void reload_config();
|
||||
|
||||
void toggle_fullscreen();
|
||||
void toggle_mouse_able();
|
||||
|
||||
void enable_mouse();
|
||||
void disable_mouse();
|
||||
|
||||
void set_camera(Camera* camera);
|
||||
Camera* camera();
|
||||
|
||||
void set_game_running(bool running);
|
||||
|
||||
void should_close_window();
|
||||
|
||||
bool is_enable_imgui() const;
|
||||
|
||||
void set_imgui_enabled(bool enable);
|
||||
|
||||
private:
|
||||
bool m_mouse_enable = false;
|
||||
bool m_mouse_enable = true;
|
||||
bool m_imgui_init = false;
|
||||
float m_aspect;
|
||||
bool m_game_running = false;
|
||||
GLFWwindow* m_window;
|
||||
int m_width;
|
||||
int m_height;
|
||||
Renderer& m_renderer;
|
||||
int m_window_width;
|
||||
int m_window_height;
|
||||
Config& m_config;
|
||||
Camera* m_camera = nullptr;
|
||||
bool m_imgui_enable = false;
|
||||
bool handle_key_event(const KeyEvent& e);
|
||||
bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e);
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -9,7 +9,6 @@ target_sources(${PROJECT_NAME}
|
||||
gameplay/biome.cpp
|
||||
gameplay/chunk_generator.cpp
|
||||
gameplay/tree.cpp
|
||||
input.cpp
|
||||
render/renderer.cpp
|
||||
shader.cpp
|
||||
texture_manager.cpp
|
||||
@@ -17,7 +16,6 @@ target_sources(${PROJECT_NAME}
|
||||
tools/shader_tools.cpp
|
||||
tools/font.cpp
|
||||
tools/perlin_noise.cpp
|
||||
ui/text.cpp
|
||||
window.cpp
|
||||
gameplay/builders/biome_builder.cpp
|
||||
gameplay/builders/plain_builder.cpp
|
||||
@@ -59,4 +57,30 @@ target_sources(${PROJECT_NAME}
|
||||
render/frame_buffer.cpp
|
||||
render/shader_manager.cpp
|
||||
render/world_renderer.cpp
|
||||
ui/widget.cpp
|
||||
ui/label.cpp
|
||||
ui/ui_vertex_data.cpp
|
||||
ui/image.cpp
|
||||
ui/button.cpp
|
||||
scene/world_scene.cpp
|
||||
scene/scene_manager.cpp
|
||||
ui/world_ui_manager.cpp
|
||||
ui/main_menu_ui_manager.cpp
|
||||
scene/main_menu_scene.cpp
|
||||
ui/column_layout.cpp
|
||||
ui/rect.cpp
|
||||
ui/ui_manager.cpp
|
||||
ui/pause_menu_ui_manager.cpp
|
||||
scene/credits_scene.cpp
|
||||
ui/credits_ui.cpp
|
||||
ui/slider.cpp
|
||||
ui/settings_ui.cpp
|
||||
scene/settings_scene.cpp
|
||||
ui/combo_button.cpp
|
||||
ui/text_field.cpp
|
||||
scene/host_game_scene.cpp
|
||||
scene/join_game_scene.cpp
|
||||
ui/host_game_ui.cpp
|
||||
ui/join_game_ui.cpp
|
||||
ui/error_ui.cpp
|
||||
)
|
||||
591
src/app.cpp
591
src/app.cpp
@@ -1,11 +1,13 @@
|
||||
#include "Cubed/app.hpp"
|
||||
|
||||
#include "Cubed/camera.hpp"
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
#include "Cubed/tools/arg_parser.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/system_info.hpp"
|
||||
#include "Cubed/tools/text_tools.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include <exception>
|
||||
@@ -16,30 +18,22 @@ App::App()
|
||||
|
||||
: m_game_config(ASSETS_PATH "config.toml"),
|
||||
m_texture_manager(m_game_config), m_audio(m_game_config),
|
||||
m_client_world(m_audio, m_game_config), m_dev_panel(*this),
|
||||
m_renderer(m_camera, m_client_world, m_texture_manager, m_dev_panel,
|
||||
m_game_config),
|
||||
m_window(m_renderer, m_game_config) {}
|
||||
m_renderer(m_texture_manager, m_game_config), m_window(m_game_config),
|
||||
m_scene_manager(*this) {}
|
||||
|
||||
App::~App() {
|
||||
if (m_client) {
|
||||
m_client->stop();
|
||||
}
|
||||
}
|
||||
App::~App() {}
|
||||
void App::cursor_position_callback(GLFWwindow* window, double xpos,
|
||||
double ypos) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
||||
if (app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos);
|
||||
return;
|
||||
}
|
||||
if (!app->m_window.is_mouse_enable()) {
|
||||
app->m_camera.update_cursor_position_camera(xpos, ypos);
|
||||
}
|
||||
|
||||
app->m_scene_manager.handle_event(
|
||||
MouseMoveEvent{static_cast<float>(xpos), static_cast<float>(ypos)});
|
||||
}
|
||||
void App::init(int argc, char** argv) {
|
||||
handle_toml();
|
||||
@@ -60,6 +54,8 @@ void App::init(int argc, char** argv) {
|
||||
window_focus_callback);
|
||||
glfwSetWindowSizeCallback(m_window.get_glfw_window(),
|
||||
window_reshape_callback);
|
||||
glfwSetFramebufferSizeCallback(m_window.get_glfw_window(),
|
||||
framebuffer_size_callback);
|
||||
glfwSetKeyCallback(m_window.get_glfw_window(), key_callback);
|
||||
glfwSetScrollCallback(m_window.get_glfw_window(), mouse_scroll_callback);
|
||||
glfwSetCursorEnterCallback(m_window.get_glfw_window(),
|
||||
@@ -67,29 +63,26 @@ void App::init(int argc, char** argv) {
|
||||
glfwSetCharCallback(m_window.get_glfw_window(), char_callback);
|
||||
|
||||
m_audio.init();
|
||||
|
||||
ChunkGenerator::init();
|
||||
BlockManager::init();
|
||||
m_renderer.init(m_argument.debug_on);
|
||||
Logger::info("Renderer Init Success");
|
||||
m_window.update_viewport();
|
||||
// MapTable::init_map();
|
||||
m_texture_manager.init_texture();
|
||||
Logger::info("Texture Load Success");
|
||||
if (!m_argument.is_client) {
|
||||
m_server.start_server(m_argument.port);
|
||||
|
||||
m_scene_manager.request_push(SceneType::MAIN_MENU);
|
||||
|
||||
{
|
||||
int w, h;
|
||||
glfwGetWindowSize(m_window.get_glfw_window(), &w, &h);
|
||||
window_reshape_callback(m_window.get_glfw_window(), w, h);
|
||||
}
|
||||
|
||||
m_client = std::make_shared<NetworkClient>(m_client_world);
|
||||
|
||||
m_client->start(m_argument.ip, m_argument.port);
|
||||
// init will send packet
|
||||
m_client_world.init(m_argument.player, m_client);
|
||||
|
||||
Logger::info("World Init Success");
|
||||
|
||||
m_camera.camera_init(&m_client_world.get_player());
|
||||
m_dev_panel.init();
|
||||
{
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(m_window.get_glfw_window(), &w, &h);
|
||||
framebuffer_size_callback(m_window.get_glfw_window(), w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void App::handle_argument(int argc, char** argv) {
|
||||
@@ -178,50 +171,358 @@ void App::key_callback(GLFWwindow* window, int key, int scancode, int action,
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
// ImGui_ImplGlfw_CursorEnterCallback(window,
|
||||
// !app->m_window.is_mouse_enable());
|
||||
if (io.WantCaptureKeyboard && app->m_window.is_mouse_enable()) {
|
||||
if ((key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_ESCAPE) &&
|
||||
action == GLFW_PRESS) {
|
||||
app->m_window.toggle_mouse_able();
|
||||
app->m_camera.reset_camera();
|
||||
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
|
||||
if ((key == GLFW_KEY_LEFT_ALT) && action == GLFW_PRESS) {
|
||||
app->dispatch_event(KeyEvent{Key::LEFT_ALT, KeyAction::PRESS});
|
||||
return;
|
||||
}
|
||||
ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
|
||||
return;
|
||||
}
|
||||
Key pkey;
|
||||
KeyAction act;
|
||||
switch (key) {
|
||||
// Letter keys
|
||||
case GLFW_KEY_A:
|
||||
pkey = Key::A;
|
||||
break;
|
||||
case GLFW_KEY_B:
|
||||
pkey = Key::B;
|
||||
break;
|
||||
case GLFW_KEY_C:
|
||||
pkey = Key::C;
|
||||
break;
|
||||
case GLFW_KEY_D:
|
||||
pkey = Key::D;
|
||||
break;
|
||||
case GLFW_KEY_E:
|
||||
pkey = Key::E;
|
||||
break;
|
||||
case GLFW_KEY_F:
|
||||
pkey = Key::F;
|
||||
break;
|
||||
case GLFW_KEY_G:
|
||||
pkey = Key::G;
|
||||
break;
|
||||
case GLFW_KEY_H:
|
||||
pkey = Key::H;
|
||||
break;
|
||||
case GLFW_KEY_I:
|
||||
pkey = Key::I;
|
||||
break;
|
||||
case GLFW_KEY_J:
|
||||
pkey = Key::J;
|
||||
break;
|
||||
case GLFW_KEY_K:
|
||||
pkey = Key::K;
|
||||
break;
|
||||
case GLFW_KEY_L:
|
||||
pkey = Key::L;
|
||||
break;
|
||||
case GLFW_KEY_M:
|
||||
pkey = Key::M;
|
||||
break;
|
||||
case GLFW_KEY_N:
|
||||
pkey = Key::N;
|
||||
break;
|
||||
case GLFW_KEY_O:
|
||||
pkey = Key::O;
|
||||
break;
|
||||
case GLFW_KEY_P:
|
||||
pkey = Key::P;
|
||||
break;
|
||||
case GLFW_KEY_Q:
|
||||
if (action == GLFW_PRESS) {
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
if (action == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_F11:
|
||||
if (action == GLFW_PRESS) {
|
||||
app->m_window.toggle_fullscreen();
|
||||
}
|
||||
pkey = Key::Q;
|
||||
break;
|
||||
case GLFW_KEY_R:
|
||||
if (action == GLFW_PRESS) {
|
||||
app->m_texture_manager.need_reload();
|
||||
}
|
||||
pkey = Key::R;
|
||||
break;
|
||||
case GLFW_KEY_LEFT_ALT:
|
||||
if (action == GLFW_PRESS) {
|
||||
app->m_window.toggle_mouse_able();
|
||||
app->m_camera.reset_camera();
|
||||
}
|
||||
case GLFW_KEY_S:
|
||||
pkey = Key::S;
|
||||
break;
|
||||
case GLFW_KEY_T:
|
||||
pkey = Key::T;
|
||||
break;
|
||||
case GLFW_KEY_U:
|
||||
pkey = Key::U;
|
||||
break;
|
||||
case GLFW_KEY_V:
|
||||
pkey = Key::V;
|
||||
break;
|
||||
case GLFW_KEY_W:
|
||||
pkey = Key::W;
|
||||
break;
|
||||
case GLFW_KEY_X:
|
||||
pkey = Key::X;
|
||||
break;
|
||||
case GLFW_KEY_Y:
|
||||
pkey = Key::Y;
|
||||
break;
|
||||
case GLFW_KEY_Z:
|
||||
pkey = Key::Z;
|
||||
break;
|
||||
|
||||
// Digit keys (main keyboard area)
|
||||
case GLFW_KEY_0:
|
||||
pkey = Key::DIGIT_0;
|
||||
break;
|
||||
case GLFW_KEY_1:
|
||||
pkey = Key::DIGIT_1;
|
||||
break;
|
||||
case GLFW_KEY_2:
|
||||
pkey = Key::DIGIT_2;
|
||||
break;
|
||||
case GLFW_KEY_3:
|
||||
pkey = Key::DIGIT_3;
|
||||
break;
|
||||
case GLFW_KEY_4:
|
||||
pkey = Key::DIGIT_4;
|
||||
break;
|
||||
case GLFW_KEY_5:
|
||||
pkey = Key::DIGIT_5;
|
||||
break;
|
||||
case GLFW_KEY_6:
|
||||
pkey = Key::DIGIT_6;
|
||||
break;
|
||||
case GLFW_KEY_7:
|
||||
pkey = Key::DIGIT_7;
|
||||
break;
|
||||
case GLFW_KEY_8:
|
||||
pkey = Key::DIGIT_8;
|
||||
break;
|
||||
case GLFW_KEY_9:
|
||||
pkey = Key::DIGIT_9;
|
||||
break;
|
||||
|
||||
// Function keys
|
||||
case GLFW_KEY_F1:
|
||||
pkey = Key::F1;
|
||||
break;
|
||||
case GLFW_KEY_F2:
|
||||
pkey = Key::F2;
|
||||
break;
|
||||
case GLFW_KEY_F3:
|
||||
pkey = Key::F3;
|
||||
break;
|
||||
case GLFW_KEY_F4:
|
||||
pkey = Key::F4;
|
||||
break;
|
||||
case GLFW_KEY_F5:
|
||||
if (action == GLFW_PRESS) {
|
||||
app->m_camera.change_perspective();
|
||||
}
|
||||
pkey = Key::F5;
|
||||
break;
|
||||
case GLFW_KEY_F6:
|
||||
pkey = Key::F6;
|
||||
break;
|
||||
case GLFW_KEY_F7:
|
||||
pkey = Key::F7;
|
||||
break;
|
||||
case GLFW_KEY_F8:
|
||||
pkey = Key::F8;
|
||||
break;
|
||||
case GLFW_KEY_F9:
|
||||
pkey = Key::F9;
|
||||
break;
|
||||
case GLFW_KEY_F10:
|
||||
pkey = Key::F10;
|
||||
break;
|
||||
case GLFW_KEY_F11:
|
||||
pkey = Key::F11;
|
||||
break;
|
||||
case GLFW_KEY_F12:
|
||||
pkey = Key::F12;
|
||||
break;
|
||||
|
||||
// Control keys
|
||||
case GLFW_KEY_BACKSPACE:
|
||||
pkey = Key::BACKSPACE;
|
||||
break;
|
||||
case GLFW_KEY_TAB:
|
||||
pkey = Key::TAB;
|
||||
break;
|
||||
case GLFW_KEY_ENTER:
|
||||
pkey = Key::ENTER;
|
||||
break;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
pkey = Key::ESCAPE;
|
||||
break;
|
||||
case GLFW_KEY_SPACE:
|
||||
pkey = Key::SPACE;
|
||||
break;
|
||||
case GLFW_KEY_CAPS_LOCK:
|
||||
pkey = Key::CAPS_LOCK;
|
||||
break;
|
||||
case GLFW_KEY_NUM_LOCK:
|
||||
pkey = Key::NUM_LOCK;
|
||||
break;
|
||||
case GLFW_KEY_SCROLL_LOCK:
|
||||
pkey = Key::SCROLL_LOCK;
|
||||
break;
|
||||
|
||||
// Modifier keys
|
||||
case GLFW_KEY_LEFT_SHIFT:
|
||||
pkey = Key::LEFT_SHIFT;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT_SHIFT:
|
||||
pkey = Key::RIGHT_SHIFT;
|
||||
break;
|
||||
case GLFW_KEY_LEFT_CONTROL:
|
||||
pkey = Key::LEFT_CTRL;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT_CONTROL:
|
||||
pkey = Key::RIGHT_CTRL;
|
||||
break;
|
||||
case GLFW_KEY_LEFT_ALT:
|
||||
pkey = Key::LEFT_ALT;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT_ALT:
|
||||
pkey = Key::RIGHT_ALT;
|
||||
break;
|
||||
case GLFW_KEY_LEFT_SUPER:
|
||||
pkey = Key::LEFT_SUPER;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT_SUPER:
|
||||
pkey = Key::RIGHT_SUPER;
|
||||
break;
|
||||
|
||||
// Navigation keys
|
||||
case GLFW_KEY_INSERT:
|
||||
pkey = Key::INSERT;
|
||||
break;
|
||||
case GLFW_KEY_DELETE:
|
||||
pkey = Key::DELETE;
|
||||
break;
|
||||
case GLFW_KEY_HOME:
|
||||
pkey = Key::HOME;
|
||||
break;
|
||||
case GLFW_KEY_END:
|
||||
pkey = Key::END;
|
||||
break;
|
||||
case GLFW_KEY_PAGE_UP:
|
||||
pkey = Key::PAGE_UP;
|
||||
break;
|
||||
case GLFW_KEY_PAGE_DOWN:
|
||||
pkey = Key::PAGE_DOWN;
|
||||
break;
|
||||
case GLFW_KEY_LEFT:
|
||||
pkey = Key::LEFT;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT:
|
||||
pkey = Key::RIGHT;
|
||||
break;
|
||||
case GLFW_KEY_UP:
|
||||
pkey = Key::UP;
|
||||
break;
|
||||
case GLFW_KEY_DOWN:
|
||||
pkey = Key::DOWN;
|
||||
break;
|
||||
|
||||
// Lock and system keys
|
||||
case GLFW_KEY_PRINT_SCREEN:
|
||||
pkey = Key::PRINT_SCREEN;
|
||||
break;
|
||||
case GLFW_KEY_PAUSE:
|
||||
pkey = Key::PAUSE;
|
||||
break;
|
||||
|
||||
// Main keyboard area symbol keys
|
||||
case GLFW_KEY_GRAVE_ACCENT:
|
||||
pkey = Key::GRAVE_ACCENT;
|
||||
break;
|
||||
case GLFW_KEY_MINUS:
|
||||
pkey = Key::MINUS;
|
||||
break;
|
||||
case GLFW_KEY_EQUAL:
|
||||
pkey = Key::EQUALS;
|
||||
break;
|
||||
case GLFW_KEY_LEFT_BRACKET:
|
||||
pkey = Key::LEFT_BRACKET;
|
||||
break;
|
||||
case GLFW_KEY_RIGHT_BRACKET:
|
||||
pkey = Key::RIGHT_BRACKET;
|
||||
break;
|
||||
case GLFW_KEY_BACKSLASH:
|
||||
pkey = Key::BACKSLASH;
|
||||
break;
|
||||
case GLFW_KEY_SEMICOLON:
|
||||
pkey = Key::SEMICOLON;
|
||||
break;
|
||||
case GLFW_KEY_APOSTROPHE:
|
||||
pkey = Key::APOSTROPHE;
|
||||
break;
|
||||
case GLFW_KEY_COMMA:
|
||||
pkey = Key::COMMA;
|
||||
break;
|
||||
case GLFW_KEY_PERIOD:
|
||||
pkey = Key::PERIOD;
|
||||
break;
|
||||
case GLFW_KEY_SLASH:
|
||||
pkey = Key::SLASH;
|
||||
break;
|
||||
|
||||
// Numpad area
|
||||
case GLFW_KEY_KP_0:
|
||||
pkey = Key::NUMPAD_0;
|
||||
break;
|
||||
case GLFW_KEY_KP_1:
|
||||
pkey = Key::NUMPAD_1;
|
||||
break;
|
||||
case GLFW_KEY_KP_2:
|
||||
pkey = Key::NUMPAD_2;
|
||||
break;
|
||||
case GLFW_KEY_KP_3:
|
||||
pkey = Key::NUMPAD_3;
|
||||
break;
|
||||
case GLFW_KEY_KP_4:
|
||||
pkey = Key::NUMPAD_4;
|
||||
break;
|
||||
case GLFW_KEY_KP_5:
|
||||
pkey = Key::NUMPAD_5;
|
||||
break;
|
||||
case GLFW_KEY_KP_6:
|
||||
pkey = Key::NUMPAD_6;
|
||||
break;
|
||||
case GLFW_KEY_KP_7:
|
||||
pkey = Key::NUMPAD_7;
|
||||
break;
|
||||
case GLFW_KEY_KP_8:
|
||||
pkey = Key::NUMPAD_8;
|
||||
break;
|
||||
case GLFW_KEY_KP_9:
|
||||
pkey = Key::NUMPAD_9;
|
||||
break;
|
||||
case GLFW_KEY_KP_ADD:
|
||||
pkey = Key::NUMPAD_ADD;
|
||||
break;
|
||||
case GLFW_KEY_KP_SUBTRACT:
|
||||
pkey = Key::NUMPAD_SUBTRACT;
|
||||
break;
|
||||
case GLFW_KEY_KP_MULTIPLY:
|
||||
pkey = Key::NUMPAD_MULTIPLY;
|
||||
break;
|
||||
case GLFW_KEY_KP_DIVIDE:
|
||||
pkey = Key::NUMPAD_DIVIDE;
|
||||
break;
|
||||
case GLFW_KEY_KP_DECIMAL:
|
||||
pkey = Key::NUMPAD_DECIMAL;
|
||||
break;
|
||||
case GLFW_KEY_KP_ENTER:
|
||||
pkey = Key::NUMPAD_ENTER;
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::error("Unknown Key {}", key);
|
||||
return;
|
||||
}
|
||||
|
||||
app->m_client_world.get_player().update_player_move_state(key, action);
|
||||
if (action == GLFW_PRESS) {
|
||||
act = KeyAction::PRESS;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
act = KeyAction::RELEASE;
|
||||
} else {
|
||||
act = KeyAction::REPEAT;
|
||||
}
|
||||
|
||||
app->dispatch_event(KeyEvent{pkey, act});
|
||||
}
|
||||
|
||||
void App::mouse_button_callback(GLFWwindow* window, int button, int action,
|
||||
@@ -229,75 +530,102 @@ void App::mouse_button_callback(GLFWwindow* window, int button, int action,
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
||||
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
|
||||
return;
|
||||
}
|
||||
MouseKey key;
|
||||
KeyAction act;
|
||||
switch (button) {
|
||||
case GLFW_MOUSE_BUTTON_LEFT:
|
||||
if (action == GLFW_PRESS) {
|
||||
if (app->m_window.is_mouse_enable()) {
|
||||
app->m_window.toggle_mouse_able();
|
||||
app->m_camera.reset_camera();
|
||||
break;
|
||||
;
|
||||
}
|
||||
Input::get_input_state().mouse_state.left = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
Input::get_input_state().mouse_state.left = false;
|
||||
}
|
||||
key = MouseKey::LEFT_BUTTON;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_RIGHT:
|
||||
if (action == GLFW_PRESS) {
|
||||
Input::get_input_state().mouse_state.right = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
Input::get_input_state().mouse_state.right = false;
|
||||
}
|
||||
key = MouseKey::RIGHT_BUTTON;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_MIDDLE:
|
||||
key = MouseKey::MIDDLE_BUTTON;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_4:
|
||||
key = MouseKey::BACK_BUTTON;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_5:
|
||||
key = MouseKey::FORWARD_BUTTON;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_6:
|
||||
key = MouseKey::EXTRA_BUTTON_1;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_7:
|
||||
key = MouseKey::EXTRA_BUTTON_2;
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_8:
|
||||
key = MouseKey::EXTRA_BUTTON_3;
|
||||
break;
|
||||
default:
|
||||
Logger::error("Unknown Mouse Button {}", button);
|
||||
return;
|
||||
}
|
||||
if (action == GLFW_PRESS) {
|
||||
act = KeyAction::PRESS;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
act = KeyAction::RELEASE;
|
||||
} else {
|
||||
act = KeyAction::REPEAT;
|
||||
}
|
||||
|
||||
app->dispatch_event(MouseButtonEvent{key, act});
|
||||
}
|
||||
|
||||
void App::window_focus_callback(GLFWwindow* window, int focused) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
||||
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_WindowFocusCallback(window, focused);
|
||||
return;
|
||||
}
|
||||
if (focused) {
|
||||
app->m_camera.reset_camera();
|
||||
auto camera = app->m_window.camera();
|
||||
if (camera) {
|
||||
camera->reset_camera();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::window_reshape_callback(GLFWwindow* window, int, int) {
|
||||
void App::window_reshape_callback(GLFWwindow* window, int width, int height) {
|
||||
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
app->m_window.update_viewport();
|
||||
}
|
||||
|
||||
app->dispatch_event(WindowResizeEvent{width, height});
|
||||
|
||||
Logger::info("Window Reshape W: {} H: {}", width, height);
|
||||
}
|
||||
void App::framebuffer_size_callback(GLFWwindow* window, int width, int height) {
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
app->dispatch_event(FrameBufferResizeEvent{width, height});
|
||||
|
||||
Logger::info("Frame Buffer Reshape W: {} H: {}", width, height);
|
||||
}
|
||||
void App::mouse_scroll_callback(GLFWwindow* window, double xoffset,
|
||||
double yoffset) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
||||
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
|
||||
return;
|
||||
}
|
||||
auto& player = app->m_client_world.get_player();
|
||||
player.update_scroll(yoffset);
|
||||
app->dispatch_event(MouseWheelEvent(static_cast<float>(yoffset)));
|
||||
}
|
||||
|
||||
void App::cursor_enter_callback(GLFWwindow* window, int entered) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
||||
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_CursorEnterCallback(window, entered);
|
||||
return;
|
||||
}
|
||||
@@ -307,9 +635,11 @@ void App::char_callback(GLFWwindow* window, unsigned int c) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
||||
ASSERT_MSG(app, "nullptr");
|
||||
if (io.WantCaptureKeyboard && app->m_window.is_mouse_enable()) {
|
||||
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
|
||||
ImGui_ImplGlfw_CharCallback(window, c);
|
||||
return;
|
||||
}
|
||||
app->dispatch_event(TextInputEvent{codepoint_to_utf8(c)});
|
||||
}
|
||||
|
||||
void App::render() {
|
||||
@@ -318,8 +648,9 @@ void App::render() {
|
||||
ImGui_ImplGlfw_Sleep(10);
|
||||
return;
|
||||
}
|
||||
m_renderer.render();
|
||||
|
||||
m_renderer.begin_frame();
|
||||
m_scene_manager.render(m_renderer);
|
||||
m_renderer.end_frame();
|
||||
glfwSwapBuffers(m_window.get_glfw_window());
|
||||
}
|
||||
|
||||
@@ -327,20 +658,33 @@ void App::run() {
|
||||
|
||||
last_time = glfwGetTime();
|
||||
while (!glfwWindowShouldClose(m_window.get_glfw_window())) {
|
||||
if (m_client_world.is_receive_exit()) {
|
||||
break;
|
||||
}
|
||||
// if (m_client_world.is_receive_exit()) {
|
||||
// break;
|
||||
// }
|
||||
update();
|
||||
render();
|
||||
}
|
||||
m_client_world.request_exit();
|
||||
if (!m_argument.is_client) {
|
||||
m_server.server_world().stop();
|
||||
}
|
||||
}
|
||||
static Gait player_gait = Gait::WALK;
|
||||
// static Gait player_gait = Gait::WALK;
|
||||
void App::update() {
|
||||
glfwPollEvents();
|
||||
{
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(m_window.get_glfw_window(), &w, &h);
|
||||
|
||||
if (w != m_renderer.frame_width() || h != m_renderer.frame_height()) {
|
||||
dispatch_event(FrameBufferResizeEvent{w, h});
|
||||
}
|
||||
}
|
||||
{
|
||||
int w, h;
|
||||
glfwGetWindowSize(m_window.get_glfw_window(), &w, &h);
|
||||
|
||||
if (w != m_renderer.window_width() || h != m_renderer.window_height()) {
|
||||
dispatch_event(WindowResizeEvent{w, h});
|
||||
}
|
||||
}
|
||||
|
||||
current_time = glfwGetTime();
|
||||
dt = current_time - last_time;
|
||||
last_time = current_time;
|
||||
@@ -359,23 +703,34 @@ void App::update() {
|
||||
std::format("RSS: {}mb", Tools::get_current_rss() / (1024 * 1024)));
|
||||
}
|
||||
m_texture_manager.update();
|
||||
m_client_world.update(dt);
|
||||
m_camera.update_move_camera();
|
||||
const auto& player = m_client_world.get_player();
|
||||
if (player_gait != player.get_gait()) {
|
||||
player_gait = player.get_gait();
|
||||
float fov = m_game_config.get("player.fov", 70.0f);
|
||||
if (player_gait == Gait::WALK) {
|
||||
m_renderer.update_fov(fov);
|
||||
}
|
||||
if (player_gait == Gait::RUN) {
|
||||
m_renderer.update_fov(fov + 5.0f);
|
||||
}
|
||||
}
|
||||
m_audio.update_listener(m_camera.get_camera_pos(),
|
||||
m_camera.get_camera_front(), glm::vec3(0, 1, 0));
|
||||
|
||||
m_audio.update();
|
||||
DebugCollector::get().get_widget().update(dt);
|
||||
m_renderer.update(dt);
|
||||
|
||||
m_scene_manager.update(dt);
|
||||
}
|
||||
|
||||
void App::dispatch_event(const Event& e) {
|
||||
if (m_window.handle_event(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_texture_manager.handle_event(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_renderer.handle_event(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DebugCollector::get().handle_event(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_scene_manager.handle_event(e)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int App::start_cubed_application(int argc, char** argv) {
|
||||
@@ -401,14 +756,14 @@ float App::delta_time() { return dt; }
|
||||
|
||||
float App::get_fps() { return fps; }
|
||||
|
||||
Camera& App::camera() { return m_camera; }
|
||||
DevPanel& App::dev_panel() { return m_dev_panel; }
|
||||
Renderer& App::renderer() { return m_renderer; }
|
||||
TextureManager& App::texture_manager() { return m_texture_manager; }
|
||||
Window& App::window() { return m_window; }
|
||||
ClientWorld& App::client_world() { return m_client_world; }
|
||||
ServerWorld& App::server_world() { return m_server.server_world(); }
|
||||
|
||||
Config& App::config() { return m_game_config; }
|
||||
const App::Argument& App::argument() const { return m_argument; }
|
||||
const Argument& App::argument() const { return m_argument; }
|
||||
AudioEngine& App::audio() { return m_audio; }
|
||||
const char* App::get_clipboard_text() {
|
||||
return glfwGetClipboardString(m_window.get_glfw_window());
|
||||
}
|
||||
} // namespace Cubed
|
||||
@@ -93,7 +93,7 @@ void AudioEngine::init() {
|
||||
}
|
||||
|
||||
void AudioEngine::play_bgm() { m_bgm->play(); }
|
||||
|
||||
void AudioEngine::stop_bgm() { m_bgm->stop(); }
|
||||
void AudioEngine::change_bgm(const std::string& sound) {
|
||||
Logger::info("change bgm {}", sound);
|
||||
m_bgm->stop();
|
||||
|
||||
@@ -79,7 +79,13 @@ void AudioSource::set_pitch(float pitch) {
|
||||
alSourcef(m_source, AL_PITCH, pitch);
|
||||
}
|
||||
|
||||
void AudioSource::play() { alSourcePlay(m_source); }
|
||||
void AudioSource::play() {
|
||||
|
||||
if (state() != AudioState::INITIAL) {
|
||||
stop();
|
||||
}
|
||||
alSourcePlay(m_source);
|
||||
}
|
||||
|
||||
void AudioSource::play_2d(const AudioBuffer& buffer) {
|
||||
set_buffer_2d(buffer);
|
||||
|
||||
@@ -115,6 +115,32 @@ bool Camera::is_first_person() const {
|
||||
return m_perspective == Perspective::FIRST_PERSON;
|
||||
}
|
||||
|
||||
bool Camera::handle_event(const Event& e) {
|
||||
return std::visit(
|
||||
Overloaded{[this](const MouseMoveEvent& e) {
|
||||
if (handle_mouse_move_event(e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[](const MouseButtonEvent&) { return false; },
|
||||
[](const MouseWheelEvent&) { return false; },
|
||||
[this](const KeyEvent& e) {
|
||||
if (handle_key_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[](const WindowResizeEvent&) { return false; },
|
||||
[](const FrameBufferResizeEvent&) { return false; }
|
||||
|
||||
} // namespace Cubed
|
||||
,
|
||||
e);
|
||||
}
|
||||
|
||||
glm::vec3 Camera::camera_collision(glm::vec3 start, glm::vec3 end,
|
||||
float radius) {
|
||||
constexpr float STEP = 0.05f;
|
||||
@@ -136,4 +162,20 @@ glm::vec3 Camera::camera_collision(glm::vec3 start, glm::vec3 end,
|
||||
return end;
|
||||
}
|
||||
|
||||
bool Camera::handle_key_event(const KeyEvent& e) {
|
||||
if (e.key == Key::LEFT_ALT && e.action == KeyAction::PRESS) {
|
||||
reset_camera();
|
||||
return true;
|
||||
}
|
||||
if (e.key == Key::F5 && e.action == KeyAction::PRESS) {
|
||||
change_perspective();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Camera::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||
update_cursor_position_camera(e.xpos, e.ypos);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,101 +1,150 @@
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
|
||||
#include "Cubed/tools/cubed_hash.hpp"
|
||||
#include "Cubed/tools/system_info.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
DebugCollector::DebugCollector() {}
|
||||
DebugCollector::DebugCollector() : m_widget(nullptr) {}
|
||||
|
||||
DebugCollector& DebugCollector::get() {
|
||||
static DebugCollector instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void DebugCollector::init_text() {
|
||||
Text version_text("version");
|
||||
Text fps_text("fps");
|
||||
Text player_pos_text("player_pos");
|
||||
Text rendered_chunk_text("rendered_chunk");
|
||||
Text rss_text("rss");
|
||||
Text cpu_text("cpu");
|
||||
Text gpu_text("gpu");
|
||||
Text opengl_version_text("opengl_version");
|
||||
Text biome_text("biome");
|
||||
Text speed_text("speed");
|
||||
void DebugCollector::init(int width, int height) {
|
||||
constexpr float SCALE = 0.6f;
|
||||
|
||||
m_widget.set_window_size(width, height);
|
||||
m_widget.set_spacing(15);
|
||||
m_widget.set_anchor(Anchor::TOP_LEFT);
|
||||
m_widget.set_offset({0, 5});
|
||||
m_widget.set_child_anchor(ColumnLayoutAnchor::LEFT);
|
||||
// version_text
|
||||
auto& version_text = m_widget.add_child<Label>("version");
|
||||
std::string version{"Version: " CUBED_VERSION};
|
||||
|
||||
#ifdef DEBUG_MODE
|
||||
version.append("-debug");
|
||||
#else
|
||||
version.append("-release");
|
||||
#endif
|
||||
version_text.position(0.0f, 100.0f)
|
||||
.scale(0.8f)
|
||||
.color(Color::WHITE)
|
||||
.text(version);
|
||||
fps_text.position(0.0f, 50.0f).text("FPS: 0");
|
||||
player_pos_text.position(0.0f, 150.0f)
|
||||
.scale(0.8f)
|
||||
.text("x: 0.00 y: 0.00 z: 0.00");
|
||||
rendered_chunk_text.text("Rendered Chunk: 0")
|
||||
.scale(0.8f)
|
||||
.position(0.0, 200.0f);
|
||||
rss_text.text("RSS: 0mb").scale(0.8f).position(0.0f, 300.0f);
|
||||
std::string os;
|
||||
|
||||
Text os_text("os");
|
||||
os_text.scale(0.8f).position(0.0f, 250.0f);
|
||||
version_text.set_color(Color::WHITE).set_text(version).set_scale(SCALE);
|
||||
m_component.try_emplace(version_text.id(), &version_text);
|
||||
|
||||
// fps
|
||||
auto& fps_text = m_widget.add_child<Label>("fps");
|
||||
fps_text.set_text("FPS: 0").set_scale(SCALE);
|
||||
m_component.try_emplace(fps_text.id(), &fps_text);
|
||||
|
||||
// player_pos
|
||||
auto& player_pos_text = m_widget.add_child<Label>("player_pos");
|
||||
player_pos_text.set_text("x: 0.00 y: 0.00 z: 0.00").set_scale(SCALE);
|
||||
m_component.try_emplace(player_pos_text.id(), &player_pos_text);
|
||||
|
||||
// rendered_chunk
|
||||
auto& rendered_chunk_text = m_widget.add_child<Label>("rendered_chunk");
|
||||
rendered_chunk_text.set_text("Rendered Chunk: 0").set_scale(SCALE);
|
||||
m_component.try_emplace(rendered_chunk_text.id(), &rendered_chunk_text);
|
||||
|
||||
// rss
|
||||
auto& rss_text = m_widget.add_child<Label>("rss");
|
||||
rss_text.set_text("RSS: 0mb").set_scale(SCALE);
|
||||
m_component.try_emplace(rss_text.id(), &rss_text);
|
||||
|
||||
// os
|
||||
std::string os;
|
||||
auto& os_text = m_widget.add_child<Label>("os");
|
||||
os_text.set_scale(SCALE);
|
||||
if (Tools::get_os_version(os)) {
|
||||
os_text.text("OS: " + os);
|
||||
os_text.set_text("OS: " + os);
|
||||
Logger::info("System: {}", os);
|
||||
} else {
|
||||
os_text.text("OS: Unknown");
|
||||
os_text.set_text("OS: Unknown");
|
||||
}
|
||||
cpu_text.text("CPU: " + Tools::get_cpu_info())
|
||||
.scale(0.7f)
|
||||
.position(0.0f, 350.0f);
|
||||
m_component.try_emplace(os_text.id(), &os_text);
|
||||
|
||||
// cpu
|
||||
auto& cpu_text = m_widget.add_child<Label>("cpu");
|
||||
cpu_text.set_text("CPU: " + Tools::get_cpu_info()).set_scale(SCALE);
|
||||
m_component.try_emplace(cpu_text.id(), &cpu_text);
|
||||
|
||||
// gpu
|
||||
auto& gpu_text = m_widget.add_child<Label>("gpu");
|
||||
gpu_text
|
||||
.text(std::string{"GPU: "} +
|
||||
reinterpret_cast<const char*>(glGetString(GL_RENDERER)))
|
||||
.scale(0.7f)
|
||||
.position(0.0f, 400.0f);
|
||||
.set_text(std::string{"GPU: "} +
|
||||
reinterpret_cast<const char*>(glGetString(GL_RENDERER)))
|
||||
.set_scale(SCALE);
|
||||
m_component.try_emplace(gpu_text.id(), &gpu_text);
|
||||
|
||||
// opengl_version
|
||||
auto& opengl_version_text = m_widget.add_child<Label>("opengl_version");
|
||||
opengl_version_text
|
||||
.text("OpenGL: " + std::to_string(GLVersion.major) + "." +
|
||||
std::to_string(GLVersion.minor))
|
||||
.scale(0.7f)
|
||||
.position(0.0f, 450.0f);
|
||||
biome_text.text("Biome: ").scale(0.8f).position(0.0f, 500.0f);
|
||||
speed_text.text("Speed: 0 m/s").scale(0.8f).position(0.0f, 550.0f);
|
||||
m_texts.insert({version_text.uuid(), std::move(version_text)});
|
||||
m_texts.insert({fps_text.uuid(), std::move(fps_text)});
|
||||
m_texts.insert({player_pos_text.uuid(), std::move(player_pos_text)});
|
||||
m_texts.insert(
|
||||
{rendered_chunk_text.uuid(), std::move(rendered_chunk_text)});
|
||||
m_texts.insert({os_text.uuid(), std::move(os_text)});
|
||||
m_texts.insert({rss_text.uuid(), std::move(rss_text)});
|
||||
m_texts.insert({cpu_text.uuid(), std::move(cpu_text)});
|
||||
m_texts.insert({gpu_text.uuid(), std::move(gpu_text)});
|
||||
m_texts.insert(
|
||||
{opengl_version_text.uuid(), std::move(opengl_version_text)});
|
||||
m_texts.insert({biome_text.uuid(), std::move(biome_text)});
|
||||
m_texts.insert({speed_text.uuid(), std::move(speed_text)});
|
||||
.set_text("OpenGL: " + std::to_string(GLVersion.major) + "." +
|
||||
std::to_string(GLVersion.minor))
|
||||
.set_scale(SCALE);
|
||||
m_component.try_emplace(opengl_version_text.id(), &opengl_version_text);
|
||||
|
||||
// biome
|
||||
auto& biome_text = m_widget.add_child<Label>("biome");
|
||||
biome_text.set_text("Biome: ").set_scale(SCALE);
|
||||
m_component.try_emplace(biome_text.id(), &biome_text);
|
||||
|
||||
// speed
|
||||
auto& speed_text = m_widget.add_child<Label>("speed");
|
||||
speed_text.set_text("Speed: 0 m/s").set_scale(SCALE);
|
||||
m_component.try_emplace(speed_text.id(), &speed_text);
|
||||
}
|
||||
|
||||
std::unordered_map<std::size_t, Text>& DebugCollector::all_texts() {
|
||||
return m_texts;
|
||||
Widget& DebugCollector::get_widget() { return m_widget; }
|
||||
|
||||
bool DebugCollector::handle_event(const Event& e) {
|
||||
return std::visit(
|
||||
Overloaded{[this](const MouseMoveEvent& e) {
|
||||
if (m_widget.handle_mouse_move_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const MouseButtonEvent& e) {
|
||||
if (m_widget.handle_mouse_button_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const MouseWheelEvent& e) {
|
||||
if (m_widget.handle_mouse_wheel_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const KeyEvent& e) {
|
||||
if (m_widget.handle_key_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[this](const WindowResizeEvent& e) {
|
||||
m_widget.handle_window_resize_event(e);
|
||||
return false;
|
||||
},
|
||||
[](const FrameBufferResizeEvent&) { return false; }
|
||||
|
||||
},
|
||||
e);
|
||||
}
|
||||
|
||||
Text& DebugCollector::text(std::string_view name) {
|
||||
std::size_t id = HASH::str(name);
|
||||
auto it = m_texts.find(id);
|
||||
ASSERT_MSG(it != m_texts.end(), "Can't Find Text");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void DebugCollector::report(std::string_view name, std::string_view content) {
|
||||
auto& t = text(name);
|
||||
t.text(content);
|
||||
void DebugCollector::report(const std::string& name, std::string_view content) {
|
||||
auto t = m_component.find(name);
|
||||
if (t == m_component.end()) {
|
||||
return;
|
||||
}
|
||||
if (!t->second) {
|
||||
return;
|
||||
}
|
||||
t->second->set_text(content);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Cubed/gameplay/cave_path.hpp"
|
||||
#include "Cubed/gameplay/client_player.hpp"
|
||||
#include "Cubed/gameplay/river.path.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
@@ -46,10 +47,12 @@ constexpr float DELTA_ANGLE_MAX = 30.0f;
|
||||
constexpr int PATH_STEP_MIN = 1;
|
||||
constexpr int PATH_STEP_MAX = 1000;
|
||||
|
||||
DevPanel::DevPanel(App& app) : m_app(app), m_config(app.config()) {}
|
||||
DevPanel::DevPanel(WorldScene& world_scene)
|
||||
: m_app(world_scene.scene_manager().app()), m_world_scene(world_scene),
|
||||
m_config(m_app.config()) {}
|
||||
|
||||
void DevPanel::init() {
|
||||
m_player = &m_app.client_world().get_player();
|
||||
m_player = &m_world_scene.client_world().get_player();
|
||||
update_config_view();
|
||||
update_player_profile();
|
||||
}
|
||||
@@ -77,7 +80,6 @@ void DevPanel::render() {
|
||||
show_player_tab_item();
|
||||
show_items_tab_item();
|
||||
show_shader_tab_item();
|
||||
show_about_table_bar();
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
@@ -86,40 +88,6 @@ void DevPanel::render() {
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void DevPanel::show_about_table_bar() {
|
||||
if (ImGui::BeginTabItem("about")) {
|
||||
ImGui::Text(
|
||||
"Cubed - A cube game like Minecraft, using C++ and OpenGL.");
|
||||
ImGui::Text("Author: zhenyan121");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Libraries Used");
|
||||
ImGui::Text("glad");
|
||||
ImGui::Text("GLFW");
|
||||
ImGui::Text("SOIL2");
|
||||
ImGui::Text("GLM");
|
||||
ImGui::Text("FreeType");
|
||||
ImGui::Text("toml++");
|
||||
ImGui::Text("Dear ImGui");
|
||||
ImGui::Text("Tbb");
|
||||
ImGui::Text("Asio");
|
||||
ImGui::Text("protobuf");
|
||||
ImGui::Text("zstd");
|
||||
ImGui::Text("OpenAl Soft");
|
||||
ImGui::Text("dr_libs");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Music");
|
||||
ImGui::Text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), "
|
||||
"CC BY 4.0.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Special Thanks");
|
||||
ImGui::Text("TANGERIME");
|
||||
ImGui::Text("SkyOnPole");
|
||||
ImGui::Text("free_w_cloud");
|
||||
ImGui::Text("Last but not least, thanks to you");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void DevPanel::show_biome_table_bar() {
|
||||
|
||||
if (ImGui::BeginTabBar("Biome")) {
|
||||
@@ -267,7 +235,7 @@ void DevPanel::show_biome_table_bar() {
|
||||
}
|
||||
|
||||
void DevPanel::show_time_table_bar() {
|
||||
ServerWorld& world = m_app.server_world();
|
||||
ServerWorld& world = m_world_scene.server_world();
|
||||
ImGui::Text("Game Tick %llu", world.game_tick());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Day Tick %llu", world.day_tick());
|
||||
@@ -357,42 +325,42 @@ void DevPanel::show_settings_tab_item() {
|
||||
if (ImGui::BeginTabItem("settings")) {
|
||||
if (ImGui::SliderFloat("FOV", &m_config_view.fov, 1.0f, 140.0f)) {
|
||||
m_config.set("player.fov", static_cast<double>(m_config_view.fov));
|
||||
m_app.renderer().hot_reload();
|
||||
m_app.renderer().reload_config();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("default##1")) {
|
||||
m_config_view.fov = DEFAULT_FOV;
|
||||
m_config.set("player.fov", static_cast<double>(m_config_view.fov));
|
||||
m_app.renderer().hot_reload();
|
||||
m_app.renderer().reload_config();
|
||||
}
|
||||
if (ImGui::SliderFloat("Sensitivity", &m_config_view.mouse_sensitivity,
|
||||
0.01f, 1.0f)) {
|
||||
m_config.set("player.mouse_sensitivity",
|
||||
static_cast<double>(m_config_view.mouse_sensitivity));
|
||||
m_player->hot_reload();
|
||||
m_player->reload_config();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("default##2")) {
|
||||
m_config_view.mouse_sensitivity = 0.15f;
|
||||
m_config.set("player.mouse_sensitivity",
|
||||
static_cast<double>(m_config_view.mouse_sensitivity));
|
||||
m_player->hot_reload();
|
||||
m_player->reload_config();
|
||||
}
|
||||
|
||||
if (ImGui::SliderInt("Distance", &m_config_view.rendering_distance, 2,
|
||||
128)) {
|
||||
m_config.set("world.rendering_distance",
|
||||
m_config_view.rendering_distance);
|
||||
m_app.client_world().hot_reload();
|
||||
m_world_scene.client_world().reload_config();
|
||||
}
|
||||
if (ImGui::Checkbox("Fullscreen", &m_config_view.fullscreen)) {
|
||||
m_config.set("window.fullscreen", m_config_view.fullscreen);
|
||||
m_app.window().hot_reload();
|
||||
m_app.window().reload_config();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("V-Sync", &m_config_view.v_sync)) {
|
||||
m_config.set("window.V-Sync", m_config_view.v_sync);
|
||||
m_app.window().hot_reload();
|
||||
m_app.window().reload_config();
|
||||
}
|
||||
if (ImGui::Checkbox("Aniso", &m_config_view.is_enable_aniso)) {
|
||||
m_config_view.is_reload = false;
|
||||
@@ -492,40 +460,42 @@ void DevPanel::show_server_world_table_bar() {
|
||||
ImGui::Text("ChunkGenerator Seed: %u", ChunkGenerator::seed());
|
||||
|
||||
ImGui::Text("Pool Threads %d Max Support Threads %d Reserved Threads %d",
|
||||
m_app.server_world().gen_pool_threads(),
|
||||
m_app.server_world().max_threads(), RESERVED_THREADS);
|
||||
m_world_scene.server_world().gen_pool_threads(),
|
||||
m_world_scene.server_world().max_threads(), RESERVED_THREADS);
|
||||
ImGui::SliderInt("Set Pool Threads", &m_threads, 1,
|
||||
m_app.server_world().max_threads());
|
||||
m_world_scene.server_world().max_threads());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set")) {
|
||||
m_app.server_world().change_pool_threads(
|
||||
m_world_scene.server_world().change_pool_threads(
|
||||
ServerWorld::ThreadPoolKind::GEN, m_threads);
|
||||
}
|
||||
if (m_threads > m_app.server_world().max_threads() - RESERVED_THREADS) {
|
||||
if (m_threads >
|
||||
m_world_scene.server_world().max_threads() - RESERVED_THREADS) {
|
||||
ImGui::TextColored(
|
||||
ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
||||
"Waring: When the threads in the thread pool exceed \n(maximum "
|
||||
"threads minus reserved threads), \nit may cause stuttering.");
|
||||
}
|
||||
|
||||
m_chunk_style = m_app.server_world().chunk_load_style();
|
||||
m_chunk_style = m_world_scene.server_world().chunk_load_style();
|
||||
if (ImGui::Combo("ChunkLoadStyle", &m_chunk_style, CHUNK_LOAD_STYLE,
|
||||
IM_ARRAYSIZE(CHUNK_LOAD_STYLE))) {
|
||||
m_app.server_world().set_chunk_load_style(m_chunk_style);
|
||||
m_world_scene.server_world().set_chunk_load_style(m_chunk_style);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Request Chunk Build")) {
|
||||
m_app.server_world().need_gen(m_player->get_uuid());
|
||||
m_world_scene.server_world().need_gen(m_player->get_uuid());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("Gen Thread", &m_gen_thread_running)) {
|
||||
if (m_gen_thread_running) {
|
||||
m_app.server_world().start_gen_thread();
|
||||
m_world_scene.server_world().start_gen_thread();
|
||||
} else {
|
||||
m_app.server_world().stop_gen_thread();
|
||||
m_world_scene.server_world().stop_gen_thread();
|
||||
}
|
||||
}
|
||||
ImGui::Text("Server Chunk Size %d", m_app.server_world().chunk_size());
|
||||
ImGui::Text("Server Chunk Size %d",
|
||||
m_world_scene.server_world().chunk_size());
|
||||
|
||||
if (ImGui::BeginTabBar("World Settings")) {
|
||||
if (ImGui::BeginTabItem("Time")) {
|
||||
@@ -551,20 +521,23 @@ void DevPanel::show_server_world_table_bar() {
|
||||
}
|
||||
void DevPanel::show_client_world_table_bar() {
|
||||
|
||||
static int rendering_distance = m_app.client_world().rendering_distance();
|
||||
static int rendering_distance =
|
||||
m_world_scene.client_world().rendering_distance();
|
||||
if (ImGui::SliderInt("Render Distance", &rendering_distance, 2, 128)) {
|
||||
m_app.client_world().rendering_distance(rendering_distance);
|
||||
m_world_scene.client_world().rendering_distance(rendering_distance);
|
||||
// Config::get().set("world.rendering_distance", rendering_distance);
|
||||
}
|
||||
if (ImGui::Button("Rebuild World")) {
|
||||
m_app.client_world().rebuild_world();
|
||||
m_world_scene.client_world().rebuild_world();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Spawn Point")) {
|
||||
m_player->set_player_pos({0.0f, 255.0f, 0.0f});
|
||||
}
|
||||
ImGui::Text("Chunk Task Id %d", m_app.client_world().get_chunk_task_id());
|
||||
ImGui::Text("Client World Chunk %d", m_app.client_world().chunk_size());
|
||||
ImGui::Text("Chunk Task Id %d",
|
||||
m_world_scene.client_world().get_chunk_task_id());
|
||||
ImGui::Text("Client World Chunk %d",
|
||||
m_world_scene.client_world().chunk_size());
|
||||
}
|
||||
|
||||
void DevPanel::show_player_tab_item() {
|
||||
@@ -649,7 +622,7 @@ void DevPanel::show_items_tab_item() {
|
||||
if (ImGui::BeginTabItem("item")) {
|
||||
ImGui::Text("Place Block ");
|
||||
ImGui::SameLine();
|
||||
auto& place_texture = textures[m_player->place_block()];
|
||||
auto& place_texture = textures[m_player->get_current_block()];
|
||||
if (place_texture) {
|
||||
ImGui::Image(static_cast<ImTextureID>(
|
||||
static_cast<intptr_t>(place_texture->id())),
|
||||
|
||||
@@ -109,7 +109,11 @@ ChunkGenerator::ChunkGenerator(ServerChunk& chunk) : m_chunk(chunk) {
|
||||
|
||||
void ChunkGenerator::init() {
|
||||
std::random_device d;
|
||||
m_generator_seed = d();
|
||||
init(d());
|
||||
}
|
||||
|
||||
void ChunkGenerator::init(unsigned seed) {
|
||||
m_generator_seed = seed;
|
||||
Logger::info("Chunk Generator Seed {}", m_generator_seed);
|
||||
PerlinNoise3D::init(m_generator_seed);
|
||||
PerlinNoise2D::init(m_generator_seed);
|
||||
|
||||
@@ -120,7 +120,7 @@ void ClientPlayer::change_mode(GameMode mode) {
|
||||
m_max_speed = m_max_run_speed;
|
||||
}
|
||||
}
|
||||
void ClientPlayer::hot_reload() {
|
||||
void ClientPlayer::reload_config() {
|
||||
auto& config = m_world.get_config();
|
||||
m_sensitivity = config.get("player.mouse_sensitivity", 0.15f);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ void ClientPlayer::update(float delta_time) {
|
||||
m_gait = compute_gait();
|
||||
update_move(delta_time);
|
||||
update_lookup_block();
|
||||
|
||||
place_block(delta_time);
|
||||
DebugCollector::get().report("player_pos",
|
||||
std::format("x: {:.2f} y: {:.2f} z: {:.2f}",
|
||||
m_player_pos.x, m_player_pos.y,
|
||||
@@ -141,48 +141,42 @@ void ClientPlayer::update(float delta_time) {
|
||||
DebugCollector::get().report("speed",
|
||||
std::format("Speed: {:.2} m/s", m_xz_speed));
|
||||
}
|
||||
void ClientPlayer::update_player_move_state(int key, int action) {
|
||||
switch (key) {
|
||||
case GLFW_KEY_W:
|
||||
if (action == GLFW_PRESS) {
|
||||
bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
|
||||
if (key == Key::W) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.forward = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.forward = false;
|
||||
|
||||
m_sprinting = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_S:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::S) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.back = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.back = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_A:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::A) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.left = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.left = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_D:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::D) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.right = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.right = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_SPACE:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::SPACE) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.up = true;
|
||||
if (space_on) {
|
||||
if (m_game_mode == CREATIVE) {
|
||||
is_fly = !is_fly ? true : false;
|
||||
is_fly = !is_fly;
|
||||
m_y_speed = 0.0f;
|
||||
}
|
||||
space_on = false;
|
||||
@@ -191,35 +185,39 @@ void ClientPlayer::update_player_move_state(int key, int action) {
|
||||
space_on = true;
|
||||
}
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.up = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_LEFT_SHIFT:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::LEFT_SHIFT) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_move_state.down = true;
|
||||
}
|
||||
if (action == GLFW_RELEASE) {
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_move_state.down = false;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_LEFT_CONTROL:
|
||||
if (action == GLFW_PRESS) {
|
||||
} else if (key == Key::LEFT_CTRL) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
m_sprinting = true;
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_F4:
|
||||
if (action == GLFW_PRESS) {
|
||||
/*
|
||||
if (action == KeyAction::RELEASE) {
|
||||
m_sprinting = false;
|
||||
}*/
|
||||
} else if (key == Key::F4) {
|
||||
if (action == KeyAction::PRESS) {
|
||||
if (m_game_mode == CREATIVE) {
|
||||
change_mode(SPECTATOR);
|
||||
} else {
|
||||
change_mode(CREATIVE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_moving = m_move_state.forward || m_move_state.back || m_move_state.left ||
|
||||
m_move_state.right;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientPlayer::update_front_vec(float offset_x, float offset_y) {
|
||||
@@ -275,28 +273,34 @@ void ClientPlayer::update_lookup_block() {
|
||||
} else {
|
||||
m_look_block = std::nullopt;
|
||||
}
|
||||
}
|
||||
void ClientPlayer::place_block(float dt) {
|
||||
|
||||
if (m_look_block != std::nullopt) {
|
||||
if (Input::get_input_state().mouse_state.left) {
|
||||
if (m_world.is_solid(m_look_block->pos)) {
|
||||
m_world.report_block_change(m_look_block->pos, 0);
|
||||
}
|
||||
Input::get_input_state().mouse_state.left = false;
|
||||
if (m_look_block == std::nullopt) {
|
||||
return;
|
||||
}
|
||||
m_place_time += dt;
|
||||
if (m_place_time < PLACE_BLOCK_INTERVAL) {
|
||||
|
||||
return;
|
||||
}
|
||||
m_place_time = 0.0f;
|
||||
if (m_mouse_state.left) {
|
||||
if (m_world.is_solid(m_look_block->pos)) {
|
||||
m_world.report_block_change(m_look_block->pos, 0);
|
||||
}
|
||||
if (Input::get_input_state().mouse_state.right) {
|
||||
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
|
||||
if (!m_world.is_solid(near_pos)) {
|
||||
AABB block_box = ClientWorld::get_block_aabb(near_pos);
|
||||
AABB player_box = get_aabb(get_player_pos());
|
||||
if (!player_box.intersects(block_box)) {
|
||||
m_world.report_block_change(near_pos, m_place_block);
|
||||
}
|
||||
}
|
||||
if (m_mouse_state.right) {
|
||||
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
|
||||
if (!m_world.is_solid(near_pos)) {
|
||||
AABB block_box = ClientWorld::get_block_aabb(near_pos);
|
||||
AABB player_box = get_aabb(get_player_pos());
|
||||
if (!player_box.intersects(block_box)) {
|
||||
m_world.report_block_change(near_pos, m_place_block);
|
||||
}
|
||||
Input::get_input_state().mouse_state.right = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientPlayer::update_move(float delta_time) {
|
||||
// if frame rate less than 1 frame per second, don't update
|
||||
if (delta_time > 1.0f) {
|
||||
@@ -523,7 +527,7 @@ Gait ClientPlayer::compute_gait() const {
|
||||
return Gait::WALK;
|
||||
}
|
||||
|
||||
void ClientPlayer::update_scroll(double yoffset) {
|
||||
bool ClientPlayer::update_scroll(float yoffset) {
|
||||
if (m_game_mode == SPECTATOR) {
|
||||
if (yoffset > 0) {
|
||||
if (m_max_speed < 500.0f) {
|
||||
@@ -548,6 +552,48 @@ void ClientPlayer::update_scroll(double yoffset) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClientPlayer::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
if (e.action == KeyAction::PRESS) {
|
||||
if (e.key == MouseKey::LEFT_BUTTON) {
|
||||
m_mouse_state.left = true;
|
||||
m_place_time = PLACE_BLOCK_INTERVAL;
|
||||
return true;
|
||||
}
|
||||
if (e.key == MouseKey::RIGHT_BUTTON) {
|
||||
m_mouse_state.right = true;
|
||||
m_place_time = PLACE_BLOCK_INTERVAL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (e.action == KeyAction::RELEASE) {
|
||||
if (e.key == MouseKey::LEFT_BUTTON) {
|
||||
m_mouse_state.left = false;
|
||||
return true;
|
||||
}
|
||||
if (e.key == MouseKey::RIGHT_BUTTON) {
|
||||
m_mouse_state.right = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClientPlayer::handle_key_event(const KeyEvent& e) {
|
||||
|
||||
if (update_player_move_state(e.key, e.action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool ClientPlayer::handle_mouse_wheel_event(const MouseWheelEvent& e) {
|
||||
if (update_scroll(e.offset)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientPlayer::update_chunk_set(const ChunkPosSet& set) {
|
||||
@@ -573,7 +619,7 @@ float& ClientPlayer::acceleration() { return m_acceleration; }
|
||||
float& ClientPlayer::deceleration() { return m_deceleration; }
|
||||
float& ClientPlayer::g() { return m_g; }
|
||||
float& ClientPlayer::fly_y_speed() { return m_fly_y_speed; }
|
||||
unsigned ClientPlayer::place_block() const { return m_place_block; };
|
||||
unsigned ClientPlayer::get_current_block() const { return m_place_block; };
|
||||
void ClientPlayer::set_gait(Gait gait) { m_gait = gait; }
|
||||
GameMode& ClientPlayer::game_mode() { return m_game_mode; }
|
||||
ClientWorld& ClientPlayer::get_world() { return m_world; }
|
||||
@@ -588,6 +634,18 @@ std::string ClientPlayer::get_uuid() const {
|
||||
return m_uuid;
|
||||
}
|
||||
const std::string& ClientPlayer::get_name() const { return m_name; }
|
||||
|
||||
void ClientPlayer::reset_key_status() {
|
||||
m_mouse_state.left = false;
|
||||
m_mouse_state.right = false;
|
||||
m_move_state.left = false;
|
||||
m_move_state.right = false;
|
||||
m_move_state.back = false;
|
||||
m_move_state.forward = false;
|
||||
m_move_state.down = false;
|
||||
m_move_state.up = false;
|
||||
}
|
||||
|
||||
void ClientPlayer::init(std::string_view name) {
|
||||
|
||||
m_name = name;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Cubed/gameplay/chunk_generator.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/packet.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/tools/math_tools.hpp"
|
||||
|
||||
#include <absl/container/inlined_vector.h>
|
||||
@@ -21,8 +22,8 @@ struct ChunkRenderData {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ClientWorld::ClientWorld(AudioEngine& auido, Config& config)
|
||||
: m_player(*this), m_audio(auido), m_config(config) {}
|
||||
ClientWorld::ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene)
|
||||
: m_player(*this), m_audio(auido), m_config(config), m_world_scene(scene) {}
|
||||
|
||||
ClientWorld::~ClientWorld() {
|
||||
m_client->close();
|
||||
@@ -398,6 +399,9 @@ void ClientWorld::init(std::string_view player_name,
|
||||
std::shared_ptr<NetworkClient> client) {
|
||||
m_player.init(player_name);
|
||||
m_client = client;
|
||||
|
||||
reload_config(false);
|
||||
|
||||
m_random.init(ChunkGenerator::seed());
|
||||
|
||||
// timer
|
||||
@@ -459,7 +463,7 @@ void ClientWorld::init(std::string_view player_name,
|
||||
req.set_name(m_player.get_name());
|
||||
while (!client->is_connected()) {
|
||||
if (client->is_connect_error()) {
|
||||
throw std::runtime_error("Can't connect to the server");
|
||||
throw std::runtime_error(client->get_error_string());
|
||||
}
|
||||
std::this_thread::sleep_for(milliseconds(200));
|
||||
}
|
||||
@@ -522,11 +526,15 @@ void ClientWorld::change_pool_threads(int threads) {
|
||||
m_thread_pool.store(std::make_shared<PriorityThreadPool>(used_thread));
|
||||
}
|
||||
|
||||
void ClientWorld::hot_reload() {
|
||||
void ClientWorld::reload_config(bool chunk_build) {
|
||||
int dist = m_config.get<int>("world.rendering_distance", PRE_LOAD_DISTANCE);
|
||||
Logger::info("Get Config Randering dist {}", dist);
|
||||
m_rendering_distance = dist <= MAX_DISTANCE ? dist : MAX_DISTANCE;
|
||||
request_chunk();
|
||||
if (chunk_build) {
|
||||
request_chunk();
|
||||
}
|
||||
|
||||
m_player.reload_config();
|
||||
}
|
||||
|
||||
void ClientWorld::client_run(std::stop_token stoken) {
|
||||
@@ -663,7 +671,7 @@ void ClientWorld::request_chunk() {
|
||||
Logger::info("Send Chunk Request Success");
|
||||
m_requesting_chunk = false;
|
||||
}
|
||||
|
||||
void ClientWorld::reset_key_status() { m_player.reset_key_status(); }
|
||||
void ClientWorld::receive_chunk(std::vector<uint8_t> raw_data,
|
||||
PacketHeader header) {
|
||||
|
||||
@@ -718,6 +726,7 @@ AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) {
|
||||
|
||||
AudioEngine& ClientWorld::get_audio() { return m_audio; }
|
||||
Config& ClientWorld::get_config() { return m_config; }
|
||||
WorldScene& ClientWorld::world_scene() { return m_world_scene; }
|
||||
|
||||
void ClientWorld::request_exit() {
|
||||
if (m_receive_exit) {
|
||||
@@ -729,6 +738,9 @@ void ClientWorld::request_exit() {
|
||||
m_client->send(make_packet(*req));
|
||||
int cnt = 0;
|
||||
while (!m_receive_exit) {
|
||||
if (m_client->is_connect_error()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(milliseconds(DEFAULT_PER_TICK_TIME));
|
||||
++cnt;
|
||||
if (cnt >= WORLD_EXIT_TIMEOUT) {
|
||||
@@ -739,6 +751,7 @@ void ClientWorld::request_exit() {
|
||||
}
|
||||
|
||||
void ClientWorld::update(float delta_time) {
|
||||
|
||||
m_player.update(delta_time);
|
||||
{
|
||||
std::lock_guard lk(m_delete_vbo_mutex);
|
||||
@@ -905,6 +918,36 @@ void ClientWorld::update(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientWorld::handle_event(const Event& e) {
|
||||
return std::visit(
|
||||
Overloaded{[this](const MouseButtonEvent& e) {
|
||||
if (m_player.handle_mouse_button_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](const MouseMoveEvent&) { return false; },
|
||||
[this](const MouseWheelEvent& e) {
|
||||
if (m_player.handle_mouse_wheel_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const KeyEvent& e) {
|
||||
if (m_player.handle_key_event(e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[](const WindowResizeEvent&) { return false; },
|
||||
[](const FrameBufferResizeEvent&) { return false; }
|
||||
|
||||
},
|
||||
e);
|
||||
}
|
||||
|
||||
glm::vec3 ClientWorld::sunlight_dir() const {
|
||||
float altitude = sin((m_day_tick - 6 * PER_HOUR) /
|
||||
static_cast<float>(DAY_TIME / 2) * std::numbers::pi) *
|
||||
|
||||
@@ -25,6 +25,11 @@ void NetworkClient::start(std::string ip, int port) {
|
||||
|
||||
bool NetworkClient::is_connected() const { return m_connected.load(); }
|
||||
bool NetworkClient::is_connect_error() const { return m_connect_error.load(); }
|
||||
std::string NetworkClient::get_error_string() const {
|
||||
std::lock_guard lock(m_error_string_mutex);
|
||||
return m_error_string;
|
||||
}
|
||||
void NetworkClient::clear_error() { m_connect_error = false; }
|
||||
asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
|
||||
Logger::info("Connect Begin");
|
||||
try {
|
||||
@@ -42,7 +47,7 @@ asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Client Error {}", e.what());
|
||||
m_connect_error = true;
|
||||
set_error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,20 +134,20 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
||||
}
|
||||
} catch (const asio::system_error& e) {
|
||||
auto ec = e.code();
|
||||
|
||||
set_error(e.what());
|
||||
if (ec == asio::error::eof || ec == asio::error::operation_aborted) {
|
||||
|
||||
Logger::info("Client disconnected");
|
||||
} else {
|
||||
Logger::warn("Asio Error {}", e.what());
|
||||
}
|
||||
|
||||
close();
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Session Error {}", e.what());
|
||||
set_error(e.what());
|
||||
close();
|
||||
} catch (...) {
|
||||
Logger::error("Unknow Error");
|
||||
Logger::error("Unknown Error");
|
||||
set_error("Unhnown Error");
|
||||
close();
|
||||
}
|
||||
co_return;
|
||||
@@ -208,4 +213,10 @@ void NetworkClient::stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkClient::set_error(std::string_view error) {
|
||||
std::lock_guard lock(m_error_string_mutex);
|
||||
m_error_string = error;
|
||||
m_connect_error = true;
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "Cubed/input.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
static InputState input_state;
|
||||
|
||||
namespace Input {
|
||||
|
||||
InputState& get_input_state() { return input_state; }
|
||||
|
||||
} // namespace Input
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/renderer.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/texture_manager.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
@@ -188,24 +189,24 @@ void PlayerRenderer::init() {
|
||||
m_inited = true;
|
||||
}
|
||||
|
||||
void PlayerRenderer::render(const Shader& shader) {
|
||||
void PlayerRenderer::render(const Shader& shader, ClientWorld& world) {
|
||||
if (!m_inited) {
|
||||
Logger::error("Player Renderer isn't init");
|
||||
return;
|
||||
}
|
||||
auto& m_camera = m_renderer.camera();
|
||||
auto& m_world = m_renderer.world();
|
||||
auto& m_player = m_world.get_player();
|
||||
glm::mat4 m_v_mat = m_camera.get_camera_lookat();
|
||||
|
||||
auto& camera = world.world_scene().camera();
|
||||
auto& m_player = world.get_player();
|
||||
glm::mat4 m_v_mat = camera.get_camera_lookat();
|
||||
glm::mat4 m_p_mat = m_renderer.world_proj_matrix();
|
||||
|
||||
auto& players = m_world.render_player_data();
|
||||
auto& players = world.render_player_data();
|
||||
shader.set_loc("proj_matrix", m_p_mat);
|
||||
|
||||
for (auto& player : players) {
|
||||
|
||||
if (player.uuid == m_player.get_uuid()) {
|
||||
if (m_camera.is_first_person()) {
|
||||
if (camera.is_first_person()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -302,15 +303,15 @@ void PlayerRenderer::render(const Shader& shader) {
|
||||
}
|
||||
|
||||
void PlayerRenderer::shadow_render(const Shader& shader,
|
||||
glm::mat4& light_matrix) {
|
||||
glm::mat4& light_matrix,
|
||||
ClientWorld& world) {
|
||||
if (!m_inited) {
|
||||
Logger::error("Player Renderer isn't init");
|
||||
return;
|
||||
}
|
||||
shader.use();
|
||||
shader.set_loc("lightSpaceMatrix", light_matrix);
|
||||
auto& m_world = m_renderer.world();
|
||||
auto& players = m_world.render_player_data();
|
||||
auto& players = world.render_player_data();
|
||||
|
||||
for (auto& player : players) {
|
||||
glm::mat4 model(1.0f);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "Cubed/render/renderer.hpp"
|
||||
|
||||
#include "Cubed/camera.hpp"
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
#include "Cubed/dev_panel.hpp"
|
||||
#include "Cubed/gameplay/client_player.hpp"
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/renderer_constants.hpp"
|
||||
#include "Cubed/texture_manager.hpp"
|
||||
@@ -19,12 +16,9 @@
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
Renderer::Renderer(const Camera& camera, ClientWorld& world,
|
||||
const TextureManager& texture_manager, DevPanel& dev_panel,
|
||||
Config& config)
|
||||
: m_camera(camera), m_dev_panel(dev_panel),
|
||||
m_texture_manager(texture_manager), m_world(world),
|
||||
m_world_renderer(*this), m_config(config) {}
|
||||
Renderer::Renderer(TextureManager& texture_manager, Config& config)
|
||||
: m_texture_manager(texture_manager), m_world_renderer(*this),
|
||||
m_config(config) {}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
if (m_init) {
|
||||
@@ -39,7 +33,9 @@ Renderer::~Renderer() {
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::hot_reload() { update_fov(m_config.get("player.fov", 70.0f)); }
|
||||
void Renderer::reload_config() {
|
||||
update_fov(m_config.get("player.fov", 70.0f));
|
||||
}
|
||||
|
||||
void Renderer::init(bool debug_on) {
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||
@@ -99,8 +95,9 @@ void Renderer::init(bool debug_on) {
|
||||
m_vao[3].bind();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Vertex2D vex{SQUARE_VERTICES[i][0], SQUARE_VERTICES[i][1],
|
||||
SQUARE_TEXTURE_POS[i][0], SQUARE_TEXTURE_POS[i][1], 0};
|
||||
Vertex2D vex{SQUARE_VERTICES_TOP_LEFT[i][0],
|
||||
SQUARE_VERTICES_TOP_LEFT[i][1], SQUARE_TEXTURE_POS[i][0],
|
||||
SQUARE_TEXTURE_POS[i][1], 0};
|
||||
m_ui.emplace_back(vex);
|
||||
}
|
||||
m_ui_vbo->buffer_data(m_ui.data(), m_ui.size() * sizeof(Vertex2D));
|
||||
@@ -113,12 +110,13 @@ void Renderer::init(bool debug_on) {
|
||||
|
||||
init_quad();
|
||||
init_text();
|
||||
hot_reload();
|
||||
reload_config();
|
||||
|
||||
m_world_renderer.init();
|
||||
|
||||
VertexArray::unbind();
|
||||
VertexBuffer::unbind();
|
||||
|
||||
m_init = true;
|
||||
}
|
||||
|
||||
@@ -139,63 +137,93 @@ void Renderer::init_quad() {
|
||||
void Renderer::init_text() {
|
||||
m_vao[4].bind();
|
||||
|
||||
DebugCollector::get().init_text();
|
||||
DebugCollector::get().init(m_window_width, m_window_height);
|
||||
}
|
||||
|
||||
void Renderer::render() {
|
||||
void Renderer::begin_frame() {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
// clear screen
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
void Renderer::end_frame() {}
|
||||
|
||||
m_world_renderer.render();
|
||||
void Renderer::begin_render_ui() {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
render_ui();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
void Renderer::end_render_ui() { glEnable(GL_DEPTH_TEST); }
|
||||
|
||||
render_text();
|
||||
|
||||
render_dev_panel();
|
||||
void Renderer::render_world(ClientWorld& world) {
|
||||
m_world_renderer.render(world);
|
||||
}
|
||||
|
||||
void Renderer::render_text() {
|
||||
void Renderer::render_rect(const Rect& rect) {
|
||||
|
||||
auto& shader = get_shader("rect");
|
||||
shader.use();
|
||||
shader.set_loc("proj_matrix", m_ui_proj_matrix);
|
||||
auto pos = rect.pos();
|
||||
glm::mat4 model_matrix =
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) *
|
||||
glm::scale(glm::mat4(1.0f),
|
||||
glm::vec3(rect.width(), rect.height(), 1.0f));
|
||||
shader.set_loc("model_matrix", model_matrix);
|
||||
|
||||
auto color = color_value(rect.color());
|
||||
shader.set_loc("inColor",
|
||||
glm::vec4(color.x, color.y, color.z, rect.alpha()));
|
||||
|
||||
m_vao[3].bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
void Renderer::render_lable(const Label& label) {
|
||||
|
||||
const auto& shader = get_shader("text");
|
||||
|
||||
shader.use();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
shader.set_loc("projection", m_ui_proj_matrix);
|
||||
|
||||
auto& texts = DebugCollector::get().all_texts();
|
||||
for (auto& t : texts) {
|
||||
t.second.render(shader);
|
||||
}
|
||||
Font::text_texture()->bind(0);
|
||||
auto& data = label.data();
|
||||
auto pos = label.pos();
|
||||
auto& text_style = label.text_style();
|
||||
auto color = color_value(text_style.color);
|
||||
data.m_vao->bind();
|
||||
glm::mat4 model_matrix =
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) *
|
||||
glm::scale(glm::mat4(1.0f),
|
||||
glm::vec3(label.scale(), label.scale(), 1.0f));
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
shader.set_loc("textColor", glm::vec3(color.x, color.y, color.z));
|
||||
shader.set_loc("mv_matrix", model_matrix);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, data.m_sum);
|
||||
}
|
||||
|
||||
void Renderer::render_ui() {
|
||||
const auto& shader = get_shader("ui");
|
||||
void Renderer::render_image(const Image& image) {
|
||||
if (!image.texture()) {
|
||||
Logger::error("Image id {} not set image!", image.id());
|
||||
return;
|
||||
}
|
||||
const auto& shader = get_shader("image");
|
||||
shader.use();
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
shader.set_loc("m_matrix", m_ui_model_matrix);
|
||||
auto pos = image.pos();
|
||||
glm::mat4 model_matrix =
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) *
|
||||
glm::scale(glm::mat4(1.0f),
|
||||
glm::vec3(image.width(), image.height(), 1.0f));
|
||||
shader.set_loc("model_matrix", model_matrix);
|
||||
shader.set_loc("proj_matrix", m_ui_proj_matrix);
|
||||
|
||||
m_vao[3].bind();
|
||||
m_texture_manager.get_ui_array()->bind(0);
|
||||
|
||||
image.texture()->bind(0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
Tools::check_opengl_error();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Renderer::update(float delta_time) { m_delta_time = delta_time; }
|
||||
@@ -207,20 +235,6 @@ void Renderer::update_fov(float fov) {
|
||||
glm::perspective(glm::radians(fov), m_aspect, NEAR_PLANE, FAR_PLANE);
|
||||
}
|
||||
|
||||
void Renderer::update_proj_matrix(float aspect, float width, float height) {
|
||||
m_aspect = aspect;
|
||||
|
||||
m_world_proj_matrix =
|
||||
glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE);
|
||||
|
||||
m_ui_proj_matrix = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
|
||||
// scale and then translate
|
||||
m_ui_model_matrix =
|
||||
glm::translate(glm::mat4(1.0f),
|
||||
glm::vec3(width / 2.0f, height / 2.0f, 0.0)) *
|
||||
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;
|
||||
@@ -228,18 +242,55 @@ void Renderer::updata_framebuffer(int width, int height) {
|
||||
m_world_renderer.updata_framebuffer(width, height);
|
||||
|
||||
FrameBuffer::unbind();
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
void Renderer::render_dev_panel() {
|
||||
|
||||
void Renderer::render_dev_panel(DevPanel& dev_panel) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
m_dev_panel.render();
|
||||
dev_panel.render();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
bool Renderer::handle_event(const Event& e) {
|
||||
return std::visit(Overloaded{[](const MouseMoveEvent&) { return false; },
|
||||
[](const MouseButtonEvent&) { return false; },
|
||||
[](const MouseWheelEvent&) { return false; },
|
||||
[](const KeyEvent&) { return false; },
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[this](const WindowResizeEvent& e) {
|
||||
handle_window_resize_event(e);
|
||||
return false;
|
||||
},
|
||||
[this](const FrameBufferResizeEvent& e) {
|
||||
handle_frame_buffer_resize_event(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
e);
|
||||
}
|
||||
bool Renderer::handle_window_resize_event(const WindowResizeEvent& e) {
|
||||
m_window_width = static_cast<float>(e.width);
|
||||
m_window_height = static_cast<float>(e.height);
|
||||
m_ui_proj_matrix =
|
||||
glm::ortho(0.0f, m_window_width, m_window_height, 0.0f, -1.0f, 1.0f);
|
||||
return false;
|
||||
}
|
||||
bool Renderer::handle_frame_buffer_resize_event(
|
||||
const FrameBufferResizeEvent& e) {
|
||||
int frame_height = e.height;
|
||||
int frame_width = e.width;
|
||||
m_frame_width = static_cast<float>(e.width);
|
||||
m_frame_height = static_cast<float>(e.height);
|
||||
m_aspect = m_frame_width / m_frame_height;
|
||||
glViewport(0, 0, frame_width, frame_height);
|
||||
m_world_proj_matrix =
|
||||
glm::perspective(glm::radians(m_fov), m_aspect, NEAR_PLANE, FAR_PLANE);
|
||||
updata_framebuffer(frame_width, frame_height);
|
||||
return false;
|
||||
}
|
||||
|
||||
float& Renderer::ambient_strength() {
|
||||
return m_world_renderer.ambient_strength();
|
||||
}
|
||||
@@ -277,9 +328,6 @@ float& Renderer::underwater_fog_density() {
|
||||
}
|
||||
float& Renderer::water_density() { return m_world_renderer.water_density(); }
|
||||
|
||||
const Camera& Renderer::camera() const { return m_camera; }
|
||||
const ClientWorld& Renderer::world() const { return m_world; }
|
||||
ClientWorld& Renderer::world() { return m_world; }
|
||||
const glm::mat4& Renderer::world_proj_matrix() const {
|
||||
return m_world_proj_matrix;
|
||||
}
|
||||
@@ -289,8 +337,10 @@ const TextureManager& Renderer::texture_mamger() const {
|
||||
|
||||
float Renderer::delta_time() const { return m_delta_time; }
|
||||
|
||||
float Renderer::height() const { return m_height; }
|
||||
float Renderer::width() const { return m_width; }
|
||||
float Renderer::window_height() const { return m_window_height; }
|
||||
float Renderer::window_width() const { return m_window_width; }
|
||||
float Renderer::frame_height() const { return m_frame_height; }
|
||||
float Renderer::frame_width() const { return m_frame_width; }
|
||||
const glm::mat4& Renderer::p_mat() const { return m_world_proj_matrix; }
|
||||
const std::vector<VertexArray>& Renderer::vao() const { return m_vao; }
|
||||
} // namespace Cubed
|
||||
@@ -13,8 +13,8 @@ void ShaderManager::init() {
|
||||
"shaders/outline_f_shader.glsl");
|
||||
register_shader("sky", "shaders/sky_v_shader.glsl",
|
||||
"shaders/sky_f_shader.glsl");
|
||||
register_shader("ui", "shaders/ui_v_shader.glsl",
|
||||
"shaders/ui_f_shader.glsl");
|
||||
register_shader("image", "shaders/image_v_shader.glsl",
|
||||
"shaders/image_f_shader.glsl");
|
||||
register_shader("text", "shaders/text_v_shader.glsl",
|
||||
"shaders/text_f_shader.glsl");
|
||||
register_shader("under_water", "shaders/under_water_v_shader.glsl",
|
||||
@@ -33,6 +33,8 @@ void ShaderManager::init() {
|
||||
"shaders/player_f_shader.glsl");
|
||||
register_shader("player_depth", "shaders/depth_player_shader.glsl",
|
||||
"shaders/depth_player_fragment_shader.glsl");
|
||||
register_shader("rect", "shaders/rect_v_shader.glsl",
|
||||
"shaders/rect_f_shader.glsl");
|
||||
}
|
||||
|
||||
void ShaderManager::register_shader(const std::string& name,
|
||||
|
||||
@@ -48,8 +48,10 @@ void Texture::parameterfv(TexturePname pname, const float* param) const {
|
||||
}
|
||||
void Texture::tex_image_2d(TextureFormat internalformat, TextureFormat format,
|
||||
GLenum type, const void* data, GLsizei width,
|
||||
GLsizei height, GLint level, GLint border) const {
|
||||
GLsizei height, GLint level, GLint border) {
|
||||
bind();
|
||||
m_width = static_cast<float>(width);
|
||||
m_height = static_cast<float>(height);
|
||||
glTexImage2D(get_gl_texture_type(), level,
|
||||
std::to_underlying(internalformat), width, height, border,
|
||||
std::to_underlying(format), type, data);
|
||||
@@ -58,8 +60,10 @@ void Texture::tex_image_2d(TextureFormat internalformat, TextureFormat format,
|
||||
void Texture::tex_image_3d(TextureFormat internalformat, TextureFormat format,
|
||||
GLenum type, const void* data, GLsizei width,
|
||||
GLsizei height, GLsizei depth, GLint level,
|
||||
GLint border) const {
|
||||
GLint border) {
|
||||
bind();
|
||||
m_width = static_cast<float>(width);
|
||||
m_height = static_cast<float>(height);
|
||||
glTexImage3D(get_gl_texture_type(), level,
|
||||
std::to_underlying(internalformat), width, height, depth,
|
||||
border, std::to_underlying(format), type, data);
|
||||
@@ -135,7 +139,8 @@ void Texture::set_clamp_to_edge(bool r, bool s, bool t) const {
|
||||
}
|
||||
|
||||
TextureType Texture::type() const { return M_TYPE; }
|
||||
|
||||
float Texture::width() const { return m_width; }
|
||||
float Texture::height() const { return m_height; }
|
||||
void Texture::unbind() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/render/renderer.hpp"
|
||||
#include "Cubed/render/renderer_constants.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/texture_manager.hpp"
|
||||
#include "Cubed/tools/math_tools.hpp"
|
||||
namespace Cubed {
|
||||
WorldRenderer::WorldRenderer(Renderer& renderer)
|
||||
: m_renderer(renderer), m_player_renderer(renderer),
|
||||
m_world(renderer.world()), m_camera(renderer.camera()),
|
||||
m_texture_manager(renderer.texture_mamger()) {}
|
||||
WorldRenderer::~WorldRenderer() {
|
||||
m_accum_texture.reset();
|
||||
@@ -30,21 +30,21 @@ WorldRenderer::~WorldRenderer() {
|
||||
|
||||
void WorldRenderer::init() { m_player_renderer.init(); }
|
||||
|
||||
void WorldRenderer::render() {
|
||||
void WorldRenderer::render(ClientWorld& world) {
|
||||
// update view matrix;
|
||||
view_matrix = m_renderer.camera().get_camera_lookat();
|
||||
view_matrix = world.world_scene().camera().get_camera_lookat();
|
||||
|
||||
m_world_fbo->bind();
|
||||
// clear world framebuffer
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
day_night_calculation();
|
||||
day_night_calculation(world);
|
||||
|
||||
render_sky();
|
||||
render_world();
|
||||
render_outline();
|
||||
render_player();
|
||||
render_sky(world);
|
||||
render_world(world);
|
||||
render_outline(world);
|
||||
render_player(world);
|
||||
|
||||
FrameBuffer::unbind();
|
||||
|
||||
@@ -55,13 +55,13 @@ void WorldRenderer::render() {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
render_underwater();
|
||||
render_underwater(world);
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
void WorldRenderer::day_night_calculation() {
|
||||
void WorldRenderer::day_night_calculation(ClientWorld& world) {
|
||||
|
||||
m_parallel_light.sundir = glm::normalize(m_renderer.world().sunlight_dir());
|
||||
m_parallel_light.sundir = glm::normalize(world.sunlight_dir());
|
||||
m_parallel_light.sun_height = (-m_parallel_light.sundir).y;
|
||||
m_parallel_light.lightdir = m_parallel_light.sundir;
|
||||
|
||||
@@ -92,7 +92,9 @@ void WorldRenderer::day_night_calculation() {
|
||||
m_ambient_strength = glm::mix(0.45f, 0.25f, day_factor);
|
||||
}
|
||||
|
||||
void WorldRenderer::render_sky() {
|
||||
void WorldRenderer::render_sky(ClientWorld& world) {
|
||||
|
||||
auto& camera = world.world_scene().camera();
|
||||
|
||||
glm::vec3 zenith = {0.20f, 0.45f, 0.95f};
|
||||
|
||||
@@ -142,9 +144,8 @@ void WorldRenderer::render_sky() {
|
||||
|
||||
sky_shader.use();
|
||||
|
||||
glm::mat4 model_mat =
|
||||
glm::translate(glm::mat4(1.0f),
|
||||
m_camera.get_camera_pos() - glm::vec3(0.5f, 0.5f, 0.5f));
|
||||
glm::mat4 model_mat = glm::translate(
|
||||
glm::mat4(1.0f), camera.get_camera_pos() - glm::vec3(0.5f, 0.5f, 0.5f));
|
||||
|
||||
glm::mat4 mv_mat = view_matrix * model_mat;
|
||||
|
||||
@@ -195,25 +196,27 @@ void WorldRenderer::render_sky() {
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
};
|
||||
// draw sun
|
||||
glm::vec3 sun_pos = m_camera.get_camera_pos() +
|
||||
normalize(-m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
|
||||
glm::vec3 sun_pos =
|
||||
camera.get_camera_pos() +
|
||||
normalize(-m_parallel_light.sundir) * (FAR_PLANE * 0.9f);
|
||||
billboard_drawer(sun_pos, SUN_SIZE, SUN_COLOR);
|
||||
|
||||
// draw moon
|
||||
|
||||
glm::vec3 moon_pos = m_camera.get_camera_pos() +
|
||||
normalize(m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
|
||||
glm::vec3 moon_pos =
|
||||
camera.get_camera_pos() +
|
||||
normalize(m_parallel_light.sundir) * (FAR_PLANE * 0.9f);
|
||||
billboard_drawer(moon_pos, MOON_SIZE, MOON_COLOR);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void WorldRenderer::render_world() {
|
||||
void WorldRenderer::render_world(ClientWorld& world) {
|
||||
|
||||
// shader map
|
||||
|
||||
auto m_height = m_renderer.height();
|
||||
auto m_width = m_renderer.width();
|
||||
auto height = m_renderer.frame_height();
|
||||
auto width = m_renderer.frame_width();
|
||||
|
||||
glm::mat4 model_mat =
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
@@ -223,22 +226,22 @@ void WorldRenderer::render_world() {
|
||||
glm::mat4 norm_mat = glm::transpose(glm::inverse(mv_mat));
|
||||
|
||||
if (m_shader_on) {
|
||||
shadow_map_generate();
|
||||
shadow_map_generate(world);
|
||||
}
|
||||
|
||||
m_world_fbo->bind();
|
||||
|
||||
glCullFace(GL_BACK);
|
||||
glViewport(0, 0, m_width, m_height);
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
render_normal_block(model_mat, mv_mat, norm_mat);
|
||||
render_normal_block(model_mat, mv_mat, norm_mat, world);
|
||||
|
||||
// copy depth buffer
|
||||
m_world_fbo->bind(FrameBufferType::READ_FRAMEBUFFER);
|
||||
|
||||
m_oit_fbo->bind(FrameBufferType::DRAW_FRAMEBUFFER);
|
||||
|
||||
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height,
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
m_oit_fbo->bind(FrameBufferType::DRAW_FRAMEBUFFER);
|
||||
|
||||
@@ -256,14 +259,14 @@ void WorldRenderer::render_world() {
|
||||
glBlendFunci(0, GL_ONE, GL_ONE);
|
||||
|
||||
glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
|
||||
render_transparent_block(mv_mat, norm_mat);
|
||||
render_transparent_block(mv_mat, norm_mat, world);
|
||||
}
|
||||
|
||||
void WorldRenderer::render_outline() {
|
||||
void WorldRenderer::render_outline(ClientWorld& world) {
|
||||
const auto& shader = m_renderer.get_shader("outline");
|
||||
shader.use();
|
||||
|
||||
const auto& block_pos = m_renderer.world().get_look_block_pos();
|
||||
const auto& block_pos = world.get_look_block_pos();
|
||||
|
||||
if (block_pos != std::nullopt) {
|
||||
|
||||
@@ -285,24 +288,24 @@ void WorldRenderer::render_outline() {
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::shadow_map_generate() {
|
||||
void WorldRenderer::shadow_map_generate(ClientWorld& world) {
|
||||
float texels_per_unit = 0.0f;
|
||||
const auto& lightdir = m_parallel_light.lightdir;
|
||||
|
||||
auto m_delta_time = m_renderer.delta_time();
|
||||
|
||||
auto& camera = world.world_scene().camera();
|
||||
// shader map
|
||||
glm::mat4& light_space_matrix = m_parallel_light.light_space_matrix;
|
||||
|
||||
auto& m_render_snapshots = m_world.render_snapshots();
|
||||
auto& camera_pos = m_camera.get_camera_pos();
|
||||
auto& m_render_snapshots = world.render_snapshots();
|
||||
auto& camera_pos = camera.get_camera_pos();
|
||||
|
||||
const auto& depth_shader = m_renderer.get_shader("depth_shader");
|
||||
|
||||
depth_shader.use();
|
||||
|
||||
glm::vec3 cam_pos = m_camera.get_camera_pos();
|
||||
glm::vec3 cam_fwd = m_camera.get_camera_front();
|
||||
glm::vec3 cam_pos = camera.get_camera_pos();
|
||||
glm::vec3 cam_fwd = camera.get_camera_front();
|
||||
float half_extent = 128.0f;
|
||||
|
||||
glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f);
|
||||
@@ -389,13 +392,13 @@ void WorldRenderer::shadow_map_generate() {
|
||||
}
|
||||
// player
|
||||
auto& player_shadow = m_renderer.get_shader("player_depth");
|
||||
m_player_renderer.shadow_render(player_shadow, light_space_matrix);
|
||||
m_player_renderer.shadow_render(player_shadow, light_space_matrix, world);
|
||||
}
|
||||
|
||||
void WorldRenderer::render_underwater() {
|
||||
void WorldRenderer::render_underwater(ClientWorld& world) {
|
||||
|
||||
const auto& shader = m_renderer.get_shader("under_water");
|
||||
|
||||
auto& camera = world.world_scene().camera();
|
||||
shader.use();
|
||||
|
||||
auto& m_vao = m_renderer.vao();
|
||||
@@ -406,10 +409,10 @@ void WorldRenderer::render_underwater() {
|
||||
|
||||
shader.set_loc("u_sceneTexture", 0);
|
||||
shader.set_loc("u_time", static_cast<float>(glfwGetTime()));
|
||||
shader.set_loc("u_underwater", m_camera.is_under_water());
|
||||
shader.set_loc("u_underwater", camera.is_under_water());
|
||||
shader.set_loc("u_waterColor", glm::vec3(0.1f, 0.25f, 0.35f));
|
||||
shader.set_loc("u_fogDensity", m_underwater_fog_density);
|
||||
shader.set_loc("cameraPos", m_camera.get_camera_pos());
|
||||
shader.set_loc("cameraPos", camera.get_camera_pos());
|
||||
shader.set_loc("sunDir", -m_parallel_light.sundir);
|
||||
shader.set_loc("waterDensity", m_water_density);
|
||||
shader.set_loc("InverseViewProjection",
|
||||
@@ -427,13 +430,14 @@ void WorldRenderer::render_underwater() {
|
||||
|
||||
void WorldRenderer::render_normal_block(const glm::mat4& model_mat,
|
||||
const glm::mat4& mv_mat,
|
||||
const glm::mat4& norm_mat) {
|
||||
const glm::mat4& norm_mat,
|
||||
ClientWorld& world) {
|
||||
|
||||
// shader map
|
||||
glm::mat4& light_space_matrix = m_parallel_light.light_space_matrix;
|
||||
|
||||
auto& m_render_snapshots = m_world.render_snapshots();
|
||||
auto& camera_pos = m_camera.get_camera_pos();
|
||||
auto& camera = world.world_scene().camera();
|
||||
auto& m_render_snapshots = world.render_snapshots();
|
||||
auto& camera_pos = camera.get_camera_pos();
|
||||
|
||||
const auto& lightdir = m_parallel_light.lightdir;
|
||||
|
||||
@@ -467,14 +471,14 @@ void WorldRenderer::render_normal_block(const glm::mat4& model_mat,
|
||||
normal_block_shader.set_loc("maxRadius", m_max_radius);
|
||||
normal_block_shader.set_loc("samples", m_samples);
|
||||
normal_block_shader.set_loc("specularStrength", m_specular_strength);
|
||||
normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos());
|
||||
normal_block_shader.set_loc("cameraPos", camera.get_camera_pos());
|
||||
normal_block_shader.set_loc("flipY", m_flip_y);
|
||||
normal_block_shader.set_loc("renderDistance", m_world.rendering_distance());
|
||||
normal_block_shader.set_loc("renderDistance", world.rendering_distance());
|
||||
normal_block_shader.set_loc("skyColor", m_sky_uniform.sky_top);
|
||||
|
||||
glm::mat4 mvp_mat = proj_mat * mv_mat;
|
||||
|
||||
auto& m_planes = m_world.planes();
|
||||
auto& m_planes = world.planes();
|
||||
|
||||
Math::extract_frustum_planes(mvp_mat, m_planes);
|
||||
|
||||
@@ -545,10 +549,11 @@ void WorldRenderer::render_normal_block(const glm::mat4& model_mat,
|
||||
}
|
||||
|
||||
void WorldRenderer::render_transparent_block(const glm::mat4& mv_mat,
|
||||
const glm::mat4& norm_mat) {
|
||||
|
||||
auto& m_render_snapshots = m_world.render_snapshots();
|
||||
const glm::mat4& norm_mat,
|
||||
ClientWorld& world) {
|
||||
|
||||
auto& m_render_snapshots = world.render_snapshots();
|
||||
auto& camera = world.world_scene().camera();
|
||||
const auto& lightdir = m_parallel_light.lightdir;
|
||||
glm::vec3 light_dir_view =
|
||||
glm::normalize(glm::mat3(view_matrix) * lightdir);
|
||||
@@ -573,11 +578,11 @@ void WorldRenderer::render_transparent_block(const glm::mat4& mv_mat,
|
||||
accum_shader.use();
|
||||
|
||||
set_accum_loc(accum_shader);
|
||||
accum_shader.set_loc("cameraPos", m_camera.get_camera_pos());
|
||||
accum_shader.set_loc("cameraPos", camera.get_camera_pos());
|
||||
|
||||
m_texture_manager.get_texture_array()->bind(0);
|
||||
|
||||
auto& m_planes = m_world.planes();
|
||||
auto& m_planes = world.planes();
|
||||
|
||||
for (const auto& snapshot : m_render_snapshots) {
|
||||
if (!snapshot) {
|
||||
@@ -619,7 +624,7 @@ void WorldRenderer::render_transparent_block(const glm::mat4& mv_mat,
|
||||
water_shader.set_loc("cloudWhiteMix", m_sky_uniform.cloud_white_mix);
|
||||
water_shader.set_loc("cloudThresholdLow", m_cloud_threshold_low);
|
||||
water_shader.set_loc("cloudThresholdHigh", m_cloud_threshold_high);
|
||||
water_shader.set_loc("underwater", m_camera.is_under_water());
|
||||
water_shader.set_loc("underwater", camera.is_under_water());
|
||||
water_shader.set_loc("refractStrength", m_refract_strength);
|
||||
water_shader.set_loc("enablePerturb", m_water_perturb);
|
||||
water_shader.set_loc("enableDepthFade", m_water_depth_fade);
|
||||
@@ -670,7 +675,7 @@ void WorldRenderer::render_transparent_block(const glm::mat4& mv_mat,
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void WorldRenderer::render_player() {
|
||||
void WorldRenderer::render_player(ClientWorld& world) {
|
||||
auto& shader = m_renderer.get_shader("player");
|
||||
shader.use();
|
||||
glm::vec3 light_dir_view =
|
||||
@@ -692,7 +697,7 @@ void WorldRenderer::render_player() {
|
||||
// shader.set_loc("skyColor", m_sky_uniform.sky_top);
|
||||
|
||||
m_depth_map_texture->bind(0);
|
||||
m_player_renderer.render(shader);
|
||||
m_player_renderer.render(shader, world);
|
||||
}
|
||||
|
||||
glm::vec3 WorldRenderer::quantize_sun_direction(const glm::vec3& lightdir,
|
||||
|
||||
28
src/scene/credits_scene.cpp
Normal file
28
src/scene/credits_scene.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "Cubed/scene/credits_scene.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
CreditsScene::CreditsScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_ui(*this) {}
|
||||
|
||||
void CreditsScene::update(float dt) { m_ui.update(dt); }
|
||||
void CreditsScene::render(Renderer& renderer) { m_ui.render(renderer); }
|
||||
bool CreditsScene::handle_event(const Event& e) {
|
||||
if (m_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void CreditsScene::on_enter() { m_ui.init(); }
|
||||
void CreditsScene::on_re_enter() {
|
||||
auto width = m_scene_manager.app().renderer().window_width();
|
||||
auto height = m_scene_manager.app().renderer().window_height();
|
||||
|
||||
handle_event(
|
||||
WindowResizeEvent{static_cast<int>(width), static_cast<int>(height)});
|
||||
};
|
||||
SceneManager& CreditsScene::scene_manager() { return m_scene_manager; }
|
||||
|
||||
} // namespace Cubed
|
||||
18
src/scene/host_game_scene.cpp
Normal file
18
src/scene/host_game_scene.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "Cubed/scene/host_game_scene.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
HostGameScene::HostGameScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_ui(*this) {}
|
||||
HostGameScene::~HostGameScene() {}
|
||||
|
||||
void HostGameScene::update(float dt) { m_ui.update(dt); }
|
||||
void HostGameScene::render(Renderer& renderer) { m_ui.render(renderer); }
|
||||
bool HostGameScene::handle_event(const Event& e) {
|
||||
return m_ui.handle_event(e);
|
||||
}
|
||||
void HostGameScene::on_enter() { m_ui.init(); }
|
||||
void HostGameScene::on_leave() {}
|
||||
void HostGameScene::on_re_enter() { m_ui.on_re_enter(); }
|
||||
SceneManager& HostGameScene::scene_manager() { return m_scene_manager; }
|
||||
|
||||
} // namespace Cubed
|
||||
16
src/scene/join_game_scene.cpp
Normal file
16
src/scene/join_game_scene.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "Cubed/scene/join_game_scene.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
JoinGameScene::JoinGameScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_ui(*this) {}
|
||||
|
||||
void JoinGameScene::update(float dt) { m_ui.update(dt); }
|
||||
void JoinGameScene::render(Renderer& renderer) { m_ui.render(renderer); }
|
||||
bool JoinGameScene::handle_event(const Event& e) {
|
||||
return m_ui.handle_event(e);
|
||||
}
|
||||
void JoinGameScene::on_enter() { m_ui.init(); }
|
||||
void JoinGameScene::on_leave() {}
|
||||
void JoinGameScene::on_re_enter() { m_ui.on_re_enter(); }
|
||||
SceneManager& JoinGameScene::scene_manager() { return m_scene_manager; }
|
||||
} // namespace Cubed
|
||||
32
src/scene/main_menu_scene.cpp
Normal file
32
src/scene/main_menu_scene.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "Cubed/scene/main_menu_scene.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
MainMenuScene::MainMenuScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_ui_manager(*this) {}
|
||||
MainMenuScene::~MainMenuScene() {}
|
||||
|
||||
void MainMenuScene::update(float dt) { m_ui_manager.update(dt); }
|
||||
void MainMenuScene::render(Renderer& renderer) {
|
||||
m_ui_manager.render(renderer);
|
||||
}
|
||||
bool MainMenuScene::handle_event(const Event& e) {
|
||||
if (m_ui_manager.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void MainMenuScene::on_enter() { m_ui_manager.init(); }
|
||||
void MainMenuScene::on_leave() {}
|
||||
void MainMenuScene::on_re_enter() {
|
||||
m_ui_manager.on_re_enter();
|
||||
auto width = m_scene_manager.app().renderer().window_width();
|
||||
auto height = m_scene_manager.app().renderer().window_height();
|
||||
|
||||
handle_event(
|
||||
WindowResizeEvent{static_cast<int>(width), static_cast<int>(height)});
|
||||
}
|
||||
SceneManager& MainMenuScene::scene_manager() { return m_scene_manager; }
|
||||
} // namespace Cubed
|
||||
115
src/scene/scene_manager.cpp
Normal file
115
src/scene/scene_manager.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
|
||||
#include "Cubed/scene/credits_scene.hpp"
|
||||
#include "Cubed/scene/host_game_scene.hpp"
|
||||
#include "Cubed/scene/join_game_scene.hpp"
|
||||
#include "Cubed/scene/main_menu_scene.hpp"
|
||||
#include "Cubed/scene/settings_scene.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
SceneManager::SceneManager(App& app) : m_app(app) {}
|
||||
SceneManager::~SceneManager() {}
|
||||
|
||||
void SceneManager::update(float dt) {
|
||||
m_pending_delete_scene.clear();
|
||||
process_operation();
|
||||
if (!m_scenes.empty()) {
|
||||
m_scenes.top()->update(dt);
|
||||
}
|
||||
}
|
||||
void SceneManager::render(Renderer& renderer) {
|
||||
if (m_scenes.empty()) {
|
||||
return;
|
||||
}
|
||||
m_scenes.top()->render(renderer);
|
||||
}
|
||||
|
||||
bool SceneManager::handle_event(const Event& e) {
|
||||
if (m_scenes.empty()) {
|
||||
return false;
|
||||
}
|
||||
return m_scenes.top()->handle_event(e);
|
||||
}
|
||||
|
||||
void SceneManager::request_change(SceneType type) {
|
||||
ASSERT(!m_operation.has_value());
|
||||
m_operation = {OperationType::CHANGE, type};
|
||||
}
|
||||
void SceneManager::request_push(SceneType type) {
|
||||
ASSERT(!m_operation.has_value());
|
||||
m_operation = {OperationType::PUSH, type};
|
||||
}
|
||||
void SceneManager::request_pop() {
|
||||
ASSERT(!m_operation.has_value());
|
||||
m_operation = {OperationType::POP, std::nullopt};
|
||||
}
|
||||
|
||||
void SceneManager::process_operation() {
|
||||
while (m_operation) {
|
||||
auto op = std::move(*m_operation);
|
||||
m_operation.reset();
|
||||
|
||||
switch (op.type) {
|
||||
case OperationType::PUSH:
|
||||
push(*op.scene);
|
||||
break;
|
||||
case OperationType::POP:
|
||||
pop();
|
||||
break;
|
||||
case OperationType::CHANGE:
|
||||
change(*op.scene);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::change(SceneType type) {
|
||||
if (!m_scenes.empty()) {
|
||||
pop(false);
|
||||
}
|
||||
|
||||
push(type);
|
||||
}
|
||||
void SceneManager::push(SceneType type) {
|
||||
auto scene = create_scene(type);
|
||||
scene->on_enter();
|
||||
m_scenes.push(std::move(scene));
|
||||
}
|
||||
void SceneManager::pop(bool re_enter) {
|
||||
if (m_scenes.empty()) {
|
||||
return;
|
||||
}
|
||||
auto scene = std::move(m_scenes.top());
|
||||
m_scenes.pop();
|
||||
scene->on_leave();
|
||||
m_pending_delete_scene.push_back(std::move(scene));
|
||||
if (!m_scenes.empty() && re_enter) {
|
||||
m_scenes.top()->on_re_enter();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Scene> SceneManager::create_scene(SceneType type) {
|
||||
switch (type) {
|
||||
case SceneType::WORLD:
|
||||
return std::make_unique<WorldScene>(*this);
|
||||
case SceneType::MAIN_MENU:
|
||||
return std::make_unique<MainMenuScene>(*this);
|
||||
case SceneType::CREDITS:
|
||||
return std::make_unique<CreditsScene>(*this);
|
||||
case SceneType::SETTINGS:
|
||||
return std::make_unique<SettingsScene>(*this);
|
||||
case SceneType::HOST_GAME:
|
||||
return std::make_unique<HostGameScene>(*this);
|
||||
case SceneType::JOIN_GAME:
|
||||
return std::make_unique<JoinGameScene>(*this);
|
||||
}
|
||||
|
||||
std::string err = std::format("Unknown Scene");
|
||||
ASSERT_MSG(false, err);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
|
||||
App& SceneManager::app() { return m_app; }
|
||||
WorldSceneParam& SceneManager::world_scene_param() { return m_world_param; }
|
||||
} // namespace Cubed
|
||||
68
src/scene/settings_scene.cpp
Normal file
68
src/scene/settings_scene.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "Cubed/scene/settings_scene.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
namespace Cubed {
|
||||
SettingsScene::SettingsScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_settings_ui(*this) {}
|
||||
SettingsScene::~SettingsScene() {}
|
||||
|
||||
void SettingsScene::update(float dt) { m_settings_ui.update(dt); }
|
||||
void SettingsScene::render(Renderer& renderer) {
|
||||
m_settings_ui.render(renderer);
|
||||
}
|
||||
bool SettingsScene::handle_event(const Event& e) {
|
||||
if (m_settings_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void SettingsScene::on_enter() {
|
||||
m_settings_ui.init();
|
||||
auto& config = m_scene_manager.app().config();
|
||||
m_slider_variable.fov = config.get("player.fov", m_slider_variable.fov);
|
||||
m_slider_variable.mouse_sensitivity = config.get(
|
||||
"player.mouse_sensitivity", m_slider_variable.mouse_sensitivity);
|
||||
m_slider_variable.music =
|
||||
config.get("volume.music", m_slider_variable.music);
|
||||
m_slider_variable.sfx = config.get("volume.SFX", m_slider_variable.sfx);
|
||||
m_slider_variable.rendering_distance = config.get(
|
||||
"world.rendering_distance", m_slider_variable.rendering_distance);
|
||||
}
|
||||
void SettingsScene::on_leave() {
|
||||
save_and_apply();
|
||||
|
||||
if (m_need_texture_reload) {
|
||||
m_scene_manager.app().texture_manager().need_reload();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsScene::on_re_enter() {
|
||||
auto width = m_scene_manager.app().renderer().window_width();
|
||||
auto height = m_scene_manager.app().renderer().window_height();
|
||||
|
||||
handle_event(
|
||||
WindowResizeEvent{static_cast<int>(width), static_cast<int>(height)});
|
||||
}
|
||||
|
||||
void SettingsScene::save_and_apply() {
|
||||
auto& config = m_scene_manager.app().config();
|
||||
config.set("player.fov", m_slider_variable.fov);
|
||||
config.set("player.mouse_sensitivity", m_slider_variable.mouse_sensitivity);
|
||||
config.set("world.rendering_distance",
|
||||
m_slider_variable.rendering_distance);
|
||||
config.set("volume.SFX", m_slider_variable.sfx);
|
||||
config.set("volume.music", m_slider_variable.music);
|
||||
config.save_to_file();
|
||||
auto& app = m_scene_manager.app();
|
||||
app.audio().reload_config();
|
||||
app.renderer().reload_config();
|
||||
}
|
||||
|
||||
SceneManager& SettingsScene::scene_manager() { return m_scene_manager; }
|
||||
SliderVariable& SettingsScene::slider_variable() { return m_slider_variable; }
|
||||
void SettingsScene::set_texture_reload(bool reload) {
|
||||
m_need_texture_reload = reload;
|
||||
}
|
||||
} // namespace Cubed
|
||||
294
src/scene/world_scene.cpp
Normal file
294
src/scene/world_scene.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/render/renderer.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
namespace Cubed {
|
||||
WorldScene::WorldScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_dev_panel(*this),
|
||||
m_client_world(scene_manager.app().audio(), scene_manager.app().config(),
|
||||
*this),
|
||||
m_pasue_menu(*this), m_hud_ui(*this), m_error_ui(*this),
|
||||
m_argument(scene_manager.app().argument()) {}
|
||||
|
||||
WorldScene::~WorldScene() {
|
||||
if (m_client) {
|
||||
m_client->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldScene::update(float dt) {
|
||||
if (m_client->is_connect_error()) {
|
||||
set_error(m_client->get_error_string());
|
||||
}
|
||||
if (m_error_ui.has_error()) {
|
||||
m_error_ui.update(dt);
|
||||
return;
|
||||
}
|
||||
m_client_world.update(dt);
|
||||
m_camera.update_move_camera();
|
||||
m_client_world.get_audio().update_listener(m_camera.get_camera_pos(),
|
||||
m_camera.get_camera_front(),
|
||||
glm::vec3(0, 1, 0));
|
||||
|
||||
/*
|
||||
const auto& player = m_client_world.get_player();
|
||||
if (player_gait != player.get_gait()) {
|
||||
player_gait = player.get_gait();
|
||||
float fov = m_client_world.get("player.fov", 70.0f);
|
||||
if (player_gait == Gait::WALK) {
|
||||
m_renderer.update_fov(fov);
|
||||
}
|
||||
if (player_gait == Gait::RUN) {
|
||||
m_renderer.update_fov(fov + 5.0f);
|
||||
}
|
||||
}*/
|
||||
if (m_paused) {
|
||||
m_pasue_menu.update(dt);
|
||||
} else {
|
||||
|
||||
m_hud_ui.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldScene::render(Renderer& renderer) {
|
||||
if (m_error_ui.has_error()) {
|
||||
m_error_ui.render(renderer);
|
||||
return;
|
||||
}
|
||||
renderer.render_world(m_client_world);
|
||||
if (m_show_hud) {
|
||||
m_hud_ui.render(renderer);
|
||||
}
|
||||
if (m_paused) {
|
||||
m_pasue_menu.render(renderer);
|
||||
} else {
|
||||
if (m_show_dev_pannel && m_show_hud) {
|
||||
renderer.render_dev_panel(m_dev_panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool WorldScene::handle_event(const Event& e) {
|
||||
if (m_error_ui.has_error()) {
|
||||
return m_error_ui.handle_event(e);
|
||||
}
|
||||
return std::visit(
|
||||
Overloaded{[this](const MouseMoveEvent& e) {
|
||||
if (handle_mouse_move_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const MouseButtonEvent& e) {
|
||||
if (handle_mouse_button_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const MouseWheelEvent& e) {
|
||||
if (handle_mouse_wheel_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[this](const KeyEvent& e) {
|
||||
if (handle_key_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[this](const WindowResizeEvent& e) {
|
||||
handle_window_resize_event(e);
|
||||
return false;
|
||||
},
|
||||
[](const FrameBufferResizeEvent&) { return false; }
|
||||
|
||||
},
|
||||
e);
|
||||
}
|
||||
void WorldScene::on_enter() {
|
||||
auto& param = m_scene_manager.world_scene_param();
|
||||
m_error_ui.init();
|
||||
if (param.host_game) {
|
||||
if (param.seed) {
|
||||
ChunkGenerator::init(*param.seed);
|
||||
param.seed = std::nullopt;
|
||||
} else {
|
||||
ChunkGenerator::init();
|
||||
}
|
||||
m_server.start_server(param.port);
|
||||
}
|
||||
m_client = std::make_shared<NetworkClient>(m_client_world);
|
||||
|
||||
m_client->start(param.ip, param.port);
|
||||
// init will send packet
|
||||
try {
|
||||
m_client_world.init(m_argument.player, m_client);
|
||||
|
||||
Logger::info("World Init Success");
|
||||
m_camera.camera_init(&m_client_world.get_player());
|
||||
m_scene_manager.app().window().set_camera(&m_camera);
|
||||
m_dev_panel.init();
|
||||
m_pasue_menu.init();
|
||||
m_hud_ui.init();
|
||||
|
||||
m_scene_manager.app().window().set_game_running(true);
|
||||
} catch (const std::exception& e) {
|
||||
m_error_ui.set_error(e.what());
|
||||
}
|
||||
}
|
||||
void WorldScene::on_leave() {
|
||||
m_client_world.request_exit();
|
||||
|
||||
m_scene_manager.app().window().set_camera(nullptr);
|
||||
m_scene_manager.app().window().set_game_running(false);
|
||||
|
||||
if (!m_argument.is_client) {
|
||||
m_server.server_world().stop();
|
||||
}
|
||||
m_scene_manager.app().audio().stop_bgm();
|
||||
}
|
||||
|
||||
void WorldScene::on_re_enter() {
|
||||
m_error_ui.on_re_enter();
|
||||
m_pasue_menu.on_re_enter();
|
||||
m_client_world.reload_config();
|
||||
auto width = m_scene_manager.app().renderer().window_width();
|
||||
auto height = m_scene_manager.app().renderer().window_height();
|
||||
|
||||
handle_event(
|
||||
WindowResizeEvent{static_cast<int>(width), static_cast<int>(height)});
|
||||
}
|
||||
|
||||
bool WorldScene::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||
if (m_paused) {
|
||||
if (m_pasue_menu.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (m_hud_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_camera.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
// world event needs to be processed last
|
||||
if (m_client_world.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool WorldScene::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
if (m_paused) {
|
||||
if (m_pasue_menu.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (m_hud_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_camera.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
// world event needs to be processed last
|
||||
if (m_client_world.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool WorldScene::handle_window_resize_event(const WindowResizeEvent& e) {
|
||||
|
||||
if (m_pasue_menu.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_hud_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_camera.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
// world event needs to be processed last
|
||||
if (m_client_world.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool WorldScene::handle_mouse_wheel_event(const MouseWheelEvent& e) {
|
||||
if (m_paused) {
|
||||
if (m_pasue_menu.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (m_hud_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_camera.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
// world event needs to be processed last
|
||||
if (m_client_world.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool WorldScene::handle_key_event(const KeyEvent& e) {
|
||||
|
||||
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
|
||||
bool pasued = pause();
|
||||
pasued = !pasued;
|
||||
set_pause(pasued);
|
||||
return true;
|
||||
}
|
||||
if (e.key == Key::F1 && e.action == KeyAction::PRESS) {
|
||||
m_show_hud = !m_show_hud;
|
||||
return true;
|
||||
}
|
||||
if (e.key == Key::F12 && e.action == KeyAction::PRESS) {
|
||||
m_show_dev_pannel = !m_show_dev_pannel;
|
||||
return true;
|
||||
}
|
||||
if (m_paused) {
|
||||
if (m_pasue_menu.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (m_hud_ui.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
if (m_camera.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
// world event needs to be processed last
|
||||
if (m_client_world.handle_event(e)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Camera& WorldScene::camera() { return m_camera; }
|
||||
SceneManager& WorldScene::scene_manager() { return m_scene_manager; }
|
||||
ClientWorld& WorldScene::client_world() { return m_client_world; }
|
||||
ServerWorld& WorldScene::server_world() { return m_server.server_world(); }
|
||||
bool WorldScene::pause() const { return m_paused; }
|
||||
void WorldScene::set_pause(bool pause) {
|
||||
m_paused = pause;
|
||||
auto& window = m_scene_manager.app().window();
|
||||
window.set_game_running(!m_paused);
|
||||
if (m_paused) {
|
||||
m_client_world.reset_key_status();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldScene::set_error(std::string_view error) {
|
||||
m_error_ui.set_error(error);
|
||||
set_pause(true);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -11,7 +11,6 @@ constexpr int BLOCK_SIZE = 16;
|
||||
constexpr int BLOCK_NORMAL_SIZE = 128;
|
||||
constexpr int CROSS_PLANE_SIZE = 16;
|
||||
constexpr int BLOCK_ITEM_SIZE = 16;
|
||||
constexpr int UI_SIZE = 16;
|
||||
constexpr int BLOCK_STATUS_SIZE = 16;
|
||||
constexpr int SKIN_SIZE = 64;
|
||||
|
||||
@@ -58,7 +57,13 @@ const Texture* TextureManager::get_texture_array() const {
|
||||
const Texture* TextureManager::get_cross_plane_array() const {
|
||||
return m_cross_plane_array.get();
|
||||
}
|
||||
const Texture* TextureManager::get_ui_array() const { return m_ui_array.get(); }
|
||||
const Texture* TextureManager::get_image_texture(const std::string& path) {
|
||||
auto it = m_ui_map.find(path);
|
||||
if (it != m_ui_map.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return load_image_texture(path);
|
||||
}
|
||||
|
||||
const Texture* TextureManager::get_pbr_texture() const {
|
||||
return m_normal_texture_array.get();
|
||||
@@ -77,15 +82,11 @@ void TextureManager::load_block_status(unsigned id) {
|
||||
|
||||
std::string path = "texture/status/" + std::to_string(id) + ".png";
|
||||
|
||||
unsigned char* image_data = nullptr;
|
||||
|
||||
image_data = (Tools::load_image_data(path));
|
||||
auto image_data = (Tools::load_image_data(path));
|
||||
|
||||
m_block_status_array->tex_sub_image_3d(
|
||||
TextureFormat::RGBA, GL_UNSIGNED_BYTE, image_data, 0, 0, id,
|
||||
TextureFormat::RGBA, GL_UNSIGNED_BYTE, image_data.data, 0, 0, id,
|
||||
BLOCK_STATUS_SIZE, BLOCK_STATUS_SIZE);
|
||||
|
||||
Tools::delete_image_data(image_data);
|
||||
}
|
||||
|
||||
void TextureManager::load_block_texture(unsigned id) {
|
||||
@@ -101,7 +102,7 @@ void TextureManager::load_block_texture(unsigned id) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* image_data[6];
|
||||
std::array<ImageData, 6> image_data;
|
||||
|
||||
std::string block_texture_path = "texture/block/" + name;
|
||||
image_data[0] = (Tools::load_image_data(block_texture_path + "/front.png"));
|
||||
@@ -114,10 +115,9 @@ void TextureManager::load_block_texture(unsigned id) {
|
||||
Tools::check_opengl_error();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
m_texture_array->tex_sub_image_3d(TextureFormat::RGBA, GL_UNSIGNED_BYTE,
|
||||
image_data[i], 0, 0, id * 6 + i,
|
||||
image_data[i].data, 0, 0, id * 6 + i,
|
||||
BLOCK_SIZE, BLOCK_SIZE);
|
||||
Tools::check_opengl_error();
|
||||
Tools::delete_image_data(image_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,41 +127,43 @@ void TextureManager::load_block_item_texture(unsigned id) {
|
||||
std::string name{BlockManager::name_form_id(id)};
|
||||
|
||||
std::string path = "texture/item/block/" + name + ".png";
|
||||
unsigned char* data = nullptr;
|
||||
data = Tools::load_image_data(path);
|
||||
|
||||
auto data = Tools::load_image_data(path);
|
||||
std::unique_ptr<Texture> texture =
|
||||
std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
texture->tex_image_2d(TextureFormat::RGBA8, TextureFormat::RGBA,
|
||||
GL_UNSIGNED_BYTE, data, BLOCK_ITEM_SIZE,
|
||||
GL_UNSIGNED_BYTE, data.data, BLOCK_ITEM_SIZE,
|
||||
BLOCK_ITEM_SIZE);
|
||||
|
||||
texture->set_nearest();
|
||||
texture->set_clamp_to_border();
|
||||
|
||||
m_item_textures.push_back(std::move(texture));
|
||||
Tools::delete_image_data(data);
|
||||
}
|
||||
|
||||
void TextureManager::load_cross_plane_texture(unsigned id) {
|
||||
std::string path =
|
||||
"texture/block/" + BlockManager::name_form_id(id) + "/cross.png";
|
||||
unsigned char* image_data = Tools::load_image_data(path);
|
||||
auto image_data = Tools::load_image_data(path);
|
||||
m_cross_plane_array->tex_sub_image_3d(TextureFormat::RGBA, GL_UNSIGNED_BYTE,
|
||||
image_data, 0, 0,
|
||||
image_data.data, 0, 0,
|
||||
BlockManager::cross_plane_index(id),
|
||||
CROSS_PLANE_SIZE, CROSS_PLANE_SIZE);
|
||||
Tools::delete_image_data(image_data);
|
||||
}
|
||||
|
||||
void TextureManager::load_ui_texture(unsigned id) {
|
||||
ASSERT_MSG(id < MAX_UI_NUM, "Exceed the max ui sum limit");
|
||||
const Texture* TextureManager::load_image_texture(const std::string& path) {
|
||||
|
||||
std::string path = "texture/ui/" + std::to_string(id) + ".png";
|
||||
unsigned char* image_data = nullptr;
|
||||
image_data = (Tools::load_image_data(path));
|
||||
m_ui_array->tex_sub_image_3d(TextureFormat::RGBA, GL_UNSIGNED_BYTE,
|
||||
image_data, 0, 0, id, UI_SIZE, UI_SIZE);
|
||||
Tools::delete_image_data(image_data);
|
||||
auto image_data = (Tools::load_image_data(path));
|
||||
std::unique_ptr<Texture> image =
|
||||
std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
image->tex_image_2d(RGBA, RGBA, GL_UNSIGNED_BYTE, image_data.data,
|
||||
image_data.width, image_data.height);
|
||||
image->set_nearest();
|
||||
auto [it, inserted] = m_ui_map.try_emplace(path, std::move(image));
|
||||
if (!inserted) {
|
||||
Logger::error("Path {} already exist!", path);
|
||||
}
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
void TextureManager::load_pbr_texture(unsigned id) {
|
||||
@@ -177,7 +179,7 @@ void TextureManager::load_pbr_texture(unsigned id) {
|
||||
|
||||
std::string path = "normal/block/" + BlockManager::name_form_id(id);
|
||||
|
||||
unsigned char* image_data[6];
|
||||
std::array<ImageData, 6> image_data;
|
||||
|
||||
image_data[0] = (Tools::load_image_data(path + "/front_n.png", false));
|
||||
image_data[1] = (Tools::load_image_data(path + "/right_n.png", false));
|
||||
@@ -187,7 +189,7 @@ void TextureManager::load_pbr_texture(unsigned id) {
|
||||
image_data[5] = (Tools::load_image_data(path + "/base_n.png", false));
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
unsigned char* data = image_data[i];
|
||||
unsigned char* data = image_data[i].data;
|
||||
bool is_fallback = false;
|
||||
if (!data) {
|
||||
is_fallback = true;
|
||||
@@ -200,7 +202,6 @@ void TextureManager::load_pbr_texture(unsigned id) {
|
||||
if (is_fallback) {
|
||||
delete[] data;
|
||||
} else {
|
||||
Tools::delete_image_data(image_data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,26 +243,16 @@ void TextureManager::init_block() {
|
||||
|
||||
Logger::info("Block Texture Load Success");
|
||||
}
|
||||
void TextureManager::init_ui() {
|
||||
m_ui_array = std::make_unique<Texture>(TextureType::TEXTURE_2D_ARRAY);
|
||||
m_ui_array->tex_image_3d(TextureFormat::RGBA, TextureFormat::RGBA,
|
||||
GL_UNSIGNED_BYTE, nullptr, UI_SIZE, UI_SIZE,
|
||||
MAX_UI_NUM);
|
||||
for (int i = 0; i < MAX_UI_NUM; i++) {
|
||||
load_ui_texture(i);
|
||||
}
|
||||
|
||||
m_ui_array->set_nearest();
|
||||
}
|
||||
void TextureManager::init_ui() {}
|
||||
|
||||
void TextureManager::init_skin() {
|
||||
m_skin = std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
std::string path = "texture/skin/player001.png";
|
||||
unsigned char* image_data = nullptr;
|
||||
image_data = (Tools::load_image_data(path));
|
||||
|
||||
auto image_data = (Tools::load_image_data(path));
|
||||
m_skin->tex_image_2d(TextureFormat::RGBA, TextureFormat::RGBA,
|
||||
GL_UNSIGNED_BYTE, image_data, SKIN_SIZE, SKIN_SIZE);
|
||||
Tools::delete_image_data(image_data);
|
||||
GL_UNSIGNED_BYTE, image_data.data, SKIN_SIZE,
|
||||
SKIN_SIZE);
|
||||
m_skin->set_nearest_and_minpmap();
|
||||
m_skin->set_aniso(m_aniso);
|
||||
}
|
||||
@@ -314,4 +305,31 @@ void TextureManager::hot_reload() {
|
||||
|
||||
int TextureManager::max_aniso() const { return static_cast<int>(m_max_aniso); }
|
||||
|
||||
bool TextureManager::handle_event(const Event& e) {
|
||||
return std::visit(
|
||||
Overloaded{[](const MouseMoveEvent&) { return false; },
|
||||
[](const MouseButtonEvent&) { return false; },
|
||||
[](const MouseWheelEvent&) { return false; },
|
||||
[this](const KeyEvent& e) {
|
||||
if (handle_key_event(e)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](const TextInputEvent&) { return false; },
|
||||
[](const WindowResizeEvent&) { return false; },
|
||||
[](const FrameBufferResizeEvent&) { return false; }
|
||||
|
||||
},
|
||||
e);
|
||||
}
|
||||
|
||||
bool TextureManager::handle_key_event(const KeyEvent& e) {
|
||||
if (e.key == Key::R && e.action == KeyAction::PRESS) {
|
||||
need_reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace Cubed
|
||||
@@ -38,9 +38,9 @@ void Font::load_character(char8_t c) {
|
||||
static_cast<int>(c), width, height);
|
||||
|
||||
Character character = {
|
||||
glm::vec2{0.0f, 0.0f},
|
||||
glm::vec2{static_cast<float>(width) / m_texture_width,
|
||||
static_cast<float>(height) / m_texture_height},
|
||||
glm::vec2{0.5f / m_texture_width, 0.5f / m_texture_height},
|
||||
glm::vec2{(width - 0.5f) / m_texture_width,
|
||||
(height - 0.5f) / m_texture_height},
|
||||
glm::ivec2(m_face->glyph->bitmap.width, m_face->glyph->bitmap.rows),
|
||||
glm::ivec2(m_face->glyph->bitmap_left, m_face->glyph->bitmap_top),
|
||||
static_cast<GLuint>(m_face->glyph->advance.x)};
|
||||
@@ -51,7 +51,7 @@ void Font::load_character(char8_t c) {
|
||||
void Font::setup_font_character() {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
m_text_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D_ARRAY);
|
||||
m_text_texture->tex_image_3d(TextureFormat::RED, TextureFormat::RED,
|
||||
m_text_texture->tex_image_3d(TextureFormat::R8, TextureFormat::RED,
|
||||
GL_UNSIGNED_BYTE, nullptr, m_texture_width,
|
||||
m_texture_height, MAX_CHARACTER);
|
||||
|
||||
@@ -62,24 +62,39 @@ void Font::setup_font_character() {
|
||||
m_text_texture->set_clamp_to_edge(false, true, true);
|
||||
}
|
||||
|
||||
std::vector<Vertex2D> Font::vertices(const std::string& text, float x, float y,
|
||||
float scale) {
|
||||
TextMesh Font::vertices(const std::string& text) {
|
||||
static Font font;
|
||||
|
||||
std::vector<Vertex2D> vertices;
|
||||
|
||||
float min_x = std::numeric_limits<float>::max();
|
||||
float min_y = std::numeric_limits<float>::max();
|
||||
float max_x = std::numeric_limits<float>::lowest();
|
||||
float max_y = std::numeric_limits<float>::lowest();
|
||||
|
||||
float pen_x = 0.0f;
|
||||
float pen_y = 0.0f;
|
||||
|
||||
for (char8_t c : text) {
|
||||
auto it = font.m_characters.find(c);
|
||||
if (it == font.m_characters.end()) {
|
||||
Logger::error("Can't find character {}", static_cast<char>(c));
|
||||
continue;
|
||||
}
|
||||
Character& ch = it->second;
|
||||
float xpos = x + ch.bearing.x * scale;
|
||||
float ypos = y - ch.bearing.y * scale;
|
||||
|
||||
float w = ch.size.x * scale;
|
||||
float h = ch.size.y * scale;
|
||||
Character& ch = it->second;
|
||||
|
||||
float xpos = pen_x + ch.bearing.x;
|
||||
float ypos = pen_y - ch.bearing.y;
|
||||
|
||||
float w = ch.size.x;
|
||||
float h = ch.size.y;
|
||||
|
||||
min_x = std::min(min_x, xpos);
|
||||
min_y = std::min(min_y, ypos);
|
||||
|
||||
max_x = std::max(max_x, xpos + w);
|
||||
max_y = std::max(max_y, ypos + h);
|
||||
|
||||
vertices.emplace_back(xpos, ypos + h, ch.uv_min.x, ch.uv_max.y,
|
||||
static_cast<float>(c));
|
||||
@@ -87,6 +102,7 @@ std::vector<Vertex2D> Font::vertices(const std::string& text, float x, float y,
|
||||
static_cast<float>(c));
|
||||
vertices.emplace_back(xpos + w, ypos, ch.uv_max.x, ch.uv_min.y,
|
||||
static_cast<float>(c));
|
||||
|
||||
vertices.emplace_back(xpos, ypos + h, ch.uv_min.x, ch.uv_max.y,
|
||||
static_cast<float>(c));
|
||||
vertices.emplace_back(xpos + w, ypos, ch.uv_max.x, ch.uv_min.y,
|
||||
@@ -94,10 +110,15 @@ std::vector<Vertex2D> Font::vertices(const std::string& text, float x, float y,
|
||||
vertices.emplace_back(xpos + w, ypos + h, ch.uv_max.x, ch.uv_max.y,
|
||||
static_cast<float>(c));
|
||||
|
||||
x += (ch.advance >> 6) * scale;
|
||||
pen_x += (ch.advance >> 6);
|
||||
}
|
||||
// Top-left anchor point
|
||||
for (auto& v : vertices) {
|
||||
v.x -= min_x;
|
||||
v.y -= min_y;
|
||||
}
|
||||
|
||||
return vertices;
|
||||
return {std::move(vertices), max_x - min_x, max_y - min_y, 0, 0};
|
||||
}
|
||||
|
||||
const Texture* Font::text_texture() { return m_text_texture.get(); }
|
||||
|
||||
@@ -174,8 +174,7 @@ void delete_image_data(unsigned char* data) {
|
||||
SOIL_free_image_data(data);
|
||||
}
|
||||
|
||||
unsigned char* load_image_data(const std::string& tex_image_path,
|
||||
bool check_exist) {
|
||||
ImageData load_image_data(const std::string& tex_image_path, bool check_exist) {
|
||||
fs::path path = ASSETS_PATH + tex_image_path;
|
||||
if (check_exist) {
|
||||
ASSERT_MSG(fs::is_regular_file(path), path.c_str());
|
||||
@@ -193,7 +192,7 @@ unsigned char* load_image_data(const std::string& tex_image_path,
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
return {data, width, height, channels};
|
||||
}
|
||||
|
||||
} // namespace Tools
|
||||
|
||||
124
src/ui/button.cpp
Normal file
124
src/ui/button.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "Cubed/ui/button.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Cubed {
|
||||
Button::Button(Widget* parent) : Widget(parent) {
|
||||
m_background = std::make_unique<Image>(this);
|
||||
m_background->set_fill(true);
|
||||
m_background->set_anchor(Anchor::TOP_LEFT);
|
||||
m_foreground = std::make_unique<Label>(this);
|
||||
m_foreground->set_anchor(Anchor::CENTER);
|
||||
}
|
||||
|
||||
void Button::on_update(float dt) {
|
||||
if (m_background) {
|
||||
m_background->update(dt);
|
||||
}
|
||||
if (m_foreground) {
|
||||
m_foreground->update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::on_render(Renderer& renderer) {
|
||||
if (m_background) {
|
||||
m_background->render(renderer);
|
||||
}
|
||||
if (m_foreground) {
|
||||
m_foreground->render(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
bool Button::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||
if (m_enable) {
|
||||
auto p = pos();
|
||||
if (e.xpos >= p.x && e.xpos <= p.x + width() && e.ypos >= p.y &&
|
||||
e.ypos <= p.y + height()) {
|
||||
m_hovered = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
m_hovered = false;
|
||||
|
||||
return Widget::handle_mouse_move_event(e);
|
||||
}
|
||||
bool Button::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
if (e.action == KeyAction::PRESS && e.key == MouseKey::LEFT_BUTTON) {
|
||||
if (m_hovered && m_clicked && m_enable) {
|
||||
m_clicked();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Widget::handle_mouse_button_event(e);
|
||||
}
|
||||
|
||||
Button& Button::set_scale(float scale) {
|
||||
m_scale = scale;
|
||||
update_text_scale();
|
||||
return *this;
|
||||
}
|
||||
|
||||
float Button::scale() const { return m_scale; }
|
||||
|
||||
float Button::width() const { return m_width * m_scale; }
|
||||
float Button::height() const { return m_height * m_scale; }
|
||||
Button& Button::set_width(float width) {
|
||||
m_width = width;
|
||||
update_text_scale();
|
||||
return *this;
|
||||
}
|
||||
Button& Button::set_height(float height) {
|
||||
m_height = height;
|
||||
update_text_scale();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Button& Button::set_background_image(const std::string& path,
|
||||
TextureManager& texture_manager) {
|
||||
m_background->set_image(path, texture_manager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Button& Button::set_default_image(TextureManager& texture_manager) {
|
||||
return set_background_image(DEFAULT_BUTTON_IMAGE, texture_manager);
|
||||
}
|
||||
|
||||
Button& Button::set_text(const std::string& text) {
|
||||
m_foreground->set_text(text);
|
||||
update_text_scale();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Button& Button::set_auto_scale(bool auto_scale) {
|
||||
m_auto_scale = auto_scale;
|
||||
return *this;
|
||||
}
|
||||
Button& Button::set_enable(bool enable) {
|
||||
m_enable = enable;
|
||||
return *this;
|
||||
}
|
||||
void Button::update_text_scale() {
|
||||
if (!m_auto_scale) {
|
||||
m_foreground->set_scale(TEXT_SCALE);
|
||||
return;
|
||||
}
|
||||
float text_w = m_foreground->real_width();
|
||||
float text_h = m_foreground->real_height();
|
||||
|
||||
if (text_w <= 0.0f || text_h <= 0.0f)
|
||||
return;
|
||||
|
||||
float available_w = std::max(0.0f, width() - 2.0f * PADDING * m_scale);
|
||||
float available_h = std::max(0.0f, height() - 2.0f * PADDING * m_scale);
|
||||
|
||||
float scale_x = available_w / text_w;
|
||||
float scale_y = available_h / text_h;
|
||||
|
||||
float scale = std::max(0.0f, std::min(scale_x, scale_y));
|
||||
scale = std::clamp(scale, 0.01f, 100.0f);
|
||||
m_foreground->set_scale(scale);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
49
src/ui/column_layout.cpp
Normal file
49
src/ui/column_layout.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
ColumnLayout::ColumnLayout(Widget* parent) : Widget(parent) {
|
||||
Widget::set_order(TraversalOrder::FRONT_TO_BACK);
|
||||
}
|
||||
ColumnLayout::~ColumnLayout() {}
|
||||
|
||||
void ColumnLayout::update(float dt) {
|
||||
layout();
|
||||
Widget::update(dt);
|
||||
}
|
||||
|
||||
float ColumnLayout::width() const { return m_content_width; }
|
||||
float ColumnLayout::height() const { return m_content_height; }
|
||||
|
||||
ColumnLayout& ColumnLayout::set_spacing(int spacing) {
|
||||
m_spacing = spacing;
|
||||
return *this;
|
||||
}
|
||||
ColumnLayout& ColumnLayout::set_child_anchor(ColumnLayoutAnchor anchor) {
|
||||
m_layout_anchor = anchor;
|
||||
return *this;
|
||||
}
|
||||
void ColumnLayout::layout() {
|
||||
auto& children = Widget::children();
|
||||
int y = 0;
|
||||
Anchor anchor = Anchor::TOP_LEFT;
|
||||
switch (m_layout_anchor) {
|
||||
case ColumnLayoutAnchor::LEFT:
|
||||
anchor = Anchor::TOP_LEFT;
|
||||
break;
|
||||
case ColumnLayoutAnchor::CENTER:
|
||||
anchor = Anchor::TOP_CENTER;
|
||||
break;
|
||||
case ColumnLayoutAnchor::RIGHT:
|
||||
anchor = Anchor::TOP_RIGHT;
|
||||
break;
|
||||
}
|
||||
for (auto& child : children) {
|
||||
child->set_anchor(anchor);
|
||||
child->set_offset({0, y});
|
||||
y += child->height() + m_spacing;
|
||||
m_content_width = std::max(m_content_width, child->width());
|
||||
}
|
||||
m_content_height = children.empty() ? 0 : (y - m_spacing);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
55
src/ui/combo_button.cpp
Normal file
55
src/ui/combo_button.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "Cubed/ui/combo_button.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
ComboButton::ComboButton(Widget* parent) : Button(parent) {}
|
||||
|
||||
Button& ComboButton::set_text(const std::string& text) {
|
||||
m_text = text;
|
||||
update_text();
|
||||
return *this;
|
||||
}
|
||||
bool ComboButton::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
if (e.action == KeyAction::PRESS && e.key == MouseKey::LEFT_BUTTON) {
|
||||
if (m_hovered && m_enable) {
|
||||
m_index = (m_index + 1) % m_sum;
|
||||
update_text();
|
||||
if (m_funs[m_index]) {
|
||||
m_funs[m_index]();
|
||||
} else {
|
||||
Logger::error("Function {} is null", m_index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Widget::handle_mouse_button_event(e);
|
||||
}
|
||||
ComboButton& ComboButton::set_combos(
|
||||
std::span<std::pair<std::string, std::function<void()>>> combos) {
|
||||
m_funs.clear();
|
||||
m_suffix.clear();
|
||||
m_sum = combos.size();
|
||||
for (auto& pair : combos) {
|
||||
m_suffix.push_back(pair.first);
|
||||
m_funs.push_back(pair.second);
|
||||
}
|
||||
update_text();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComboButton& ComboButton::set_index(int index) {
|
||||
m_index = index;
|
||||
update_text();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ComboButton::update_text() {
|
||||
if (m_suffix.empty()) {
|
||||
return;
|
||||
}
|
||||
auto text = m_text + ": " + m_suffix[m_index];
|
||||
m_foreground->set_text(text);
|
||||
update_text_scale();
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
83
src/ui/credits_ui.cpp
Normal file
83
src/ui/credits_ui.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "Cubed/ui/credits_ui.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/scene/credits_scene.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
#include "Cubed/ui/button.hpp"
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/rect.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
CreditsUI::CreditsUI(CreditsScene& scene) : m_scene(scene) {}
|
||||
|
||||
void CreditsUI::init() {
|
||||
auto& renderer = m_scene.scene_manager().app().renderer();
|
||||
|
||||
auto image = std::make_unique<Image>(nullptr);
|
||||
image->set_fill(true)
|
||||
.set_image("texture/ui/background.png",
|
||||
m_scene.scene_manager().app().texture_manager())
|
||||
.set_anchor(Anchor::TOP_LEFT)
|
||||
.set_window_size(renderer.window_width(), renderer.window_height());
|
||||
|
||||
auto& rect = image->add_child<Rect>();
|
||||
rect.set_fill(true).set_color(Color::BLACK).set_alpha(0.7f);
|
||||
|
||||
{
|
||||
auto& button = rect.add_child<Button>();
|
||||
button.set_background_image(
|
||||
"texture/ui/button001.png",
|
||||
m_scene.scene_manager().app().texture_manager());
|
||||
|
||||
button.set_text("Return");
|
||||
button.set_anchor(Anchor::BOTTOM_CENTER).set_offset({0, -20});
|
||||
button.set_clicked([this]() { m_scene.scene_manager().request_pop(); });
|
||||
}
|
||||
{
|
||||
auto& layout = rect.add_child<ColumnLayout>();
|
||||
layout.set_spacing(20)
|
||||
.set_anchor(Anchor::TOP_CENTER)
|
||||
.set_offset({0, 20});
|
||||
layout.add_child<Label>().set_text("Cubed");
|
||||
auto add_text = [&](std::string_view view, float scale = 0.5f) {
|
||||
layout.add_child<Label>().set_text(view).set_scale(scale);
|
||||
};
|
||||
add_text("A cube game like Minecraft, using C++ and OpenGL.");
|
||||
add_text("Author: zhenyan121");
|
||||
add_text("Libraries Used", 0.8f);
|
||||
add_text("glad");
|
||||
add_text("GLFW");
|
||||
add_text("SOIL2");
|
||||
add_text("GLM");
|
||||
add_text("FreeType");
|
||||
add_text("toml++");
|
||||
add_text("Dear ImGui");
|
||||
add_text("Tbb");
|
||||
add_text("Asio");
|
||||
add_text("protobuf");
|
||||
add_text("zstd");
|
||||
add_text("OpenAl Soft");
|
||||
add_text("dr_libs");
|
||||
add_text("Music", 0.8f);
|
||||
add_text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), CC "
|
||||
"BY 4.0.");
|
||||
add_text("Special Thanks", 0.8f);
|
||||
add_text("TANGERIME");
|
||||
add_text("SkyOnPole");
|
||||
add_text("free_w_cloud");
|
||||
add_text("Last but not least, thanks to you");
|
||||
}
|
||||
|
||||
m_root_widget = std::move(image);
|
||||
}
|
||||
|
||||
bool CreditsUI::handle_key_event(const KeyEvent& e) {
|
||||
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
|
||||
m_scene.scene_manager().request_pop();
|
||||
return true;
|
||||
}
|
||||
return UIManager::handle_key_event(e);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
70
src/ui/error_ui.cpp
Normal file
70
src/ui/error_ui.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "Cubed/ui/error_ui.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
namespace Cubed {
|
||||
ErrorUI::ErrorUI(WorldScene& scene) : m_scene(scene) {}
|
||||
|
||||
void ErrorUI::init() {
|
||||
auto bi = std::make_unique<Image>(nullptr);
|
||||
auto& renderer = m_scene.scene_manager().app().renderer();
|
||||
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
||||
|
||||
bi->set_window_size(renderer.window_width(), renderer.window_height());
|
||||
bi->set_anchor(Anchor::TOP_LEFT);
|
||||
bi->set_image("texture/ui/background.png", texture_manager);
|
||||
bi->set_fill(true);
|
||||
|
||||
auto& rect = bi->add_child<Rect>();
|
||||
rect.set_fill(true);
|
||||
rect.set_alpha(0.7f);
|
||||
rect.set_color(Color::BLACK);
|
||||
|
||||
auto& layout = rect.add_child<ColumnLayout>();
|
||||
layout.set_anchor(Anchor::CENTER);
|
||||
layout.set_offset({0, 20});
|
||||
layout.set_spacing(30.0f);
|
||||
|
||||
{
|
||||
auto& label = layout.add_child<Label>();
|
||||
label.set_text("No Error");
|
||||
label.set_color(Color::RED);
|
||||
label.set_scale(0.7f);
|
||||
m_error_label = &label;
|
||||
}
|
||||
|
||||
{
|
||||
auto& button = layout.add_child<Button>();
|
||||
button.set_default_image(texture_manager);
|
||||
button.set_text("Return");
|
||||
button.set_clicked([this, &button]() {
|
||||
button.set_enable(false);
|
||||
m_scene.scene_manager().request_pop();
|
||||
});
|
||||
}
|
||||
|
||||
m_root_widget = std::move(bi);
|
||||
}
|
||||
void ErrorUI::on_re_enter() {}
|
||||
|
||||
void ErrorUI::set_error(std::string_view error) {
|
||||
m_has_error = true;
|
||||
m_error_str = error;
|
||||
if (!m_error_str.empty() && m_error_label) {
|
||||
m_error_label->set_text(m_error_str);
|
||||
}
|
||||
}
|
||||
bool ErrorUI::has_error() const { return m_has_error; }
|
||||
void ErrorUI::clear_error() { m_has_error = false; }
|
||||
|
||||
bool ErrorUI::handle_key_event(const KeyEvent& e) {
|
||||
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
|
||||
m_scene.scene_manager().request_pop();
|
||||
return true;
|
||||
}
|
||||
return UIManager::handle_key_event(e);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
123
src/ui/host_game_ui.cpp
Normal file
123
src/ui/host_game_ui.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "Cubed/ui/host_game_ui.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/scene/host_game_scene.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
#include "Cubed/ui/button.hpp"
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/text_field.hpp"
|
||||
namespace Cubed {
|
||||
HostGameUI::HostGameUI(HostGameScene& scene) : m_scene(scene) {}
|
||||
void HostGameUI::init() {
|
||||
auto bi = std::make_unique<Image>(nullptr);
|
||||
auto& renderer = m_scene.scene_manager().app().renderer();
|
||||
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
||||
|
||||
bi->set_window_size(renderer.window_width(), renderer.window_height());
|
||||
bi->set_anchor(Anchor::TOP_LEFT);
|
||||
bi->set_image("texture/ui/background.png", texture_manager);
|
||||
bi->set_fill(true);
|
||||
|
||||
auto& rect = bi->add_child<Rect>();
|
||||
rect.set_fill(true);
|
||||
rect.set_alpha(0.7f);
|
||||
rect.set_color(Color::BLACK);
|
||||
|
||||
auto& layout = rect.add_child<ColumnLayout>();
|
||||
layout.set_anchor(Anchor::CENTER);
|
||||
layout.set_offset({0, 20});
|
||||
layout.set_spacing(20.0f);
|
||||
auto& param = m_scene.scene_manager().world_scene_param();
|
||||
param.host_game = true;
|
||||
{
|
||||
auto& label = layout.add_child<Label>();
|
||||
label.set_text("Create A New World");
|
||||
label.set_scale(0.7f);
|
||||
}
|
||||
{
|
||||
auto& label = layout.add_child<Label>();
|
||||
label.set_text("NoError");
|
||||
label.set_color(Color::RED);
|
||||
label.set_scale(0.7f);
|
||||
label.set_visible(false);
|
||||
m_error_label = &label;
|
||||
}
|
||||
{
|
||||
auto& text_seed = layout.add_child<TextField>();
|
||||
text_seed.set_show_text("WorldSeed");
|
||||
text_seed.set_app(&m_scene.scene_manager().app());
|
||||
text_seed.set_default_image(texture_manager);
|
||||
text_seed.set_on_finish([this, &text_seed]() {
|
||||
unsigned seed = 0;
|
||||
auto& text = text_seed.input_text();
|
||||
auto r =
|
||||
std::from_chars(text.data(), text.data() + text.size(), seed);
|
||||
|
||||
if (r.ec != std::errc{} || r.ptr != text.data() + text.size()) {
|
||||
std::string error = std::format("Invalid seed: {}", text);
|
||||
Logger::error("{}", error);
|
||||
set_error(error);
|
||||
return;
|
||||
}
|
||||
clear_error();
|
||||
m_scene.scene_manager().world_scene_param().seed = seed;
|
||||
});
|
||||
}
|
||||
{
|
||||
auto& text_port = layout.add_child<TextField>();
|
||||
text_port.set_default_image(texture_manager);
|
||||
text_port.set_show_text("Port: 25530");
|
||||
text_port.set_app(&m_scene.scene_manager().app());
|
||||
text_port.set_on_finish([this, &text_port]() {
|
||||
int port = 25530;
|
||||
auto& text = text_port.input_text();
|
||||
auto r =
|
||||
std::from_chars(text.data(), text.data() + text.size(), port);
|
||||
if (r.ec != std::errc{} || r.ptr != text.data() + text.size()) {
|
||||
std::string error = std::format("Invalid port: {}", text);
|
||||
Logger::error("{}", error);
|
||||
set_error(error);
|
||||
return;
|
||||
}
|
||||
if (port > 65535 || port < 0) {
|
||||
std::string error = std::format("Port {} out of range", port);
|
||||
Logger::error("{}", error);
|
||||
set_error(error);
|
||||
return;
|
||||
}
|
||||
clear_error();
|
||||
m_scene.scene_manager().world_scene_param().port = port;
|
||||
});
|
||||
}
|
||||
{
|
||||
auto& button = layout.add_child<Button>();
|
||||
button.set_default_image(texture_manager);
|
||||
button.set_text("Start Game");
|
||||
button.set_clicked([this, &button]() {
|
||||
button.set_enable(false);
|
||||
m_scene.scene_manager().request_change(SceneType::WORLD);
|
||||
});
|
||||
}
|
||||
{
|
||||
auto& button = layout.add_child<Button>();
|
||||
button.set_default_image(texture_manager);
|
||||
button.set_text("Return");
|
||||
button.set_clicked([this, &button]() {
|
||||
button.set_enable(false);
|
||||
m_scene.scene_manager().request_pop();
|
||||
});
|
||||
}
|
||||
m_root_widget = std::move(bi);
|
||||
}
|
||||
void HostGameUI::on_re_enter() {}
|
||||
|
||||
void HostGameUI::set_error(std::string_view error) {
|
||||
if (!m_error_label) {
|
||||
return;
|
||||
}
|
||||
m_error_label->set_text(error);
|
||||
m_error_label->set_visible(true);
|
||||
}
|
||||
void HostGameUI::clear_error() { m_error_label->set_visible(false); }
|
||||
} // namespace Cubed
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user