17 Commits

Author SHA1 Message Date
80ba6dad89 feat(ui): add Rect widget with fill, color, and parent-relative sizing 2026-07-13 20:48:51 +08:00
acb0b94d7b 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.
2026-07-13 18:15:34 +08:00
e7082bdbe0 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.
2026-07-13 16:01:00 +08:00
f5e53fd13d 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.
2026-07-13 14:52:35 +08:00
0cbb353a2a fix(render): remove duplicate scaling in render_image model matrix 2026-07-13 13:10:44 +08:00
e8157ab549 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.
2026-07-13 13:06:09 +08:00
1fa3b9cce8 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.
2026-07-13 11:40:27 +08:00
b5e0bb9290 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.
2026-07-13 11:32:52 +08:00
576a2f8072 feat(ui): add button scaling and change square vertices to top-left origin 2026-07-13 11:11:30 +08:00
01c9f61d1c 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.
2026-07-13 10:38:30 +08:00
8bbed7b7b9 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.
2026-07-12 21:03:50 +08:00
674c023228 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.
2026-07-12 20:43:04 +08:00
d00ada1391 fix(font): use correct texture format and clamp to edge for font atlas 2026-07-12 20:33:24 +08:00
cb1eb25e7b refactor: add framebuffer resize event and separate UI management 2026-07-12 18:38:42 +08:00
5712ba0600 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.
2026-07-12 16:05:59 +08:00
2d28488126 refactor(render): replace UI shaders with image shaders and Image widget 2026-07-11 14:54:08 +08:00
09653c318a 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.
2026-07-11 11:44:31 +08:00
73 changed files with 3074 additions and 745 deletions

View 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);
}

View 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;
}

View File

@@ -0,0 +1,9 @@
#version 460
out vec4 color;
uniform vec4 inColor;
void main(void) {
color = inColor;
}

View 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);
}

View File

@@ -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));
}

View File

@@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

View File

@@ -1,27 +1,19 @@
#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 +25,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,12 +37,9 @@ 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;
@@ -56,18 +47,17 @@ public:
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;
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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
@@ -40,9 +49,6 @@ public:
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();
@@ -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;
};

View File

@@ -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;
@@ -97,6 +99,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 +136,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;

View 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

View File

@@ -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

127
include/Cubed/input/key.hpp Normal file
View File

@@ -0,0 +1,127 @@
#pragma once
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

View 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
};
}

View File

@@ -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] = {

View File

@@ -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 {

View File

@@ -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 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

View File

@@ -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

View File

@@ -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;

View 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;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
MainMenuUIManager m_ui_manager;
};
} // namespace Cubed

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Cubed/input/event.hpp"
namespace Cubed {
class Renderer;
enum class SceneType { MAIN_MENU, WORLD };
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() {};
};
} // namespace Cubed

View File

@@ -0,0 +1,47 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include <memory>
#include <stack>
#include <vector>
namespace Cubed {
class App;
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();
private:
enum class OperationType { PUSH, POP, CHANGE };
struct SceneOperation {
OperationType type;
std::optional<SceneType> scene;
};
App& m_app;
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();
std::unique_ptr<Scene> create_scene(SceneType);
};
} // namespace Cubed

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Cubed/argument.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/scene/scene.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;
Camera& camera();
SceneManager& scene_manager();
ClientWorld& client_world();
private:
SceneManager& m_scene_manager;
DevPanel m_dev_panel;
Camera m_camera;
std::shared_ptr<NetworkClient> m_client;
ClientWorld m_client_world;
WorldUIManager m_ui_manager;
const Argument& m_argument;
};
} // namespace Cubed

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View 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,
};

View File

@@ -0,0 +1,58 @@
#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;
void render(Renderer& renderer) override;
void update(float dt) override;
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
void set_window_size(int width, int height) override;
Button& set_scale(float scale);
Widget& set_anchor(Anchor anchor) override;
Widget& set_offset(glm::ivec2 offset) override;
glm::vec2 pos() const override;
float width() const override;
float height() const override;
float scale() const;
template <typename F> Button& set_clicked(F&& f) {
m_clicked = std::forward<F>(f);
return *this;
}
template <typename T, typename... Args> T& set_background(Args&&... args) {
auto w = std::make_unique<T>(std::forward<Args>(args)..., m_parent);
T& ref = *w;
m_background = std::move(w);
return ref;
}
template <typename T, typename... Args> T& set_foreground(Args&&... args) {
auto w = std::make_unique<T>(std::forward<Args>(args)..., this);
T& ref = *w;
m_foreground = std::move(w);
m_foreground->set_anchor(Anchor::CENTER);
return ref;
}
private:
std::function<void()> m_clicked;
std::unique_ptr<Image> m_background;
std::unique_ptr<Label> m_foreground;
bool m_hovered = false;
};
} // namespace Cubed

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Cubed/ui/widget.hpp"
namespace Cubed {
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;
void set_spacing(int spacing);
// No need for parent node pointer; do not modify children's anchors and
// scale.
void layout();
private:
int m_spacing = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,27 @@
#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(Widget* parent);
void update(float dt) override;
void render(Renderer& renderer) override;
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;
private:
const Texture* m_texture = nullptr;
glm::vec2 m_pos{0.0f, 0.0f};
float m_scale = 1.0f;
};
} // namespace Cubed

