feat: sync entity direction and rotate models accordingly

Move direction into the transform component and include it in server-to-client entity updates. Client now sets transform direction from the network message and uses it to compute yaw for model rotation, so entities visually face their movement direction. Refactor net_utils to support arbitrary Vec3 fields.
This commit is contained in:
2026-08-04 10:46:31 +08:00
parent 6b1f9d7354
commit e2bbc2d57f
12 changed files with 37 additions and 18 deletions

View File

@@ -35,6 +35,7 @@ private:
struct UpdateInfo {
EntityID id;
glm::vec3 pos;
glm::vec3 direction;
};
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;

View File

@@ -12,8 +12,6 @@ struct BaseServerCreature {
Movement movement{};
Direction direction{};
Gravity gravity{};
Health health{};

View File

@@ -11,9 +11,11 @@ class Camera;
class ModelRender {
public:
ModelRender(Renderer& renderer);
void render_model(ModelID id, const glm::vec3& pos, Camera& camera);
void render_model(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera);
void shadow_pass(ModelID id, const glm::vec3& pos, Camera& camera);
void shadow_pass(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera);
private:
Renderer& m_renderer;

View File

@@ -5,16 +5,19 @@
#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();
inline void set_net_vec3(Vec3* p, const glm::vec3& 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) {
template <Ptr T> void set_net_pos(T ptr, const glm::vec3& pos) {
set_net_vec3(ptr->mutable_pos(), pos);
}
inline glm::vec3 get_net_vec3(const Vec3* p) {
return glm::vec3{p->x(), p->y(), p->z()};
}
inline glm::vec3 get_net_pos(const Vec3& p) {
inline glm::vec3 get_net_vec3(const Vec3& p) {
return glm::vec3{p.x(), p.y(), p.z()};
}
} // namespace Tools

View File

@@ -49,6 +49,7 @@ void ClientEntityManager::handle_entity_update(UpdateInfo& info) {
auto creature = m_registry.try_get<BaseClientCreature>(e);
ASSERT(creature);
creature->transform.position.value = info.pos;
creature->transform.direction.value = info.direction;
}
void ClientEntityManager::receive_entity_create(S2CEntityCreate& s2c) {
@@ -66,7 +67,8 @@ void ClientEntityManager::receive_entity_destory(EntityID id) {
void ClientEntityManager::receive_entity_update(S2CEntityUpdate& msg) {
UpdateInfo e;
e.id = msg.id();
e.pos = Tools::get_net_pos(msg.pos());
e.pos = Tools::get_net_vec3(msg.pos());
e.direction = Tools::get_net_vec3(msg.direction());
m_tasks.emplace(Command::UPDATE, std::move(e));
}

View File

@@ -56,6 +56,9 @@ void ServerEntityManager::update_send() {
p->set_id(entity.id);
Tools::set_net_pos(p, creature.transform.position.value);
Tools::set_net_vec3(p->mutable_direction(),
creature.transform.direction.value);
for (auto& s : sessions) {
s->send(make_packet(p));
}

View File

@@ -875,7 +875,7 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
void ServerWorld::handle_entity_create(C2SEntityCreateRequest& req) {
m_entity_manager.add_entity(req.name(), Tools::get_net_pos(req.pos()));
m_entity_manager.add_entity(req.name(), Tools::get_net_vec3(req.pos()));
}
void ServerWorld::handle_entity_destory(C2SEntityDestoryRequest& req) {
m_entity_manager.destory(req.id());

View File

@@ -13,7 +13,8 @@ void SpeedSystem::update(float dt, entt::registry& registry) {
auto& v = creature.velocity.value;
if (moveboost.count <= moveboost.duration) {
++moveboost.count;
v += creature.direction.value * creature.movement.acceleration;
v += creature.transform.direction.value *
creature.movement.acceleration;
} else {
// Decelerated by friction in all directions
auto decay = [](float& c, float d) {

View File

@@ -29,7 +29,7 @@ void WanderAISystem::do_ai(BaseServerCreature& creature,
MoveBoost& move_boost) {
thread_local Random r{std::random_device()()};
if (r.random_bool(DIRECTION_PROBABILITY)) {
creature.direction.value = r.random_direction_horizontal();
creature.transform.direction.value = r.random_direction_horizontal();
}
if (r.random_bool(MOVE_PROBABILITY)) {

View File

@@ -26,4 +26,5 @@ message C2SEntityCreateRequest {
message S2CEntityUpdate {
uint64 id = 1;
Vec3 pos = 2;
Vec3 direction = 3;
}

View File

@@ -6,20 +6,23 @@
namespace Cubed {
ModelRender::ModelRender(Renderer& renderer) : m_renderer(renderer) {}
void ModelRender::render_model(ModelID id, const glm::vec3& pos,
void ModelRender::render_model(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera) {
auto& root = ModelManager::model(id).node;
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos);
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) *
glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0});
auto& shader = m_renderer.get_shader("model_shader");
glm::mat4 view = camera.get_camera_lookat();
shader.set_loc("proj_matrix", m_renderer.p_mat());
render_node(root, transform, view, shader, false);
}
void ModelRender::shadow_pass(ModelID id, const glm::vec3& pos,
void ModelRender::shadow_pass(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera) {
auto& root = ModelManager::model(id).node;
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos);
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) *
glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0});
auto& shader = m_renderer.get_shader("depth_model");
glm::mat4 view = camera.get_camera_lookat();

View File

@@ -300,8 +300,11 @@ void WorldRenderer::shadow_entity(ClientWorld& world,
auto view = registry.view<BaseClientCreature>();
for (auto entity : view) {
auto& creature = view.get<BaseClientCreature>(entity);
float yaw = std::atan2(creature.transform.direction.value.x,
creature.transform.direction.value.z);
m_renderer.model_renderer().shadow_pass(
creature.model, creature.transform.position.value,
creature.model, creature.transform.position.value, yaw,
world.world_scene().camera());
}
m_player_renderer.render(shader, world, true);
@@ -721,8 +724,10 @@ void WorldRenderer::render_entity(ClientWorld& world) {
auto view = registry.view<BaseClientCreature>();
for (auto entity : view) {
auto& creature = view.get<BaseClientCreature>(entity);
float yaw = std::atan2(creature.transform.direction.value.x,
creature.transform.direction.value.z);
m_renderer.model_renderer().render_model(
creature.model, creature.transform.position.value,
creature.model, creature.transform.position.value, yaw,
world.world_scene().camera());
}