3 Commits

Author SHA1 Message Date
1af41db615 refactor(dev_panel): remove seed editing and server rebuild world, move client rebuild
- Remove text editing for perlin seed and filter function
- Remove ServerWorld::rebuild_world() method and its atomic flag
- Move 'Rebuild World' button to client world tab
- Remove unused perlin_noise_input_buffer
2026-06-30 18:36:22 +08:00
1e17474122 refactor(math_tools): convert free functions to inline in header 2026-06-30 17:44:31 +08:00
b24ce458dc perf(world): cull distant players from updates and rendering
Add distance2 utility function. On client, skip rendering other players if distance squared exceeds rendering distance. On server, only send player position and block change updates to players in relevant chunks.
2026-06-30 17:41:12 +08:00
8 changed files with 132 additions and 195 deletions

View File

@@ -26,9 +26,6 @@ class DevPanel {
int gait = 0;
float pos[3] = {0.0f, 0.0f, 0.0f};
};
struct TextEditing {
bool perlin_seed = false;
};
public:
DevPanel(App& app);
@@ -40,7 +37,6 @@ private:
ConfigView m_config;
ClientPlayer* m_player;
PlayerProfile m_player_profile;
TextEditing m_text_editing;
bool m_need_save_config = false;
bool m_gen_thread_running = true;
int m_theme = 0;

View File

@@ -32,7 +32,6 @@ public:
void update();
void hot_reload();
void rebuild_world();
int rendering_distance() const;
void rendering_distance(int rendering_distance);
void start_gen_thread();
@@ -131,7 +130,6 @@ private:
std::atomic<bool> m_could_gen{true};
std::atomic<bool> m_gen_running{false};
std::atomic<bool> m_need_gen_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<bool> m_init{false};
std::atomic<bool> m_stopped{false};
std::atomic<int> m_rendering_distance{24};

View File

@@ -1,18 +1,108 @@
#pragma once
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace Cubed {
namespace Math {
void extract_frustum_planes(const glm::mat4& mvp_matrix,
std::vector<glm::vec4>& planes);
inline void extract_frustum_planes(const glm::mat4& mvp_matrix,
std::vector<glm::vec4>& planes) {
if (planes.size() != 6) {
planes.resize(6);
}
float smootherstep(float edge0, float edge1, float x);
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
const std::vector<glm::vec4>& planes);
float deterministic_random(int x, int z, uint64_t seed);
glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t);
const float* m = glm::value_ptr(mvp_matrix);
// left plane
planes[0] =
glm::vec4(m[3] + m[0], m[7] + m[4], m[11] + m[8], m[15] + m[12]);
// right plane
planes[1] =
glm::vec4(m[3] - m[0], m[7] - m[4], m[11] - m[8], m[15] - m[12]);
// bottom plane
planes[2] =
glm::vec4(m[3] + m[1], m[7] + m[5], m[11] + m[9], m[15] + m[13]);
// top plane
planes[3] =
glm::vec4(m[3] - m[1], m[7] - m[5], m[11] - m[9], m[15] - m[13]);
// near plane
planes[4] =
glm::vec4(m[3] + m[2], m[7] + m[6], m[11] + m[10], m[15] + m[14]);
// far plane
planes[5] =
glm::vec4(m[3] - m[2], m[7] - m[6], m[11] - m[10], m[15] - m[14]);
for (auto& p : planes) {
p = glm::normalize(p);
}
}
inline float smootherstep(float edge0, float edge1, float x) {
x = std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f);
}
inline bool is_aabb_in_frustum(const glm::vec3& center,
const glm::vec3& half_extents,
const std::vector<glm::vec4>& planes) {
for (const auto& plane : planes) {
// distance
float d = glm::dot(glm::vec3(plane), center) + plane.w;
float r = half_extents.x * std::abs(plane.x) +
half_extents.y * std::abs(plane.y) +
half_extents.z * std::abs(plane.z);
if (d + r < 0) {
return false;
}
}
return true;
}
inline float deterministic_random(int x, int z, uint64_t seed) {
uint64_t h = seed;
h = h * 6364136223846793005ULL + (uint64_t)x;
h = h * 6364136223846793005ULL + (uint64_t)z;
return (float)(h >> 40) / (float)(1 << 24);
}
inline glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t) {
float cos_theta = glm::clamp(glm::dot(from, to), -1.0f, 1.0f);
if (cos_theta > 0.9995f) {
return glm::normalize(glm::mix(from, to, t));
}
if (cos_theta < -0.9995f) {
glm::vec3 axis = (std::fabs(from.x) < 0.9f)
? glm::vec3(1.0f, 0.0f, 0.0f)
: glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 ortho = glm::normalize(glm::cross(from, axis));
float angle = glm::pi<float>() * t;
glm::vec3 rotated =
from * std::cos(angle) + glm::cross(ortho, from) * std::sin(angle);
return glm::normalize(rotated);
}
float theta = std::acos(cos_theta);
float sin_theta = std::sin(theta);
float a = std::sin((1.0f - t) * theta) / sin_theta;
float b = std::sin(t * theta) / sin_theta;
return glm::normalize(a * from + b * to);
}
inline float distance2(const glm::vec3& a, const glm::vec3& b) {
glm::vec3 diff = a - b;
return glm::dot(diff, diff);
}
} // namespace Math