View File

@@ -0,0 +1,48 @@
#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);
virtual void update(float dt) override;
virtual void render(Renderer& renderer) override;
const UIVertexData& data() const;
const TextStyle& text_style() const;
float width() const override;
float height() const override;
float offset_x() const;
float offset_y() const;
float scale() 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

View File

@@ -0,0 +1,36 @@
#pragma once
#include "Cubed/input/event.hpp"
#include "Cubed/ui/widget.hpp"
#include <memory>
#include <unordered_map>
namespace Cubed {
class Renderer;
class MainMenuScene;
class MainMenuUIManager {
public:
MainMenuUIManager(MainMenuScene& m_scene);
MainMenuUIManager(const MainMenuUIManager&) = delete;
MainMenuUIManager(MainMenuUIManager&&) = delete;
MainMenuUIManager& operator=(const MainMenuUIManager&) = delete;
MainMenuUIManager& operator=(MainMenuUIManager&&) = delete;
~MainMenuUIManager();
void init();
void render(Renderer& renderer);
void update(float dt);
bool handle_event(const Event& e);
private:
MainMenuScene& m_scene;
std::unique_ptr<Widget> m_root_widget;
std::unordered_map<std::string, Widget*> m_widgets;
bool handle_mouse_move_event(const MouseMoveEvent& e);
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_window_resize_event(const WindowResizeEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
bool handle_key_event(const KeyEvent& e);
};
} // namespace Cubed

41
include/Cubed/ui/rect.hpp Normal file
View 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();
void update(float dt) override;
void render(Renderer& renderer) override;
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:
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

View File

@@ -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

View 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

View File

@@ -0,0 +1,61 @@
#pragma once
#include "Cubed/input/event.hpp"
#include "Cubed/ui/anchor.hpp"
#include <glm/glm.hpp>
#include <memory>
#include <vector>
namespace Cubed {
class Renderer;
class Widget {
public:
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 void set_window_size(int width, int height);
// 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);
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;
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;
};
} // namespace Cubed

View File

@@ -0,0 +1,34 @@
#pragma once
#include "Cubed/input/event.hpp"
#include "Cubed/ui/widget.hpp"
#include <string>
#include <unordered_map>
namespace Cubed {
class WorldScene;
class WorldUIManager {
public:
WorldUIManager(const WorldUIManager&) = delete;
WorldUIManager(WorldUIManager&&) = delete;
WorldUIManager& operator=(const WorldUIManager&) = delete;
WorldUIManager& operator=(WorldUIManager&&) = delete;
WorldUIManager(WorldScene& scene);
~WorldUIManager();
void init();
void update(float dt);
void render(Renderer& renderer);
bool handle_event(const Event& e);
private:
WorldScene& m_scene;
std::unordered_map<std::string, std::unique_ptr<Widget>> m_widgets;
bool handle_mouse_move_event(const MouseMoveEvent& e);
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_window_resize_event(const WindowResizeEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
bool handle_key_event(const KeyEvent& e);
};
} // namespace Cubed

View File

@@ -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,34 @@ public:
GLFWwindow* get_glfw_window();
void init();
void imgui_init();
void update_viewport();
// end of frame to reload!
bool handle_event(const Event& e);
void hot_reload();
void toggle_fullscreen();
void toggle_mouse_able();
void set_camera(Camera* camera);
Camera* camera();
void set_game_running(bool running);
void should_close_window();
private:
bool m_mouse_enable = false;
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 handle_key_event(const KeyEvent& e);
bool handle_window_resize_event(const WindowResizeEvent& e);
bool handle_mouse_button_event(const MouseButtonEvent& e);
};
} // namespace Cubed

View File

@@ -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,16 @@ 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
)

View File

