Improve random Chunk lookup in client

This commit is contained in:
mrkubax10 2024-05-03 16:38:57 +02:00
parent 5b25027e51
commit 3b4f29f53f
5 changed files with 111 additions and 52 deletions

View File

@ -25,9 +25,6 @@ SOFTWARE.
#ifndef POLYGUN_WORLD_CHUNK_MANAGER_BASE_HPP
#define POLYGUN_WORLD_CHUNK_MANAGER_BASE_HPP
#include <vector>
#include <memory>
#include "common/math/rect3d.hpp"
namespace polygun::world {
@ -45,9 +42,6 @@ namespace polygun::world {
math::Rect3D get_node_bbox(const math::Vector3i& node_pos);
virtual Chunk& get_chunk(const math::Vector3i& pos) = 0;
protected:
std::vector<std::unique_ptr<Chunk>> m_loaded_chunks;
};
}

View File

@ -24,6 +24,8 @@ SOFTWARE.
#include "game/world/chunk_manager.hpp"
#include <cstring>
#include "common/network/network_manager.hpp"
#include "game/engine/engine.hpp"
#include "game/engine/game_config.hpp"
@ -31,6 +33,10 @@ SOFTWARE.
#include "game/renderer/shader.hpp"
#include "game/world/local_player.hpp"
// TODO: Load this from GameConfig
#define VIEW_RANGE 2
#define VIEW_AREA_SIZE VIEW_RANGE*2
using namespace polygun::world;
ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture_atlas) :
@ -38,14 +44,17 @@ ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture
world::ChunkManagerBase(),
m_engine(engine),
m_texture_atlas(texture_atlas),
m_loaded_area(math::Rect3D::with_center(math::Vector3f(0), math::Vector3f(VIEW_AREA_SIZE))),
m_chunk_cache(-1),
m_pending_chunks(),
m_loaded_chunks(new Chunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
m_dummy_chunk(engine, this, 0.0f),
m_updated_chunks(0),
m_greedy_chunk_shader(engine->get_master_renderer()->create_shader()),
m_chunk_shader(engine->get_master_renderer()->create_shader()),
m_network_manager(nullptr)
{
memset(m_loaded_chunks, 0, VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE*sizeof(Chunk*));
m_greedy_chunk_shader->load_from_file("greedy_chunk");
m_chunk_shader->load_from_file("chunk");
}
@ -53,6 +62,8 @@ ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture
ChunkManager::~ChunkManager() {
delete m_chunk_shader;
delete m_greedy_chunk_shader;
unload_all_chunks();
delete[] m_loaded_chunks;
}
void ChunkManager::on_packet(network::NetworkPacket& packet) {
@ -68,12 +79,15 @@ void ChunkManager::on_packet(network::NetworkPacket& packet) {
if(it==m_pending_chunks.end())
break;
m_pending_chunks.erase(it);
std::unique_ptr<Chunk> chunk = std::make_unique<Chunk>(m_engine, this, m_texture_atlas.get_texture_unit_size(), false);
if(!m_loaded_area.contains(pos.convert<float>()))
break;
Chunk* const chunk = new Chunk(m_engine, this, m_texture_atlas.get_texture_unit_size(), false);
chunk->set_pos(pos);
chunk->set_modified(true);
packet.read(chunk.get());
m_loaded_chunks.push_back(std::move(chunk));
packet.read(chunk);
LOG_VERBOSE("Received data for chunk at %d %d %d", pos[0], pos[1], pos[2]);
pos-=m_loaded_area.get_pos().convert<int>();
m_loaded_chunks[pos[2]*VIEW_AREA_SIZE*VIEW_AREA_SIZE+pos[1]*VIEW_AREA_SIZE+pos[0]] = chunk;
m_updated_chunks++;
break;
}
@ -108,29 +122,75 @@ void ChunkManager::on_packet(network::NetworkPacket& packet) {
}
void ChunkManager::on_local_player_move(const LocalPlayer& player) {
// TODO: Make sure that chunk visible by other local players won't get unloaded
math::Vector3f pos = player.get_position();
pos[0] = std::floor(pos[0]/Chunk::CHUNK_SIZE);
pos[1] = std::floor(pos[1]/Chunk::CHUNK_SIZE);
pos[2] = std::floor(pos[2]/Chunk::CHUNK_SIZE);
// TODO: Load this from GameConfig
const unsigned VIEW_RANGE = 2;
for(int x = pos[0]-VIEW_RANGE; x<=pos[0]+VIEW_RANGE; x++) {
for(int y = pos[1]-VIEW_RANGE; y<=pos[1]+VIEW_RANGE; y++) {
for(int z = pos[2]-VIEW_RANGE; z<=pos[2]+VIEW_RANGE; z++) {
request_chunk(math::Vector3i{x, y, z});
const math::Rect3D new_loaded_area = math::Rect3D::with_center(pos, math::Vector3f(VIEW_AREA_SIZE));
const math::Vector3i diff = new_loaded_area.get_pos().convert<int>()-m_loaded_area.get_pos().convert<int>();
m_loaded_area = new_loaded_area;
acquire();
if(std::abs(diff[0])>=VIEW_AREA_SIZE || std::abs(diff[1])>=VIEW_AREA_SIZE || std::abs(diff[2])>=VIEW_AREA_SIZE)
unload_all_chunks();
else {
if(diff[0]!=0) {
const int start = diff[0]>0?diff[0]:(VIEW_AREA_SIZE+diff[0]-1);
const int end = diff[0]>0?VIEW_AREA_SIZE:0;
for(int x = start; diff[0]>0?(x<end):(x>=end); x+=diff[0]) {
for(int y = 0; y<VIEW_AREA_SIZE; y++) {
for(int z = 0; z<VIEW_AREA_SIZE; z++) {
Chunk*& old_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x-diff[0]];
if(old_chunk_ptr)
delete old_chunk_ptr;
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
}
}
}
}
if(diff[1]!=0) {
const int start = diff[1]>0?diff[1]:(VIEW_AREA_SIZE+diff[1]-1);
const int end = diff[1]>0?VIEW_AREA_SIZE:0;
for(int x = 0; x<VIEW_AREA_SIZE; x++) {
for(int y = start; diff[1]>0?(y<end):(y>=end); y+=diff[1]) {
for(int z = 0; z<VIEW_AREA_SIZE; z++) {
Chunk*& old_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+(y-diff[1])*VIEW_AREA_SIZE+x];
if(old_chunk_ptr)
delete old_chunk_ptr;
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
}
}
}
}
if(diff[2]!=0) {
const int start = diff[2]>0?diff[2]:(VIEW_AREA_SIZE+diff[2]-1);
const int end = diff[2]>0?VIEW_AREA_SIZE:0;
for(int x = 0; x<VIEW_AREA_SIZE; x++) {
for(int y = 0; y<VIEW_AREA_SIZE; y++) {
for(int z = start; diff[2]>0?(z<end):(z>=end); z+=diff[2]) {
Chunk*& old_chunk_ptr = m_loaded_chunks[(z-diff[2])*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
if(old_chunk_ptr)
delete old_chunk_ptr;
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
}
}
}
}
}
// TODO: Make sure that chunk visible by other local players won't get unloaded
// FIXME: Chunk unloading is not symmertical
acquire();
for(size_t i = 0; i<m_loaded_chunks.size(); i++) {
const Chunk& chunk = *m_loaded_chunks[i];
if(std::abs(pos[0]-chunk.get_pos()[0])>VIEW_RANGE || std::abs(pos[1]-chunk.get_pos()[1])>VIEW_RANGE || std::abs(pos[2]-chunk.get_pos()[2])>VIEW_RANGE) {
if(static_cast<size_t>(m_chunk_cache)==i)
m_chunk_cache = -1;
m_loaded_chunks.erase(m_loaded_chunks.begin()+i);
i--;
for(int x = 0; x<VIEW_AREA_SIZE; x++) {
for(int y = 0; y<VIEW_AREA_SIZE; y++) {
for(int z = 0; z<VIEW_AREA_SIZE; z++) {
if(!m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x])
request_chunk(math::Vector3i{x, y, z}+m_loaded_area.get_pos().convert<int>());
}
}
}
release();
@ -138,12 +198,14 @@ void ChunkManager::on_local_player_move(const LocalPlayer& player) {
void ChunkManager::update_chunks() {
acquire();
for(std::unique_ptr<Chunk>& chunk : m_loaded_chunks) {
if(chunk->is_modified()) {
for(unsigned i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
Chunk* const chunk = m_loaded_chunks[i];
if(chunk && chunk->is_modified()) {
chunk->generate_mesh();
chunk->set_modified(false);
m_updated_chunks--;
}
}
release();
}
@ -152,8 +214,9 @@ void ChunkManager::render() {
acquire();
renderer::MeshRenderer* const mesh_renderer = m_engine->get_mesh_renderer();
mesh_renderer->set_shader(engine::GameConfig::get().m_meshing_mode==engine::GameConfig::MeshingMode::MESHING_MODE_GREEDY?m_greedy_chunk_shader:m_chunk_shader);
for(std::unique_ptr<Chunk>& chunk : m_loaded_chunks) {
if(!chunk->has_mesh())
for(unsigned i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
Chunk* const chunk = m_loaded_chunks[i];
if(!chunk || !chunk->has_mesh())
continue;
mesh_renderer->translate(chunk->get_pos().convert<float>()*math::Vector3f(Chunk::CHUNK_SIZE));
chunk->render(m_texture_atlas.get_atlas_texture());
@ -178,14 +241,6 @@ void ChunkManager::try_fill_node(const math::Vector3i& from, const math::Vector3
void ChunkManager::request_chunk(const math::Vector3i& pos) {
acquire();
std::vector<std::unique_ptr<Chunk>>::iterator it = std::find_if(m_loaded_chunks.begin(), m_loaded_chunks.end(), [pos](const std::unique_ptr<Chunk>& chunk) {
return chunk->get_pos()==pos;
});
if(it!=m_loaded_chunks.end()) {
release();
return;
}
std::vector<math::Vector3i>::iterator it2 = std::find(m_pending_chunks.begin(), m_pending_chunks.end(), pos);
if(it2!=m_pending_chunks.end()) {
release();
@ -201,22 +256,26 @@ void ChunkManager::request_chunk(const math::Vector3i& pos) {
Chunk& ChunkManager::get_chunk(const math::Vector3i& pos) {
acquire();
Chunk* const chunk = get_loaded_chunk(pos);
release();
if(!chunk)
if(!m_loaded_area.contains(pos.convert<float>())) {
release();
return m_dummy_chunk;
}
const math::Vector3i local_chunk_pos = pos-m_loaded_area.get_pos().convert<int>();
Chunk* chunk = m_loaded_chunks[local_chunk_pos[2]*VIEW_AREA_SIZE*VIEW_AREA_SIZE+local_chunk_pos[1]*VIEW_AREA_SIZE+local_chunk_pos[0]];
if(!chunk)
chunk = &m_dummy_chunk;
release();
return *chunk;
}
Chunk* ChunkManager::get_loaded_chunk(const math::Vector3i& pos) {
if(m_chunk_cache>=0 && m_loaded_chunks[m_chunk_cache]->get_pos()==pos)
return m_loaded_chunks[m_chunk_cache].get();
std::vector<std::unique_ptr<Chunk>>::iterator it = std::find_if(m_loaded_chunks.begin(), m_loaded_chunks.end(), [pos](const std::unique_ptr<Chunk>& chunk) {
return chunk->get_pos()==pos;
});
if(it!=m_loaded_chunks.end()) {
m_chunk_cache = m_loaded_chunks.end()-it;
return it->get();
void ChunkManager::unload_all_chunks() {
for(size_t i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
if(m_loaded_chunks[i]) {
delete m_loaded_chunks[i];
m_loaded_chunks[i] = nullptr;
}
}
return nullptr;
}

View File

@ -68,8 +68,10 @@ namespace polygun::world {
private:
engine::Engine* m_engine;
engine::TextureAtlas& m_texture_atlas;
math::Rect3D m_loaded_area;
int m_chunk_cache;
std::vector<math::Vector3i> m_pending_chunks;
Chunk** m_loaded_chunks;
Chunk m_dummy_chunk;
unsigned m_updated_chunks;
renderer::Shader* m_greedy_chunk_shader;
@ -77,7 +79,7 @@ namespace polygun::world {
network::NetworkManager* m_network_manager;
private:
Chunk* get_loaded_chunk(const math::Vector3i& pos);
void unload_all_chunks();
};
}

View File

@ -47,7 +47,8 @@ ChunkManager::ChunkManager(Server& server) :
m_map_read_only(true),
m_last_write(time(nullptr)),
m_map_chunk_count(0),
m_map_chunk_headers_array_offset(0)
m_map_chunk_headers_array_offset(0),
m_loaded_chunks()
{}
ChunkManager::~ChunkManager() {

View File

@ -27,7 +27,9 @@ SOFTWARE.
#include "common/world/chunk_manager_base.hpp"
#include <memory>
#include <string>
#include <vector>
namespace polygun::server {
class Server;
@ -50,6 +52,7 @@ namespace polygun::server {
time_t m_last_write;
uint32_t m_map_chunk_count;
uint64_t m_map_chunk_headers_array_offset;
std::vector<std::unique_ptr<world::Chunk>> m_loaded_chunks;
private:
void load_header();