View File

@@ -15,7 +15,6 @@ target_sources(${PROJECT_NAME}
shader.cpp
texture_manager.cpp
tools/cubed_random.cpp
tools/math_tools.cpp
tools/shader_tools.cpp
tools/font.cpp
tools/perlin_noise.cpp

View File

@@ -17,7 +17,6 @@ static constexpr const char* THEMES[] = {"Dark", "Light"};
static constexpr const char* GAITS[] = {"Walk", "Run"};
static constexpr const char* GAME_MODES[] = {"Creative", "Spectator"};
static constexpr const char* CHUNK_LOAD_STYLE[] = {"Random", "Center"};
static char perlin_noise_input_buffer[64];
constexpr float TEMP_MIN = 0.0f;
constexpr float TEMP_MAX = 1.0f;
@@ -47,16 +46,6 @@ constexpr float DELTA_ANGLE_MAX = 30.0f;
constexpr int PATH_STEP_MIN = 1;
constexpr int PATH_STEP_MAX = 1000;
static int filter_unsigned(ImGuiInputTextCallbackData* data) {
if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) {
char c = data->EventChar;
if (c < '0' || c > '9') {
return 1;
}
}
return 0;
}
DevPanel::DevPanel(App& app) : m_app(app) {}
void DevPanel::init() {
@@ -483,25 +472,8 @@ void DevPanel::show_world_tab_item() {
}
void DevPanel::show_server_world_table_bar() {
if (m_text_editing.perlin_seed) {
if (ImGui::InputText("ChunkGenerator Seed", perlin_noise_input_buffer,
sizeof(perlin_noise_input_buffer),
ImGuiInputTextFlags_CallbackCharFilter |
ImGuiInputTextFlags_EnterReturnsTrue,
filter_unsigned)) {
ChunkGenerator::seed(static_cast<unsigned int>(
std::strtoul(perlin_noise_input_buffer, nullptr, 10)));
m_text_editing.perlin_seed = false;
m_player->set_player_pos({0.0f, 255.0f, 0.0f});
m_app.server_world().rebuild_world();
}
}
if (!m_text_editing.perlin_seed) {
ImGui::Text("ChunkGenerator Seed: %u", ChunkGenerator::seed());
if (ImGui::IsItemClicked()) {
m_text_editing.perlin_seed = true;
}
}
ImGui::Text("ChunkGenerator Seed: %u", ChunkGenerator::seed());
ImGui::Text("Pool Threads %d Max Support Threads %d Reserved Threads %d",
m_app.server_world().gen_pool_threads(),
@@ -525,12 +497,8 @@ void DevPanel::show_server_world_table_bar() {
IM_ARRAYSIZE(CHUNK_LOAD_STYLE))) {
m_app.server_world().set_chunk_load_style(m_chunk_style);
}
if (ImGui::Button("Rebuild World")) {
m_app.server_world().rebuild_world();
}
ImGui::SameLine();
if (ImGui::Button("Request Chunk Build")) {
Logger::warn("This Request Chunk Build button is not finish");
m_app.server_world().need_gen(m_player->get_uuid());
}
ImGui::SameLine();
@@ -572,6 +540,10 @@ void DevPanel::show_client_world_table_bar() {
m_app.client_world().rendering_distance(rendering_distance);
// Config::get().set("world.rendering_distance", rendering_distance);
}
if (ImGui::Button("Rebuild World")) {
m_app.client_world().rebuild_world();
}
ImGui::SameLine();
if (ImGui::Button("Spawn Point")) {
m_player->set_player_pos({0.0f, 255.0f, 0.0f});
}

View File

@@ -3,6 +3,7 @@
#include "Cubed/config.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/packet.hpp"
#include "Cubed/tools/math_tools.hpp"
#include <numbers>
@@ -608,6 +609,11 @@ void ClientWorld::update(float delta_time) {
for (auto& [uuid, player] : m_other_players) {
player.render_pos =
glm::mix(player.render_pos, player.target_pos, 0.15f);
if (Math::distance2(player.render_pos, m_player.get_player_pos()) >
m_rendering_distance * CHUNK_SIZE * m_rendering_distance *
CHUNK_SIZE) {
continue;
}
m_render_player_data.emplace_back(player.name, player.render_pos);
}
}

View File

@@ -504,40 +504,6 @@ void ServerWorld::hot_reload() {
m_rendering_distance = dist <= MAX_DISTANCE ? dist : MAX_DISTANCE;
}
void ServerWorld::rebuild_world() {
if (m_is_rebuilding.exchange(true)) {
return;
}
stop_gen_thread();
stop_thread_pool();
stop_server_thread();
m_cave_carcer.reload(ChunkGenerator::seed());
m_river_worm.reload(ChunkGenerator::seed());
m_chunks.clear();
{
std::lock_guard lock(m_new_chunk_mutex);
m_new_chunks.clear();
}
m_could_gen = true;
ChunkGenerator::reload();
start_thread_pool();
start_gen_thread();
start_server_thread();
Arena arena;
auto* rsp = Arena::Create<S2C_ClearAllChunks>(&arena);
rsp->set_clear(true);
{
std::lock_guard lock(m_player_mutex);
for (auto& [uuid, player] : m_players) {
player.get_session()->send(make_packet(*rsp));
}
}
m_is_rebuilding = false;
}
void ServerWorld::update() {
// poll_finished_chunks();
{
@@ -585,7 +551,7 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
it->second.update_sync_gametick(m_game_ticks);
name = it->second.get_name();
}
ChunkPos pos = get_chunk_pos(x, z);
// update other player pos;
std::vector<std::shared_ptr<Session>> other;
{
@@ -594,11 +560,16 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
if (o_uuid == uuid) {
continue;
}
other.emplace_back(player.get_session());
if (player.has_player(pos)) {
other.emplace_back(player.get_session());
}
}
}
for (auto& session : other) {
if (!session) {
continue;
}
Arena arena;
auto* rsp = Arena::Create<PlayerInfoRsp>(&arena);
rsp->set_uuid(uuid);
@@ -694,7 +665,9 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
}
for (auto& s : sessions) {
s->send(make_packet(*rsp));
if (s) {
s->send(make_packet(*rsp));
}
}
}
@@ -741,15 +714,23 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
pos->set_y(y);
pos->set_z(z);
rsp->set_block(req.block());
std::vector<std::shared_ptr<Session>> sessions;
auto chunk_pos = get_chunk_pos(x, z);
{
std::shared_lock lock(m_player_mutex);
for (auto& [uuid, player] : m_players) {
auto session = player.get_session();
if (session) {
session->send(make_packet(*rsp));
if (player.has_player(chunk_pos)) {
auto session = player.get_session();
sessions.emplace_back(std::move(session));
}
}
}
for (auto& x : sessions) {
if (x) {
x->send(make_packet(*rsp));
}
}
}
int ServerWorld::rendering_distance() const {

View File

@@ -1,105 +0,0 @@
#include "Cubed/tools/math_tools.hpp"
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace Cubed {
namespace Math {
void extract_frustum_planes(const glm::mat4& mvp_matrix,
std::vector<glm::vec4>& planes) {
if (planes.size() != 6) {
planes.resize(6);
}
const float* m = glm::value_ptr(mvp_matrix);
// left plane
planes[0] =
glm::vec4(m[3] + m[0], m[7] + m[4], m[11] + m[8], m[15] + m[12]);
// right plane
planes[1] =
glm::vec4(m[3] - m[0], m[7] - m[4], m[11] - m[8], m[15] - m[12]);
// bottom plane
planes[2] =
glm::vec4(m[3] + m[1], m[7] + m[5], m[11] + m[9], m[15] + m[13]);
// top plane
planes[3] =
glm::vec4(m[3] - m[1], m[7] - m[5], m[11] - m[9], m[15] - m[13]);
// near plane
planes[4] =
glm::vec4(m[3] + m[2], m[7] + m[6], m[11] + m[10], m[15] + m[14]);
// far plane
planes[5] =
glm::vec4(m[3] - m[2], m[7] - m[6], m[11] - m[10], m[15] - m[14]);
for (auto& p : planes) {
p = glm::normalize(p);
}
}
float smootherstep(float edge0, float edge1, float x) {
x = std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f);
}
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
const std::vector<glm::vec4>& planes) {
for (const auto& plane : planes) {
// distance
float d = glm::dot(glm::vec3(plane), center) + plane.w;
float r = half_extents.x * std::abs(plane.x) +
half_extents.y * std::abs(plane.y) +
half_extents.z * std::abs(plane.z);
if (d + r < 0) {
return false;
}
}
return true;
}
float deterministic_random(int x, int z, uint64_t seed) {
uint64_t h = seed;
h = h * 6364136223846793005ULL + (uint64_t)x;
h = h * 6364136223846793005ULL + (uint64_t)z;
return (float)(h >> 40) / (float)(1 << 24);
}
glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t) {
float cos_theta = glm::clamp(glm::dot(from, to), -1.0f, 1.0f);
if (cos_theta > 0.9995f) {
return glm::normalize(glm::mix(from, to, t));
}
if (cos_theta < -0.9995f) {
glm::vec3 axis = (std::fabs(from.x) < 0.9f)
? glm::vec3(1.0f, 0.0f, 0.0f)
: glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 ortho = glm::normalize(glm::cross(from, axis));
float angle = glm::pi<float>() * t;
glm::vec3 rotated =
from * std::cos(angle) + glm::cross(ortho, from) * std::sin(angle);
return glm::normalize(rotated);
}
float theta = std::acos(cos_theta);
float sin_theta = std::sin(theta);
float a = std::sin((1.0f - t) * theta) / sin_theta;
float b = std::sin(t * theta) / sin_theta;
return glm::normalize(a * from + b * to);
}
} // namespace Math
} // namespace Cubed