@@ -1,5 +1,6 @@
#include "Cubed/app.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/tools/arg_parser.hpp"
@@ -16,16 +17,10 @@ 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();
@@ -38,7 +33,8 @@ void App::cursor_position_callback(GLFWwindow* window, double xpos,
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) {
@@ -60,6 +56,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(),
@@ -72,7 +70,6 @@ void App::init(int argc, char** argv) {
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");
@@ -80,16 +77,19 @@ void App::init(int argc, char** argv) {
m_server.start_server(m_argument.port);
}
m_client = std::make_shared<NetworkClient>(m_client_world);
m_scene_manager.request_push(SceneType::MAIN_MENU);
m_client->start(m_argument.ip, m_argument.port);
// init will send packet
m_client_world.init(m_argument.player, m_client);
{
int w, h;
glfwGetWindowSize(m_window.get_glfw_window(), &w, &h);
window_reshape_callback(m_window.get_glfw_window(), w, h);
}
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) {
@@ -179,49 +179,357 @@ void App::key_callback(GLFWwindow* window, int key, int scancode, int action,
// 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 ((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,
@@ -233,30 +541,46 @@ void App::mouse_button_callback(GLFWwindow* window, int button, int action,
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) {
@@ -268,17 +592,29 @@ void App::window_focus_callback(GLFWwindow* window, int 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();
@@ -289,8 +625,7 @@ void App::mouse_scroll_callback(GLFWwindow* window, double xoffset,
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) {
@@ -318,8 +653,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 +663,37 @@ 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 +712,30 @@ 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 (m_scene_manager.handle_event(e)) {
return;
}
}
int App::start_cubed_application(int argc, char** argv) {
@@ -401,14 +761,11 @@ 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; }
} // namespace Cubed

View File

@@ -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

View File

@@ -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});
// 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

View File

@@ -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();
}
@@ -383,7 +386,7 @@ void DevPanel::show_settings_tab_item() {
128)) {
m_config.set("world.rendering_distance",
m_config_view.rendering_distance);
m_app.client_world().hot_reload();
m_world_scene.client_world().hot_reload();
}
if (ImGui::Checkbox("Fullscreen", &m_config_view.fullscreen)) {
m_config.set("window.fullscreen", m_config_view.fullscreen);
@@ -551,20 +554,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 +655,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())),

View File

@@ -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,47 @@ 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 +618,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; }

View File

@@ -21,8 +21,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();
@@ -718,6 +718,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) {
@@ -905,6 +906,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) *

View File

@@ -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

View File

@@ -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);

View File

@@ -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) {
@@ -99,8 +93,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));
@@ -119,6 +114,7 @@ void Renderer::init(bool debug_on) {
VertexArray::unbind();
VertexBuffer::unbind();
m_init = true;
}
@@ -139,63 +135,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 +233,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 +240,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 +326,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 +335,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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,

View File

@@ -0,0 +1,21 @@
#include "Cubed/scene/main_menu_scene.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() {}
SceneManager& MainMenuScene::scene_manager() { return m_scene_manager; }
} // namespace Cubed

View File

@@ -0,0 +1,99 @@
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/scene/main_menu_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();
}
push(type);
}
void SceneManager::push(SceneType type) {
auto scene = create_scene(type);
scene->on_enter();
m_scenes.push(std::move(scene));
}
void SceneManager::pop() {
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));
}
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);
}
std::string err = std::format("Unknown Scene");
ASSERT_MSG(false, err);
throw std::runtime_error(err);
}
App& SceneManager::app() { return m_app; }
} // namespace Cubed

88
src/scene/world_scene.cpp Normal file
View File

@@ -0,0 +1,88 @@
#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_ui_manager(*this), m_argument(scene_manager.app().argument()) {}
WorldScene::~WorldScene() {
if (m_client) {
m_client->stop();
}
}
void WorldScene::update(float dt) {
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);
}
}*/
m_ui_manager.update(dt);
}
void WorldScene::render(Renderer& renderer) {
renderer.render_world(m_client_world);
renderer.render_dev_panel(m_dev_panel);
m_ui_manager.render(renderer);
}
bool WorldScene::handle_event(const Event& e) {
if (m_ui_manager.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;
}
void WorldScene::on_enter() {
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_scene_manager.app().window().set_camera(&m_camera);
m_dev_panel.init();
m_ui_manager.init();
m_scene_manager.app().window().set_game_running(true);
}
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);
}
Camera& WorldScene::camera() { return m_camera; }
SceneManager& WorldScene::scene_manager() { return m_scene_manager; }
ClientWorld& WorldScene::client_world() { return m_client_world; }
} // namespace Cubed

View File

@@ -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

View File

@@ -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(); }

View File

@@ -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

