30 Commits

Author SHA1 Message Date
4653ae9f06 refactor(ui/slider): improve track/thumb initialization and validation
Rename track/thumb accessors from set_track/set_thumb to get_track/get_thumb.
Add private init methods and a constant THUMB_WIDTH.
Add validation to set_width with log and assert.
Modify set_height to assume non-null m_thumb.
2026-07-14 21:19:56 +08:00
a6fbbe8e1e feat(ui): add Slider widget for value selection 2026-07-14 21:09:21 +08:00
03e7b13dd1 feat(credits): add ESC key handling; remove about tab from dev panel 2026-07-14 17:11:37 +08:00
e911413d84 feat(scene): add credits scene and UI 2026-07-14 16:28:26 +08:00
279f6162b8 refactor(app): remove redundant ImGui WantCaptureMouse check in cursor callback 2026-07-14 15:38:47 +08:00
618d3f1294 feat(ui): add HUD visibility toggle and image fill features 2026-07-14 15:27:55 +08:00
969c78ce5e refactor(scene): dispatch events via typed handlers and move pause toggle to WorldScene 2026-07-14 11:55:01 +08:00
03f539eb9f feat(scene): implement pause menu with UI manager and escape key toggling
Add PauseMenuUIManager class and integrate pause state into WorldScene.
Escape key toggles pause in ClientPlayer and PauseMenuUIManager.
Refactor Window mouse and imgui control to support pause state.
2026-07-14 11:27:39 +08:00
d1db165ca3 refactor: move server logic to WorldScene and refactor UI methods 2026-07-14 09:44:52 +08:00
558fd6311d refactor(ui): extract common UI manager logic into base class 2026-07-14 09:15:54 +08:00
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
zhenyan121
03259f323c refactor: config (#30)
* refactor(config): replace singleton with dependency injection

* refactor(client_player): return ChunkPosSet by value in non-const getter

Copy the internal set under the mutex lock to avoid exposing a mutable reference that could be used unsafely after the lock is released. This fixes a potential data race when the caller holds the returned reference beyond the critical section. Also update the caller in `client_world` to remove the now-unnecessary `std::move`.

* fix(gameplay): prevent use-after-free in pending delete queues during destruction

* refactor(config): separate game and server configuration with explicit paths
2026-07-11 09:22:40 +08:00
zhenyan121
913809a5f0 refactor: renderer (#29)
* refactor(render): move render files to subdirectory, add RAII vertex buffer/array classes

* refactor(render): encapsulate OpenGL textures into Texture class

* refactor(texture): encapsulate textures with unique_ptr and bind methods

Replace raw GLuint framebuffer textures with std::unique_ptr<Texture>. Update TextureManager to return const Texture* instead of GLuint. Use Texture::bind() for active texture binding. Add RGBA16F and RGB formats. Add texture parameter methods. Update destructors accordingly.

* refactor(render): use Texture, VertexBuffer, VertexArray classes

* feat(render): add FrameBuffer class to encapsulate framebuffer operations

* refactor(render): extract world rendering into WorldRenderer class

* refactor(render): rename projection and model matrix members for clarity

* chore(render): remove unused matrix members

* fix(texture-manager): correct delet_texture typo and add null checks for textures
2026-07-10 14:51:43 +08:00
zhenyan121
b1341d7da4 feat: sound (#28)
* build: add OpenAL dependency

* feat(deps): add dr_libs audio library

* feat(audio): integrate OpenAL audio engine with WAV/MP3/FLAC support

* feat: add bgm001

* feat(audio): add audio fade in/out and fix delta time naming

* fix(audio-source): improve state, volume, and const correctness

* feat(audio): add 3D audio playback with source pool and auto-loading

* feat: add ogg loader

* feat(audio): add footstep sounds and fix audio listener orientation

* feat(sound): add block place and break sounds

* feat(audio): add per-channel volume control and config reload

* fix(audio): set distance model, reduce max distance, fix sound pos

- Set OpenAL distance model to AL_INVERSE_DISTANCE_CLAMPED in engine.
- Reduced AL_MAX_DISTANCE from 100 to 48 for better falloff.
- Fixed block sound position to use world coordinates instead of local block coordinates.

* refactor(game_time): introduce Timer for time-based updates and rename existing to TickTimer

Add a new `Timer` class that operates in seconds (using `TimeType`) alongside the existing `TickTimer` which uses `TickType`. Rename the original `Timer` to `TickTimer` throughout the codebase. Update `ClientWorld` to maintain separate maps for tick-based and time-based timers, and replace the hardcoded footstep timer in `ClientPlayer` with a time-based callback timer. Also add `play_2d` to `AudioEngine` and load an ambient bird sound to demonstrate the new timer functionality.

* feat(gameplay): add ocean wave ambient sounds with random selection

Add four ocean wave FLAC sound files and play one randomly when player is within 10 blocks of sea level and in an ocean biome. Include a Random member initialized from ChunkGenerator seed for consistent randomization.

* feat(gameplay): detect underwater state and play transition/bubble sounds

- Add is_underwater() and set_underwater() to ClientPlayer.
- Change get_world() to return non-const reference for audio access.
- Update Camera::update_move_camera() to detect water block changes and trigger transition sound.
- Add repeating timer in ClientWorld to play random bubble ambient sounds while underwater.
- Include new ambient sound assets for water transitions and bubbles.
- Also add sand walk sound asset (unused in this commit).

* feat(assets): add and update block break/place/walk sounds

Add sounds for dirt, grass_block, leaf, log, sand, snowy_grass_block, stone.
Update existing leaf/walk, sand/place, sand/walk, and stone/place sounds.

* feat(gameplay): add walking sound for players and adjust underwater bubble interval

- Move walk/run sound interval constants from anonymous namespace to ClientPlayer header
- Add moving_time field to PlayerInfo to track continuous movement
- Implement play_walk_sound callback triggered by WALK and RUN gaits
- Increase underwater bubble timer from 1.0s to 1.5s

* feat(asset): add grass block break and place sound effects

* feat(renderer): add --no-debug flag to disable OpenGL debug output

* feat(audio): add day/night-based BGM switching

* feat(audio): add underwater low-pass filter effect

* feat(audio): add pitch control to audio sources

Implement set_pitch method that clamps pitch between 0.0 and 1.0 and applies it via AL_PITCH. Includes minor whitespace cleanup in audio_engine.cpp.

* feat(audio): implement reverb effect and integrate into underwater system

* asset(sound): update dirt and grass block break/place sounds

* feat(network): synchronize player water sound

* fix: include <numbers>

* fix(audio): play ambient water bubble sound in 3D at player position
2026-07-08 19:41:10 +08:00
179 changed files with 40409 additions and 2553 deletions

1
.gitignore vendored
View File

@@ -41,6 +41,7 @@ CMakeError.log
*~
.DS_Store
assets/config.toml
assets/server-config.toml
.venv/
pyout/
vcpkg_installed/

View File

@@ -32,6 +32,8 @@ add_subdirectory(third_party/glad)
add_subdirectory(third_party/imgui)
add_subdirectory(third_party/dr_libs)
add_executable(${PROJECT_NAME})
add_subdirectory(src)
@@ -129,6 +131,7 @@ target_link_libraries(${PROJECT_NAME}
Freetype::Freetype
tomlplusplus::tomlplusplus
imgui
dr_libs
tbb
protobuf::libprotobuf
absl::log
@@ -137,6 +140,7 @@ target_link_libraries(${PROJECT_NAME}
absl::strings
absl::flat_hash_map
zstd::zstd
OpenAL::OpenAL
$<$<PLATFORM_ID:Windows>:ws2_32>
)

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sound/bgm/bgm001.mp3 Normal file

Binary file not shown.

BIN
assets/sound/bgm/bgm002.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

View File

@@ -5,6 +5,7 @@ find_package(OpenGL REQUIRED)
find_package(Protobuf REQUIRED)
find_package(absl REQUIRED)
find_package(zstd REQUIRED)
find_package(OpenAL REQUIRED)
if (UNIX AND NOT APPLE)
find_package(Freetype REQUIRED)
find_package(glfw3 REQUIRED)

View File

@@ -1,24 +1,17 @@
#pragma once
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/gameplay/network_server.hpp"
#include "Cubed/gameplay/server_world.hpp"
#include "Cubed/audio/audio_engine.hpp"
#define GLFW_INCLUDE_NONE
#include "Cubed/camera.hpp"
#include "Cubed/argument.hpp"
#include "Cubed/config.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/renderer.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"};
};
App();
~App();
static void cursor_position_callback(GLFWwindow* window, double xpos,
@@ -30,6 +23,8 @@ public:
static void window_focus_callback(GLFWwindow* window, int focused);
static void window_reshape_callback(GLFWwindow* window, int new_width,
int new_height);
static void framebuffer_size_callback(GLFWwindow* window, int new_width,
int new_height);
static void mouse_scroll_callback(GLFWwindow* window, double xoffset,
double yoffset);
static void cursor_enter_callback(GLFWwindow* window, int entered);
@@ -37,38 +32,38 @@ public:
static int start_cubed_application(int argc, char** argv);
static unsigned int seed();
static float delte_time();
static float delta_time();
static float get_fps();
Camera& camera();
DevPanel& dev_panel();
Renderer& renderer();
TextureManager& texture_manager();
Window& window();
ClientWorld& client_world();
ServerWorld& server_world();
Config& config();
const Argument& argument() const;
AudioEngine& audio();
private:
Camera m_camera;
Config m_game_config;
TextureManager m_texture_manager;
NetworkServer m_server;
std::shared_ptr<NetworkClient> m_client;
ClientWorld m_client_world;
DevPanel m_dev_panel{*this};
Renderer m_renderer{m_camera, m_client_world, m_texture_manager,
m_dev_panel};
AudioEngine m_audio;
Window m_window{m_renderer};
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 delta_time = 0.0f;
inline static double dt = 0.0f;
inline static double fps_time_count = 0.0f;
inline static int frame_count = 0;
inline static int fps = 0;
Argument m_argument;
void init(int argc, char** argv);
void handle_argument(int argc, char** argv);
void handle_toml();
@@ -79,6 +74,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

@@ -0,0 +1,24 @@
#pragma once
#include "Cubed/audio/audio_data.hpp"
#include <AL/al.h>
namespace Cubed {
class AudioBuffer {
public:
AudioBuffer(const AudioData& data);
AudioBuffer(const AudioBuffer&) = delete;
AudioBuffer(AudioBuffer&&) = delete;
AudioBuffer& operator=(const AudioBuffer&) = delete;
AudioBuffer& operator=(AudioBuffer&&) = delete;
~AudioBuffer();
ALuint buffer() const;
float duration() const;
uint32_t channels() const;
private:
ALuint m_buffer = 0;
float m_duration = 0.0f;
uint32_t m_channels = 0;
void set_data(const AudioData& data);
};
} // namespace Cubed

View File

@@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
#include <vector>
namespace Cubed {
struct AudioData {
std::vector<int16_t> pcm;
uint32_t channels;
uint32_t sample_rate;
};
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include <AL/al.h>
namespace Cubed {
class AudioEffect {
public:
AudioEffect();
AudioEffect(const AudioEffect&) = delete;
AudioEffect(AudioEffect&&) = delete;
AudioEffect& operator=(const AudioEffect&) = default;
AudioEffect& operator=(AudioEffect&&) = delete;
~AudioEffect();
void set_reverb(float decay_time, float reverb_gain, float gain_hf,
float density = 1.0f, float diffusion = 1.0f);
ALuint effect() const;
private:
ALuint m_effect = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,18 @@
#pragma once
#include "Cubed/audio/audio_effect.hpp"
namespace Cubed {
class AudioEffectSlot {
public:
AudioEffectSlot();
AudioEffectSlot(const AudioEffectSlot&) = delete;
AudioEffectSlot(AudioEffectSlot&&) = delete;
AudioEffectSlot& operator=(const AudioEffectSlot&) = delete;
AudioEffectSlot& operator=(AudioEffectSlot&&) = delete;
~AudioEffectSlot();
void set_effect(const AudioEffect& effect);
ALuint slot() const;
private:
ALuint m_slot = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,62 @@
#pragma once
#include "Cubed/audio/audio_fade.hpp"
#include "Cubed/audio/audio_source.hpp"
#include "Cubed/audio/sound_manager.hpp"
#include "Cubed/audio/source_pool.hpp"
#include "Cubed/config.hpp"
#include <AL/al.h>
#include <AL/alc.h>
#include <glm/glm.hpp>
#include <memory>
#include <string>
#include <unordered_map>
namespace Cubed {
class ClientWorld;
class AudioEngine {
public:
AudioEngine(Config& config);
AudioEngine(const AudioEngine&) = delete;
AudioEngine(AudioEngine&&) = delete;
AudioEngine& operator=(const AudioEngine&) = delete;
AudioEngine& operator=(AudioEngine&&) = delete;
~AudioEngine();
void init();
void play_bgm();
void change_bgm(const std::string& sound);
void play_3d(const std::string& sound, const glm::vec3& pos,
bool check = false);
void play_2d(const std::string& sound, bool check = false);
void update_listener(const glm::vec3& pos, const glm::vec3& forward,
const glm::vec3& up);
void update();
void reload_config();
void underwater_change(bool underwater);
float& bgm_target_volume();
private:
using FadeMap = std::unordered_map<std::string, AudioFade>;
bool m_init{false};
ALCdevice* device{nullptr};
ALCcontext* context{nullptr};
glm::vec3 listener_pos;
std::unique_ptr<AudioSource> m_bgm;
FadeMap m_fade_map;
SoundManager m_sounds;
Config& m_config;
std::shared_ptr<SourcePool> m_pool;
bool m_efx_supported = false;
bool m_underwater = false;
float m_music_volume = 1.0f;
float m_sfx_volume = 1.0f;
std::unique_ptr<AudioFilter> m_low_pass_filter;
std::unique_ptr<AudioEffect> m_underwater_effect;
std::unique_ptr<AudioEffectSlot> m_underwater_slot;
};
} // namespace Cubed

View File

@@ -0,0 +1,15 @@
#pragma once
#include "Cubed/tools/log.hpp"
#include <AL/al.h>
namespace Cubed {
inline void check_al_error(
std::source_location loaction = std::source_location::current()) {
ALenum error = alGetError();
if (error != AL_NO_ERROR) {
Logger::error("File {} Line {} Function {} OpenAL Error {} ",
loaction.file_name(), loaction.line(),
loaction.function_name(), alGetString(error));
}
}
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include "Cubed/audio/audio_source.hpp"
namespace Cubed {
class AudioFade {
public:
AudioFade(AudioSource* source, float fade_in = 0.0f, float fade_out = 0.0f);
~AudioFade();
void update();
void reset();
private:
AudioSource* m_source = nullptr;
float m_in_duration = 0.0f;
float m_out_duration = 0.0f;
float m_start_gain = 0.0f;
bool m_fade_in = true;
bool m_active = true;
};
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include <AL/al.h>
namespace Cubed {
class AudioFilter {
public:
AudioFilter();
AudioFilter(const AudioFilter&) = delete;
AudioFilter(AudioFilter&&) = delete;
AudioFilter& operator=(const AudioFilter&) = delete;
AudioFilter& operator=(AudioFilter&&) = delete;
~AudioFilter();
void set_lowpass(float gain, float gain_hf);
ALuint filter() const;
private:
ALuint m_filter = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,17 @@
#pragma once
#include "Cubed/audio/audio_data.hpp"
#include <filesystem>
namespace Cubed {
class AudioLoader {
public:
static AudioData load(const std::filesystem::path& path);
private:
static AudioData load_wav(const std::filesystem::path& path);
static AudioData load_mp3(const std::filesystem::path& path);
static AudioData load_flac(const std::filesystem::path& path);
static AudioData load_ogg(const std::filesystem::path& path);
};
} // namespace Cubed

View File

@@ -0,0 +1,50 @@
#pragma once
#include "Cubed/audio/audio_buffer.hpp"
#include "Cubed/audio/audio_effect_slot.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include <AL/al.h>
#include <glm/glm.hpp>
namespace Cubed {
enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
class AudioSource {
public:
AudioSource(float volume = 1.0f);
~AudioSource();
void set_buffer_2d(const AudioBuffer& buffer);
void set_buffer_3d(const AudioBuffer& buffer, const glm::vec3& vec3);
void set_loop(bool on = true);
void set_volume(float volume);
void set_pitch(float pitch);
void play();
void play_2d(const AudioBuffer& buffer);
void play_3d(const AudioBuffer& buffer, const glm::vec3& pos);
void stop();
void pause();
float duration() const;
float current_time() const;
float target_volume() const;
float& target_volume();
void set_target_volume(float volume);
AudioState state() const;
void mark_in_use();
bool in_use() const;
void reset();
void set_filter(const AudioFilter& filter);
void clear_filter();
void set_effect_slot(const AudioEffectSlot& slot);
void clear_effect_slot();
private:
ALuint m_source = 0;
float m_target_volume = 1.0f;
float m_duration = 0.0f;
bool m_using;
};
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include "Cubed/audio/audio_buffer.hpp"
#include <string>
#include <unordered_map>
namespace Cubed {
class SoundManager {
public:
SoundManager();
~SoundManager();
const AudioBuffer& load(const std::string& name);
const AudioBuffer& get_buffer(const std::string& name);
void init();
void clear();
private:
std::unordered_map<std::string, AudioBuffer> m_buffers;
};
} // namespace Cubed

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Cubed/audio/audio_source.hpp"
namespace Cubed {
class SourcePool {
private:
std::vector<AudioSource> m_sources;
public:
explicit SourcePool(size_t size);
~SourcePool();
SourcePool(const SourcePool&) = delete;
SourcePool(SourcePool&&) = delete;
SourcePool& operator=(const SourcePool&) = delete;
SourcePool& operator=(SourcePool&&) = delete;
void update();
AudioSource* acquire();
std::vector<AudioSource>& sources();
};
} // 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,99 +1,75 @@
#pragma once
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/toml.utils.hpp"
namespace Cubed {
class Config {
public:
Config();
explicit Config(std::string_view path);
Config(const Config&) = delete;
Config(Config&&) = delete;
Config& operator=(const Config&) = delete;
Config& operator=(Config&&) = delete;
~Config();
static Config& get();
toml::table& table();
void load_or_create_config();
void load_config();
void save_to_file();
template <TOML::TomlValueType T> T get(std::string_view key) const {
size_t cur = 0;
auto pos = key.find('.');
const toml::table* table = &m_tbl;
while (pos != std::string_view::npos) {
std::string_view s = key.substr(cur, pos - cur);
if (s.empty()) {
Logger::error("Empty key/table name in path '{}'", key);
ASSERT(false);
std::abort();
}
cur = pos + 1;
pos = key.find('.', cur);
if (auto* next = (*table)[s].as_table()) {
table = next;
} else {
Logger::error("Can't find table {}", s);
ASSERT(false);
std::abort();
}
template <TOML::TomlValueType T>
T get(std::string_view key, T default_value) {
if (auto* node = find_node(m_tbl, key)) {
if (auto value = node->value<T>())
return *value;
}
std::string_view n_key = key.substr(cur);
if (n_key.empty()) {
Logger::error("Trailing dot in path '{}'", key);
ASSERT(false);
std::abort();
}
auto opt = (*table)[n_key].value<T>();
if (opt) {
return *opt;
set(key, default_value);
save_to_file();
return default_value;
}
template <TOML::TomlValueType T> void set(std::string_view key, T&& value) {
auto pos = key.rfind('.');
toml::table* table;
std::string_view name;
if (pos == std::string_view::npos) {
table = &m_tbl;
name = key;
} else {
Logger::error("Can't find key {}", n_key);
ASSERT(false);
std::abort();
table = find_or_create_table(key.substr(0, pos));
name = key.substr(pos + 1);
}
// Insert node at the last level's table
table->insert_or_assign(name, std::forward<T>(value));
}
template <typename T> void set(std::string_view key, T&& val) {
if constexpr (!TOML::TomlValueType<std::decay_t<T>>) {
static_assert(false, "Type Not Support");
}
size_t cur = 0;
auto pos = key.find('.');
toml::table* table = &m_tbl;
while (pos != std::string_view::npos) {
std::string_view s = key.substr(cur, pos - cur);
if (s.empty()) {
Logger::error("Empty key/table name in path '{}'", key);
ASSERT(false);
std::abort();
}
cur = pos + 1;
pos = key.find('.', cur);
if (auto* next = (*table)[s].as_table()) {
table = next;
} else {
auto [it, inserted] = table->insert_or_assign(s, toml::table{});
table = it->second.as_table();
}
}
std::string_view n_key = key.substr(cur);
if (n_key.empty()) {
Logger::error("Trailing dot in path '{}'", key);
ASSERT(false);
std::abort();
}
table->insert_or_assign(n_key, std::forward<T>(val));
}
template <typename T> void set_and_save(std::string_view key, T&& val) {
set(key, std::forward(val));
save_to_file();
}
toml::node_view<toml::node> val_view(std::string_view key);
private:
toml::table m_tbl;
constexpr static inline std::string_view CONGIF_PATH =
ASSETS_PATH "config.toml";
void create_config();
const std::string CONGIF_PATH;
const toml::node* find_node(const toml::table& root,
std::string_view path) const;
// Follow the path to find the last-level toml::table, creating it if it
// does not exist
toml::table* find_or_create_table(std::string_view path);
};
template <>
inline float Config::get(std::string_view key, float default_value) {
return static_cast<float>(
Config::get<double>(key, static_cast<double>(default_value)));
}
template <> inline void Config::set(std::string_view key, float&& value) {
Config::set<double>(key, static_cast<double>(value));
}
} // 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

@@ -1,11 +1,14 @@
#pragma once
#include "Cubed/config.hpp"
#include <toml++/toml.hpp>
namespace Cubed {
class App;
class WorldScene;
class ClientPlayer;
class App;
class DevPanel {
struct ConfigView {
float fov = 70.0f;
@@ -20,6 +23,8 @@ class DevPanel {
bool is_enable_aniso = false;
bool is_support_aniso = true;
bool is_reload = true;
float volume_music = 1.0f;
float volume_sfx = 1.0f;
};
struct PlayerProfile {
int game_mode = 0;
@@ -28,13 +33,15 @@ class DevPanel {
};
public:
DevPanel(App& app);
DevPanel(WorldScene& app);
void init();
void render();
private:
App& m_app;
ConfigView m_config;
WorldScene& m_world_scene;
Config& m_config;
ConfigView m_config_view;
ClientPlayer* m_player;
PlayerProfile m_player_profile;
bool m_need_save_config = false;
@@ -46,7 +53,6 @@ private:
int m_samples_idx = 1;
int m_threads = 1;
int m_chunk_style = 0;
void show_about_table_bar();
void show_biome_table_bar();
void show_time_table_bar();
void show_cave_table_bar();

View File

@@ -76,6 +76,7 @@ public:
void need_upload();
void set_chunk_block(int index, unsigned id);
BlockType get_chunk_block(int index);
bool is_temp_chunk() const;
ChunkPos chunk_pos() const;
BiomeType biome() const;

View File

@@ -4,8 +4,10 @@
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#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>
@@ -16,13 +18,23 @@ namespace Cubed {
class ClientWorld;
class ClientPlayer {
public:
static constexpr float WALK_SOUND_INTERVAL = 0.45f;
static constexpr float RUN_SOUND_INTERVAL = 0.3f;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
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();
ChunkPosSet get_chunk_pos_set();
static AABB get_aabb(const glm::vec3& pos);
const glm::vec3& get_front() const;
@@ -37,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();
@@ -49,12 +58,12 @@ public:
float& g();
float& fly_y_speed();
unsigned place_block() const;
unsigned get_current_block() const;
void set_gait(Gait gait);
GameMode& game_mode();
const ClientWorld& get_world() const;
ClientWorld& get_world();
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
@@ -69,6 +78,9 @@ public:
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
void place_block(float dt);
private:
using enum GameMode;
@@ -78,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;
@@ -99,6 +112,7 @@ private:
bool m_moving = false;
bool m_sprinting = false;
bool m_underwater = false;
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};
@@ -113,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{};
@@ -123,17 +138,24 @@ private:
float m_angle{0.0f};
float m_walk_time{0.0f};
std::unordered_map<std::string, Timer> m_timers;
mutable std::shared_mutex m_player_pos_mutex;
mutable std::shared_mutex m_chunk_pos_mutex;
ChunkPosSet m_player_chunk_pos_set;
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;
};
} // namespace Cubed

View File

@@ -1,10 +1,14 @@
#pragma once
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/client_chunk.hpp"
#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"
#include <absl/container/flat_hash_set.h>
@@ -26,6 +30,7 @@ struct PlayerInfo {
Gait gait;
float angle = 0.0f;
float walk_time = 0.0f;
float moving_time = 0.0f;
};
struct PlayerRenderData {
@@ -37,14 +42,15 @@ struct PlayerRenderData {
Gait gait;
float angle;
};
class WorldScene;
class ClientWorld {
public:
ClientWorld();
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;
@@ -55,8 +61,8 @@ public:
void rebuild_world();
void push_delete_vbo(GLuint vbo);
void push_delete_vao(GLuint vao);
void push_delete_vbo(std::unique_ptr<VertexBuffer>& vbo);
void push_delete_vao(std::unique_ptr<VertexArray>& vao);
// void hot_reload();
// void rebuild_world();
@@ -66,6 +72,8 @@ public:
void receive_remote_player(const PlayerInfoRsp& rsp);
void receive_player_logout(const LogoutRsp& rsp);
void receive_player_water_sound(const PlayerWaterSound& rsp);
void send_player_water_sound(bool underwater, const glm::vec3& pos);
int rendering_distance() const;
void rendering_distance(int rendering_distance);
int get_chunk_task_id() const;
@@ -89,15 +97,23 @@ public:
bool is_receive_exit();
int chunk_size() const;
static AABB get_block_aabb(const glm::ivec3& pos);
AudioEngine& get_audio();
Config& get_config();
WorldScene& world_scene();
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct,
std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
m_ticktimers.emplace(
std::piecewise_construct, std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
}
private:
std::atomic<bool> m_is_pending_delete_queue_free{false};
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
std::vector<std::unique_ptr<VertexBuffer>> m_pending_delete_vbo;
std::vector<std::unique_ptr<VertexArray>> m_pending_delete_vao;
enum class ChunkLoadStyle { RANDOM, CENTER };
using ChunkHashMap =
tbb::concurrent_hash_map<ChunkPos, std::shared_ptr<ClientChunk>,
@@ -107,28 +123,36 @@ private:
using OtherPlayerHashMap = std::unordered_map<std::string, PlayerInfo>;
using chunk_acc = ChunkHashMap::accessor;
using chunk_cacc = ChunkHashMap::const_accessor;
struct PendingSound {
std::string sound;
glm::vec3 sound_pos;
};
static constexpr int WORLD_EXIT_TIMEOUT = 200;
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
ClientPlayer m_player;
OtherPlayerHashMap m_player_info;
ChunkHashMap m_chunks;
AudioEngine& m_audio;
Config& m_config;
WorldScene& m_world_scene;
std::vector<glm::vec4> m_planes;
std::jthread m_client_thread;
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
mutable std::shared_mutex m_player_info_mutex;
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
std::vector<GLuint> m_pending_delete_vbo;
std::vector<GLuint> m_pending_delete_vao;
tbb::concurrent_queue<PendingSound> m_pending_sound;
std::deque<ChunkPos> m_dirty_queue;
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
std::vector<PlayerRenderData> m_render_player_data;
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
std::unordered_map<std::string, Timer> m_timers;
std::atomic<bool> m_game_running{false};
std::atomic<bool> m_receive_exit{false};
std::atomic<int> m_rendering_distance{24};
@@ -142,6 +166,8 @@ private:
std::atomic<std::shared_ptr<PriorityThreadPool>> m_thread_pool;
Random m_random;
void client_run(std::stop_token token);
void report_player_info();

View File

@@ -6,7 +6,9 @@
#include <functional>
#include <utility>
namespace Cubed {
using TickType = long long;
using TimeType = float;
constexpr int DEFAULT_PER_TICK_TIME = 50;
@@ -14,10 +16,10 @@ constexpr TickType DAY_TIME = 24000;
constexpr TickType PER_HOUR = 1000;
class Timer {
class TickTimer {
public:
template <typename Fn>
Timer(TickType threshold, Fn&& f)
TickTimer(TickType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
}
@@ -37,4 +39,37 @@ private:
TickType m_current = 0;
};
class Timer {
public:
template <typename Fn>
Timer(TimeType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
}
bool update(TimeType dt) {
m_current += dt;
bool triggered = false;
while (m_current >= m_threshold) {
m_current -= m_threshold;
m_fn();
triggered = true;
}
return triggered;
}
void reset() { m_current = 0.0f; }
void set_threshold(TimeType threshold) {
ASSERT_MSG(threshold > 0, "Threshold must be greater than 0");
m_threshold = threshold;
}
private:
std::function<void()> m_fn;
TimeType m_threshold;
TimeType m_current = 0;
};
} // namespace Cubed

View File

@@ -1,4 +1,5 @@
#pragma once
#include "Cubed/config.hpp"
#include "Cubed/gameplay/server_world.hpp"
#include "Cubed/gameplay/session.hpp"
@@ -8,17 +9,19 @@ namespace Cubed {
class NetworkServer {
public:
NetworkServer(int port = 25530);
explicit NetworkServer();
~NetworkServer();
void stop();
// Run in another thread after initialization is complete
void start_server(int port = 25530);
void start_server(int port);
void start_server();
int port() const;
ServerWorld& server_world();
private:
Config m_config;
asio::io_context m_io;
std::thread m_net_thread;
int m_port = 25530;

View File

@@ -54,6 +54,7 @@ enum class PacketEnum : uint16_t {
PLAYER_INFO = 2001,
C2S_PLAYER_INFO = 2002,
PLAYER_INFO_RSP = 2003,
PLAYER_WATER_SOUND = 2004,
CHUNK_DATA_REQ = 3001,
CHUNK_DATA_RSP = 3002,
BLOCK_CHANGE_REQ = 3003,
@@ -117,6 +118,9 @@ template <> constexpr uint16_t get_packet_id<Ping>() {
template <> constexpr uint16_t get_packet_id<Pong>() {
return std::to_underlying(PacketEnum::PONG);
}
template <> constexpr uint16_t get_packet_id<PlayerWaterSound>() {
return std::to_underlying(PacketEnum::PLAYER_WATER_SOUND);
}
template <typename T>
requires std::derived_from<T, google::protobuf::Message>

View File

@@ -1,5 +1,6 @@
#pragma once
#include "Cubed/config.hpp"
#include "Cubed/gameplay/cave_carver.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_time.hpp"
@@ -25,7 +26,7 @@ class Session;
class ServerWorld {
public:
enum class ThreadPoolKind { NET, GEN };
ServerWorld();
ServerWorld(Config& config);
~ServerWorld();
void stop();
void handle_player_exit(const std::string& uuid);
@@ -71,6 +72,7 @@ public:
bool set_block(const glm::ivec3& block_pos, unsigned id);
void sync_player_pos(const C2S_PlayerInfo& rsp);
void sync_player_water_sound(const PlayerWaterSound& rsp);
void handle_player_login(const std::string& player_name,
std::shared_ptr<Session> session);
glm::vec3 get_player_pos(const std::string& uuid) const;
@@ -117,6 +119,9 @@ private:
using uuid_acc = PlayerUUIDMap::accessor;
using uuid_cacc = PlayerUUIDMap::const_accessor;
Config& m_config;
// key = uuid
PlayerHashMap m_players;
ChunkHashMap m_chunks;
@@ -156,7 +161,7 @@ private:
PlayerUUIDMap m_uuid_to_name;
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
tbb::concurrent_unordered_map<std::string, TickTimer> m_timers;
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue;

View File

@@ -1,15 +1,18 @@
#pragma once
#include "Cubed/primitive_data.hpp"
#include "Cubed/render/vertex_array.hpp"
#include "Cubed/render/vertex_buffer.hpp"
#include <atomic>
#include <glad/glad.h>
#include <memory>
#include <vector>
namespace Cubed {
class ClientWorld;
struct VertexData {
std::vector<Vertex3D> m_vertices;
GLuint m_vbo = 0;
GLuint m_vao = 0;
std::unique_ptr<VertexBuffer> m_vbo;
std::unique_ptr<VertexArray> m_vao;
std::atomic<std::size_t> m_sum{0};
ClientWorld& m_world;
VertexData(ClientWorld& world);

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

@@ -1,26 +0,0 @@
#pragma once
// #include <string>
// #include <unordered_map>
// #include <vector>
namespace Cubed {
class MapTable {
private:
/*
static inline std::unordered_map<unsigned, std::string> id_to_name_map;
static inline std::unordered_map<size_t, unsigned> name_to_id_map;
static inline std::vector<std::string> item_id_to_name;
*/
public:
// please using reference
/*
static std::string_view get_name_from_id(unsigned id);
static unsigned get_id_from_name(const std::string& name);
static std::string_view item_name(unsigned id);
static const std::vector<std::string>& item_map();
*/
static void init_map();
};
} // namespace Cubed

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

@@ -0,0 +1,46 @@
#pragma once
#include "Cubed/render/texture.hpp"
#include <glad/glad.h>
#include <span>
enum class Attachment : GLenum {
COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0,
COLOR_ATTACHMENT1 = GL_COLOR_ATTACHMENT1,
DEPTH_ATTACHMENT = GL_DEPTH_ATTACHMENT
};
enum class FrameBufferType : GLenum {
FRAMEBUFFER = GL_FRAMEBUFFER,
READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER,
DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER
};
namespace Cubed {
class FrameBuffer {
public:
FrameBuffer();
~FrameBuffer();
FrameBuffer(const FrameBuffer&) = delete;
FrameBuffer(FrameBuffer&&) noexcept;
FrameBuffer& operator=(const FrameBuffer&) = delete;
FrameBuffer& operator=(FrameBuffer&&) noexcept;
void bind(FrameBufferType type = FrameBufferType::FRAMEBUFFER) const;
GLuint id() const;
void attach(Attachment attachment, const Texture& texture,
GLint level = 0) const;
static void unbind();
bool check_status() const;
void draw_buffer(GLenum buf) const;
void read_buffer(GLenum src) const;
void draw_buffer(GLsizei n, const GLenum* bufs) const;
void draw_buffer(std::span<const GLenum> bufs) const;
private:
GLuint m_fbo = 0;
};
} // namespace Cubed

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

@@ -0,0 +1,130 @@
#pragma once
#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"
#include "Cubed/render/vertex_array.hpp"
#include "Cubed/render/vertex_buffer.hpp"
#include "Cubed/render/world_renderer.hpp"
#include "Cubed/shader.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 TextureManager;
class ClientWorld;
class DevPanel;
class Renderer {
public:
constexpr static int NUM_VAO = 7;
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 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);
float& ambient_strength();
bool& discard_transparent();
bool& shader_on();
bool& water_perturb();
bool& water_depth_fade();
bool& pbr();
bool& flip_y();
int& shadow_mode();
int& light_cull_face();
int& light_size_uv();
float& min_radius();
float& max_radius();
int& samples();
float& specular_strength();
float& cloud_speed();
float& cloud_threshold_low();
float& cloud_threshold_high();
float& refract_strength();
float& underwater_fog_density();
float& water_density();
const glm::mat4& world_proj_matrix() const;
const TextureManager& texture_mamger() const;
float delta_time() 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:
TextureManager& m_texture_manager;
bool m_init = false;
float m_aspect = 0.0f;
float m_fov = DEFAULT_FOV;
float m_delta_time = 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;
std::unique_ptr<VertexBuffer> m_sky_vbo;
std::unique_ptr<VertexBuffer> m_outline_indices_vbo;
std::unique_ptr<VertexBuffer> m_outline_vbo;
std::unique_ptr<VertexBuffer> m_ui_vbo;
std::unique_ptr<VertexBuffer> m_player_vbo;
std::unique_ptr<VertexBuffer> m_quad_vbo;
glm::mat4 m_ui_proj_matrix;
ShaderManager m_shaders;
/*
0 - quad vao (center)
1 - sky vao
2 - outline vao
3 - ui vao (top-left)
4 - text vao
*/
std::vector<VertexArray> m_vao;
std::vector<Vertex2D> m_ui;
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();
};
} // namespace Cubed

View File

@@ -0,0 +1,21 @@
#pragma once
#include <glm/glm.hpp>
namespace Cubed {
constexpr glm::vec3 SUN_COLOR{1.00f, 0.95f, 0.80f};
constexpr glm::vec3 MOON_COLOR{0.75f, 0.80f, 1.00f};
constexpr glm::vec3 SUNSET_SUNLIGHT_COLOR{1.00f, 0.45f, 0.15f};
constexpr glm::vec3 NOON_SUNLIGHT_COLOR{1.00f, 0.90f, 0.65f};
constexpr glm::vec3 SUNSET_AMBIENT_COLOR{0.18f, 0.12f, 0.35f};
constexpr glm::vec3 NOON_AMBIENT_COLOR{0.35f, 0.50f, 0.85f};
constexpr glm::vec3 MOONLIGHT_COLOR{0.55f, 0.70f, 1.00f};
constexpr glm::vec3 NIGHT_AMBIENT_COLOR{0.08f, 0.10f, 0.18f};
constexpr float FAR_PLANE = 1000.0f;
constexpr float NEAR_PLANE = 0.1f;
constexpr float SUN_SIZE = 50.0f;
constexpr float MOON_SIZE = 50.0f;
constexpr float DEPTH_MAP_SIZE = 4096.0f;
constexpr float ANGLE_STEP_DEG = 0.5f;
} // namespace Cubed

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Cubed/shader.hpp"
#include <string>
#include <unordered_map>
namespace Cubed {
class ShaderManager {
public:
ShaderManager();
~ShaderManager();
ShaderManager(const ShaderManager&) = delete;
ShaderManager(ShaderManager&&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete;
ShaderManager& operator=(ShaderManager&&) = delete;
void init();
const Shader& get_shader(const std::string& name) const;
private:
std::unordered_map<std::string, Shader> m_shaders;
void register_shader(const std::string& name, const std::string& v_shader,
const std::string& f_shader);
};
} // namespace Cubed

View File

@@ -0,0 +1,98 @@
#pragma once
#include <cstddef>
#include <glad/glad.h>
namespace Cubed {
enum TextureType : GLenum {
TEXTURE_2D = GL_TEXTURE_2D,
TEXTURE_2D_ARRAY = GL_TEXTURE_2D_ARRAY
};
enum TexturePname : GLenum {
MIN_FILTER = GL_TEXTURE_MIN_FILTER,
MAG_FILTER = GL_TEXTURE_MAG_FILTER,
WRAP_S = GL_TEXTURE_WRAP_S,
WRAP_T = GL_TEXTURE_WRAP_T,
WRAP_R = GL_TEXTURE_WRAP_R,
BORDER_COLOR = GL_TEXTURE_BORDER_COLOR,
COMPARE_MODE = GL_TEXTURE_COMPARE_MODE
};
enum TextureParam : GLenum {
LINEAR = GL_LINEAR,
NEAREST = GL_NEAREST,
LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR,
CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER,
CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
T_NONE = GL_NONE,
REPEAT = GL_REPEAT
};
enum TextureFormat : GLenum {
DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F,
DEPTH_COMPONENT = GL_DEPTH_COMPONENT,
R16F = GL_R16F,
RGBA16F = GL_RGBA16F,
RED = GL_RED,
RGBA = GL_RGBA,
R8 = GL_R8,
RGB = GL_RGB,
RGBA8 = GL_RGBA8,
};
class Texture {
public:
explicit Texture(TextureType type);
~Texture();
Texture(const Texture&) = delete;
Texture(Texture&&) noexcept;
Texture& operator=(const Texture&) = delete;
Texture& operator=(Texture&&) noexcept;
void bind() const;
void bind(size_t unit) const;
static void unbind();
static void active(size_t id);
GLuint id() const;
void parameter(TexturePname pname, TextureParam param) const;
void parameterfv(TexturePname pname, const float* param) const;
void tex_image_2d(TextureFormat internalformat, TextureFormat format,
GLenum type, const void* data, GLsizei width,
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);
void tex_sub_image_3d(TextureFormat format, GLenum type, const void* data,
GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLsizei depth = 1,
GLint level = 0) const;
void set_aniso(int aniso) const;
void gen_mipmap() const;
void set_linear() const;
void set_nearest_and_minpmap() const;
void set_nearest() const;
void set_repeat(bool r = true, bool s = true, bool t = true) const;
void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const;
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

@@ -0,0 +1,26 @@
#pragma once
#include <glad/glad.h>
namespace Cubed {
class VertexArray {
public:
VertexArray();
VertexArray(const VertexArray&) = delete;
VertexArray(VertexArray&&) noexcept;
VertexArray& operator=(const VertexArray&) = delete;
VertexArray& operator=(VertexArray&&) noexcept;
~VertexArray();
void bind() const;
static void unbind();
GLuint id() const;
void attribute(GLuint index, GLint size, GLenum type, GLsizei stride,
const void* ptr, bool normalized = false) const;
private:
GLuint m_vao = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,36 @@
#pragma once
#include <glad/glad.h>
namespace Cubed {
enum class BufferType : GLenum {
ARRAY_BUFFER = GL_ARRAY_BUFFER,
ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER
};
enum class BufferUsage : GLenum {
STATIC_DRAW = GL_STATIC_DRAW,
DYNAMIC_DRAW = GL_DYNAMIC_DRAW
};
class VertexBuffer {
public:
VertexBuffer(BufferType type = BufferType::ARRAY_BUFFER);
VertexBuffer(const VertexBuffer&) = delete;
VertexBuffer(VertexBuffer&&) noexcept;
VertexBuffer& operator=(const VertexBuffer&) = delete;
VertexBuffer& operator=(VertexBuffer&&) noexcept;
~VertexBuffer();
void bind() const;
static void unbind();
GLuint id() const;
void buffer_data(const void* data, GLsizeiptr size,
BufferUsage usage = BufferUsage::STATIC_DRAW) const;
private:
GLuint m_vbo = 0;
BufferType m_type = BufferType::ARRAY_BUFFER;
GLenum get_buffer_target() const;
};
} // namespace Cubed

View File

@@ -0,0 +1,150 @@
#pragma once
#include "Cubed/render/frame_buffer.hpp"
#include "Cubed/render/player_renderer.hpp"
#include "Cubed/render/texture.hpp"
#include <glm/glm.hpp>
#include <memory>
namespace Cubed {
class Renderer;
class ClientWorld;
class TextureManager;
class Camera;
class WorldRenderer {
public:
struct ParallelLight {
glm::vec3 sundir; // direction from sun to vertex
glm::vec3 lightdir;
float sun_height = 0.0f;
float day_light = 0.0f;
float day_factor = 0.0f;
glm::vec3 sun_color;
glm::vec3 directional_light_color;
glm::vec3 finnal_ambient_color;
glm::mat4 light_space_matrix;
};
struct SkyUniform {
glm::vec3 sky_top;
glm::vec3 sky_bottom;
glm::vec3 sun_dir_view;
float horizon_sharpness;
float cloud_white_mix;
};
WorldRenderer(Renderer& renderer);
~WorldRenderer();
WorldRenderer(const WorldRenderer&) = delete;
WorldRenderer(WorldRenderer&&) = delete;
WorldRenderer& operator=(const WorldRenderer&) = delete;
WorldRenderer& operator=(WorldRenderer&&) = delete;
void init();
void render(ClientWorld& world);
void updata_framebuffer(int width, int height);
float& ambient_strength();
bool& discard_transparent();
bool& shader_on();
bool& water_perturb();
bool& water_depth_fade();
bool& pbr();
bool& flip_y();
int& shadow_mode();
int& light_cull_face();
int& light_size_uv();
float& min_radius();
float& max_radius();
int& samples();
float& specular_strength();
float& cloud_speed();
float& cloud_threshold_low();
float& cloud_threshold_high();
float& refract_strength();
float& underwater_fog_density();
float& water_density();
const FrameBuffer* world_fbo() const;
private:
Renderer& m_renderer;
PlayerRenderer m_player_renderer;
std::unique_ptr<Texture> m_accum_texture;
std::unique_ptr<Texture> m_reveal_texture;
std::unique_ptr<FrameBuffer> m_world_fbo;
std::unique_ptr<Texture> m_screen_texture;
std::unique_ptr<Texture> m_screen_depth_texture;
std::unique_ptr<FrameBuffer> m_oit_fbo;
std::unique_ptr<Texture> m_oit_depth_texture;
std::unique_ptr<FrameBuffer> m_depth_map_fbo;
std::unique_ptr<Texture> m_depth_map_texture;
glm::vec3 m_blend_from_lightdir;
glm::vec3 m_blend_to_lightdir;
float m_blend_t = 1.0f;
bool m_blend_initialized = false;
static constexpr float BLEND_DURATION = 0.15f;
float m_underwater_fog_density = 0.08f;
float m_water_density = 0.12f;
float m_ambient_strength = 0.1f;
bool m_discard_tranparent = true;
bool m_shader_on = true;
bool m_water_perturb = true;
bool m_water_depth_fade = true;
bool m_pbr = true;
bool m_flip_y = false;
int m_shadow_mode = 0;
int m_light_size_uv = 20;
float m_min_radius = 2.0f;
float m_max_radius = 20.0f;
int m_samples = 16;
float m_specular_strength = 0.5f;
float m_cloud_threshold_low = 0.5f;
float m_cloud_threshold_high = 0.75f;
float m_cloud_time = 0.0f;
float m_cloud_speed = 5.0f;
float m_refract_strength = 0.03f;
int m_light_cull_face = 0;
float moon_intensity = 0.3f;
float sun_intensity = 1.00f;
ParallelLight m_parallel_light;
SkyUniform m_sky_uniform;
glm::mat4 view_matrix;
const TextureManager& m_texture_manager;
void day_night_calculation(ClientWorld& world);
void render_sky(ClientWorld& world);
void render_world(ClientWorld& world);
void shadow_map_generate(ClientWorld& world);
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,
ClientWorld& world);
void render_transparent_block(const glm::mat4& mv_mat,
const glm::mat4& norm_mat,
ClientWorld& world);
glm::vec3 quantize_sun_direction(const glm::vec3& sundir,
float angle_step_deg) const;
glm::vec3 get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_sundir,
float dt);
};
} // namespace Cubed

View File

@@ -1,210 +0,0 @@
#pragma once
#include "Cubed/constants.hpp"
#include "Cubed/player_renderer.hpp"
#include "Cubed/primitive_data.hpp"
#include "Cubed/shader.hpp"
#include "Cubed/ui/text.hpp"
#include <glm/glm.hpp>
#include <vector>
namespace Cubed {
class Camera;
class TextureManager;
class ClientWorld;
class DevPanel;
class Renderer {
public:
constexpr static int NUM_VAO = 7;
Renderer(const Camera& camera, ClientWorld& world,
const TextureManager& texture_manager, DevPanel& dev_panel);
~Renderer();
void hot_reload();
void init();
const Shader& get_shader(const std::string& name) const;
void render();
void update(float delta_time);
void update_fov(float fov);
void update_proj_matrix(float aspect, float width, float height);
void updata_framebuffer(int width, int height);
float& ambient_strength();
bool& discard_transparent();
bool& shader_on();
bool& water_perturb();
bool& water_depth_fade();
bool& pbr();
bool& flip_y();
int& shadow_mode();
int& light_cull_face();
int& light_size_uv();
float& min_radius();
float& max_radius();
int& samples();
float& specular_strength();
float& cloud_speed();
float& cloud_threshold_low();
float& cloud_threshold_high();
float& refract_strength();
float& underwater_fog_density();
float& water_density();
const Camera& camera() const;
const ClientWorld& world() const;
ClientWorld& world();
const glm::mat4& proj_mat() const;
const TextureManager& texture_mamger() const;
float delta_time() const;
private:
struct ParallelLight {
glm::vec3 sundir; // direction from sun to vertex
glm::vec3 lightdir;
float sun_height = 0.0f;
float day_light = 0.0f;
float day_factor = 0.0f;
glm::vec3 sun_color;
glm::vec3 directional_light_color;
glm::vec3 finnal_ambient_color;
glm::mat4 light_space_matrix;
};
struct SkyUniform {
glm::vec3 sky_top;
glm::vec3 sky_bottom;
glm::vec3 sun_dir_view;
float horizon_sharpness;
float cloud_white_mix;
};
static constexpr glm::vec3 SUN_COLOR{1.00f, 0.95f, 0.80f};
static constexpr glm::vec3 MOON_COLOR{0.75f, 0.80f, 1.00f};
static constexpr glm::vec3 SUNSET_SUNLIGHT_COLOR{1.00f, 0.45f, 0.15f};
static constexpr glm::vec3 NOON_SUNLIGHT_COLOR{1.00f, 0.90f, 0.65f};
static constexpr glm::vec3 SUNSET_AMBIENT_COLOR{0.18f, 0.12f, 0.35f};
static constexpr glm::vec3 NOON_AMBIENT_COLOR{0.35f, 0.50f, 0.85f};
static constexpr glm::vec3 MOONLIGHT_COLOR{0.55f, 0.70f, 1.00f};
static constexpr glm::vec3 NIGHT_AMBIENT_COLOR{0.08f, 0.10f, 0.18f};
static constexpr float FAR_PLANE = 1000.0f;
static constexpr float NEAR_PLANE = 0.1f;
static constexpr float SUN_SIZE = 50.0f;
static constexpr float MOON_SIZE = 50.0f;
static constexpr float DEPTH_MAP_SIZE = 4096.0f;
static constexpr float ANGLE_STEP_DEG = 0.5f;
float m_ambient_strength = 0.1f;
const Camera& m_camera;
DevPanel& m_dev_panel;
const TextureManager& m_texture_manager;
ClientWorld& m_world;
PlayerRenderer m_player_renderer;
bool m_discard_tranparent = true;
bool m_shader_on = true;
bool m_water_perturb = true;
bool m_water_depth_fade = true;
bool m_pbr = true;
bool m_flip_y = false;
bool m_init = false;
int m_shadow_mode = 0;
int m_light_cull_face = 0;
float m_aspect = 0.0f;
float m_fov = DEFAULT_FOV;
float m_delta_time = 0.0f;
float m_cloud_time = 0.0f;
float m_cloud_speed = 5.0f;
float m_width = 0.0f;
float m_height = 0.0f;
glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat, m_norm_mat;
GLuint m_sky_vbo = 0;
GLuint m_text_vbo = 0;
GLuint m_outline_indices_vbo = 0;
GLuint m_outline_vbo = 0;
GLuint m_ui_vbo = 0;
GLuint m_player_vbo = 0;
GLuint m_fbo = 0;
GLuint m_screen_texture = 0;
GLuint m_screen_depth_texture = 0;
GLuint m_oit_fbo = 0;
GLuint m_accum_texture = 0;
GLuint m_reveal_texture = 0;
GLuint m_oit_depth_texture = 0;
GLuint m_depth_map_fbo = 0;
GLuint m_depth_map_texture = 0;
GLuint m_quad_vbo = 0;
glm::mat4 m_ui_proj;
glm::mat4 m_ui_m_matrix;
std::unordered_map<std::size_t, Shader> m_shaders;
glm::vec3 m_blend_from_lightdir;
glm::vec3 m_blend_to_lightdir;
float m_blend_t = 1.0f;
bool m_blend_initialized = false;
static constexpr float BLEND_DURATION = 0.15f;
int m_light_size_uv = 20;
float m_min_radius = 2.0f;
float m_max_radius = 20.0f;
int m_samples = 16;
float m_specular_strength = 0.5f;
float moon_intensity = 0.3f;
float sun_intensity = 1.00f;
float m_cloud_threshold_low = 0.5f;
float m_cloud_threshold_high = 0.75f;
float m_refract_strength = 0.03f;
float m_underwater_fog_density = 0.08f;
float m_water_density = 0.12f;
ParallelLight m_parallel_light;
SkyUniform m_sky_uniform;
/*
0 - quad vao
1 - sky vao
2 - outline vao
3 - ui vao
4 - text vao
*/
std::vector<GLuint> m_vao;
std::vector<Vertex2D> m_ui;
void init_quad();
void init_text();
void day_night_calculation();
void render_outline();
void render_sky();
void render_text();
void render_ui();
void render_world();
void render_player();
void render_underwater();
void render_dev_panel();
glm::vec3 quantize_sun_direction(const glm::vec3& sundir,
float angle_step_deg) const;
glm::vec3 get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_sundir,
float dt);
};
} // namespace Cubed

View File

@@ -0,0 +1,20 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/credits_ui.hpp"
namespace Cubed {
class SceneManager;
class CreditsScene : public Scene {
public:
CreditsScene(SceneManager& scene_manager);
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
CreditsUI m_ui;
};
} // namespace Cubed

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,39 @@
#pragma once
#include "Cubed/input/event.hpp"
namespace Cubed {
class Renderer;
enum class SceneType { MAIN_MENU, WORLD, CREDITS };
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() {};
protected:
virtual bool handle_mouse_move_event(const MouseMoveEvent&) {
return false;
}
virtual bool handle_mouse_button_event(const MouseButtonEvent&) {
return false;
}
virtual bool handle_window_resize_event(const WindowResizeEvent&) {
return false;
}
virtual bool handle_mouse_wheel_event(const MouseWheelEvent&) {
return false;
}
virtual bool handle_key_event(const KeyEvent&) { return false; }
};
} // 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,55 @@
#pragma once
#include "Cubed/argument.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/gameplay/network_server.hpp"
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/pasue_menu_ui_manager.hpp"
#include "Cubed/ui/world_ui_manager.hpp"
namespace Cubed {
class SceneManager;
class WorldScene : public Scene {
public:
WorldScene(const WorldScene&) = delete;
WorldScene(WorldScene&&) = delete;
WorldScene& operator=(const WorldScene&) = delete;
WorldScene& operator=(WorldScene&&) = delete;
WorldScene(SceneManager& scene_manager);
~WorldScene();
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
Camera& camera();
SceneManager& scene_manager();
ClientWorld& client_world();
ServerWorld& server_world();
bool pause() const;
void set_pause(bool pause);
private:
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
bool handle_window_resize_event(const WindowResizeEvent& e) override;
bool handle_mouse_wheel_event(const MouseWheelEvent& e) override;
bool handle_key_event(const KeyEvent& e) override;
SceneManager& m_scene_manager;
DevPanel m_dev_panel;
Camera m_camera;
NetworkServer m_server;
std::shared_ptr<NetworkClient> m_client;
ClientWorld m_client_world;
bool m_paused = false;
bool m_show_hud = true;
bool m_show_dev_pannel = true;
PauseMenuUIManager m_pasue_menu;
WorldUIManager m_hud_ui;
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,7 +1,11 @@
#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>
#include <memory>
namespace Cubed {
@@ -9,24 +13,22 @@ class TextureManager {
private:
bool m_need_reload = false;
bool m_init = false;
GLuint m_block_status_array = 0;
GLuint m_texture_array = 0;
GLuint m_cross_plane_array = 0;
GLuint m_ui_array = 0;
GLuint m_normal_texture_array = 0;
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_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;
GLuint m_skin = 0;
Config& m_config;
int m_aniso = 1;
std::vector<GLuint> m_item_textures;
void load_block_status(unsigned status_id);
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();
@@ -34,25 +36,27 @@ private:
void init_block_status();
void init_skin();
void hot_reload();
bool handle_key_event(const KeyEvent& e);
public:
TextureManager();
TextureManager(Config& config);
~TextureManager();
void delet_texture();
GLuint get_block_status_array() const;
GLuint get_texture_array() const;
GLuint get_cross_plane_array() const;
GLuint get_ui_array() const;
GLuint get_pbr_texture() const;
const std::vector<GLuint>& item_textures() const;
GLuint get_skin() const;
void delete_texture();
const Texture* get_block_status_array() const;
const Texture* get_texture_array() const;
const Texture* get_cross_plane_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;
// Must call after MapTable::init_map() and glfwMakeContextCurrent(window);
void init_texture();
void need_reload();
void update();
int max_aniso() const;
bool handle_event(const Event& e);
};
} // namespace Cubed

View File

@@ -1,5 +1,8 @@
#pragma once
#include "Cubed/render/texture.hpp"
#include <ft2build.h>
#include <memory>
#include FT_FREETYPE_H
#include "Cubed/primitive_data.hpp"
@@ -19,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 {
@@ -26,10 +39,8 @@ 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 GLuint text_texture();
static TextMesh vertices(const std::string& text);
static const Texture* text_texture();
static const std::string& font_path();
private:
@@ -39,7 +50,7 @@ private:
float m_texture_width = 64;
float m_texture_height = 64;
static inline GLuint m_text_texture;
static inline std::unique_ptr<Texture> m_text_texture;
static inline std::string m_font_path{ASSETS_PATH
"fonts/IBMPlexSans-Regular.ttf"};
std::unordered_map<char8_t, Character> m_characters;

View File

@@ -104,6 +104,8 @@ inline float distance2(const glm::vec3& a, const glm::vec3& b) {
return glm::dot(diff, diff);
}
inline float lerp(float a, float b, float t) { return a + t * (b - a); }
} // namespace Math
} // namespace Cubed

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

@@ -14,6 +14,7 @@ concept TomlValueType =
std::same_as<std::decay_t<T>, toml::date> ||
std::same_as<std::decay_t<T>, toml::time> ||
std::same_as<std::decay_t<T>, toml::date_time> ||
std::same_as<std::decay_t<T>, float> ||
std::same_as<std::decay_t<T>, std::string>;
template <TomlValueType T>

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;
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;
void on_render(Renderer& renderer) override;
void on_update(float dt) override;
};
} // 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;
ColumnLayout& 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

Some files were not shown because too many files have changed in this diff Show More