Compare commits
29 Commits
v0.1.3
...
d6f304e6ca
| Author | SHA1 | Date | |
|---|---|---|---|
| d6f304e6ca | |||
| 826c6600d0 | |||
| b166bab4f3 | |||
| efa49a98b7 | |||
| 3a9ab6a14a | |||
| d15037e7a3 | |||
| 0ff5820e6e | |||
| 5e96c1573b | |||
| 90273e87f2 | |||
| 1e106d75a3 | |||
| bd1a9557a5 | |||
| 831569cec6 | |||
| a5c1c7d4aa | |||
| 802d2ecb5a | |||
| ab605f7590 | |||
| 15d5af34b9 | |||
| 6d0e60e241 | |||
| 7b530968af | |||
| 930226cc47 | |||
| f9bf2f4b89 | |||
| df3ac5b5db | |||
| 2568eb7d18 | |||
| 2e3b4f4f0f | |||
| 5951a5e81c | |||
| 849386a3bc | |||
| 3e7ce71219 | |||
| 94fed9a810 | |||
| f4174724c6 | |||
|
|
def311c7dd |
@@ -39,6 +39,8 @@ add_subdirectory(third_party/imgui)
|
||||
|
||||
add_subdirectory(third_party/dr_libs)
|
||||
|
||||
add_subdirectory(third_party/stb)
|
||||
|
||||
add_executable(${PROJECT_NAME})
|
||||
|
||||
add_subdirectory(src)
|
||||
@@ -130,14 +132,12 @@ target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
SDL3::SDL3
|
||||
glad
|
||||
glm::glm
|
||||
OpenGL::GL
|
||||
soil2
|
||||
Freetype::Freetype
|
||||
tomlplusplus::tomlplusplus
|
||||
imgui
|
||||
dr_libs
|
||||
tbb
|
||||
stb
|
||||
protobuf::libprotobuf
|
||||
absl::log
|
||||
absl::check
|
||||
@@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME}
|
||||
OpenAL::OpenAL
|
||||
harfbuzz::harfbuzz
|
||||
Opus::Opus
|
||||
assimp::assimp
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
|
||||
)
|
||||
|
||||
@@ -8,4 +8,5 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'air'
|
||||
name_key = 'block.air'
|
||||
roughness = 1.0
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'dirt'
|
||||
name_key = 'block.dirt'
|
||||
roughness = 1.0
|
||||
@@ -8,4 +8,5 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'grass'
|
||||
roughness = 0.9
|
||||
name_key = 'block.grass'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'grass_block'
|
||||
roughness = 0.9
|
||||
name_key = 'block.grass_block'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'leaf'
|
||||
roughness = 0.7
|
||||
name_key = 'block.leaf'
|
||||
roughness = 0.69999999999999996
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = false
|
||||
name = 'log'
|
||||
roughness = 0.7
|
||||
name_key = 'block.log'
|
||||
roughness = 0.69999999999999996
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'sand'
|
||||
roughness = 0.8
|
||||
name_key = 'block.sand'
|
||||
roughness = 0.80000000000000004
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'snowy_grass_block'
|
||||
roughness = 0.9
|
||||
name_key = 'block.snowy_grass_block'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,4 +8,5 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'stone'
|
||||
name_key = 'block.stone'
|
||||
roughness = 0.75
|
||||
@@ -9,3 +9,4 @@ is_discard = false
|
||||
is_blend = false
|
||||
is_transitional = false
|
||||
roughness = 1.0
|
||||
name_key = "block.template"
|
||||
|
||||
@@ -8,4 +8,5 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'water'
|
||||
name_key = 'block.water'
|
||||
roughness = 0.02
|
||||
@@ -35,5 +35,15 @@
|
||||
"joingame.join_a_world": "Join A World",
|
||||
"joingame.server_ip": "Server Ip",
|
||||
"joingame.join_world": "Join World",
|
||||
"error.disable_voice": "Server Disable Voice Chat"
|
||||
"error.disable_voice": "Server Disable Voice Chat",
|
||||
"block.air": "air",
|
||||
"block.dirt": "dirt",
|
||||
"block.grass_block": "grass block",
|
||||
"block.grass": "grass",
|
||||
"block.leaf": "leaf",
|
||||
"block.log": "log",
|
||||
"block.sand": "sand",
|
||||
"block.snowy_grass_block": "snowy grass block",
|
||||
"block.stone": "stone",
|
||||
"block.water": "water"
|
||||
}
|
||||
@@ -35,5 +35,15 @@
|
||||
"joingame.join_a_world": "加入世界",
|
||||
"joingame.server_ip": "服务器IP",
|
||||
"joingame.join_world": "加入世界",
|
||||
"error.disable_voice": "服务器已禁用语音聊天"
|
||||
"error.disable_voice": "服务器已禁用语音聊天",
|
||||
"block.air": "空气",
|
||||
"block.dirt": "泥土",
|
||||
"block.grass_block": "草方块",
|
||||
"block.grass": "草",
|
||||
"block.leaf": "树叶",
|
||||
"block.log": "原木",
|
||||
"block.sand": "沙子",
|
||||
"block.snowy_grass_block": "覆雪草方块",
|
||||
"block.stone": "石头",
|
||||
"block.water": "水"
|
||||
}
|
||||
16
assets/model/creature/pig/collision.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"boxes": [
|
||||
{
|
||||
"center": [
|
||||
0,
|
||||
0.78,
|
||||
0
|
||||
],
|
||||
"half": [
|
||||
0.53,
|
||||
0.78,
|
||||
0.406
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/model/creature/pig/pig.glb
Normal file
BIN
assets/model/creature/player/player.glb
Normal file
@@ -7,7 +7,6 @@ uniform mat4 lightSpaceMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
|
||||
out vec2 tc;
|
||||
flat out int tex_layer;
|
||||
|
||||
void main() {
|
||||
tc = texCoord;
|
||||
@@ -3,7 +3,6 @@
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in vec3 aNormal;
|
||||
layout (location = 3) in vec3 aTangent;
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
|
Before Width: | Height: | Size: 99 B After Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 893 B |
|
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 890 B After Width: | Height: | Size: 249 KiB |
|
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 215 KiB |
BIN
assets/texture/ui/slot.png
Normal file
|
After Width: | Height: | Size: 122 B |
@@ -10,29 +10,8 @@ find_package(harfbuzz REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL3 REQUIRED)
|
||||
find_package(Opus REQUIRED)
|
||||
|
||||
find_package(assimp REQUIRED)
|
||||
# Third-party libraries
|
||||
FetchContent_Declare(
|
||||
glm
|
||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||
GIT_TAG 1.0.3
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(glm)
|
||||
FetchContent_Declare(
|
||||
soil2
|
||||
GIT_REPOSITORY https://github.com/SpartanJ/SOIL2.git
|
||||
GIT_TAG 1.31
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(soil2)
|
||||
FetchContent_Declare(
|
||||
tomlplusplus
|
||||
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
|
||||
GIT_TAG v3.4.0
|
||||
)
|
||||
FetchContent_MakeAvailable(tomlplusplus)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct AABB {
|
||||
glm::vec3 min{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 max{0.0f, 0.0f, 0.0f};
|
||||
|
||||
AABB(glm::vec3 min_point, glm::vec3 max_point)
|
||||
: min(min_point), max(max_point) {}
|
||||
|
||||
bool intersects(const AABB& other) const {
|
||||
return (min.x <= other.max.x && max.x >= other.min.x) &&
|
||||
(min.y <= other.max.y && max.y >= other.min.y) &&
|
||||
(min.z <= other.max.z && max.z >= other.min.z);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -49,7 +49,6 @@ private:
|
||||
Window m_window;
|
||||
|
||||
TextureManager m_texture_manager;
|
||||
|
||||
AudioEngine m_audio;
|
||||
|
||||
Renderer m_renderer;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
class ClientPlayer;
|
||||
class LocalPlayer;
|
||||
|
||||
class Camera {
|
||||
private:
|
||||
@@ -18,7 +18,7 @@ private:
|
||||
THIRD_PERSON_FRONT,
|
||||
};
|
||||
|
||||
ClientPlayer* m_player;
|
||||
LocalPlayer* m_player;
|
||||
float m_last_mouse_x, m_last_mouse_y;
|
||||
glm::vec3 m_camera_pos;
|
||||
bool m_under_water = false;
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
void update_move_camera();
|
||||
|
||||
void camera_init(ClientPlayer* player);
|
||||
void camera_init(LocalPlayer* player);
|
||||
void hot_reload();
|
||||
void update_cursor_position_camera(float offset_x, float offset_y);
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
void change_perspective();
|
||||
bool is_first_person() const;
|
||||
bool handle_event(const Event& e);
|
||||
ClientPlayer* player();
|
||||
LocalPlayer* player();
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace Cubed {
|
||||
|
||||
class WorldScene;
|
||||
class ClientPlayer;
|
||||
class LocalPlayer;
|
||||
class App;
|
||||
class DevPanel {
|
||||
struct ConfigView {
|
||||
@@ -42,7 +42,7 @@ private:
|
||||
WorldScene& m_world_scene;
|
||||
Config& m_config;
|
||||
ConfigView m_config_view;
|
||||
ClientPlayer* m_player;
|
||||
LocalPlayer* m_player;
|
||||
PlayerProfile m_player_profile;
|
||||
bool m_need_save_config = false;
|
||||
bool m_gen_thread_running = true;
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
void show_server_world_table_bar();
|
||||
void show_client_world_table_bar();
|
||||
void show_player_tab_item();
|
||||
void show_items_tab_item();
|
||||
// void show_items_tab_item();
|
||||
void show_shader_tab_item();
|
||||
|
||||
void update_config_view();
|
||||
|
||||
@@ -42,6 +42,7 @@ struct LookBlock {
|
||||
|
||||
struct BlockData {
|
||||
std::string name;
|
||||
std::string name_key;
|
||||
BlockType id = 0;
|
||||
|
||||
bool is_liquid = false;
|
||||
@@ -55,11 +56,11 @@ struct BlockData {
|
||||
bool is_blend = false;
|
||||
bool is_transitional = false;
|
||||
float roughness = 1.0f;
|
||||
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
|
||||
bool passable, bool cross_plane, bool transparent, bool gas,
|
||||
bool discard, bool blend, bool transitional, float r)
|
||||
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
|
||||
is_passable(passable), is_cross_plane(cross_plane),
|
||||
BlockData(std::string_view b_name, std::string_view name_k, BlockType b_id,
|
||||
bool liquid, bool passable, bool cross_plane, bool transparent,
|
||||
bool gas, bool discard, bool blend, bool transitional, float r)
|
||||
: name(b_name), name_key(name_k), id(b_id), is_liquid(liquid),
|
||||
is_gas(gas), is_passable(passable), is_cross_plane(cross_plane),
|
||||
is_transparent(transparent), is_discard(discard), is_blend(blend),
|
||||
is_transitional(transitional), roughness(r) {}
|
||||
};
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
static unsigned sums();
|
||||
static unsigned cross_plane_sum();
|
||||
static const std::string& name_form_id(BlockType id);
|
||||
|
||||
static std::string local_name(BlockType id);
|
||||
static bool is_gas(BlockType id);
|
||||
static bool is_liquid(BlockType id);
|
||||
|
||||
|
||||
27
include/Cubed/gameplay/chunk.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <tuple>
|
||||
namespace Cubed {
|
||||
class Chunk {
|
||||
public:
|
||||
Chunk() = default;
|
||||
Chunk(const Chunk&) = delete;
|
||||
Chunk(Chunk&&) = delete;
|
||||
Chunk& operator=(const Chunk&) = delete;
|
||||
Chunk& operator=(Chunk&&) = delete;
|
||||
virtual ~Chunk() = default;
|
||||
static int index(int x, int y, int z);
|
||||
static int index(const glm::vec3& pos);
|
||||
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
|
||||
int chunk_x, int chunk_z);
|
||||
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/vertex_data.hpp"
|
||||
#include "world/chunk_data.pb.h"
|
||||
@@ -26,7 +27,7 @@ struct ChunkRenderSnapshot {
|
||||
glm::vec3 center;
|
||||
glm::vec3 half_extents;
|
||||
};
|
||||
class ClientChunk {
|
||||
class ClientChunk : public Chunk {
|
||||
public:
|
||||
ClientChunk(ClientWorld& world);
|
||||
~ClientChunk();
|
||||
@@ -35,17 +36,6 @@ public:
|
||||
ClientChunk(ClientChunk&&) noexcept;
|
||||
ClientChunk& operator=(ClientChunk&&) noexcept;
|
||||
|
||||
static int index(int x, int y, int z);
|
||||
static int index(const glm::vec3& pos);
|
||||
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
|
||||
int chunk_x, int chunk_z);
|
||||
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
BiomeType get_biome() const;
|
||||
ChunkPos get_chunk_pos() const;
|
||||
const std::vector<BlockType>& get_chunk_blocks() const;
|
||||
|
||||
68
include/Cubed/gameplay/client_entity_manager.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
#include "world/entity.pb.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
namespace Cubed {
|
||||
class ClientWorld;
|
||||
class ClientEntityManager {
|
||||
public:
|
||||
enum class Command { CREATE, DESTORY };
|
||||
|
||||
ClientEntityManager(ClientWorld& world);
|
||||
void update();
|
||||
void init();
|
||||
|
||||
void receive_entity_create(S2CEntityCreate& s2c);
|
||||
void receive_entity_destory(EntityID id);
|
||||
|
||||
void destory(EntityID id);
|
||||
void create(std::string_view name, const glm::vec3& pos);
|
||||
|
||||
const entt::registry& get_registry() const;
|
||||
|
||||
private:
|
||||
struct EntityCreateElement {
|
||||
EntityID id;
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
|
||||
using acc = EntityMap::accessor;
|
||||
using cacc = EntityMap::const_accessor;
|
||||
using CreateFunc = std::function<void(EntityID id)>;
|
||||
using TaskElement = std::variant<EntityCreateElement, EntityID>;
|
||||
using TaskPair = std::pair<Command, TaskElement>;
|
||||
|
||||
ClientWorld& m_world;
|
||||
entt::registry m_registry;
|
||||
EntityMap m_entities;
|
||||
std::unordered_map<std::string_view, CreateFunc> m_factories;
|
||||
tbb::concurrent_queue<TaskPair> m_tasks;
|
||||
|
||||
void handle_task();
|
||||
void handle_entity_destory(EntityID id);
|
||||
// not thread safe
|
||||
void handle_entity_create(EntityID id, std::string_view name,
|
||||
const glm::vec3& pos);
|
||||
template <typename... Args>
|
||||
void create_entity_in_registry(EntityID id, Args&&... args) {
|
||||
{
|
||||
cacc a;
|
||||
if (m_entities.find(a, id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto entity = m_registry.create();
|
||||
|
||||
((m_registry.emplace<std::remove_cvref_t<Args>>(
|
||||
entity, std::forward<Args>(args))),
|
||||
...);
|
||||
m_entities.emplace(id, entity);
|
||||
return;
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,161 +1,25 @@
|
||||
#pragma once
|
||||
#include "Cubed/AABB.hpp"
|
||||
#include "Cubed/constants.hpp"
|
||||
#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/event.hpp"
|
||||
#include "Cubed/input/input.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/identity.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/player.hpp"
|
||||
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();
|
||||
|
||||
static AABB get_aabb(const glm::vec3& pos);
|
||||
const glm::vec3& get_front() const;
|
||||
Gait get_gait() const;
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
// thread safe
|
||||
glm::vec3 get_player_pos() const;
|
||||
const MoveState& get_move_state() const;
|
||||
|
||||
void change_mode(GameMode mode);
|
||||
void reload_config();
|
||||
void set_player_pos(const glm::vec3& pos);
|
||||
void set_place_block(unsigned id);
|
||||
void update(float delta_time);
|
||||
|
||||
float& max_walk_speed();
|
||||
float& max_run_speed();
|
||||
float& max_speed();
|
||||
float& acceleration();
|
||||
float& deceleration();
|
||||
float& g();
|
||||
float& fly_y_speed();
|
||||
|
||||
unsigned get_current_block() const;
|
||||
|
||||
void set_gait(Gait gait);
|
||||
GameMode& game_mode();
|
||||
|
||||
ClientWorld& get_world();
|
||||
|
||||
void set_uuid(std::string_view uuid);
|
||||
std::string get_uuid() const;
|
||||
const std::string& get_name() const;
|
||||
void reset_key_status();
|
||||
void init(std::string_view name);
|
||||
|
||||
float yaw() const;
|
||||
float pitch() const;
|
||||
float& angle();
|
||||
float& walk_time();
|
||||
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;
|
||||
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
|
||||
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
|
||||
float m_acceleration = DEFAULT_ACCELERATION;
|
||||
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;
|
||||
|
||||
float m_sensitivity = 0.15f;
|
||||
|
||||
float m_max_speed = m_max_walk_speed;
|
||||
float m_y_speed = 0.0f;
|
||||
float m_fly_y_speed = 7.5f;
|
||||
bool can_up = true;
|
||||
|
||||
float space_on_time = 0.0f;
|
||||
bool space_on = false;
|
||||
bool is_fly = false;
|
||||
|
||||
float m_xz_speed = 0.0f;
|
||||
|
||||
unsigned m_place_block = 1;
|
||||
|
||||
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};
|
||||
// player is tow block tall, the pos is the lower pos
|
||||
|
||||
glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f};
|
||||
ChunkPos m_last_chunk_pos{0, 0};
|
||||
|
||||
glm::vec3 m_front{0, 0, -1};
|
||||
glm::vec3 m_right{0, 0, 0};
|
||||
static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f};
|
||||
|
||||
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{};
|
||||
mutable std::shared_mutex m_uuid_mutex;
|
||||
std::string m_uuid;
|
||||
ClientWorld& m_world;
|
||||
|
||||
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;
|
||||
struct ClientPlayer {
|
||||
Position pos{};
|
||||
Position render_pos{};
|
||||
EntityInfo entity{};
|
||||
WalkPose walk{};
|
||||
Orientation angle{};
|
||||
Orientation render_angle{};
|
||||
};
|
||||
|
||||
struct PlayerRenderData {
|
||||
EntityInfo info{};
|
||||
Position render_pos{};
|
||||
Orientation angle{};
|
||||
Gait gait{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
45
include/Cubed/gameplay/client_player_manager.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/client_player.hpp"
|
||||
#include "Cubed/gameplay/local_player.hpp"
|
||||
#include "Cubed/gameplay/network_client.hpp"
|
||||
#include "Cubed/tools/sparse_vector.hpp"
|
||||
namespace Cubed {
|
||||
class ClientWorld;
|
||||
class ClientPlayerManager {
|
||||
public:
|
||||
ClientPlayerManager(const ClientPlayerManager&) = delete;
|
||||
ClientPlayerManager(ClientPlayerManager&&) = delete;
|
||||
ClientPlayerManager& operator=(const ClientPlayerManager&) = delete;
|
||||
ClientPlayerManager& operator=(ClientPlayerManager&&) = delete;
|
||||
ClientPlayerManager(ClientWorld& world);
|
||||
~ClientPlayerManager();
|
||||
|
||||
void init(std::string_view local_name);
|
||||
void update(float dt);
|
||||
|
||||
std::span<PlayerRenderData> render_player_data();
|
||||
|
||||
bool has_player(const Hitbox& hitbox) const;
|
||||
|
||||
LocalPlayer& get_local();
|
||||
const LocalPlayer& get_local() const;
|
||||
|
||||
void receive_remote_player(const PlayerInfoRsp& rsp);
|
||||
void receive_player_logout(const LogoutRsp& rsp);
|
||||
|
||||
void reload_config();
|
||||
|
||||
void report_player_info(NetworkClient* client);
|
||||
|
||||
private:
|
||||
ClientWorld& m_world;
|
||||
mutable std::shared_mutex m_players_mutex;
|
||||
using PlayerHandle = SparseVector<ClientPlayer>::Handle;
|
||||
SparseVector<ClientPlayer> m_players;
|
||||
std::vector<PlayerRenderData> m_render_data;
|
||||
std::unordered_map<std::string, PlayerHandle> m_players_handle;
|
||||
LocalPlayer m_local;
|
||||
|
||||
void update_players_data(float dt);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -5,9 +5,12 @@
|
||||
#include "Cubed/gameplay/chat_message.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/client_chunk.hpp"
|
||||
#include "Cubed/gameplay/client_player.hpp"
|
||||
#include "Cubed/gameplay/client_entity_manager.hpp"
|
||||
#include "Cubed/gameplay/client_player_manager.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/local_player.hpp"
|
||||
#include "Cubed/gameplay/network_client.hpp"
|
||||
#include "Cubed/gameplay/world.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/tools/cubed_random.hpp"
|
||||
#include "Cubed/tools/priority_thread_pool.hpp"
|
||||
@@ -19,46 +22,26 @@
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
namespace Cubed {
|
||||
|
||||
struct PlayerInfo {
|
||||
std::string name;
|
||||
std::string uuid;
|
||||
glm::vec3 render_pos;
|
||||
glm::vec3 target_pos;
|
||||
float render_yaw;
|
||||
float yaw;
|
||||
float render_pitch;
|
||||
float pitch;
|
||||
Gait gait;
|
||||
float angle = 0.0f;
|
||||
float walk_time = 0.0f;
|
||||
float moving_time = 0.0f;
|
||||
};
|
||||
|
||||
struct PlayerRenderData {
|
||||
std::string name;
|
||||
std::string uuid;
|
||||
glm::vec3 render_pos;
|
||||
float yaw;
|
||||
float pitch;
|
||||
Gait gait;
|
||||
float angle;
|
||||
};
|
||||
class WorldScene;
|
||||
class ClientWorld {
|
||||
class ClientWorld : public World {
|
||||
public:
|
||||
ClientWorld(const ClientWorld&) = delete;
|
||||
ClientWorld(ClientWorld&&) = delete;
|
||||
ClientWorld& operator=(const ClientWorld&) = delete;
|
||||
ClientWorld& operator=(ClientWorld&&) = delete;
|
||||
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);
|
||||
void update(float dt);
|
||||
bool handle_event(const Event& e);
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
ClientPlayer& get_player();
|
||||
const ClientPlayer& get_player() const;
|
||||
int get_block(const glm::ivec3& block_pos) const;
|
||||
bool is_solid(const glm::ivec3& block_pos) const;
|
||||
bool can_pass_block(const glm::ivec3& block_pos) const;
|
||||
BlockType get_block_tpye(const glm::ivec3& block_pos) const;
|
||||
LocalPlayer& get_player();
|
||||
const LocalPlayer& get_player() const;
|
||||
int get_block(const glm::ivec3& block_pos) const override;
|
||||
bool is_solid(const glm::ivec3& block_pos) const override;
|
||||
bool can_pass_block(const glm::ivec3& block_pos) const override;
|
||||
BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
|
||||
|
||||
void rebuild_world();
|
||||
|
||||
@@ -71,7 +54,6 @@ public:
|
||||
void receive_block_change(const BlockChangeRsp& rsp);
|
||||
void receive_time(const UpdateTime& rsp);
|
||||
|
||||
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);
|
||||
@@ -90,8 +72,6 @@ public:
|
||||
void reset_key_status();
|
||||
std::vector<glm::vec4>& planes();
|
||||
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
|
||||
const std::vector<PlayerRenderData>& render_player_data() const;
|
||||
std::vector<PlayerRenderData>& render_player_data();
|
||||
|
||||
glm::vec3 sunlight_dir() const;
|
||||
bool sphere_collide_world(glm::vec3 center, float radius) const;
|
||||
@@ -99,22 +79,26 @@ public:
|
||||
void request_exit();
|
||||
bool is_receive_exit();
|
||||
int chunk_size() const;
|
||||
static AABB get_block_aabb(const glm::ivec3& pos);
|
||||
|
||||
AudioEngine& get_audio();
|
||||
const AudioEngine& get_audio() const;
|
||||
Config& get_config();
|
||||
WorldScene& world_scene();
|
||||
ClientPlayerManager& player_manager();
|
||||
ClientEntityManager& entity_manager();
|
||||
std::shared_ptr<NetworkClient> get_client() const;
|
||||
void set_direct_exit();
|
||||
|
||||
void receive_chat_message(ChatMsg& msg);
|
||||
void send_chat_message(ChatMessage& message);
|
||||
void receive_voice_message(VoiceMsg& msg);
|
||||
bool enable_voice_chat() const;
|
||||
int get_per_tick_time() const override;
|
||||
template <typename Fn>
|
||||
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)));
|
||||
void register_timer(std::string_view id, float 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)));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -135,7 +119,6 @@ private:
|
||||
ChunkPos::TBBHash>;
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
using ChunkPosVector = std::vector<ChunkPos>;
|
||||
using OtherPlayerHashMap = std::unordered_map<std::string, PlayerInfo>;
|
||||
using chunk_acc = ChunkHashMap::accessor;
|
||||
using chunk_cacc = ChunkHashMap::const_accessor;
|
||||
|
||||
@@ -146,16 +129,13 @@ private:
|
||||
|
||||
static constexpr int WORLD_EXIT_TIMEOUT = 200;
|
||||
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
|
||||
ClientPlayer m_player;
|
||||
OtherPlayerHashMap m_player_info;
|
||||
ClientEntityManager m_entity_manager;
|
||||
ClientPlayerManager m_player_manager;
|
||||
ChunkHashMap m_chunks;
|
||||
AudioEngine& m_audio;
|
||||
Config& m_config;
|
||||
WorldScene& m_world_scene;
|
||||
std::vector<glm::vec4> m_planes;
|
||||
std::jthread m_client_thread;
|
||||
|
||||
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;
|
||||
@@ -165,9 +145,7 @@ private:
|
||||
|
||||
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, TickTimer> m_ticktimers;
|
||||
std::unordered_map<std::string, Timer> m_timers;
|
||||
std::atomic<bool> m_exit_direct{false};
|
||||
std::atomic<bool> m_game_running{false};
|
||||
@@ -175,6 +153,7 @@ private:
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<TickType> m_game_ticks{0};
|
||||
std::atomic<TickType> m_day_tick{6000};
|
||||
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME;
|
||||
std::atomic<bool> m_requesting_chunk{false};
|
||||
std::atomic<bool> m_is_rebuilding{false};
|
||||
std::atomic<int> m_chunk_task_id{0};
|
||||
@@ -186,10 +165,6 @@ private:
|
||||
|
||||
Random m_random;
|
||||
|
||||
void client_run(std::stop_token token);
|
||||
|
||||
void report_player_info();
|
||||
|
||||
void set_block(const glm::ivec3& pos, unsigned id);
|
||||
|
||||
void update_chunk(const ChunkPosSet& old, const ChunkPosSet& now);
|
||||
|
||||
6
include/Cubed/gameplay/creatures/pig.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
struct PigTag {};
|
||||
|
||||
} // namespace Cubed
|
||||
13
include/Cubed/gameplay/ecs/animation.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/player.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
struct WalkPose {
|
||||
Gait gait = Gait::STOP;
|
||||
// for arm roll caculate
|
||||
float walk_time = 0.0f;
|
||||
// for sound play
|
||||
float moving_time = 0.0f;
|
||||
};
|
||||
} // namespace Cubed
|
||||
14
include/Cubed/gameplay/ecs/client_entity.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
namespace Cubed {
|
||||
struct BaseClientCreature {
|
||||
|
||||
Transform transform;
|
||||
|
||||
WalkPose pose;
|
||||
|
||||
ModelID model;
|
||||
};
|
||||
} // namespace Cubed
|
||||
11
include/Cubed/gameplay/ecs/entity.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
namespace Cubed {
|
||||
using EntityID = uint64_t;
|
||||
|
||||
struct Entity {
|
||||
EntityID id;
|
||||
explicit Entity(EntityID id) : id(id) {}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/health.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct Health {
|
||||
float hp = 20;
|
||||
float max_hp = 20;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/identity.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Cubed {
|
||||
struct EntityInfo {
|
||||
std::string name;
|
||||
std::string uuid;
|
||||
};
|
||||
} // namespace Cubed
|
||||
26
include/Cubed/gameplay/ecs/movement.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
struct Velocity {
|
||||
|
||||
glm::vec3 value{0.0f};
|
||||
|
||||
glm::vec3 max{4.5f, 7.5f, 7.5f};
|
||||
};
|
||||
|
||||
struct Movement {
|
||||
|
||||
float acceleration = DEFAULT_ACCELERATION;
|
||||
|
||||
float deceleration = DEFAULT_DECELERATION;
|
||||
|
||||
float jump_power = 7.5f;
|
||||
};
|
||||
|
||||
struct Gravity {
|
||||
|
||||
float value = DEFAULT_G;
|
||||
};
|
||||
} // namespace Cubed
|
||||
27
include/Cubed/gameplay/ecs/server_entity.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/health.hpp"
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
struct BaseServerCreature {
|
||||
Transform transform{};
|
||||
|
||||
Velocity velocity{};
|
||||
|
||||
Movement movement{};
|
||||
|
||||
MoveState move_state{};
|
||||
|
||||
Direction direction{};
|
||||
|
||||
Gravity gravity{};
|
||||
|
||||
Health health{};
|
||||
|
||||
HitboxID hitbox{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
18
include/Cubed/gameplay/ecs/state.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
struct MoveState {
|
||||
|
||||
bool forward = false;
|
||||
bool back = false;
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
|
||||
bool down = false;
|
||||
bool up = false;
|
||||
|
||||
bool is_fly = false;
|
||||
bool can_up = true;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
24
include/Cubed/gameplay/ecs/transform.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
struct Position {
|
||||
glm::vec3 value{0.0f};
|
||||
};
|
||||
|
||||
struct Orientation {
|
||||
float yaw = 0.0f;
|
||||
float pitch = 0.0f;
|
||||
float roll = 0.0f;
|
||||
};
|
||||
|
||||
struct Direction {
|
||||
glm::vec3 value{0.0f};
|
||||
};
|
||||
|
||||
struct Transform {
|
||||
Position position{};
|
||||
Orientation orientation{};
|
||||
Direction direction{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
26
include/Cubed/gameplay/hitbox.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
using HitboxID = uint32_t;
|
||||
|
||||
struct Hitbox {
|
||||
glm::vec3 center{0.0f};
|
||||
glm::vec3 half{0.0f};
|
||||
|
||||
Hitbox(glm::vec3 center_point, glm::vec3 half_size)
|
||||
: center(center_point), half(half_size) {}
|
||||
|
||||
glm::vec3 min() const { return center - half; }
|
||||
|
||||
glm::vec3 max() const { return center + half; }
|
||||
|
||||
bool intersects(const Hitbox& other) const {
|
||||
return (glm::abs(center.x - other.center.x) <= half.x + other.half.x) &&
|
||||
(glm::abs(center.y - other.center.y) <= half.y + other.half.y) &&
|
||||
(glm::abs(center.z - other.center.z) <= half.z + other.half.z);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
37
include/Cubed/gameplay/hitbox_manager.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class HitboxManager {
|
||||
public:
|
||||
struct Handle {
|
||||
Hitbox box;
|
||||
HitboxID id = 0;
|
||||
};
|
||||
HitboxManager();
|
||||
~HitboxManager();
|
||||
static HitboxManager& instance();
|
||||
|
||||
[[nodiscard]]
|
||||
Handle get_hitbox(const std::string& key);
|
||||
[[nodiscard]]
|
||||
Handle get_hitbox(HitboxID id);
|
||||
[[nodiscard]]
|
||||
static Handle hitbox(const std::string& name);
|
||||
[[nodiscard]]
|
||||
static Handle hitbox(HitboxID id);
|
||||
HitboxID get_hitbox_id(const std::string& name);
|
||||
const std::string& get_hitbox_name(HitboxID id);
|
||||
|
||||
private:
|
||||
using HitboxMap = tbb::concurrent_hash_map<HitboxID, Hitbox>;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, HitboxID>;
|
||||
using NameMap = tbb::concurrent_hash_map<HitboxID, std::string>;
|
||||
HitboxID m_next = 0;
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
HitboxMap m_hitboxes;
|
||||
Handle load(std::string_view name);
|
||||
};
|
||||
} // namespace Cubed
|
||||
8
include/Cubed/gameplay/item_stack.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
namespace Cubed {
|
||||
struct ItemStack {
|
||||
BlockType type = 0;
|
||||
size_t sum = 0;
|
||||
};
|
||||
} // namespace Cubed
|
||||
160
include/Cubed/gameplay/local_player.hpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/identity.hpp"
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/game_mode.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
#include "Cubed/gameplay/item_stack.hpp"
|
||||
#include "Cubed/gameplay/player.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/input/input.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
namespace Cubed {
|
||||
|
||||
class ClientWorld;
|
||||
class LocalPlayer {
|
||||
public:
|
||||
static constexpr size_t HOTBAR_SUM = 10;
|
||||
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>;
|
||||
LocalPlayer(ClientWorld& world);
|
||||
~LocalPlayer();
|
||||
|
||||
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();
|
||||
|
||||
const glm::vec3& get_front() const;
|
||||
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
// thread safe
|
||||
glm::vec3 get_player_pos() const;
|
||||
const MoveState& get_move_state() const;
|
||||
|
||||
void change_mode(GameMode mode);
|
||||
void reload_config();
|
||||
void set_player_pos(const glm::vec3& pos);
|
||||
void update(float delta_time);
|
||||
|
||||
float& max_walk_speed();
|
||||
float& max_run_speed();
|
||||
float& fly_y_speed();
|
||||
|
||||
const ItemStack& get_current_itemstack() const;
|
||||
|
||||
GameMode& game_mode();
|
||||
|
||||
ClientWorld& get_world();
|
||||
|
||||
void set_uuid(std::string_view uuid);
|
||||
std::string get_uuid() const;
|
||||
const std::string& get_name() const;
|
||||
void reset_input_status();
|
||||
void init(std::string_view name);
|
||||
|
||||
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);
|
||||
|
||||
int selected_hotbar() const;
|
||||
void set_hotbar(int pos, const ItemStack& item);
|
||||
std::span<const ItemStack, HOTBAR_SUM> get_hotbar() const;
|
||||
|
||||
glm::vec3& max_speed();
|
||||
float& acceleration();
|
||||
float& deceleration();
|
||||
float& g();
|
||||
void set_gait(Gait gait);
|
||||
float yaw() const;
|
||||
float pitch() const;
|
||||
float& roll();
|
||||
float& walk_time();
|
||||
Gait get_gait() const;
|
||||
|
||||
private:
|
||||
using enum GameMode;
|
||||
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
|
||||
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
|
||||
float m_max_y_speed = 7.5f;
|
||||
static constexpr float MAX_SPACE_ON_TIME = 0.3f;
|
||||
static constexpr float PLACE_BLOCK_INTERVAL = 0.2f;
|
||||
|
||||
EntityInfo m_info;
|
||||
Position m_pos;
|
||||
WalkPose m_walk_pose;
|
||||
Velocity m_velocity;
|
||||
Orientation m_angle;
|
||||
Movement m_movement;
|
||||
Gravity m_gravity;
|
||||
MoveState m_move_state;
|
||||
Direction m_direction;
|
||||
HitboxID m_hitbox = 0;
|
||||
|
||||
float m_place_time = PLACE_BLOCK_INTERVAL;
|
||||
|
||||
std::array<ItemStack, HOTBAR_SUM> m_hotbar;
|
||||
float m_sensitivity = 0.15f;
|
||||
|
||||
float space_on_time = 0.0f;
|
||||
bool space_on = false;
|
||||
|
||||
int m_selected_hotbar = 0;
|
||||
|
||||
bool m_moving = false;
|
||||
bool m_sprinting = false;
|
||||
bool m_underwater = false;
|
||||
|
||||
// player is tow block tall, the pos is the lower pos
|
||||
ChunkPos m_last_chunk_pos{0, 0};
|
||||
|
||||
glm::vec3 m_front{0, 0, -1};
|
||||
glm::vec3 m_right{0, 0, 0};
|
||||
|
||||
MouseState m_mouse_state{};
|
||||
GameMode m_game_mode = CREATIVE;
|
||||
std::optional<LookBlock> m_look_block = std::nullopt;
|
||||
std::string m_name{};
|
||||
mutable std::shared_mutex m_uuid_mutex;
|
||||
std::string m_uuid;
|
||||
ClientWorld& m_world;
|
||||
|
||||
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_player_chunk();
|
||||
|
||||
void play_walk_sound(float dt);
|
||||
Gait compute_gait() const;
|
||||
};
|
||||
} // namespace Cubed
|
||||
7
include/Cubed/gameplay/model.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using ModelID = uint32_t;
|
||||
struct Model {
|
||||
ModelID id;
|
||||
};
|
||||
@@ -61,6 +61,10 @@ enum class PacketEnum : uint16_t {
|
||||
BLOCK_CHANGE_RSP = 3004,
|
||||
S2C_CLEAR_ALL_CHUNKS = 3005,
|
||||
UPDATE_TIME = 3006,
|
||||
S2C_ENTITY_CREATE = 3007,
|
||||
S2C_ENTITY_DESTORY = 3008,
|
||||
C2S_ENTITY_CREATE_REQUEST = 3009,
|
||||
C2S_ENTITY_DESTORY_REQUEST = 3010,
|
||||
CHAT_MSG = 4001,
|
||||
VOICE_MSG = 4002,
|
||||
PING = 9001,
|
||||
@@ -111,6 +115,18 @@ template <> constexpr uint16_t get_packet_id<BlockChangeRsp>() {
|
||||
template <> constexpr uint16_t get_packet_id<S2C_ClearAllChunks>() {
|
||||
return std::to_underlying(PacketEnum::S2C_CLEAR_ALL_CHUNKS);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityCreate>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_CREATE);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityDestory>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<C2SEntityCreateRequest>() {
|
||||
return std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<C2SEntityDestoryRequest>() {
|
||||
return std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<UpdateTime>() {
|
||||
return std::to_underlying(PacketEnum::UPDATE_TIME);
|
||||
}
|
||||
@@ -180,6 +196,12 @@ Packet make_packet(const T& msg) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
Packet make_packet(const T* msg) {
|
||||
return make_packet(*msg);
|
||||
}
|
||||
|
||||
inline PacketHeader decode_packet_header(std::span<const uint8_t> header) {
|
||||
if (header.size() < HEADER_LEN)
|
||||
throw std::runtime_error("Invalid header");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
namespace Cubed {
|
||||
@@ -17,5 +19,5 @@ inline Gait get_gait_from_id(int id) {
|
||||
throw std::runtime_error("Unknown Gait");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr glm::vec3 PLAYER_SIZE{0.6f, 1.8f, 0.6f};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk.hpp"
|
||||
#include "Cubed/gameplay/chunk_generator.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
|
||||
@@ -11,7 +12,7 @@
|
||||
#include <tuple>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class ServerChunk {
|
||||
class ServerChunk : public Chunk {
|
||||
public:
|
||||
ServerChunk(ServerWorld& world, ChunkPos chunk_pos,
|
||||
bool temp_chunk = false);
|
||||
|
||||
59
include/Cubed/gameplay/server_entity_manager.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class Session;
|
||||
class ServerEntityManager {
|
||||
public:
|
||||
ServerEntityManager(ServerWorld& world);
|
||||
|
||||
void init();
|
||||
void update();
|
||||
// not thread safe
|
||||
void add_entity(std::string_view name, const glm::vec3& pos);
|
||||
void destory(EntityID id);
|
||||
void handle_player_login(std::shared_ptr<Session> session);
|
||||
|
||||
private:
|
||||
enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY };
|
||||
struct EntityCreateElement {
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
|
||||
using acc = EntityMap::accessor;
|
||||
using cacc = EntityMap::const_accessor;
|
||||
using CreateFunc = std::function<EntityID()>;
|
||||
using TaskElement =
|
||||
std::variant<std::shared_ptr<Session>, EntityCreateElement, EntityID>;
|
||||
using TaskPair = std::pair<Command, TaskElement>;
|
||||
ServerWorld& m_world;
|
||||
tbb::concurrent_queue<TaskPair> m_tasks;
|
||||
entt::registry m_registry;
|
||||
EntityID m_next = 0;
|
||||
EntityMap m_entities;
|
||||
std::unordered_map<std::string_view, CreateFunc> m_factories;
|
||||
void create_entity(std::string_view name, const glm::vec3& pos);
|
||||
void handle_entity_create(EntityID id, std::string_view name,
|
||||
const glm::vec3& pos);
|
||||
void handle_entity_destory(EntityID id);
|
||||
void handle_task();
|
||||
void send_all_entities(std::shared_ptr<Session>& session);
|
||||
template <typename... Args>
|
||||
EntityID create_entity_in_factory(Args&&... args) {
|
||||
auto entity = m_registry.create();
|
||||
|
||||
((m_registry.emplace<std::remove_cvref_t<Args>>(
|
||||
entity, std::forward<Args>(args))),
|
||||
...);
|
||||
auto id = m_next++;
|
||||
m_entities.emplace(id, entity);
|
||||
return id;
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -7,7 +7,9 @@
|
||||
#include "Cubed/gameplay/packet.hpp" // IWYU pragma: keep
|
||||
#include "Cubed/gameplay/river_worm.hpp"
|
||||
#include "Cubed/gameplay/server_chunk.hpp"
|
||||
#include "Cubed/gameplay/server_entity_manager.hpp"
|
||||
#include "Cubed/gameplay/server_player.hpp"
|
||||
#include "Cubed/gameplay/world.hpp"
|
||||
#include "Cubed/tools/priority_thread_pool.hpp"
|
||||
#include "Cubed/tools/recent_queue.hpp"
|
||||
#include "Cubed/tools/sensitive_filter.hpp"
|
||||
@@ -25,7 +27,7 @@
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Session;
|
||||
class ServerWorld {
|
||||
class ServerWorld : public World {
|
||||
public:
|
||||
enum class ThreadPoolKind { NET, GEN };
|
||||
ServerWorld(Config& config);
|
||||
@@ -84,7 +86,19 @@ public:
|
||||
|
||||
void handle_chat_message(ChatMsg& msg);
|
||||
void handle_voice_message(VoiceMsg& msg);
|
||||
|
||||
void handle_entity_create(C2SEntityCreateRequest& req);
|
||||
void handle_entity_destory(C2SEntityDestoryRequest& req);
|
||||
|
||||
int chunk_size() const;
|
||||
|
||||
std::vector<std::shared_ptr<Session>> get_all_session() const;
|
||||
|
||||
int get_block(const glm::ivec3& block_pos) const override;
|
||||
bool is_solid(const glm::ivec3& block_pos) const override;
|
||||
bool can_pass_block(const glm::ivec3& block_pos) const override;
|
||||
BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
|
||||
int get_per_tick_time() const override;
|
||||
template <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
@@ -119,13 +133,13 @@ private:
|
||||
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
|
||||
|
||||
using chunk_acc = ChunkHashMap::accessor;
|
||||
using chunk_caac = ChunkHashMap::const_accessor;
|
||||
using chunk_cacc = ChunkHashMap::const_accessor;
|
||||
|
||||
using uuid_acc = PlayerUUIDMap::accessor;
|
||||
using uuid_cacc = PlayerUUIDMap::const_accessor;
|
||||
|
||||
Config& m_config;
|
||||
|
||||
ServerEntityManager m_entity_manager;
|
||||
// key = uuid
|
||||
PlayerHashMap m_players;
|
||||
ChunkHashMap m_chunks;
|
||||
@@ -155,7 +169,7 @@ private:
|
||||
std::atomic<bool> m_tick_running{true};
|
||||
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
|
||||
|
||||
mutable std::shared_mutex m_player_mutex;
|
||||
mutable std::shared_mutex m_players_mutex;
|
||||
std::mutex m_need_gen_queue_mutex;
|
||||
std::condition_variable_any m_gen_cv;
|
||||
|
||||
|
||||
19
include/Cubed/gameplay/systems/physical_system.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/world.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
class PhysicalSystem {
|
||||
public:
|
||||
static glm::vec3 get_move_distance(float dt, const Direction& d,
|
||||
const Velocity& v);
|
||||
|
||||
static std::tuple<bool, bool, bool>
|
||||
update(float dt, World& world, glm::vec3& moved_pos, Velocity& v,
|
||||
Direction& direction, MoveState& move_state, HitboxID hitbox);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
16
include/Cubed/gameplay/systems/speed_system.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
class SpeedSystem {
|
||||
public:
|
||||
static void update(float dt, Velocity& v, MoveState& move_state,
|
||||
Movement& movement, Direction& direction,
|
||||
const Gravity& g);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
29
include/Cubed/gameplay/world.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
class World {
|
||||
public:
|
||||
World() = default;
|
||||
World(const World&) = delete;
|
||||
World(World&&) = delete;
|
||||
World& operator=(const World&) = delete;
|
||||
World& operator=(World&&) = delete;
|
||||
virtual ~World() = default;
|
||||
|
||||
virtual int get_block(const glm::ivec3& block_pos) const = 0;
|
||||
virtual bool is_solid(const glm::ivec3& block_pos) const = 0;
|
||||
virtual bool can_pass_block(const glm::ivec3& block_pos) const = 0;
|
||||
virtual BlockType get_block_tpye(const glm::ivec3& block_pos) const = 0;
|
||||
virtual int get_per_tick_time() const = 0;
|
||||
|
||||
static Hitbox get_block_aabb(const glm::ivec3& pos) {
|
||||
return {glm::vec3{static_cast<float>(pos.x) + 0.5f,
|
||||
static_cast<float>(pos.y) + 0.5f,
|
||||
static_cast<float>(pos.z) + 0.5f},
|
||||
glm::vec3{0.5f, 0.5f, 0.5f}};
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct MoveState {
|
||||
bool forward = false;
|
||||
bool back = false;
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool down = false;
|
||||
bool up = false;
|
||||
};
|
||||
|
||||
struct MouseState {
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
|
||||
45
include/Cubed/render/model_manager.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
#include "Cubed/tools/model_loader.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class ModelManager {
|
||||
public:
|
||||
struct Handle {
|
||||
const ModelNode& node;
|
||||
ModelID id = 0;
|
||||
};
|
||||
|
||||
ModelManager();
|
||||
ModelManager(const ModelManager&) = delete;
|
||||
ModelManager(ModelManager&&) = delete;
|
||||
ModelManager& operator=(const ModelManager&) = delete;
|
||||
ModelManager& operator=(ModelManager&&) = delete;
|
||||
static ModelManager& instance();
|
||||
~ModelManager();
|
||||
[[nodiscard]]
|
||||
Handle get_model(const std::string& model_name);
|
||||
[[nodiscard]]
|
||||
Handle get_model(ModelID id);
|
||||
[[nodiscard]]
|
||||
static Handle model(const std::string& model_name);
|
||||
[[nodiscard]]
|
||||
static Handle model(ModelID id);
|
||||
ModelID get_model_id(const std::string& name);
|
||||
const std::string& get_model_name(ModelID id);
|
||||
void init();
|
||||
|
||||
private:
|
||||
ModelLoader m_loader;
|
||||
ModelID m_next = 0;
|
||||
using ModelMap = tbb::concurrent_hash_map<ModelID, ModelNode>;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, ModelID>;
|
||||
using NameMap = tbb::concurrent_hash_map<ModelID, std::string>;
|
||||
ModelMap m_models;
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
Handle load_model(std::string_view model_name);
|
||||
};
|
||||
} // namespace Cubed
|
||||
42
include/Cubed/render/model_node.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/texture.hpp"
|
||||
#include "Cubed/render/vertex_array.hpp"
|
||||
#include "Cubed/render/vertex_buffer.hpp"
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
namespace Cubed {
|
||||
|
||||
struct Mesh {
|
||||
std::vector<Vertex3D> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
std::unique_ptr<VertexBuffer> vbo;
|
||||
std::unique_ptr<VertexBuffer> ebo;
|
||||
std::unique_ptr<VertexArray> vao;
|
||||
std::unique_ptr<Texture> texture;
|
||||
void upload() {
|
||||
vao = std::make_unique<VertexArray>();
|
||||
vao->bind();
|
||||
vbo = std::make_unique<VertexBuffer>();
|
||||
vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D));
|
||||
ebo = std::make_unique<VertexBuffer>(BufferType::ELEMENT_ARRAY_BUFFER);
|
||||
ebo->buffer_data(indices.data(), indices.size() * sizeof(uint32_t));
|
||||
vao->attribute(0, 3, GL_FLOAT, sizeof(Vertex3D), (void*)0);
|
||||
vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, s));
|
||||
vao->attribute(2, 3, GL_FLOAT, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, nx));
|
||||
};
|
||||
};
|
||||
struct ModelNode {
|
||||
std::string name;
|
||||
glm::mat4 transform{1.0f};
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<ModelNode> children;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
24
include/Cubed/render/model_renderer.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
#include "Cubed/shader.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
class Camera;
|
||||
class ModelRender {
|
||||
public:
|
||||
ModelRender(Renderer& renderer);
|
||||
void render_model(ModelID id, const glm::vec3& pos, Camera& camera);
|
||||
|
||||
void shadow_pass(ModelID id, const glm::vec3& pos, Camera& camera);
|
||||
|
||||
private:
|
||||
Renderer& m_renderer;
|
||||
void render_node(const ModelNode& node, const glm::mat4& parent,
|
||||
const glm::mat4& view, const Shader& shader, bool shadow);
|
||||
void render_mesh(const Mesh& mesh, bool shadow);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -13,9 +13,7 @@ public:
|
||||
PlayerRenderer(Renderer& renderer);
|
||||
~PlayerRenderer();
|
||||
void init();
|
||||
void render(const Shader& shader, ClientWorld& world);
|
||||
void shadow_render(const Shader& shader, glm::mat4& light_matrix,
|
||||
ClientWorld& world);
|
||||
void render(const Shader& shader, ClientWorld& world, bool shadow_render);
|
||||
|
||||
private:
|
||||
struct PlayerVertex {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/primitive_data.hpp"
|
||||
#include "Cubed/render/model_renderer.hpp"
|
||||
#include "Cubed/render/player_renderer.hpp"
|
||||
#include "Cubed/render/shader_manager.hpp"
|
||||
#include "Cubed/render/vertex_array.hpp"
|
||||
@@ -19,6 +20,7 @@
|
||||
namespace Cubed {
|
||||
class TextureManager;
|
||||
class ClientWorld;
|
||||
class ModelManager;
|
||||
class DevPanel;
|
||||
class Renderer {
|
||||
public:
|
||||
@@ -79,9 +81,10 @@ public:
|
||||
|
||||
bool handle_event(const Event& e);
|
||||
|
||||
ModelRender& model_renderer();
|
||||
|
||||
private:
|
||||
TextureManager& m_texture_manager;
|
||||
|
||||
bool m_init = false;
|
||||
|
||||
float m_aspect = 0.0f;
|
||||
@@ -118,6 +121,7 @@ private:
|
||||
std::vector<Vertex2D> m_ui;
|
||||
|
||||
WorldRenderer m_world_renderer;
|
||||
ModelRender m_model_renderer;
|
||||
Config& m_config;
|
||||
|
||||
bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||
|
||||
@@ -38,9 +38,11 @@ enum TextureFormat : GLenum {
|
||||
R8 = GL_R8,
|
||||
RGB = GL_RGB,
|
||||
RGBA8 = GL_RGBA8,
|
||||
BGRA = GL_BGRA
|
||||
|
||||
};
|
||||
|
||||
// You need to set the texture scaling method, otherwise it will render as
|
||||
// black.
|
||||
class Texture {
|
||||
public:
|
||||
explicit Texture(TextureType type);
|
||||
|
||||
@@ -132,7 +132,8 @@ private:
|
||||
|
||||
void render_underwater(ClientWorld& world);
|
||||
void render_outline(ClientWorld& world);
|
||||
void render_player(ClientWorld& world);
|
||||
void shadow_entity(ClientWorld& world, const glm::mat4& light_matrix);
|
||||
void render_entity(ClientWorld& world);
|
||||
|
||||
void render_normal_block(const glm::mat4& model_mat,
|
||||
const glm::mat4& mv_mat, const glm::mat4& norm_mat,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Cubed/gameplay/network_server.hpp"
|
||||
#include "Cubed/scene/scene.hpp"
|
||||
#include "Cubed/ui/error_ui.hpp"
|
||||
#include "Cubed/ui/inventory_ui.hpp"
|
||||
#include "Cubed/ui/pasue_menu_ui_manager.hpp"
|
||||
#include "Cubed/ui/world_ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
@@ -42,6 +43,7 @@ public:
|
||||
bool is_recording() const;
|
||||
|
||||
private:
|
||||
enum class PauseUI { PAUSE_MENU, INVENTORY };
|
||||
SceneManager& m_scene_manager;
|
||||
DevPanel m_dev_panel;
|
||||
Camera m_camera;
|
||||
@@ -53,6 +55,8 @@ private:
|
||||
bool m_show_dev_pannel = true;
|
||||
bool m_chatting = false;
|
||||
PauseMenuUIManager m_pasue_menu;
|
||||
InventoryUI m_inventory_ui;
|
||||
PauseUI m_paused_ui = PauseUI::PAUSE_MENU;
|
||||
WorldUIManager m_hud_ui;
|
||||
ErrorUI m_error_ui;
|
||||
const Argument& m_argument;
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
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 std::vector<std::unique_ptr<Texture>>& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
void init_texture();
|
||||
|
||||
|
||||
7
include/Cubed/tools/cubed_concepts.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
namespace Cubed {
|
||||
template <typename T>
|
||||
concept Ptr = std::is_pointer_v<T>;
|
||||
}
|
||||
22
include/Cubed/tools/model_loader.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
namespace Cubed {
|
||||
|
||||
class ModelLoader {
|
||||
public:
|
||||
ModelLoader();
|
||||
ModelNode load(const std::string& path);
|
||||
|
||||
private:
|
||||
Assimp::Importer m_importer;
|
||||
ModelNode process_node(aiNode* node, const aiScene* scene);
|
||||
Mesh process_mesh(aiMesh* mesh, const aiScene* scene);
|
||||
bool process_texture(Mesh& mesh, aiMaterial* material, const aiScene* scene,
|
||||
aiTextureType type);
|
||||
glm::mat4 convert_matrix(const aiMatrix4x4& matrix);
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
19
include/Cubed/tools/name_space.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
inline std::vector<std::string_view> parse_namespace(std::string_view str) {
|
||||
std::vector<std::string_view> space;
|
||||
space.reserve(4);
|
||||
std::size_t p = str.find(':');
|
||||
std::size_t start = 0;
|
||||
while (p != std::string_view::npos) {
|
||||
space.emplace_back(str.substr(start, p));
|
||||
start = p + 1;
|
||||
p = str.find(':', p + 1);
|
||||
}
|
||||
space.emplace_back(str.substr(start));
|
||||
return space;
|
||||
}
|
||||
} // namespace Cubed
|
||||
21
include/Cubed/tools/net_utils.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/tools/cubed_concepts.hpp"
|
||||
#include "common/vector3.pb.h"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
namespace Cubed {
|
||||
namespace Tools {
|
||||
template <Ptr T> void set_net_pos(T ptr, const glm::vec3& pos) {
|
||||
Vec3* p = ptr->mutable_pos();
|
||||
p->set_x(pos.x);
|
||||
p->set_y(pos.y);
|
||||
p->set_z(pos.z);
|
||||
}
|
||||
inline glm::vec3 get_net_pos(const Vec3* p) {
|
||||
return glm::vec3{p->x(), p->y(), p->z()};
|
||||
}
|
||||
inline glm::vec3 get_net_pos(const Vec3& p) {
|
||||
return glm::vec3{p.x(), p.y(), p.z()};
|
||||
}
|
||||
} // namespace Tools
|
||||
} // namespace Cubed
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <SOIL2.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -48,7 +47,7 @@ bool check_opengl_error();
|
||||
std::string read_shader_source(const std::string& file_path);
|
||||
|
||||
ImageData load_image_data(const std::string& tex_image_path,
|
||||
bool check_exist = true);
|
||||
bool check_exist = true, bool full_path = false);
|
||||
|
||||
} // namespace Tools
|
||||
|
||||
|
||||
318
include/Cubed/tools/sparse_vector.hpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
template <typename T> class SparseVector {
|
||||
private:
|
||||
std::vector<std::optional<T>> m_data;
|
||||
std::vector<uint32_t> m_free_list;
|
||||
std::vector<uint32_t> m_generation;
|
||||
|
||||
std::vector<uint32_t> m_dense;
|
||||
std::vector<uint32_t> m_dense_index;
|
||||
|
||||
public:
|
||||
struct Handle {
|
||||
uint32_t index;
|
||||
uint32_t generation;
|
||||
|
||||
uint64_t value() const noexcept {
|
||||
return (uint64_t(index) << 32) | generation;
|
||||
}
|
||||
|
||||
bool operator==(const Handle&) const = default;
|
||||
|
||||
struct Hash {
|
||||
size_t operator()(const Handle& h) const noexcept {
|
||||
return h.value();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
|
||||
using size_type = size_t;
|
||||
|
||||
class iterator { // NOLINT
|
||||
private:
|
||||
SparseVector* m_owner;
|
||||
size_t m_index;
|
||||
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
iterator(SparseVector* owner, size_t index)
|
||||
: m_owner(owner), m_index(index) {}
|
||||
|
||||
reference operator*() {
|
||||
return m_owner->m_data[m_owner->m_dense[m_index]].value();
|
||||
}
|
||||
|
||||
pointer operator->() { return &(**this); }
|
||||
|
||||
iterator& operator++() {
|
||||
++m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) {
|
||||
iterator tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& other) const {
|
||||
return m_owner == other.m_owner && m_index == other.m_index;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
iterator operator+(difference_type n) const {
|
||||
return iterator(m_owner, m_index + n);
|
||||
}
|
||||
|
||||
iterator operator-(difference_type n) const {
|
||||
return iterator(m_owner, m_index - n);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator& other) const {
|
||||
return static_cast<difference_type>(m_index) -
|
||||
static_cast<difference_type>(other.m_index);
|
||||
}
|
||||
|
||||
iterator& operator+=(difference_type n) {
|
||||
m_index += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator& operator-=(difference_type n) {
|
||||
m_index -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator[](difference_type n) const { return *(*this + n); }
|
||||
|
||||
bool operator<(const iterator& other) const {
|
||||
return m_index < other.m_index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator& other) const {
|
||||
return m_index > other.m_index;
|
||||
}
|
||||
|
||||
bool operator<=(const iterator& other) const {
|
||||
return m_index <= other.m_index;
|
||||
}
|
||||
|
||||
bool operator>=(const iterator& other) const {
|
||||
return m_index >= other.m_index;
|
||||
}
|
||||
friend iterator operator+(difference_type n, const iterator& it) {
|
||||
return it + n;
|
||||
}
|
||||
};
|
||||
|
||||
class const_iterator { // NOLINT
|
||||
private:
|
||||
const SparseVector* m_owner;
|
||||
size_t m_index;
|
||||
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const T*;
|
||||
using reference = const T&;
|
||||
|
||||
const_iterator(const SparseVector* owner, size_t index)
|
||||
: m_owner(owner), m_index(index) {}
|
||||
|
||||
reference operator*() const {
|
||||
return m_owner->m_data[m_owner->m_dense[m_index]].value();
|
||||
}
|
||||
|
||||
pointer operator->() const { return &(**this); }
|
||||
|
||||
const_iterator& operator++() {
|
||||
++m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int) {
|
||||
const_iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
const_iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator--(int) {
|
||||
const_iterator tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& other) const {
|
||||
return m_owner == other.m_owner && m_index == other.m_index;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const_iterator operator+(difference_type n) const {
|
||||
return const_iterator(m_owner, m_index + n);
|
||||
}
|
||||
|
||||
const_iterator operator-(difference_type n) const {
|
||||
return const_iterator(m_owner, m_index - n);
|
||||
}
|
||||
|
||||
difference_type operator-(const const_iterator& other) const {
|
||||
return static_cast<difference_type>(m_index) -
|
||||
static_cast<difference_type>(other.m_index);
|
||||
}
|
||||
|
||||
const_iterator& operator+=(difference_type n) {
|
||||
m_index += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator& operator-=(difference_type n) {
|
||||
m_index -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator[](difference_type n) const { return *(*this + n); }
|
||||
|
||||
bool operator<(const const_iterator& other) const {
|
||||
return m_index < other.m_index;
|
||||
}
|
||||
|
||||
bool operator>(const const_iterator& other) const {
|
||||
return m_index > other.m_index;
|
||||
}
|
||||
|
||||
bool operator<=(const const_iterator& other) const {
|
||||
return m_index <= other.m_index;
|
||||
}
|
||||
|
||||
bool operator>=(const const_iterator& other) const {
|
||||
return m_index >= other.m_index;
|
||||
}
|
||||
friend const_iterator operator+(difference_type n,
|
||||
const const_iterator& it) {
|
||||
return it + n;
|
||||
}
|
||||
};
|
||||
|
||||
template <class U> [[nodiscard]] Handle insert(U&& value) {
|
||||
uint32_t id;
|
||||
if (!m_free_list.empty()) {
|
||||
id = m_free_list.back();
|
||||
m_free_list.pop_back();
|
||||
m_data[id].emplace(std::forward<U>(value));
|
||||
|
||||
m_dense_index[id] = m_dense.size();
|
||||
} else {
|
||||
id = m_data.size();
|
||||
m_data.push_back(std::forward<U>(value));
|
||||
m_generation.push_back(1);
|
||||
m_dense_index.emplace_back(m_dense.size());
|
||||
}
|
||||
|
||||
m_dense.push_back(id);
|
||||
return {id, m_generation[id]};
|
||||
}
|
||||
|
||||
template <typename... Args> Handle emplace(Args&&... args) {
|
||||
return insert(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void erase(Handle h) {
|
||||
|
||||
if (!exists(h)) {
|
||||
return;
|
||||
}
|
||||
uint32_t id = h.index;
|
||||
m_free_list.push_back(id);
|
||||
uint32_t index = m_dense_index[id];
|
||||
uint32_t last_id = m_dense.back();
|
||||
m_dense[index] = last_id;
|
||||
m_dense_index[last_id] = index;
|
||||
|
||||
m_dense.pop_back();
|
||||
m_data[id].reset();
|
||||
++m_generation[id];
|
||||
}
|
||||
|
||||
T& operator[](Handle h) {
|
||||
assert(exists(h));
|
||||
return m_data[h.index].value();
|
||||
}
|
||||
|
||||
const T& operator[](Handle h) const {
|
||||
assert(exists(h));
|
||||
return m_data[h.index].value();
|
||||
}
|
||||
|
||||
bool exists(Handle h) const {
|
||||
return h.index < m_generation.size() &&
|
||||
m_generation[h.index] == h.generation;
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
m_data.reserve(n);
|
||||
m_generation.reserve(n);
|
||||
m_dense.reserve(n);
|
||||
m_dense_index.reserve(n);
|
||||
}
|
||||
|
||||
size_t size() const { return m_dense.size(); }
|
||||
|
||||
bool empty() const { return m_dense.empty(); }
|
||||
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
|
||||
iterator end() { return iterator(this, m_dense.size()); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, 0); }
|
||||
|
||||
const_iterator end() const { return const_iterator(this, m_dense.size()); }
|
||||
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
|
||||
const_iterator cend() const { return end(); }
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -29,6 +29,12 @@ std::optional<T> safe_get_value(const toml::table& table, std::string_view key,
|
||||
return value;
|
||||
}
|
||||
|
||||
inline std::optional<std::string>
|
||||
safe_get_value(const toml::table& table, std::string_view key,
|
||||
std::string_view default_value) {
|
||||
return safe_get_value(table, key, std::string(default_value));
|
||||
}
|
||||
|
||||
} // namespace TOML
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -12,4 +12,7 @@ enum class Anchor {
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM_CENTER,
|
||||
BOTTOM_RIGHT,
|
||||
|
||||
FOLLOW_MOUSE
|
||||
|
||||
};
|
||||
@@ -12,9 +12,6 @@ public:
|
||||
ColumnLayout& operator=(ColumnLayout&&) = delete;
|
||||
ColumnLayout(Widget* parent);
|
||||
~ColumnLayout();
|
||||
|
||||
void update(float dt) override;
|
||||
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
|
||||
@@ -29,5 +26,7 @@ private:
|
||||
float m_content_height = 0.0f;
|
||||
float m_content_width = 0.0f;
|
||||
ColumnLayoutAnchor m_layout_anchor = ColumnLayoutAnchor::CENTER;
|
||||
|
||||
void on_update(float dt) override;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -13,13 +13,19 @@ public:
|
||||
Image& operator=(const Image&) = delete;
|
||||
Image& operator=(Image&&) = delete;
|
||||
Image(Widget* parent);
|
||||
|
||||
Image& set_image(const std::string& path, TextureManager& texture_manager);
|
||||
// If you need to set width and height to the material's size, pass true,
|
||||
// If you set fill parent, please pass false.
|
||||
Image& set_image(const std::string& path, TextureManager& texture_manager,
|
||||
bool change_size);
|
||||
// If you need to set width and height to the material's size, pass true
|
||||
// If you set fill parent, please pass false.
|
||||
Image& set_texture(const Texture* texture, bool change_size);
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
const Texture* texture() const;
|
||||
Image& set_scale(float scale);
|
||||
float scale() const;
|
||||
bool has_texture() const;
|
||||
|
||||
private:
|
||||
void on_render(Renderer& renderer) override;
|
||||
|
||||
30
include/Cubed/ui/inventory_ui.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/item_slot.hpp"
|
||||
#include "Cubed/ui/label.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class WorldScene;
|
||||
class InventoryUI : public UIManager {
|
||||
public:
|
||||
InventoryUI(WorldScene& m_scene);
|
||||
|
||||
void init() override;
|
||||
void on_re_enter();
|
||||
void update(float dt) override;
|
||||
|
||||
private:
|
||||
WorldScene& m_scene;
|
||||
std::vector<ItemSlot*> m_slots;
|
||||
std::vector<ItemSlot*> m_hotbar;
|
||||
Label* m_item_info = nullptr;
|
||||
|
||||
Image* m_selected_image = nullptr;
|
||||
BlockType m_selected_block = 0;
|
||||
void update_item_info();
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
|
||||
ItemSlot* get_hovered_slot();
|
||||
std::pair<ItemSlot*, size_t> get_hovered_hotbar_slot();
|
||||
};
|
||||
} // namespace Cubed
|
||||
33
include/Cubed/ui/item_slot.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
class TextureManager;
|
||||
class ItemSlot : public Widget {
|
||||
public:
|
||||
static constexpr float DEFAULT_WIDTH = 16.0f;
|
||||
static constexpr float DEFAULT_HEIGHT = 16.0f;
|
||||
ItemSlot(Widget* parent);
|
||||
|
||||
ItemSlot& set_default_background(TextureManager& m_texture_manager);
|
||||
ItemSlot& set_scale(float m_scale);
|
||||
ItemSlot& set_item(BlockType id, const Texture* texture);
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
BlockType block() const;
|
||||
bool hovered() const;
|
||||
|
||||
private:
|
||||
static constexpr const char* DEFAULT_SLOT_PATH = "texture/ui/slot.png";
|
||||
void on_render(Renderer& renderer) override;
|
||||
void on_update(float dt) override;
|
||||
std::unique_ptr<Image> m_background;
|
||||
std::unique_ptr<Image> m_foreground;
|
||||
BlockType m_block_type;
|
||||
float m_scale = 1.0f;
|
||||
bool m_hovered = false;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
Rect& set_alpha(float alpha);
|
||||
|
||||
Color color() const;
|
||||
virtual bool supports_border() const override;
|
||||
|
||||
private:
|
||||
void on_update(float dt) override;
|
||||
|
||||
22
include/Cubed/ui/row_layout.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
class RowLayout : public Widget {
|
||||
public:
|
||||
RowLayout(Widget* parent);
|
||||
~RowLayout();
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
|
||||
RowLayout& set_spacing(int spacing);
|
||||
|
||||
void layout();
|
||||
|
||||
private:
|
||||
int m_spacing = 0;
|
||||
float m_content_height = 0.0f;
|
||||
float m_content_width = 0.0f;
|
||||
void on_update(float dt) override;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -2,9 +2,11 @@
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/ui/anchor.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
|
||||
@@ -31,7 +33,7 @@ public:
|
||||
virtual void render(Renderer& renderer);
|
||||
virtual Widget& set_anchor(Anchor anchor);
|
||||
virtual Widget& set_offset(glm::ivec2 offset);
|
||||
virtual Widget& set_window_size(int width, int height);
|
||||
static void set_window_size(int width, int height);
|
||||
virtual Widget& set_visible(bool visible);
|
||||
// Returns the final display size
|
||||
|
||||
@@ -43,7 +45,9 @@ public:
|
||||
virtual Widget& set_fill_parent(bool fill);
|
||||
virtual Widget& set_fill_width(bool fill);
|
||||
virtual Widget& set_fill_height(bool fill);
|
||||
|
||||
virtual Widget& set_border_size(int size);
|
||||
virtual Widget& set_border_visale(bool visable);
|
||||
virtual bool supports_border() const;
|
||||
virtual glm::vec2 pos() const;
|
||||
|
||||
virtual bool handle_key_event(const KeyEvent& e);
|
||||
@@ -63,16 +67,17 @@ public:
|
||||
|
||||
protected:
|
||||
Widget* m_parent = nullptr;
|
||||
float m_window_height = 0.0f;
|
||||
float m_window_width = 0.0f;
|
||||
float m_width = 0.0f;
|
||||
float m_height = 0.0f;
|
||||
static inline float m_window_height = 0.0f;
|
||||
static inline float m_window_width = 0.0f;
|
||||
|
||||
// Center is at the top-left corner, position is at the top-left corner
|
||||
Anchor m_anchor = Anchor::TOP_LEFT;
|
||||
bool m_visible = true;
|
||||
bool m_fill_parent = false;
|
||||
bool m_fill_height = false;
|
||||
bool m_fill_width = false;
|
||||
bool m_show_border = false;
|
||||
int m_border_size = 5;
|
||||
glm::ivec2 m_offset{0, 0};
|
||||
|
||||
std::vector<std::unique_ptr<Widget>>& children();
|
||||
@@ -82,9 +87,22 @@ protected:
|
||||
virtual void on_update(float dt);
|
||||
virtual void on_render(Renderer& renderer);
|
||||
virtual glm::vec2 compute_position() const;
|
||||
virtual void update_border();
|
||||
void set_width_internal(float width);
|
||||
void set_height_internal(float height);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Widget>> m_children;
|
||||
/*
|
||||
0 - up
|
||||
1 - down
|
||||
2 - left
|
||||
3 - right
|
||||
*/
|
||||
std::array<std::unique_ptr<Widget>, 4> m_border;
|
||||
TraversalOrder m_order = TraversalOrder::BACK_TO_FRONT;
|
||||
glm::vec2 m_mouse_pos;
|
||||
float m_width = 0.0f;
|
||||
float m_height = 0.0f;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "Cubed/ui/chat_box.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/item_slot.hpp"
|
||||
#include "Cubed/ui/row_layout.hpp"
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
|
||||
@@ -26,10 +28,14 @@ public:
|
||||
|
||||
private:
|
||||
bool handle_key_event(const KeyEvent& e) override;
|
||||
void update_hotbar();
|
||||
WorldScene& m_scene;
|
||||
|
||||
ChatBox* m_chat_box = nullptr;
|
||||
Image* m_mircophone = nullptr;
|
||||
Label* m_disbable_voice = nullptr;
|
||||
RowLayout* m_hotbar = nullptr;
|
||||
|
||||
std::vector<ItemSlot*> m_hotbar_slot;
|
||||
};
|
||||
} // namespace Cubed
|
||||
134
include/entt/config/config.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef ENTT_CONFIG_CONFIG_H
|
||||
#define ENTT_CONFIG_CONFIG_H
|
||||
|
||||
#if __has_include(<entt/ext/config.h>)
|
||||
# include <entt/ext/config.h>
|
||||
#endif
|
||||
|
||||
#include <version>
|
||||
#include "version.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#ifdef ENTT_USE_STL
|
||||
# define ENTT_FORCE_STL
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_exceptions) && !defined(ENTT_NO_EXCEPTION)
|
||||
# define ENTT_THROW throw
|
||||
# define ENTT_TRY try
|
||||
# define ENTT_CATCH catch(...)
|
||||
#else
|
||||
# define ENTT_THROW
|
||||
# define ENTT_TRY if(true)
|
||||
# define ENTT_CATCH if(false)
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_consteval)
|
||||
# define ENTT_CONSTEVAL consteval
|
||||
#else
|
||||
# define ENTT_CONSTEVAL constexpr
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_USE_ATOMIC
|
||||
# include "../stl/atomic.hpp"
|
||||
# define ENTT_MAYBE_ATOMIC(Type) stl::atomic<Type>
|
||||
#else
|
||||
# define ENTT_MAYBE_ATOMIC(Type) Type
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_ID_TYPE
|
||||
# include "../stl/cstdint.hpp"
|
||||
# define ENTT_ID_TYPE stl::uint32_t
|
||||
#else
|
||||
# include "../stl/cstdint.hpp" // provides coverage for types in the std namespace
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_SPARSE_PAGE
|
||||
# define ENTT_SPARSE_PAGE 4096
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_PACKED_PAGE
|
||||
# define ENTT_PACKED_PAGE 1024
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT
|
||||
# define ENTT_ASSERT(condition, msg) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT_CONSTEXPR
|
||||
# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0))
|
||||
#elif !defined ENTT_ASSERT_CONSTEXPR
|
||||
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
|
||||
#endif
|
||||
|
||||
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
|
||||
|
||||
#ifdef ENTT_NO_ETO
|
||||
# define ENTT_ETO_TYPE(Type) void
|
||||
#else
|
||||
# define ENTT_ETO_TYPE(Type) Type
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_NO_MIXIN
|
||||
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
|
||||
#else
|
||||
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_STANDARD_CPP
|
||||
# define ENTT_NONSTD false
|
||||
#else
|
||||
# define ENTT_NONSTD true
|
||||
# if defined __clang__ || defined __GNUC__
|
||||
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
|
||||
# elif defined _MSC_VER
|
||||
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_EXPORT
|
||||
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
|
||||
# define ENTT_EXPORT __declspec(dllexport)
|
||||
# define ENTT_IMPORT __declspec(dllimport)
|
||||
# define ENTT_HIDDEN
|
||||
# elif defined __GNUC__ && __GNUC__ >= 4
|
||||
# define ENTT_EXPORT __attribute__((visibility("default")))
|
||||
# define ENTT_IMPORT __attribute__((visibility("default")))
|
||||
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
|
||||
# else /* Unsupported compiler */
|
||||
# define ENTT_EXPORT
|
||||
# define ENTT_IMPORT
|
||||
# define ENTT_HIDDEN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_API
|
||||
# if defined ENTT_API_EXPORT
|
||||
# define ENTT_API ENTT_EXPORT
|
||||
# elif defined ENTT_API_IMPORT
|
||||
# define ENTT_API ENTT_IMPORT
|
||||
# else /* No API */
|
||||
# define ENTT_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER
|
||||
# pragma detect_mismatch("entt.version", ENTT_VERSION)
|
||||
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
|
||||
# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
|
||||
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
|
||||
#endif
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif
|
||||
11
include/entt/config/macro.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef ENTT_CONFIG_MACRO_H
|
||||
#define ENTT_CONFIG_MACRO_H
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#define ENTT_STR(arg) #arg
|
||||
#define ENTT_XSTR(arg) ENTT_STR(arg)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif
|
||||
18
include/entt/config/version.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef ENTT_CONFIG_VERSION_H
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#define ENTT_VERSION_MAJOR 4
|
||||
#define ENTT_VERSION_MINOR 0
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#endif
|
||||
1026
include/entt/container/dense_map.hpp
Normal file
890
include/entt/container/dense_set.hpp
Normal file
@@ -0,0 +1,890 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_SET_HPP
|
||||
|
||||
#include <compare>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../stl/bit.hpp"
|
||||
#include "../stl/cmath.hpp"
|
||||
#include "../stl/concepts.hpp"
|
||||
#include "../stl/cstddef.hpp"
|
||||
#include "../stl/functional.hpp"
|
||||
#include "../stl/iterator.hpp"
|
||||
#include "../stl/limits.hpp"
|
||||
#include "../stl/memory.hpp"
|
||||
#include "../stl/tuple.hpp"
|
||||
#include "../stl/type_traits.hpp"
|
||||
#include "../stl/utility.hpp"
|
||||
#include "../stl/vector.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
namespace internal {
|
||||
|
||||
static constexpr stl::size_t dense_set_placeholder_position = (stl::numeric_limits<stl::size_t>::max)();
|
||||
|
||||
template<typename It>
|
||||
class dense_set_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_iterator;
|
||||
|
||||
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
|
||||
|
||||
public:
|
||||
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using difference_type = stl::ptrdiff_t;
|
||||
using iterator_category = stl::random_access_iterator_tag;
|
||||
|
||||
constexpr dense_set_iterator() noexcept
|
||||
: it{} {}
|
||||
|
||||
constexpr dense_set_iterator(const It iter) noexcept
|
||||
: it{iter} {}
|
||||
|
||||
template<typename Other>
|
||||
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
|
||||
constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
|
||||
: it{other.it} {}
|
||||
|
||||
constexpr dense_set_iterator &operator++() noexcept {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator++(int) noexcept {
|
||||
const dense_set_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator--() noexcept {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator--(int) noexcept {
|
||||
const dense_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
|
||||
dense_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return it[value].second;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return stl::addressof(operator[](0));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it - other.it;
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr auto operator<=>(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it <=> other.it;
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename It>
|
||||
class dense_set_local_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_local_iterator;
|
||||
|
||||
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
|
||||
|
||||
public:
|
||||
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using difference_type = stl::ptrdiff_t;
|
||||
using iterator_category = stl::forward_iterator_tag;
|
||||
|
||||
constexpr dense_set_local_iterator() noexcept = default;
|
||||
|
||||
constexpr dense_set_local_iterator(It iter, const stl::size_t pos) noexcept
|
||||
: it{iter},
|
||||
offset{pos} {}
|
||||
|
||||
template<typename Other>
|
||||
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
|
||||
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
constexpr dense_set_local_iterator &operator++() noexcept {
|
||||
return offset = it[static_cast<difference_type>(offset)].first, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_local_iterator operator++(int) noexcept {
|
||||
const dense_set_local_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return stl::addressof(it[static_cast<difference_type>(offset)].second);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Other> &other) const noexcept {
|
||||
return offset == other.offset;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr stl::size_t index() const noexcept {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
It it{};
|
||||
stl::size_t offset{dense_set_placeholder_position};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Associative container for unique objects of a given type.
|
||||
*
|
||||
* Internally, elements are organized into buckets. Which bucket an element is
|
||||
* placed into depends entirely on its hash. Elements with the same hash code
|
||||
* appear in the same bucket.
|
||||
*
|
||||
* @tparam Type Value type of the associative container.
|
||||
* @tparam Hash Type of function to use to hash the values.
|
||||
* @tparam KeyEqual Type of function to use to compare the values for equality.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||
class dense_set {
|
||||
static constexpr float default_threshold = 0.875f;
|
||||
static constexpr stl::size_t minimum_capacity = 8u;
|
||||
static constexpr stl::size_t placeholder_position = internal::dense_set_placeholder_position;
|
||||
|
||||
using node_type = stl::pair<stl::size_t, Type>;
|
||||
using alloc_traits = stl::allocator_traits<Allocator>;
|
||||
static_assert(stl::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||
using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
|
||||
using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
|
||||
|
||||
[[nodiscard]] stl::size_t value_to_bucket(const auto &value) const noexcept {
|
||||
return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
|
||||
}
|
||||
|
||||
[[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return begin() + static_cast<iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) const {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return cbegin() + static_cast<const_iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
return cend();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
|
||||
const auto index = value_to_bucket(value);
|
||||
|
||||
if(auto it = constrained_find(value, index); it != end()) {
|
||||
return stl::make_pair(it, false);
|
||||
}
|
||||
|
||||
packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(value));
|
||||
sparse.first()[index] = packed.first().size() - 1u;
|
||||
rehash_if_required();
|
||||
|
||||
return stl::make_pair(--end(), true);
|
||||
}
|
||||
|
||||
void move_and_pop(const stl::size_t pos) {
|
||||
if(const auto last = size() - 1u; pos != last) {
|
||||
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
|
||||
packed.first()[pos] = stl::move(packed.first().back());
|
||||
for(; *curr != last; curr = &packed.first()[*curr].first) {}
|
||||
*curr = pos;
|
||||
}
|
||||
|
||||
packed.first().pop_back();
|
||||
}
|
||||
|
||||
void rehash_if_required() {
|
||||
if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
|
||||
rehash(bc * 2u);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Type;
|
||||
/*! @brief Value type of the container. */
|
||||
using value_type = Type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = stl::size_t;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = stl::ptrdiff_t;
|
||||
/*! @brief Type of function to use to hash the elements. */
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the elements for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::dense_set_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = stl::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = stl::reverse_iterator<const_iterator>;
|
||||
/*! @brief Forward iterator type. */
|
||||
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_set()
|
||||
: dense_set{minimum_capacity} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_set(const allocator_type &allocator)
|
||||
: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_set(const size_type cnt, const allocator_type &allocator)
|
||||
: dense_set{cnt, hasher{}, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator, hash
|
||||
* function and user supplied minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_set{cnt, hash, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator, hash
|
||||
* function, compare function and user supplied minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param equal Compare function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal} {
|
||||
rehash(cnt);
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
dense_set(const dense_set &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_set(const dense_set &other, const allocator_type &allocator)
|
||||
: sparse{stl::piecewise_construct, stl::forward_as_tuple(other.sparse.first(), allocator), stl::forward_as_tuple(other.sparse.second())},
|
||||
packed{stl::piecewise_construct, stl::forward_as_tuple(other.packed.first(), allocator), stl::forward_as_tuple(other.packed.second())},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
dense_set(dense_set &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_set(dense_set &&other, const allocator_type &allocator)
|
||||
: sparse{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))},
|
||||
packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::move(other.packed.second()))},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_set() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_set &operator=(const dense_set &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_set &operator=(dense_set &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_set &other) noexcept {
|
||||
using stl::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return sparse.first().get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* If the array is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||
return packed.first().data();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
return packed.first().data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
return packed.first().data() + packed.first().size();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
return packed.first().data() + packed.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* If the array is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
|
||||
return stl::make_reverse_iterator(cend());
|
||||
}
|
||||
|
||||
/*! @copydoc crbegin */
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
return crbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||
return stl::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crend() const noexcept {
|
||||
return stl::make_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
/*! @copydoc crend */
|
||||
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||
return crend();
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||
return stl::make_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a container is empty.
|
||||
* @return True if the container is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return packed.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum possible number of elements.
|
||||
* @return Maximum possible number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type max_size() const noexcept {
|
||||
return packed.first().max_size();
|
||||
}
|
||||
|
||||
/*! @brief Clears the container. */
|
||||
void clear() noexcept {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into the container, if it does not exist.
|
||||
* @param value An element to insert into the container.
|
||||
* @return A pair consisting of an iterator to the inserted element (or to
|
||||
* the element that prevented the insertion) and a bool denoting whether the
|
||||
* insertion took place.
|
||||
*/
|
||||
stl::pair<iterator, bool> insert(const value_type &value) {
|
||||
return insert_or_do_nothing(value);
|
||||
}
|
||||
|
||||
/*! @copydoc insert */
|
||||
stl::pair<iterator, bool> insert(value_type &&value) {
|
||||
return insert_or_do_nothing(stl::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts elements into the container, if they do not exist.
|
||||
* @param first An iterator to the first element of the range of elements.
|
||||
* @param last An iterator past the last element of the range of elements.
|
||||
*/
|
||||
void insert(stl::input_iterator auto first, stl::input_iterator auto last) {
|
||||
for(; first != last; ++first) {
|
||||
insert(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an element in-place, if it does not exist.
|
||||
*
|
||||
* The element is also constructed when the container already has the key,
|
||||
* in which case the newly constructed object is destroyed immediately.
|
||||
*
|
||||
* @tparam Args Types of arguments to forward to the constructor of the
|
||||
* element.
|
||||
* @param args Arguments to forward to the constructor of the element.
|
||||
* @return A pair consisting of an iterator to the inserted element (or to
|
||||
* the element that prevented the insertion) and a bool denoting whether the
|
||||
* insertion took place.
|
||||
*/
|
||||
template<typename... Args>
|
||||
stl::pair<iterator, bool> emplace(Args &&...args) {
|
||||
if constexpr(((sizeof...(Args) == 1u) && ... && stl::is_same_v<stl::decay_t<Args>, value_type>)) {
|
||||
return insert_or_do_nothing(stl::forward<Args>(args)...);
|
||||
} else {
|
||||
auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::forward<Args>(args)...));
|
||||
const auto index = value_to_bucket(node.second);
|
||||
|
||||
if(auto it = constrained_find(node.second, index); it != end()) {
|
||||
packed.first().pop_back();
|
||||
return stl::make_pair(it, false);
|
||||
}
|
||||
|
||||
stl::swap(node.first, sparse.first()[index]);
|
||||
rehash_if_required();
|
||||
|
||||
return stl::make_pair(--end(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from a given position.
|
||||
* @param pos An iterator to the element to remove.
|
||||
* @return An iterator following the removed element.
|
||||
*/
|
||||
iterator erase(const_iterator pos) {
|
||||
const auto diff = pos - cbegin();
|
||||
erase(*pos);
|
||||
return begin() + diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given elements from a container.
|
||||
* @param first An iterator to the first element of the range of elements.
|
||||
* @param last An iterator past the last element of the range of elements.
|
||||
* @return An iterator following the last removed element.
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
const auto dist = first - cbegin();
|
||||
|
||||
for(auto from = last - cbegin(); from != dist; --from) {
|
||||
erase(packed.first()[static_cast<size_type>(from) - 1u].second);
|
||||
}
|
||||
|
||||
return (begin() + dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the element associated with a given value.
|
||||
* @param value Value of an element to remove.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const value_type &value) {
|
||||
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
|
||||
if(packed.second()(packed.first()[*curr].second, value)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].first;
|
||||
move_and_pop(index);
|
||||
return 1u;
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements matching a value (either 1 or 0).
|
||||
* @param key Key value of an element to search for.
|
||||
* @return Number of elements matching the key (either 1 or 0).
|
||||
*/
|
||||
[[nodiscard]] size_type count(const value_type &key) const {
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements matching a key (either 1 or 0).
|
||||
* @param key Key value of an element to search for.
|
||||
* @return Number of elements matching the key (either 1 or 0).
|
||||
*/
|
||||
[[nodiscard]] size_type count(const auto &key) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return An iterator to an element with the given value. If no such
|
||||
* element is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const value_type &value) {
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const value_type &value) const {
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element that compares _equivalent_ to a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return An iterator to an element with the given value. If no such
|
||||
* element is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const auto &value)
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const auto &value) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return A pair of iterators pointing to the first element and past the
|
||||
* last element of the range.
|
||||
*/
|
||||
[[nodiscard]] stl::pair<iterator, iterator> equal_range(const value_type &value) {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements that compare _equivalent_
|
||||
* to a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return A pair of iterators pointing to the first element and past the
|
||||
* last element of the range.
|
||||
*/
|
||||
[[nodiscard]] stl::pair<iterator, iterator> equal_range(const auto &value)
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const auto &value) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const value_type &value) const {
|
||||
return (find(value) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element that compares
|
||||
* _equivalent_ to a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const auto &value) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
return (find(value) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
|
||||
return {packed.first().data(), sparse.first()[index]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator begin(const size_type index) const {
|
||||
return cbegin(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator begin(const size_type index) {
|
||||
return {packed.first().data(), sparse.first()[index]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator end(const size_type index) const {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of buckets.
|
||||
* @return The number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_count() const {
|
||||
return sparse.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum number of buckets.
|
||||
* @return The maximum number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type max_bucket_count() const {
|
||||
return sparse.first().max_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a given bucket.
|
||||
* @param index The index of the bucket to examine.
|
||||
* @return The number of elements in the given bucket.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_size(const size_type index) const {
|
||||
return static_cast<size_type>(stl::distance(begin(index), end(index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the bucket for a given element.
|
||||
* @param value The value of the element to examine.
|
||||
* @return The bucket for the given element.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket(const value_type &value) const {
|
||||
return value_to_bucket(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the average number of elements per bucket.
|
||||
* @return The average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float load_factor() const {
|
||||
return static_cast<float>(size()) / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum average number of elements per bucket.
|
||||
* @return The maximum average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float max_load_factor() const {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the desired maximum average number of elements per bucket.
|
||||
* @param value A desired maximum average number of elements per bucket.
|
||||
*/
|
||||
void max_load_factor(const float value) {
|
||||
ENTT_ASSERT(value > 0.f, "Invalid load factor");
|
||||
threshold = value;
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param cnt New number of buckets.
|
||||
*/
|
||||
void rehash(const size_type cnt) {
|
||||
auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
|
||||
const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
|
||||
value = value > cap ? value : cap;
|
||||
|
||||
if(const auto sz = stl::bit_ceil(value); sz != bucket_count()) {
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = placeholder_position;
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
const auto index = value_to_bucket(packed.first()[pos].second);
|
||||
packed.first()[pos].first = stl::exchange(sparse.first()[index], pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param cnt New number of elements.
|
||||
*/
|
||||
void reserve(const size_type cnt) {
|
||||
packed.first().reserve(cnt);
|
||||
rehash(static_cast<size_type>(stl::ceil(static_cast<float>(cnt) / max_load_factor())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to hash the elements.
|
||||
* @return The function used to hash the elements.
|
||||
*/
|
||||
[[nodiscard]] hasher hash_function() const {
|
||||
return sparse.second();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to compare elements for equality.
|
||||
* @return The function used to compare elements for equality.
|
||||
*/
|
||||
[[nodiscard]] key_equal key_eq() const {
|
||||
return packed.second();
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold{default_threshold};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
38
include/entt/container/fwd.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef ENTT_CONTAINER_FWD_HPP
|
||||
#define ENTT_CONTAINER_FWD_HPP
|
||||
|
||||
#include "../stl/functional.hpp"
|
||||
#include "../stl/memory.hpp"
|
||||
#include "../stl/utility.hpp"
|
||||
#include "../stl/vector.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename = stl::hash<Key>,
|
||||
typename = stl::equal_to<>,
|
||||
typename = stl::allocator<stl::pair<const Key, Type>>>
|
||||
class dense_map;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = stl::hash<Type>,
|
||||
typename = stl::equal_to<>,
|
||||
typename = stl::allocator<Type>>
|
||||
class dense_set;
|
||||
|
||||
template<typename...>
|
||||
class basic_table;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Element types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using table = basic_table<stl::vector<Type>...>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||