72
src/ui/button.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include "Cubed/ui/button.hpp"
namespace Cubed {
Button::Button(Widget* parent) : Widget(parent) {}
void Button::update(float dt) {
if (m_background) {
m_background->update(dt);
}
if (m_foreground) {
m_foreground->update(dt);
}
}
void Button::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) {
auto pos = m_background->pos();
if (e.xpos >= pos.x && e.xpos <= pos.x + width() && e.ypos >= pos.y &&
e.ypos <= pos.y + height()) {
m_hovered = true;
return true;
}
m_hovered = false;
return false;
}
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_clicked();
return true;
}
}
return false;
}
Button& Button::set_scale(float scale) {
m_background->set_scale(scale);
return *this;
}
void Button::set_window_size(int width, int height) {
m_background->set_window_size(width, height);
}
Widget& Button::set_anchor(Anchor anchor) {
m_background->set_anchor(anchor);
return *this;
}
Widget& Button::set_offset(glm::ivec2 offset) {
m_background->set_offset(offset);
return *this;
}
glm::vec2 Button::pos() const { return m_background->pos(); }
float Button::scale() const { return m_background->scale(); }
float Button::width() const { return m_background->width(); }
float Button::height() const { return m_background->height(); }
} // namespace Cubed

37
src/ui/column_layout.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "Cubed/ui/column_layout.hpp"
namespace Cubed {
ColumnLayout::ColumnLayout(Widget* parent) : Widget(parent) {}
ColumnLayout::~ColumnLayout() {}
void ColumnLayout::update(float dt) {
Widget::update(dt);
layout();
}
float ColumnLayout::width() const {
if (m_parent) {
return m_parent->width();
}
return m_window_width;
}
float ColumnLayout::height() const {
if (m_parent) {
return m_parent->height();
}
return m_window_height;
}
void ColumnLayout::set_spacing(int spacing) { m_spacing = spacing; }
void ColumnLayout::layout() {
auto& children = Widget::children();
int y = 0;
for (auto& child : children) {
child->set_offset({0, y});
child->set_anchor(m_anchor);
y += child->height() + m_spacing;
}
}
} // namespace Cubed

39
src/ui/image.cpp Normal file
View File

@@ -0,0 +1,39 @@
#include "Cubed/ui/image.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/texture_manager.hpp"
namespace Cubed {
Image::Image(Widget* parent) : Widget(parent) {}
void Image::update(float) {}
void Image::render(Renderer& renderer) { renderer.render_image(*this); }
Image& Image::set_image(const std::string& path,
TextureManager& texture_manager) {
m_texture = texture_manager.get_image_texture(path);
return *this;
}
Image& Image::set_scale(float scale) {
m_scale = scale;
return *this;
}
float Image::scale() const { return m_scale; }
float Image::height() const {
if (!m_texture) {
Logger::error("Image id {} not set image!", m_id);
return 0.0f;
}
return m_texture->height() * m_scale;
}
float Image::width() const {
if (!m_texture) {
Logger::error("Image id {} not set image!", m_id);
return 0.0f;
}
return m_texture->width() * m_scale;
}
const Texture* Image::texture() const { return m_texture; }
} // namespace Cubed

50
src/ui/label.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include "Cubed/ui/label.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/tools/font.hpp"
namespace Cubed {
Label::Label(const std::string& id, Widget* parent) : Widget(id, parent) {}
Label::Label(Widget* parent) : Widget(parent) {}
Label& Label::set_text(std::string_view text) {
m_text.text = text;
update_vertices();
return *this;
}
Label& Label::set_color(Color color) {
m_text.color = color;
return *this;
}
Label& Label::set_scale(float scale) {
m_scale = scale;
return *this;
}
void Label::update(float dt) { on_update(dt); }
void Label::on_update(float dt) { (void)dt; }
void Label::render(Renderer& renderer) { on_render(renderer); }
void Label::on_render(Renderer& renderer) { renderer.render_lable(*this); }
void Label::update_vertices() {
auto textmesh = Font::vertices(m_text.text);
m_data.m_vertices = std::move(textmesh.vertices);
m_offset_x = textmesh.min_x;
m_offset_y = textmesh.min_y;
m_real_width = textmesh.width;
m_real_height = textmesh.height;
m_data.update_sum();
m_data.upload();
}
const UIVertexData& Label::data() const { return m_data; }
const TextStyle& Label::text_style() const { return m_text; }
float Label::width() const { return m_real_width * m_scale; }
float Label::height() const { return m_real_height * m_scale; }
float Label::offset_x() const { return m_offset_x; }
float Label::offset_y() const { return m_offset_y; }
float Label::scale() const { return m_scale; }
} // namespace Cubed

View File

