refactor(gameplay): replace entt components with SparseVector for player data

Refactor client world to use a custom SparseVector for player data instead of entt registry. Consolidate Transform, ViewAngles, and related structs into Position, Orientation, WalkPose. Introduce PlayerData and PlayerRenderData. Remove the unused move_system.cpp. Unify player render and shadow render into a single function.
This commit is contained in:
2026-07-27 20:21:44 +08:00
parent 6d0e60e241
commit 15d5af34b9
9 changed files with 477 additions and 208 deletions

View File

@@ -96,6 +96,5 @@ target_sources(${PROJECT_NAME}
render/model_manager.cpp
render/model_renderer.cpp
gameplay/chunk.cpp
gameplay/move_system.cpp
gameplay/hitbox_manager.cpp
)

View File

@@ -295,13 +295,11 @@ void ClientWorld::report_block_change(const glm::ivec3& pos,
unsigned id) const {
if (id != 0) {
AABB block_box = get_block_aabb(pos);
std::shared_lock lock(m_registry_mutex);
std::shared_lock lock(m_players_date_mutex);
for (auto& [key, player] : m_player_entities) {
auto [transform, entity_info] =
m_registry.get<Transform, EntityInfo>(player);
for (const auto& player : m_players_data) {
AABB box = ClientPlayer::get_aabb(transform.pos);
AABB box = ClientPlayer::get_aabb(player.pos.value);
if (box.intersects(block_box)) {
return;
}
@@ -333,22 +331,31 @@ void ClientWorld::receive_remote_player(const PlayerInfoRsp& rsp) {
auto pitch = rsp.pitch();
auto yaw = rsp.yaw();
{
std::lock_guard lock(m_registry_mutex);
std::lock_guard lock(m_players_date_mutex);
glm::vec3 pos{rsp.pos().x(), rsp.pos().y(), rsp.pos().z()};
auto it = m_player_entities.find(rsp.uuid());
if (it == m_player_entities.end()) {
auto entity =
create_entity(EntityInfo{rsp.name(), rsp.uuid()},
Transform{pos, pos, get_gait_from_id(rsp.gait())},
ViewAngles{yaw, yaw, pitch, pitch});
m_player_entities.try_emplace(rsp.uuid(), entity);
auto it = m_players_handle.find(rsp.uuid());
if (it == m_players_handle.end()) {
PlayerData data{};
data.entity.name = rsp.name();
data.entity.uuid = rsp.uuid();
data.angle.pitch = pitch;
data.angle.yaw = yaw;
data.render_angle.pitch = pitch;
data.render_angle.yaw = yaw;
data.pos.value = pos;
data.render_pos.value = pos;
data.walk.gait = get_gait_from_id(rsp.gait());
auto handle = m_players_data.emplace(std::move(data));
m_players_handle.try_emplace(rsp.uuid(), handle);
} else {
auto& transform = m_registry.get<Transform>(it->second);
transform.pos = pos;
transform.gait = get_gait_from_id(rsp.gait());
auto& view_angles = m_registry.get<ViewAngles>(it->second);
view_angles.yaw = yaw;
view_angles.pitch = pitch;
auto it = m_players_handle.find(rsp.uuid());
if (it != m_players_handle.end()) {
auto& data = m_players_data[it->second];
data.pos.value = pos;
data.walk.gait = get_gait_from_id(rsp.gait());
data.angle.yaw = yaw;
data.angle.pitch = pitch;
}
}
// Logger::info("Player {} pos Update", rsp.name());
}
@@ -364,13 +371,13 @@ void ClientWorld::receive_player_logout(const LogoutRsp& rsp) {
return;
}
{
std::lock_guard lock(m_registry_mutex);
auto it = m_player_entities.find(rsp.uuid());
if (it == m_player_entities.end()) {
std::lock_guard lock(m_players_date_mutex);
auto it = m_players_handle.find(rsp.uuid());
if (it == m_players_handle.end()) {
Logger::warn("Player {} not find", rsp.uuid());
} else {
m_registry.destroy(it->second);
m_player_entities.erase(rsp.uuid());
m_players_data.erase(it->second);
m_players_handle.erase(it);
Logger::info("Player {} erase", rsp.uuid());
}
}
@@ -480,8 +487,6 @@ void ClientWorld::init(std::string_view player_name,
Logger::info("Send Login Request");
m_client->send(make_packet(req), 0);
m_audio.play_bgm();
create_entity(Transform{glm::vec3{0.0f, 100.0f, 0.0f}},
Model{"model/creature/pig.glb"});
}
void ClientWorld::receive_login_rsp(LoginRsp& rsp) {
@@ -876,43 +881,43 @@ void ClientWorld::update(float delta_time) {
void ClientWorld::update_players(float dt) {
auto update_renderinfo = [this, dt](entt::entity player) {
auto& transform = m_registry.get<Transform>(player);
transform.render_pos =
glm::mix(transform.render_pos, transform.pos, 0.15f);
if (Math::distance2(transform.render_pos, m_player.get_player_pos()) >
auto update_renderinfo = [this, dt](PlayerData& player) {
player.render_pos.value =
glm::mix(player.render_pos.value, player.pos.value, 0.15f);
if (Math::distance2(player.render_pos.value,
m_player.get_player_pos()) >
m_rendering_distance * CHUNK_SIZE * m_rendering_distance *
CHUNK_SIZE) {
return;
}
auto& view_angles = m_registry.get<ViewAngles>(player);
view_angles.render_yaw =
glm::mix(view_angles.render_yaw, view_angles.yaw, 0.15);
view_angles.render_pitch =
glm::mix(view_angles.render_pitch, view_angles.pitch, 0.15);
player.render_angle.yaw =
glm::mix(player.render_angle.yaw, player.angle.yaw, 0.15);
player.render_angle.pitch =
glm::mix(player.render_angle.pitch, player.angle.pitch, 0.15);
if (transform.gait == Gait::WALK || transform.gait == Gait::RUN) {
if (player.walk.gait == Gait::WALK || player.walk.gait == Gait::RUN) {
transform.walk_time += dt;
player.walk.walk_time += dt;
float speed = transform.gait == Gait::RUN ? 14.0f : 8.0f;
float amp = transform.gait == Gait::RUN ? 50.0f : 35.0f;
float speed = player.walk.gait == Gait::RUN ? 14.0f : 8.0f;
float amp = player.walk.gait == Gait::RUN ? 50.0f : 35.0f;
// float amp = 90.0f;
view_angles.angle =
glm::sin(transform.walk_time * speed) * glm::radians(amp);
} else if (transform.gait == Gait::STOP) {
player.render_angle.roll =
glm::sin(player.walk.walk_time * speed) * glm::radians(amp);
} else if (player.walk.gait == Gait::STOP) {
float t = glm::clamp(dt * 10.0f, 0.0f, 1.0f);
view_angles.angle = glm::mix(view_angles.angle, 0.0f, t);
player.render_angle.roll =
glm::mix(player.render_angle.roll, 0.0f, t);
}
// walking sound
if (transform.gait == Gait::STOP) {
transform.moving_time = 0.0f;
if (player.walk.gait == Gait::STOP) {
player.walk.moving_time = 0.0f;
} else {
transform.moving_time += dt;
player.walk.moving_time += dt;
}
auto play_walk_sound = [&]() {
glm::ivec3 block = glm::floor(transform.render_pos);
glm::ivec3 block = glm::floor(player.render_pos.value);
block.y -= 1;
BlockType id = get_block_tpye(block);
if (id == 0) {
@@ -920,31 +925,32 @@ void ClientWorld::update_players(float dt) {
}
std::string name = BlockManager::name_form_id(id);
std::string sound = "block/" + name + "/walk.ogg";
m_audio.play_3d(sound, transform.render_pos);
m_audio.play_3d(sound, player.render_pos.value);
};
if (transform.gait == Gait::WALK) {
if (transform.moving_time >= ClientPlayer::WALK_SOUND_INTERVAL) {
transform.moving_time = 0.0f;
if (player.walk.gait == Gait::WALK) {
if (player.walk.moving_time >= ClientPlayer::WALK_SOUND_INTERVAL) {
player.walk.moving_time = 0.0f;
play_walk_sound();
}
}
if (transform.gait == Gait::RUN) {
if (transform.moving_time >= ClientPlayer::RUN_SOUND_INTERVAL) {
transform.moving_time = 0.0f;
if (player.walk.gait == Gait::RUN) {
if (player.walk.moving_time >= ClientPlayer::RUN_SOUND_INTERVAL) {
player.walk.moving_time = 0.0f;
play_walk_sound();
}
}
auto& player_info = m_registry.get<EntityInfo>(player);
m_render_player_data.emplace_back(
player_info.name, player_info.uuid, transform.render_pos,
view_angles.render_yaw, view_angles.render_pitch, transform.gait,
view_angles.angle);
PlayerRenderData render_data;
render_data.render_pos = player.render_pos;
render_data.angle = player.render_angle;
render_data.gait = player.walk.gait;
render_data.info = player.entity;
m_render_player_data.emplace_back(std::move(render_data));
};
{
std::lock_guard lock(m_registry_mutex);
std::lock_guard lock(m_players_date_mutex);
for (auto& [_, player] : m_player_entities) {
for (auto& player : m_players_data) {
update_renderinfo(player);
}
{
@@ -963,11 +969,15 @@ void ClientWorld::update_players(float dt) {
float t = glm::clamp(dt * 10.0f, 0.0f, 1.0f);
angle = glm::mix(angle, 0.0f, t);
}
m_render_player_data.emplace_back(
m_player.get_name(), m_player.get_uuid(),
m_player.get_player_pos(), m_player.yaw(), m_player.pitch(),
m_player.get_gait(), m_player.angle());
PlayerRenderData render_data{};
render_data.render_pos.value = m_player.get_player_pos();
render_data.angle.yaw = m_player.yaw();
render_data.angle.pitch = m_player.pitch();
render_data.angle.roll = angle;
render_data.gait = m_player.get_gait();
render_data.info.name = m_player.get_name();
render_data.info.uuid = m_player.get_uuid();
m_render_player_data.emplace_back(std::move(render_data));
}
}
}

View File

@@ -189,7 +189,8 @@ void PlayerRenderer::init() {
m_inited = true;
}
void PlayerRenderer::render(const Shader& shader, ClientWorld& world) {
void PlayerRenderer::render(const Shader& shader, ClientWorld& world,
bool shadow_render) {
if (!m_inited) {
Logger::error("Player Renderer isn't init");
return;
@@ -205,7 +206,7 @@ void PlayerRenderer::render(const Shader& shader, ClientWorld& world) {
for (auto& player : players) {
if (player.uuid == m_player.get_uuid()) {
if (player.info.uuid == m_player.get_uuid()) {
if (camera.is_first_person()) {
continue;
}
@@ -213,14 +214,14 @@ void PlayerRenderer::render(const Shader& shader, ClientWorld& world) {
glm::mat4 model(1.0f);
model = glm::translate(model, player.render_pos);
model = glm::translate(model, player.render_pos.value);
// model = glm::translate(model, glm::vec3(0.5f, 0.0f, 0.5f));
// glm::rotate(..., +yaw, Y) follows the OpenGL righthanded coordinate
// system, where a positive angle means counterclockwise rotation.
// Therefore the model must use -yaw to align with the direction of
// m_front.
model = glm::rotate(model, glm::radians(-player.yaw + 180.0f),
model = glm::rotate(model, glm::radians(-player.angle.yaw + 180.0f),
glm::vec3(0, 1, 0));
model = glm::translate(model, glm::vec3(-0.5f, 0.0f, -0.5f));
@@ -234,99 +235,16 @@ void PlayerRenderer::render(const Shader& shader, ClientWorld& world) {
return mat;
};
for (int i = 0; i < BODY_PART_NUM; i++) {
switch (i) {
case 0: {
glm::mat4 head_model = model;
head_model = glm::translate(head_model, HEAD_PIVOT);
head_model =
glm::rotate(head_model, glm::radians(-player.pitch),
glm::vec3(1, 0, 0));
head_model = glm::translate(head_model, -HEAD_PIVOT);
glm::mat4 head_mv = m_v_mat * head_model;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(head_mv)));
shader.set_loc("modelMatrix", head_model);
shader.set_loc("mv_matrix", head_mv);
} break;
case 1: {
glm::mat4 mv_mat = m_v_mat * model;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(mv_mat)));
shader.set_loc("modelMatrix", model);
shader.set_loc("mv_matrix", mv_mat);
} break;
case 2: { // left arm
glm::mat4 model_mat =
make_rotated(LEFT_ARM_PIVOT, player.angle);
glm::mat4 mv_mat = m_v_mat * model_mat;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(mv_mat)));
shader.set_loc("modelMatrix", model_mat);
shader.set_loc("mv_matrix", mv_mat);
} break;
case 3: { // right arm
glm::mat4 model_mat =
make_rotated(RIGHT_ARM_PIVOT, -player.angle);
glm::mat4 mv_mat = m_v_mat * model_mat;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(mv_mat)));
shader.set_loc("modelMatrix", model_mat);
shader.set_loc("mv_matrix", mv_mat);
} break;
case 4: { // left leg
glm::mat4 model_mat =
make_rotated(LEFT_LEG_PIVOT, -player.angle);
glm::mat4 mv_mat = m_v_mat * model_mat;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(mv_mat)));
shader.set_loc("modelMatrix", model_mat);
shader.set_loc("mv_matrix", mv_mat);
} break;
case 5: { // right leg
glm::mat4 model_mat =
make_rotated(RIGHT_LEG_PIVOT, player.angle);
auto set_loc = [&](glm::mat4& model_matrix) {
if (!shadow_render) {
glm::mat4 model_view = m_v_mat * model_matrix;
glm::mat4 mv_mat = m_v_mat * model_mat;
shader.set_loc("norm_matrix",
glm::transpose(glm::inverse(mv_mat)));
shader.set_loc("modelMatrix", model_mat);
shader.set_loc("mv_matrix", mv_mat);
} break;
glm::transpose(glm::inverse(model_view)));
shader.set_loc("mv_matrix", model_view);
}
glBindVertexArray(m_vao[i]);
glEnable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size());
}
}
}
void PlayerRenderer::shadow_render(const Shader& shader, ClientWorld& world) {
if (!m_inited) {
Logger::error("Player Renderer isn't init");
return;
}
auto& players = world.render_player_data();
for (auto& player : players) {
glm::mat4 model(1.0f);
model = glm::translate(model, player.render_pos);
// model = glm::translate(model, glm::vec3(0.5f, 0.0f, 0.5f));
model = glm::rotate(model, glm::radians(-player.yaw + 180.0f),
glm::vec3(0, 1, 0));
model = glm::translate(model, glm::vec3(-0.5f, 0.0f, -0.5f));
auto make_rotated = [&](glm::vec3 pivot, float angle) {
glm::mat4 mat = model;
mat = glm::translate(mat, pivot);
mat = glm::rotate(mat, angle, glm::vec3(1, 0, 0));
mat = glm::translate(mat, -pivot);
return mat;
shader.set_loc("modelMatrix", model_matrix);
};
for (int i = 0; i < BODY_PART_NUM; i++) {
@@ -335,33 +253,37 @@ void PlayerRenderer::shadow_render(const Shader& shader, ClientWorld& world) {
glm::mat4 head_model = model;
head_model = glm::translate(head_model, HEAD_PIVOT);
head_model =
glm::rotate(head_model, glm::radians(-player.pitch),
glm::rotate(head_model, glm::radians(-player.angle.pitch),
glm::vec3(1, 0, 0));
head_model = glm::translate(head_model, -HEAD_PIVOT);
shader.set_loc("modelMatrix", head_model);
set_loc(head_model);
} break;
case 1: {
shader.set_loc("modelMatrix", model);
set_loc(model);
} break;
case 2: { // left arm
shader.set_loc("modelMatrix",
make_rotated(LEFT_ARM_PIVOT, player.angle));
glm::mat4 matrix =
make_rotated(LEFT_ARM_PIVOT, player.angle.roll);
set_loc(matrix);
} break;
case 3: { // right arm
shader.set_loc("modelMatrix",
make_rotated(RIGHT_ARM_PIVOT, -player.angle));
glm::mat4 matrix =
make_rotated(RIGHT_ARM_PIVOT, -player.angle.roll);
set_loc(matrix);
} break;
case 4: { // left leg
shader.set_loc("modelMatrix",
make_rotated(LEFT_LEG_PIVOT, -player.angle));
glm::mat4 matrix =
make_rotated(LEFT_LEG_PIVOT, -player.angle.roll);
set_loc(matrix);
} break;
case 5: { // right leg
shader.set_loc("modelMatrix",
glm::mat4 matrix =
make_rotated(RIGHT_LEG_PIVOT, player.angle.roll);
make_rotated(RIGHT_LEG_PIVOT, player.angle));
set_loc(matrix);
} break;
}
glBindVertexArray(m_vao[i]);
glEnable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size());

View File

@@ -3,7 +3,6 @@
#include "Cubed/camera.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/gameplay/entity.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/render/renderer_constants.hpp"
#include "Cubed/scene/world_scene.hpp"
@@ -292,17 +291,18 @@ void WorldRenderer::render_outline(ClientWorld& world) {
void WorldRenderer::shadow_entity(ClientWorld& world,
const glm::mat4& light_matrix) {
auto& registry = world.get_registry();
auto view = registry.view<Transform, Model>();
auto& shader = m_renderer.get_shader("depth_model");
shader.use();
shader.set_loc("lightSpaceMatrix", light_matrix);
for (auto entity : view) {
auto [transform, model] = view.get<Transform, Model>(entity);
m_renderer.model_renderer().shadow_pass(model.name, transform.pos,
world.world_scene().camera());
}
m_player_renderer.shadow_render(shader, world);
/*
auto& registry = world.get_registry();
auto view = registry.view<Transform, Model>();
for (auto entity : view) {
auto [transform, model] = view.get<Transform, Model>(entity);
m_renderer.model_renderer().shadow_pass(model.name, transform.pos,
world.world_scene().camera());
}*/
m_player_renderer.render(shader, world, true);
}
void WorldRenderer::shadow_map_generate(ClientWorld& world) {
@@ -714,15 +714,17 @@ void WorldRenderer::render_entity(ClientWorld& world) {
m_depth_map_texture->bind(0);
glEnable(GL_DEPTH_TEST);
auto& registry = world.get_registry();
/*
auto& registry = world.get_registry();
auto view = registry.view<Transform, Model>();
for (auto entity : view) {
auto [transform, model] = view.get<Transform, Model>(entity);
m_renderer.model_renderer().render_model(model.name, transform.pos,
world.world_scene().camera());
}
*/
m_player_renderer.render(shader, world);
m_player_renderer.render(shader, world, false);
}
glm::vec3 WorldRenderer::quantize_sun_direction(const glm::vec3& lightdir,