@@ -0,0 +1,145 @@
#include "Cubed/ui/main_menu_ui_manager.hpp"
#include "Cubed/app.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/scene/main_menu_scene.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/ui/button.hpp"
#include "Cubed/ui/column_layout.hpp"
namespace Cubed {
MainMenuUIManager::MainMenuUIManager(MainMenuScene& scene) : m_scene(scene) {}
MainMenuUIManager::~MainMenuUIManager() {}
void MainMenuUIManager::init() {
auto rect = std::make_unique<Rect>(nullptr);
rect->set_fill(true);
rect->set_anchor(Anchor::TOP_LEFT);
rect->set_color(Color::WHITE);
auto& renderer = m_scene.scene_manager().app().renderer();
rect->set_window_size(renderer.window_width(), renderer.window_height());
auto& layout = rect->add_child<ColumnLayout>();
layout.set_spacing(20);
layout.set_anchor(Anchor::CENTER);
layout.set_window_size(renderer.window_width(), renderer.window_height());
{
auto& start_game_button = layout.add_child<Button>();
auto& back = start_game_button.set_background<Image>();
back.set_image("texture/ui/button001.png",
m_scene.scene_manager().app().texture_manager());
auto& fore = start_game_button.set_foreground<Label>();
fore.set_text("Start Game");
fore.set_scale(0.6f);
start_game_button.set_clicked([this]() {
m_scene.scene_manager().request_push(SceneType::WORLD);
});
start_game_button.set_scale(4.0f);
}
auto& exit_game = layout.add_child<Button>();
{
auto& back = exit_game.set_background<Image>();
back.set_image("texture/ui/button001.png",
m_scene.scene_manager().app().texture_manager());
auto& fore = exit_game.set_foreground<Label>();
fore.set_scale(0.6f).set_text("Exit");
exit_game.set_scale(4.0f);
exit_game.set_clicked([this]() {
m_scene.scene_manager().app().window().should_close_window();
});
}
m_widgets.try_emplace("background", rect.get());
m_widgets.try_emplace("main menu layout", &layout);
m_root_widget = std::move(rect);
}
void MainMenuUIManager::update(float dt) { m_root_widget->update(dt); }
void MainMenuUIManager::render(Renderer& renderer) {
renderer.begin_render_ui();
m_root_widget->render(renderer);
renderer.end_render_ui();
}
bool MainMenuUIManager::handle_event(const 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);
}
bool MainMenuUIManager::handle_mouse_move_event(const MouseMoveEvent& e) {
if (m_root_widget->handle_mouse_move_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_mouse_button_event(const MouseButtonEvent& e) {
if (m_root_widget->handle_mouse_button_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_window_resize_event(const WindowResizeEvent& e) {
if (m_root_widget->handle_window_resize_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_mouse_wheel_event(const MouseWheelEvent& e) {
if (m_root_widget->handle_mouse_wheel_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_key_event(const KeyEvent& e) {
if (m_root_widget->handle_key_event(e)) {
return true;
}
return false;
}
} // namespace Cubed

58
src/ui/rect.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "Cubed/ui/rect.hpp"
#include "Cubed/render/renderer.hpp"
namespace Cubed {
Rect::Rect(Widget* parent)
: Widget(parent) {
};
Rect::~Rect() {}
void Rect::update(float dt) {
if (m_fill) {
if (!m_parent) {
m_width = m_window_width;
m_height = m_window_height;
} else {
m_width = m_parent->width();
m_height = m_parent->height();
}
}
Widget::update(dt);
}
void Rect::render(Renderer& renderer) {
renderer.render_rect(*this);
Widget::render(renderer);
}
float Rect::width() const { return m_width * m_scale; }
float Rect::height() const { return m_height * m_scale; }
float Rect::alpha() const { return m_alpha; }
Rect& Rect::set_width(float width) {
m_width = width;
return *this;
}
Rect& Rect::set_height(float height) {
m_height = height;
return *this;
}
Rect& Rect::set_fill(bool fill) {
m_fill = fill;
return *this;
}
Rect& Rect::set_scale(float scale) {
m_scale = scale;
return *this;
}
Rect& Rect::set_color(Color color) {
m_color = color;
return *this;
}
Rect& Rect::set_alpha(float alpha) {
m_alpha = std::clamp(alpha, 0.0f, 1.0f);
return *this;
}
Color Rect::color() const { return m_color; }
} // namespace Cubed

View File

@@ -1,93 +0,0 @@
#include "Cubed/ui/text.hpp"
#include "Cubed/shader.hpp"
#include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/font.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace Cubed {
Text::Text(std::string_view name)
: NAME(name), UUID(HASH::str(name)),
m_vbo(std::make_unique<VertexBuffer>()),
m_vao(std::make_unique<VertexArray>()) {}
Text::Text(std::string_view name, std::string_view str, glm::vec2 pos,
Color color)
: NAME(name), UUID(HASH::str(name)),
m_vbo(std::make_unique<VertexBuffer>()),
m_vao(std::make_unique<VertexArray>()) {
m_text.assign(str);
m_pos = pos;
m_color = color_value(color);
update_vertices();
}
Text::~Text() { m_vbo.reset(); }
Text::Text(Text&& other) noexcept
: m_scale(other.m_scale), m_pos(other.m_pos), NAME(other.NAME),
UUID(other.UUID), m_text(std::move(other.m_text)), m_color(other.m_color),
m_model_matrix(other.m_model_matrix),
m_vertices(std::move(other.m_vertices)), m_vbo(std::move(other.m_vbo)),
m_vao(std::move(other.m_vao)) {}
Text& Text::color(Color color) {
m_color = color_value(color);
return *this;
}
Text& Text::position(float x, float y) {
m_pos = glm::vec2{x, y};
return *this;
}
Text& Text::scale(float s) {
m_scale = s;
return *this;
}
std::size_t Text::uuid() const { return UUID; }
Text& Text::text(std::string_view str) {
m_text.assign(str);
update_vertices();
return *this;
}
void Text::render(const Shader& shader) {
ASSERT_MSG(m_vbo != 0, "VBO not initialized!");
ASSERT_MSG(!m_vertices.empty(), "Text String Not Set");
Font::text_texture()->bind(0);
m_vao->bind();
m_model_matrix =
glm::translate(glm::mat4(1.0f), glm::vec3(m_pos.x, m_pos.y, 0.0f)) *
glm::scale(glm::mat4(1.0f), glm::vec3(m_scale, m_scale, 1.0f));
shader.set_loc("textColor", glm::vec3(m_color.x, m_color.y, m_color.z));
shader.set_loc("mv_matrix", m_model_matrix);
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
}
void Text::update_vertices() {
m_vertices = Font::vertices(m_text);
upload_to_gpu();
}
void Text::upload_to_gpu() {
ASSERT_MSG(m_vbo, "Vbo Is Not Gen");
m_vao->bind();
m_vbo->buffer_data(m_vertices.data(), m_vertices.size() * sizeof(Vertex2D),
BufferUsage::DYNAMIC_DRAW);
m_vao->attribute(0, 2, GL_FLOAT, sizeof(Vertex2D), (void*)0);
m_vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, s));
m_vao->attribute(2, 1, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, layer));
}
bool Text::operator==(const Text& other) const { return UUID == other.uuid(); }
} // namespace Cubed

46
src/ui/ui_vertex_data.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "Cubed/ui/ui_vertex_data.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
namespace Cubed {
UIVertexData::UIVertexData() {}
UIVertexData::~UIVertexData() {
m_vao.reset();
m_vbo.reset();
}
void UIVertexData::upload() {
if (m_sum == 0) {
Logger::error("You need update_sum first");
ASSERT(false);
}
if (m_vertices.size() == 0) {
return;
}
if (!m_vao) {
m_vao = std::make_unique<VertexArray>();
}
if (!m_vbo) {
m_vbo = std::make_unique<VertexBuffer>();
}
m_vao->bind();
m_vbo->buffer_data(m_vertices.data(), m_vertices.size() * sizeof(Vertex2D),
BufferUsage::DYNAMIC_DRAW);
m_vao->attribute(0, 2, GL_FLOAT, sizeof(Vertex2D), (void*)0);
m_vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, s));
m_vao->attribute(2, 1, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, layer));
VertexArray::unbind();
VertexBuffer::unbind();
// Release memory
m_vertices.clear();
}
void UIVertexData::update_sum() { m_sum = m_vertices.size(); }
} // namespace Cubed

166
src/ui/widget.cpp Normal file
View File

@@ -0,0 +1,166 @@
#include "Cubed/ui/widget.hpp"
#include "Cubed/tools/log.hpp"
namespace Cubed {
Widget::Widget(const std::string& id, Widget* parent)
: m_parent(parent), m_id(id) {}
Widget::Widget(Widget* parent) : m_parent(parent) {}
void Widget::update(float dt) {
on_update(dt);
for (auto& child : m_children) {
child->update(dt);
}
}
void Widget::render(Renderer& renderer) {
on_render(renderer);
for (auto& child : m_children) {
child->render(renderer);
}
}
void Widget::on_update(float) {}
void Widget::on_render(Renderer&) {}
std::vector<std::unique_ptr<Widget>>& Widget::children() { return m_children; }
const std::vector<std::unique_ptr<Widget>>& Widget::children() const {
return m_children;
}
glm::vec2 Widget::compute_position() const {
glm::vec2 pos{0, 0};
float parent_w;
float parent_h;
if (!m_parent) {
if (m_window_height == 0 || m_window_width == 0) {
Logger::error("Window Size is 0 !");
}
parent_h = m_window_height;
parent_w = m_window_width;
} else {
parent_h = m_parent->height();
parent_w = m_parent->width();
}
const float W = width();
const float H = height();
switch (m_anchor) {
case Anchor::TOP_LEFT:
pos = {0, 0};
break;
case Anchor::TOP_CENTER:
pos = {(parent_w - W) / 2, 0};
break;
case Anchor::TOP_RIGHT:
pos = {parent_w - W, 0};
break;
case Anchor::CENTER_LEFT:
pos = {0, (parent_h - H) / 2};
break;
case Anchor::CENTER:
pos = {(parent_w - W) / 2, (parent_h - H) / 2};
break;
case Anchor::CENTER_RIGHT:
pos = {parent_w - W, (parent_h - H) / 2};
break;
case Anchor::BOTTOM_LEFT:
pos = {0, parent_h - H};
break;
case Anchor::BOTTOM_CENTER:
pos = {(parent_w - W) / 2, parent_h - H};
break;
case Anchor::BOTTOM_RIGHT:
pos = {parent_w - W, parent_h - H};
break;
}
if (m_parent) {
pos += m_parent->pos();
}
pos += m_offset;
return pos;
}
Widget& Widget::set_anchor(Anchor anchor) {
m_anchor = anchor;
return *this;
}
Widget& Widget::set_offset(glm::ivec2 offset) {
m_offset = offset;
return *this;
}
void Widget::set_window_size(int width, int height) {
m_window_height = height;
m_window_width = width;
}
float Widget::width() const {
if (m_parent) {
return m_parent->width();
}
return m_window_width;
}
float Widget::height() const {
if (m_parent) {
return m_parent->height();
}
return m_window_height;
}
glm::vec2 Widget::pos() const { return compute_position(); }
const std::string& Widget::id() const { return m_id; }
bool Widget::handle_key_event(const KeyEvent& e) {
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_key_event(e)) {
return true;
}
}
return false;
}
bool Widget::handle_mouse_button_event(const MouseButtonEvent& e) {
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_mouse_button_event(e)) {
return true;
}
}
return false;
}
bool Widget::handle_mouse_wheel_event(const MouseWheelEvent& e) {
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_mouse_wheel_event(e)) {
return true;
}
}
return false;
}
bool Widget::handle_window_resize_event(const WindowResizeEvent& e) {
set_window_size(e.width, e.height);
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_window_resize_event(e)) {
return true;
}
}
return false;
}
bool Widget::handle_mouse_move_event(const MouseMoveEvent& e) {
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_mouse_move_event(e)) {
return true;
}
}
return false;
}
} // namespace Cubed

120
src/ui/world_ui_manager.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "Cubed/ui/world_ui_manager.hpp"
#include "Cubed/app.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/scene/world_scene.hpp"
namespace Cubed {
WorldUIManager::WorldUIManager(WorldScene& scene) : m_scene(scene) {}
WorldUIManager::~WorldUIManager() {}
void WorldUIManager::init() {
auto crosshair = std::make_unique<Image>(nullptr);
crosshair->set_image("texture/ui/0.png",
m_scene.scene_manager().app().texture_manager());
auto& renderer = m_scene.scene_manager().app().renderer();
crosshair->set_window_size(renderer.window_width(),
renderer.window_height());
crosshair->set_anchor(Anchor::CENTER);
crosshair->set_scale(3.0f);
m_widgets.try_emplace("crosshair", std::move(crosshair));
}
void WorldUIManager::update(float dt) {
for (auto& w : m_widgets) {
w.second->update(dt);
}
}
void WorldUIManager::render(Renderer& renderer) {
renderer.begin_render_ui();
auto& widget = DebugCollector::get().get_widget();
widget.render(renderer);
for (auto& widget : m_widgets) {
widget.second->render(renderer);
}
renderer.end_render_ui();
}
bool WorldUIManager::handle_event(const 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);
}
bool WorldUIManager::handle_mouse_move_event(const MouseMoveEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_move_event(e)) {
return true;
}
}
return false;
}
bool WorldUIManager::handle_mouse_button_event(const MouseButtonEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_button_event(e)) {
return true;
}
}
return false;
}
bool WorldUIManager::handle_window_resize_event(const WindowResizeEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_window_resize_event(e)) {
return true;
}
}
return false;
}
bool WorldUIManager::handle_mouse_wheel_event(const MouseWheelEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_wheel_event(e)) {
return true;
}
}
return false;
}
bool WorldUIManager::handle_key_event(const KeyEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_key_event(e)) {
return true;
}
}
return false;
}
} // namespace Cubed

View File

@@ -1,6 +1,6 @@
#include "Cubed/window.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/font.hpp"
#include "Cubed/tools/log.hpp"
@@ -14,8 +14,7 @@ namespace Cubed {
static int windowed_xpos = 0, windowed_ypos = 0;
static int windowed_width = 800, windowed_height = 600;
Window::Window(Renderer& renderer, Config& config)
: m_renderer(renderer), m_config(config) {}
Window::Window(Config& config) : m_config(config) {}
Window::~Window() {
if (m_imgui_init) {
@@ -41,14 +40,66 @@ const GLFWwindow* Window::get_glfw_window() const { return m_window; }
GLFWwindow* Window::get_glfw_window() { return m_window; }
void Window::update_viewport() {
glfwGetFramebufferSize(m_window, &m_width, &m_height);
m_aspect = (float)m_width / (float)m_height;
glViewport(0, 0, m_width, m_height);
m_renderer.update_proj_matrix(m_aspect, m_width, m_height);
m_renderer.updata_framebuffer(m_width, m_height);
m_config.set("window.width", windowed_width);
m_config.set("window.height", windowed_height);
bool Window::handle_event(const Event& e) {
return std::visit(
Overloaded{[](const MouseMoveEvent&) { return false; },
[this](const MouseButtonEvent& e) {
if (handle_mouse_button_event(e)) {
return true;
}
return false;
},
[](const MouseWheelEvent&) { 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);
}
bool Window::handle_key_event(const KeyEvent& e) {
if (e.key == Key::F11 && e.action == KeyAction::PRESS) {
toggle_fullscreen();
return true;
}
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
if (!m_game_running) {
glfwSetWindowShouldClose(m_window, GLFW_TRUE);
return true;
}
}
if (e.key == Key::LEFT_ALT && e.action == KeyAction::PRESS) {
toggle_mouse_able();
return true;
}
return false;
}
bool Window::handle_window_resize_event(const WindowResizeEvent& e) {
m_window_height = e.height;
m_window_width = e.width;
return false;
}
bool Window::handle_mouse_button_event(const MouseButtonEvent& e) {
if (e.key == MouseKey::LEFT_BUTTON && e.action == KeyAction::PRESS) {
if (is_mouse_enable()) {
toggle_mouse_able();
return true;
}
}
return false;
}
void Window::init() {
@@ -60,15 +111,18 @@ void Window::init() {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
m_width = m_config.get("window.width", 800);
m_height = m_config.get("window.height", 600);
m_window_width = m_config.get("window.width", 800);
m_window_height = m_config.get("window.height", 600);
if (m_config.get("window.fullscreen", false)) {
GLFWmonitor* primary_monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(primary_monitor);
m_window = glfwCreateWindow(mode->width, mode->height, "Cubed",
primary_monitor, NULL);
} else {
m_window = glfwCreateWindow(m_width, m_height, "Cubed", NULL, NULL);
m_window = glfwCreateWindow(m_window_width, m_window_height, "Cubed",
NULL, NULL);
}
glfwMakeContextCurrent(m_window);
@@ -77,8 +131,10 @@ void Window::init() {
} else {
glfwSwapInterval(0);
}
if (m_game_running) {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
}
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
if (glfwRawMouseMotionSupported()) {
glfwSetInputMode(m_window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
} else {
@@ -121,12 +177,13 @@ void Window::hot_reload() {
Logger::error("Can't Find Monitor");
}
}
update_viewport();
if (!m_mouse_enable) {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
} else {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
m_config.set("window.width", windowed_width);
m_config.set("window.height", windowed_height);
}
void Window::toggle_fullscreen() {
@@ -148,10 +205,14 @@ void Window::toggle_fullscreen() {
GL_DONT_CARE);
m_config.set("window.fullscreen", true);
}
update_viewport();
m_config.set("window.width", windowed_width);
m_config.set("window.height", windowed_height);
}
void Window::toggle_mouse_able() {
if (!m_game_running) {
m_mouse_enable = true;
}
// auto& io = ImGui::GetIO();
if (m_mouse_enable) {
// io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
@@ -166,7 +227,20 @@ void Window::toggle_mouse_able() {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
m_mouse_enable = true;
}
if (m_camera) {
m_camera->reset_camera();
}
}
void Window::set_camera(Camera* camera) { m_camera = camera; }
Camera* Window::camera() { return m_camera; }
void Window::set_game_running(bool running) {
m_game_running = running;
m_mouse_enable = running;
toggle_mouse_able();
}
void Window::should_close_window() { glfwSetWindowShouldClose(m_window, true); }
void Window::imgui_init() {
float dpi_scale_x, dpi_scale_y;