Merge branch 'sunlight'

This commit is contained in:
mrkubax10 2024-06-03 11:25:52 +02:00
commit 5c168d342b
23 changed files with 594 additions and 312 deletions

View File

@ -130,6 +130,7 @@ if(BUILD_CLIENT)
src/game/engine/engine.cpp
src/game/engine/game_config.cpp
src/game/engine/greedy_meshing.cpp
src/game/engine/node_side.cpp
src/game/engine/normal_meshing.cpp
src/game/engine/ray.cpp
src/game/engine/resource_manager.cpp
@ -170,6 +171,7 @@ if(BUILD_CLIENT)
src/game/window/mouse.cpp
src/game/window/window.cpp
src/game/world/chunk_manager.cpp
src/game/world/client_chunk.cpp
src/game/world/local_player.cpp
src/game/world/physic_manager.cpp
src/game/world/player_manager.cpp

View File

@ -92,6 +92,7 @@ namespace polygun::math {
typedef Vector<int, 4> Vector4i;
typedef Vector<float, 4> Vector4f;
typedef Vector<unsigned char, 3> RGBColor;
typedef Vector<unsigned char, 4> RGBAColor;
typedef Vector<float, 4> RGBAColorf;
}

View File

@ -29,15 +29,6 @@ SOFTWARE.
#include "common/binary_utils.hpp"
#include "common/math/vector.hpp"
#include "common/logger.hpp"
#if defined(BUILD_CLIENT)
#include "game/renderer/mesh.hpp"
#include "game/engine/content_registry.hpp"
#include "game/engine/engine.hpp"
#include "game/engine/game_config.hpp"
#include "game/engine/greedy_meshing.hpp"
#include "game/engine/normal_meshing.hpp"
#include "game/world/chunk_manager.hpp"
#endif
static void huffman_encode(uint16_t num, std::vector<uint8_t>& output) {
bool next_byte = false;
@ -68,110 +59,11 @@ namespace polygun::world {
m_pos(),
m_last_accessed(time(nullptr)),
m_modified(false),
#if defined(BUILD_CLIENT)
m_copy(false),
m_engine(nullptr),
m_chunk_manager(nullptr),
m_mesh(nullptr),
m_texture_unit_size(0.0f),
m_loading_opacity(0.0f),
m_unloaded(false),
m_chunk_lightmap(),
#endif
m_chunk_data()
{
memset(m_chunk_data, 0, sizeof(m_chunk_data));
}
#if defined(BUILD_CLIENT)
Chunk::Chunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size, bool create_mesh) :
m_pos(),
m_last_accessed(time(nullptr)),
m_modified(false),
m_copy(false),
m_engine(engine),
m_chunk_manager(chunk_manager),
m_mesh(create_mesh?engine->get_master_renderer()->create_mesh():nullptr),
m_texture_unit_size(texture_unit_size),
m_loading_opacity(0.0f),
m_unloaded(false),
m_chunk_lightmap(),
m_chunk_data()
{
memset(m_chunk_lightmap, 0xFF, sizeof(m_chunk_lightmap));
memset(m_chunk_data, 0, sizeof(m_chunk_data));
}
Chunk::Chunk(const Chunk& other) :
m_pos(),
m_last_accessed(other.m_last_accessed),
m_modified(other.m_modified),
m_copy(true),
m_engine(other.m_engine),
m_chunk_manager(other.m_chunk_manager),
m_mesh(other.m_mesh),
m_texture_unit_size(other.m_texture_unit_size),
m_loading_opacity(other.m_loading_opacity),
m_unloaded(false),
m_chunk_lightmap(),
m_chunk_data()
{
memcpy(m_chunk_lightmap, other.m_chunk_lightmap, sizeof(m_chunk_lightmap));
memcpy(m_chunk_data, other.m_chunk_data, sizeof(m_chunk_data));
}
Chunk::~Chunk() {
if(!m_copy && m_mesh)
delete m_mesh;
}
void Chunk::render(renderer::Texture* atlas_texture) {
m_engine->get_mesh_renderer()->render_chunk_mesh(m_mesh, atlas_texture, m_loading_opacity);
}
void Chunk::update(float delta) {
if(m_unloaded && m_loading_opacity>0)
m_loading_opacity-=delta*2.0f;
else if(m_loading_opacity<1.0f)
m_loading_opacity+=delta*2.0f;
}
void Chunk::generate_mesh(engine::ContentRegistry& content_registry) {
if(!m_mesh)
m_mesh = m_engine->get_master_renderer()->create_mesh();
switch(engine::GameConfig::get().m_meshing_mode) {
case engine::GameConfig::MeshingMode::MESHING_MODE_GREEDY:
if(m_engine->get_master_renderer()->supports_greedy_meshing()) {
engine::greedy_meshing meshing;
std::vector<float> vertices;
engine::vertices_indices mesh = meshing.generate_mesh(meshing.merge(*this), *this);
for (size_t i = 0; i < mesh.vertices.size(); i++) {
vertices.push_back(mesh.vertices[i][0]);
vertices.push_back(mesh.vertices[i][1]);
vertices.push_back(mesh.vertices[i][2]);
}
m_mesh->load_vertices(vertices);
m_mesh->load_indices(mesh.indices);
m_mesh->add_custom_buffer(mesh.node_ids.data(), mesh.node_ids.size()*sizeof(float), 1, misc::VarType::VAR_TYPE_FLOAT);
break;
}
[[fallthrough]];
case engine::GameConfig::MeshingMode::MESHING_MODE_NORMAL: {
std::vector<float> vertices;
std::vector<unsigned> indices;
std::vector<float> uvs;
std::vector<float> colors;
engine::normal_meshing::generate_mesh(*this, content_registry, m_texture_unit_size, vertices, indices, uvs, colors);
m_mesh->load_vertices(vertices);
m_mesh->load_uvs(uvs);
m_mesh->load_indices(indices);
m_mesh->load_colors(colors);
break;
}
}
}
#endif
uint8_t Chunk::to_compressed_data(std::vector<uint8_t>& output) const {
// Try CHUNK_COMPRESSION_MODE_FILL
const uint16_t match = m_chunk_data[0];
@ -309,20 +201,6 @@ namespace polygun::world {
return false;
}
#if defined(BUILD_CLIENT)
void Chunk::set_light_value(const math::Vector3i& pos, NodeSide side, uint8_t val) {
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3+side/2;
const unsigned char offset_in_byte = (side%2)*4;
m_chunk_lightmap[index] |= (val<<offset_in_byte)&(0xF<<offset_in_byte);
}
uint8_t Chunk::get_light_value(const math::Vector3i& pos, NodeSide side) const {
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3+side/2;
const unsigned char offset_in_byte = (side%2)*4;
return (m_chunk_lightmap[index]>>offset_in_byte)&0xF;
}
#endif
void Chunk::serialize(network::NetworkPacket& packet) const {
std::vector<uint8_t> compressed_data;
const uint8_t compression_mode = to_compressed_data(compressed_data);

View File

@ -26,63 +26,22 @@ SOFTWARE.
#include "common/network/network_serializable.hpp"
#include <config.hpp>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "common/math/vector.hpp"
#if defined(BUILD_CLIENT)
namespace polygun::renderer {
class Mesh;
class Texture;
}
namespace polygun::engine {
class ContentRegistry;
class Engine;
}
#endif
namespace polygun::world {
#if defined(BUILD_CLIENT)
class ChunkManager;
enum NodeSide {
NODE_SIDE_FRONT,
NODE_SIDE_BACK,
NODE_SIDE_RIGHT,
NODE_SIDE_LEFT,
NODE_SIDE_TOP,
NODE_SIDE_BOTTOM
};
#endif
enum ChunkCompressionMode {
CHUNK_COMPRESSION_MODE_NONE,
CHUNK_COMPRESSION_MODE_HUFFMAN,
CHUNK_COMPRESSION_MODE_FILL
};
// Note: In client code instances of this class shouldn't be copied, otherwise memory after free errors may happen.
class Chunk final : public network::NetworkSerializable {
class Chunk : public network::NetworkSerializable {
public:
Chunk();
#if defined(BUILD_CLIENT)
Chunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size, bool create_mesh = true);
Chunk(const Chunk& other);
~Chunk();
#else
~Chunk() = default;
#endif
#if defined(BUILD_CLIENT)
void render(renderer::Texture* atlas_texture);
void update(float delta);
void generate_mesh(engine::ContentRegistry& content_registry);
void begin_animated_unload() { m_unloaded = true; }
#endif
uint8_t to_compressed_data(std::vector<uint8_t>& output) const;
void load_from_compressed_data(const char* data, uint16_t data_size, uint8_t compression_mode);
@ -97,33 +56,16 @@ namespace polygun::world {
bool is_air(const math::Vector3i& pos) const;
uint16_t get_node(const math::Vector3i& pos) const;
bool is_neighbour_air(const math::Vector3i& pos) const;
#if defined(BUILD_CLIENT)
bool has_mesh() const { return m_mesh; }
float get_loading_opacity() const { return m_loading_opacity; }
void set_light_value(const math::Vector3i& pos, NodeSide side, uint8_t val);
uint8_t get_light_value(const math::Vector3i& pos, NodeSide side) const;
#endif
virtual void serialize(network::NetworkPacket& packet) const override;
virtual bool deserialize(network::NetworkPacket& packet) override;
static const int CHUNK_SIZE = 32;
private:
protected:
math::Vector3i m_pos;
time_t m_last_accessed;
bool m_modified;
#if defined(BUILD_CLIENT)
bool m_copy;
engine::Engine* m_engine;
ChunkManager* m_chunk_manager;
renderer::Mesh* m_mesh;
float m_texture_unit_size;
float m_loading_opacity;
bool m_unloaded;
// Each light value is in 0-15 range so 2 node faces are stored in one byte
uint8_t m_chunk_lightmap[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE*3];
#endif
uint16_t m_chunk_data[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE];
};
}

View File

@ -28,21 +28,6 @@ SOFTWARE.
using namespace polygun::world;
static polygun::math::Vector3i node_pos_to_local_node_pos(const polygun::math::Vector3i& node_pos) {
polygun::math::Vector3i local_node_pos = node_pos%polygun::math::Vector3i(Chunk::CHUNK_SIZE);
for(unsigned char i = 0; i<3; i++) {
if(node_pos[i]<0)
local_node_pos[i] = Chunk::CHUNK_SIZE+(local_node_pos[i]==0?-Chunk::CHUNK_SIZE:local_node_pos[i]);
}
return local_node_pos;
}
void ChunkManagerBase::add_node(uint16_t node, const math::Vector3i& node_pos) {
Chunk& chunk = get_chunk_with_node(node_pos);
chunk.add_node(node, node_pos_to_local_node_pos(node_pos));
chunk.set_modified(true);
}
void ChunkManagerBase::fill_node(uint16_t node, math::Vector3i from, math::Vector3i to) {
for(unsigned char i = 0; i<3; i++) {
if(to[i]<from[i])
@ -58,11 +43,11 @@ void ChunkManagerBase::fill_node(uint16_t node, math::Vector3i from, math::Vecto
}
uint16_t ChunkManagerBase::get_node(const math::Vector3i& node_pos) {
Chunk& chunk = get_chunk_with_node(node_pos);
return chunk.get_node(node_pos_to_local_node_pos(node_pos));
Chunk* const chunk = get_chunk_with_node(node_pos);
return chunk->get_node(node_pos_to_local_node_pos(node_pos));
}
Chunk& ChunkManagerBase::get_chunk_with_node(const math::Vector3i& node_pos) {
Chunk* ChunkManagerBase::get_chunk_with_node(const math::Vector3i& node_pos) {
math::Vector3i chunk_pos = node_pos;
for(unsigned char i = 0; i<3; i++) {
if(node_pos[i]<0) {
@ -75,16 +60,6 @@ Chunk& ChunkManagerBase::get_chunk_with_node(const math::Vector3i& node_pos) {
return get_chunk(chunk_pos);
}
#if defined(BUILD_CLIENT)
void ChunkManagerBase::set_light_value(const math::Vector3i& pos, NodeSide side, uint8_t val) {
get_chunk_with_node(pos).set_light_value(node_pos_to_local_node_pos(pos), side, val);
}
uint8_t ChunkManagerBase::get_light_node(const math::Vector3i& pos, NodeSide side) {
return get_chunk_with_node(pos).get_light_value(node_pos_to_local_node_pos(pos), side);
}
#endif
polygun::math::Rect3D ChunkManagerBase::get_node_bbox(const math::Vector3i& node_pos) {
const uint16_t node_id = get_node(node_pos);
// TODO: Check node definition to see if node isn't solid
@ -92,3 +67,18 @@ polygun::math::Rect3D ChunkManagerBase::get_node_bbox(const math::Vector3i& node
return math::Rect3D(math::Vector3f(INFINITY), math::Vector3f(0));
return math::Rect3D(node_pos.convert<float>(), math::Vector3f(1));
}
polygun::math::Vector3i ChunkManagerBase::node_pos_to_local_node_pos(const polygun::math::Vector3i& node_pos) {
polygun::math::Vector3i local_node_pos = node_pos%polygun::math::Vector3i(Chunk::CHUNK_SIZE);
for(unsigned char i = 0; i<3; i++) {
if(node_pos[i]<0)
local_node_pos[i] = Chunk::CHUNK_SIZE+(local_node_pos[i]==0?-Chunk::CHUNK_SIZE:local_node_pos[i]);
}
return local_node_pos;
}
void ChunkManagerBase::add_node(uint16_t node, const math::Vector3i& node_pos) {
Chunk* const chunk = get_chunk_with_node(node_pos);
chunk->add_node(node, node_pos_to_local_node_pos(node_pos));
chunk->set_modified(true);
}

View File

@ -40,18 +40,18 @@ namespace polygun::world {
public:
virtual ~ChunkManagerBase() = default;
void add_node(uint16_t node, const math::Vector3i& node_pos);
// Note: Copying Vector3i is intended here
void fill_node(uint16_t node, math::Vector3i from, math::Vector3i to);
uint16_t get_node(const math::Vector3i& node_pos);
Chunk& get_chunk_with_node(const math::Vector3i& node_pos);
#if defined(BUILD_CLIENT)
void set_light_value(const math::Vector3i& pos, NodeSide side, uint8_t val);
uint8_t get_light_node(const math::Vector3i& pos, NodeSide side);
#endif
Chunk* get_chunk_with_node(const math::Vector3i& node_pos);
math::Rect3D get_node_bbox(const math::Vector3i& node_pos);
virtual Chunk& get_chunk(const math::Vector3i& pos) = 0;
virtual void add_node(uint16_t node, const math::Vector3i& node_pos);
virtual Chunk* get_chunk(const math::Vector3i& pos) = 0;
protected:
static math::Vector3i node_pos_to_local_node_pos(const polygun::math::Vector3i& node_pos);
};
}

View File

@ -103,6 +103,7 @@ void ContentRegistry::set_registered_node_count(uint16_t registered_node_count)
air_node.m_string_id = "builtin:air";
air_node.m_display_icon = "builtin/texture/air";
air_node.m_invisible = true;
air_node.m_transparent = true;
m_valid_node_defs[0] = true;
release();

View File

@ -25,12 +25,12 @@ SOFTWARE.
#include "game/engine/greedy_meshing.hpp"
#include "common/world/chunk.hpp"
#include "game/world/client_chunk.hpp"
namespace polygun::engine {
cuboid_list greedy_meshing::merge(const world::Chunk& chunk) {
world::Chunk chunk_copy = chunk;
cuboid_list greedy_meshing::merge(const world::ClientChunk& chunk) {
world::ClientChunk chunk_copy = chunk;
cuboid_list cuboids;
cuboid cuboid_to_append;
@ -154,7 +154,7 @@ namespace polygun::engine {
}
static bool is_face_covered(world::Chunk& chunk, int slice_axis_A_start, int slice_axis_A_end, int slice_axis_B_start, int slice_axis_B_end, int slice_axis_C, int face_axis_idx) {
static bool is_face_covered(world::ClientChunk& chunk, int slice_axis_A_start, int slice_axis_A_end, int slice_axis_B_start, int slice_axis_B_end, int slice_axis_C, int face_axis_idx) {
// check if face is fully covered with voxels
// bottom/top face check (y axis)
@ -213,7 +213,7 @@ namespace polygun::engine {
};
vertices_indices greedy_meshing::generate_mesh(const cuboid_list& cuboids, world::Chunk& chunk) {
vertices_indices greedy_meshing::generate_mesh(const cuboid_list& cuboids, world::ClientChunk& chunk) {
std::vector<math::Vector3f> vertices;
std::vector<unsigned int> indices;

View File

@ -31,7 +31,7 @@ SOFTWARE.
#include "common/math/vector.hpp"
namespace polygun::world {
class Chunk;
class ClientChunk;
}
namespace polygun::engine {
@ -54,8 +54,8 @@ namespace polygun::engine {
greedy_meshing() = default;
~greedy_meshing() = default;
vertices_indices generate_mesh(const cuboid_list& cuboids, world::Chunk& chunk);
cuboid_list merge(const world::Chunk& chunk);
vertices_indices generate_mesh(const cuboid_list& cuboids, world::ClientChunk& chunk);
cuboid_list merge(const world::ClientChunk& chunk);
private:
// greedy merging

View File

@ -0,0 +1,40 @@
/*
PolyGun
Copyright (c) 2024 mrkubax10 <mrkubax10@onet.pl>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "game/engine/node_side.hpp"
#include "common/math/vector.hpp"
static const polygun::math::Vector3i g_node_side_offsets[] = {
polygun::math::Vector3i{0, 0, 1},
polygun::math::Vector3i{0, 0, -1},
polygun::math::Vector3i{1, 0, 0},
polygun::math::Vector3i{-1, 0, 0},
polygun::math::Vector3i{0, 1, 0},
polygun::math::Vector3i{0, -1, 0}
};
const polygun::math::Vector3i& polygun::engine::get_node_side_offset(polygun::engine::NodeSide side) {
return g_node_side_offsets[side];
}

View File

@ -0,0 +1,49 @@
/*
PolyGun
Copyright (c) 2024 mrkubax10 <mrkubax10@onet.pl>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef POLYGUN_ENGINE_NODE_SIDE_HPP
#define POLYGUN_ENGINE_NODE_SIDE_HPP
#include <cstddef>
namespace polygun::math {
template <typename T, size_t N>
class Vector;
typedef Vector<int, 3> Vector3i;
}
namespace polygun::engine {
enum NodeSide {
NODE_SIDE_FRONT,
NODE_SIDE_BACK,
NODE_SIDE_RIGHT,
NODE_SIDE_LEFT,
NODE_SIDE_TOP,
NODE_SIDE_BOTTOM,
NODE_SIDE_COUNT
};
const math::Vector3i& get_node_side_offset(NodeSide side);
}
#endif // POLYGUN_ENGINE_NODE_SIDE_HPP

View File

@ -28,8 +28,8 @@ SOFTWARE.
#include "common/logger.hpp"
#include "common/math/vector.hpp"
#include "common/world/chunk.hpp"
#include "game/engine/content_registry.hpp"
#include "game/world/client_chunk.hpp"
static void push_indices(size_t vertices_length, std::vector<unsigned>& output_indices) {
output_indices.push_back(vertices_length-4);
@ -54,16 +54,16 @@ static void push_uvs(float texture_unit_size, unsigned texture_index, std::vecto
output_uvs.push_back(0.0f);
}
static void push_lightmap(float emission, std::vector<float>& output_lightmap) {
static void push_lightmap(polygun::math::RGBColor light_color, std::vector<float>& output_lightmap) {
for(unsigned char i = 0; i<4; i++) {
output_lightmap.push_back(emission);
output_lightmap.push_back(emission);
output_lightmap.push_back(emission);
output_lightmap.push_back(light_color[0]/255.0f);
output_lightmap.push_back(light_color[1]/255.0f);
output_lightmap.push_back(light_color[2]/255.0f);
output_lightmap.push_back(1.0f);
}
}
void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, ContentRegistry& content_registry, float texture_unit_size,
void polygun::engine::normal_meshing::generate_mesh(const world::ClientChunk& chunk, ContentRegistry& content_registry, float texture_unit_size,
std::vector<float>& output_vertices, std::vector<unsigned>& output_indices, std::vector<float>& output_uvs, std::vector<float>& output_lightmap) {
unsigned vertex_count = 0;
for(int x = 0; x<world::Chunk::CHUNK_SIZE; x++) {
@ -111,8 +111,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_LEFT)/15.0f, output_lightmap);
push_lightmap(0.7f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_LEFT), output_lightmap);
}
if(sides[1]) {
output_vertices.push_back(x+1);
@ -134,8 +133,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_RIGHT)/15.0f, output_lightmap);
push_lightmap(0.7f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_RIGHT), output_lightmap);
}
if(sides[2]) {
output_vertices.push_back(x);
@ -157,8 +155,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_BACK)/15.0f, output_lightmap);
push_lightmap(0.7f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_BACK), output_lightmap);
}
if(sides[3]) {
output_vertices.push_back(x);
@ -180,8 +177,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_FRONT)/15.0f, output_lightmap);
push_lightmap(0.7f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_FRONT), output_lightmap);
}
if(sides[4]) {
output_vertices.push_back(x);
@ -203,8 +199,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_BOTTOM)/15.0f, output_lightmap);
push_lightmap(0.3f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_BOTTOM), output_lightmap);
}
if(sides[5]) {
output_vertices.push_back(x);
@ -226,8 +221,7 @@ void polygun::engine::normal_meshing::generate_mesh(const world::Chunk& chunk, C
vertex_count+=4;
push_indices(vertex_count, output_indices);
push_uvs(texture_unit_size, node_id-1, output_uvs);
//push_lightmap(chunk.get_light_value(pos, world::NodeSide::NODE_SIDE_TOP)/15.0f, output_lightmap);
push_lightmap(1.0f, output_lightmap);
push_lightmap(chunk.get_light_value(pos, engine::NodeSide::NODE_SIDE_TOP), output_lightmap);
}
}
}

View File

@ -28,14 +28,14 @@ SOFTWARE.
#include <vector>
namespace polygun::world {
class Chunk;
class ClientChunk;
}
namespace polygun::engine {
class ContentRegistry;
namespace normal_meshing {
void generate_mesh(const world::Chunk& chunk, ContentRegistry& content_registry, float texture_unit_size, std::vector<float>& output_vertices,
void generate_mesh(const world::ClientChunk& chunk, ContentRegistry& content_registry, float texture_unit_size, std::vector<float>& output_vertices,
std::vector<unsigned>& output_indices, std::vector<float>& output_uvs, std::vector<float>& output_lightmap);
}
}

View File

@ -41,13 +41,23 @@ void ThreadHelper::update() {
release();
}
void ThreadHelper::invoke_on_main_thread(const std::function<void()>& callback) {
unsigned ThreadHelper::invoke_on_main_thread(const std::function<void()>& callback) {
if(std::this_thread::get_id()==g_main_thread_id) {
callback();
return;
return 0;
}
acquire();
m_main_thread_callbacks.push_back(callback);
const unsigned callback_index = m_main_thread_callbacks.size();
release();
return callback_index;
}
void ThreadHelper::cancel_main_thread_callback(unsigned index) {
if(index==0)
return;
acquire();
m_main_thread_callbacks.erase(m_main_thread_callbacks.begin()+index-1);
release();
}

View File

@ -37,7 +37,8 @@ namespace polygun::engine {
~ThreadHelper() = default;
void update();
void invoke_on_main_thread(const std::function<void()>& callback);
unsigned invoke_on_main_thread(const std::function<void()>& callback);
void cancel_main_thread_callback(unsigned index);
static void create();
static void cleanup();

View File

@ -95,7 +95,7 @@ void GameSessionScreen::begin() {
m_resource_manager = new engine::ResourceManager(m_engine->get_master_renderer());
m_content_registry = new engine::ContentRegistry;
m_texture_atlas = new engine::TextureAtlas(m_engine->get_master_renderer(), *m_content_registry, *m_resource_manager);
m_chunk_manager.reset(new world::ChunkManager(m_engine, *m_texture_atlas));
m_chunk_manager.reset(new world::ChunkManager(m_engine, *m_texture_atlas, *m_content_registry));
m_player_manager.reset(new world::PlayerManager(m_engine, *m_chunk_manager));
m_physic_manager.reset(new world::PhysicManager(m_chunk_manager.get()));
m_engine->get_mesh_renderer()->invalidate_cache();
@ -147,7 +147,7 @@ void GameSessionScreen::update(double delta) {
m_player_manager->update(delta);
if(m_player_manager->get_master_local_player().has_mode_changed())
switch_hud(m_player_manager->get_master_local_player().get_mode());
m_chunk_manager->update_chunks(delta, *m_content_registry);
m_chunk_manager->update_chunks(delta);
m_physic_manager->update(delta);
default:
break;

View File

@ -27,6 +27,7 @@ SOFTWARE.
#include <cstring>
#include "common/network/network_manager.hpp"
#include "game/engine/content_registry.hpp"
#include "game/engine/engine.hpp"
#include "game/engine/game_config.hpp"
#include "game/engine/texture_atlas.hpp"
@ -40,27 +41,34 @@ SOFTWARE.
using namespace polygun::world;
ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture_atlas) :
ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture_atlas, engine::ContentRegistry& content_registry) :
utils::ThreadSafe(),
world::ChunkManagerBase(),
m_engine(engine),
m_texture_atlas(texture_atlas),
m_content_registry(content_registry),
m_loaded_area_offset(0),
m_pending_chunks(),
m_loaded_chunks(new Chunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
m_loaded_chunks(new ClientChunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
m_unloaded_chunks(),
m_updated_chunks(),
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)
m_network_manager(nullptr),
m_mesh_update_thread_running(true),
m_mesh_update_thread()
{
memset(m_loaded_chunks, 0, VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE*sizeof(Chunk*));
memset(m_loaded_chunks, 0, VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE*sizeof(ClientChunk*));
m_greedy_chunk_shader->load_from_file("greedy_chunk");
m_chunk_shader->load_from_file("chunk");
m_mesh_update_thread.reset(new std::thread(&ChunkManager::mesh_update_thread_func, this));
}
ChunkManager::~ChunkManager() {
m_mesh_update_thread_running = false;
if(m_mesh_update_thread->joinable())
m_mesh_update_thread->join();
delete m_chunk_shader;
delete m_greedy_chunk_shader;
unload_all_chunks();
@ -87,13 +95,13 @@ void ChunkManager::on_packet(network::NetworkPacket& packet) {
return;
}
}
Chunk* const chunk = new Chunk(m_engine, this, m_texture_atlas.get_texture_unit_size(), false);
ClientChunk* const chunk = new ClientChunk(m_engine, this, m_texture_atlas.get_texture_unit_size());
chunk->set_pos(pos);
chunk->set_modified(true);
packet.read(chunk);
LOG_VERBOSE("Received data for chunk at %d %d %d", pos[0], pos[1], pos[2]);
m_loaded_chunks[local_chunk_pos[2]*VIEW_AREA_SIZE*VIEW_AREA_SIZE+local_chunk_pos[1]*VIEW_AREA_SIZE+local_chunk_pos[0]] = chunk;
m_updated_chunks++;
m_updated_chunks.push_back(chunk);
break;
}
case network::NetworkPacketID::PACKET_INVALIDATE_MAP:
@ -151,12 +159,18 @@ void ChunkManager::on_local_player_move(const LocalPlayer& player) {
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]];
ClientChunk*& old_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x-diff[0]];
if(old_chunk_ptr)
unload_chunk_animated(old_chunk_ptr);
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
ClientChunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
if(moved_chunk_ptr) {
if(x==start)
moved_chunk_ptr->set_neighbour(diff[0]>0?engine::NodeSide::NODE_SIDE_LEFT:engine::NodeSide::NODE_SIDE_RIGHT, nullptr);
else if(x==end-(diff[0]>0))
moved_chunk_ptr->set_neighbour(diff[0]>0?engine::NodeSide::NODE_SIDE_RIGHT:engine::NodeSide::NODE_SIDE_LEFT, nullptr);
}
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
moved_chunk_ptr = nullptr;
}
}
}
@ -167,12 +181,18 @@ void ChunkManager::on_local_player_move(const LocalPlayer& player) {
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];
ClientChunk*& old_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+(y-diff[1])*VIEW_AREA_SIZE+x];
if(old_chunk_ptr)
unload_chunk_animated(old_chunk_ptr);
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
ClientChunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
if(moved_chunk_ptr) {
if(y==start)
moved_chunk_ptr->set_neighbour(diff[1]>0?engine::NodeSide::NODE_SIDE_BOTTOM:engine::NodeSide::NODE_SIDE_TOP, nullptr);
else if(y==end-(diff[0]>0))
moved_chunk_ptr->set_neighbour(diff[1]>0?engine::NodeSide::NODE_SIDE_TOP:engine::NodeSide::NODE_SIDE_BOTTOM, nullptr);
}
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
moved_chunk_ptr = nullptr;
}
}
}
@ -183,17 +203,22 @@ void ChunkManager::on_local_player_move(const LocalPlayer& player) {
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];
ClientChunk*& old_chunk_ptr = m_loaded_chunks[(z-diff[2])*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
if(old_chunk_ptr)
unload_chunk_animated(old_chunk_ptr);
Chunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
ClientChunk*& moved_chunk_ptr = m_loaded_chunks[z*VIEW_AREA_SIZE*VIEW_AREA_SIZE+y*VIEW_AREA_SIZE+x];
if(moved_chunk_ptr) {
if(z==start)
moved_chunk_ptr->set_neighbour(diff[2]>0?engine::NodeSide::NODE_SIDE_BACK:engine::NodeSide::NODE_SIDE_FRONT, nullptr);
else if(y==end-(diff[0]>0))
moved_chunk_ptr->set_neighbour(diff[2]>0?engine::NodeSide::NODE_SIDE_FRONT:engine::NodeSide::NODE_SIDE_BACK, nullptr);
}
old_chunk_ptr = moved_chunk_ptr;
moved_chunk_ptr = nullptr;
moved_chunk_ptr = nullptr;
}
}
}
}
}
for(int x = 0; x<VIEW_AREA_SIZE; x++) {
for(int y = 0; y<VIEW_AREA_SIZE; y++) {
@ -206,48 +231,68 @@ void ChunkManager::on_local_player_move(const LocalPlayer& player) {
release();
}
void ChunkManager::update_chunks(float delta, engine::ContentRegistry& content_registry) {
void ChunkManager::update_chunks(float delta) {
acquire();
for(size_t i = 0; i<m_unloaded_chunks.size(); i++) {
Chunk* const chunk = m_unloaded_chunks[i];
ClientChunk* const chunk = m_unloaded_chunks[i];
chunk->update(delta);
if(chunk->get_loading_opacity()<=0.0f) {
if(m_updated_chunks.empty() && chunk->get_loading_opacity()<=0.0f && chunk->get_update_guard().try_lock()) {
delete chunk;
m_unloaded_chunks.erase(m_unloaded_chunks.begin()+i);
i--;
}
}
for(unsigned i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
Chunk* const chunk = m_loaded_chunks[i];
if(chunk) {
if(chunk->is_modified()) {
chunk->generate_mesh(content_registry);
chunk->set_modified(false);
m_updated_chunks--;
break;
}
ClientChunk* const chunk = m_loaded_chunks[i];
if(chunk && !chunk->is_modified())
chunk->update(delta);
}
}
release();
}
void ChunkManager::generate_column_sun_lightmap(const math::Vector2i& pos) {
for(int x = pos[0]*Chunk::CHUNK_SIZE; x<pos[0]*Chunk::CHUNK_SIZE+Chunk::CHUNK_SIZE; x++) {
for(int z = pos[1]*Chunk::CHUNK_SIZE; z<pos[1]*Chunk::CHUNK_SIZE+Chunk::CHUNK_SIZE; z++)
generate_node_column_sun_lightmap(math::Vector2i{x, z});
}
}
void ChunkManager::generate_node_column_sun_lightmap(const math::Vector2i& pos) {
unsigned char light_value = 255;
for(int chunk_y = m_loaded_area_offset[1]+VIEW_AREA_SIZE; chunk_y>=m_loaded_area_offset[1]; chunk_y--) {
math::Vector3i node_pos{pos[0], chunk_y*Chunk::CHUNK_SIZE, pos[1]};
ClientChunk* const chunk = static_cast<ClientChunk*>(get_chunk_with_node(node_pos));
for(char node_y = Chunk::CHUNK_SIZE-1; node_y>=0; node_y--) {
node_pos[1] = chunk_y*Chunk::CHUNK_SIZE+node_y;
const math::Vector3i local_node_pos = node_pos_to_local_node_pos(node_pos);
chunk->set_sunlight_value(local_node_pos, math::RGBColor(light_value));
const NodeDef& def = m_content_registry.get_node_def(chunk->get_node(local_node_pos));
if(!def.m_transparent)
light_value = 128;
}
chunk->set_sun_lightmap_needs_update(false);
}
}
void ChunkManager::render() {
acquire();
renderer::MeshRenderer* const mesh_renderer = m_engine->get_mesh_renderer();
mesh_renderer->set_alpha_blending_mode(true);
mesh_renderer->set_shader(engine::GameConfig::get().m_meshing_mode==engine::GameConfig::MeshingMode::MESHING_MODE_GREEDY?m_greedy_chunk_shader:m_chunk_shader);
for(Chunk* chunk : m_unloaded_chunks) {
for(ClientChunk* chunk : m_unloaded_chunks) {
mesh_renderer->translate(chunk->get_pos().convert<float>()*math::Vector3f(Chunk::CHUNK_SIZE));
chunk->render(m_texture_atlas.get_atlas_texture());
chunk->render(mesh_renderer, m_texture_atlas.get_atlas_texture());
}
for(unsigned i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
Chunk* const chunk = m_loaded_chunks[i];
ClientChunk* 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());
chunk->render(mesh_renderer, m_texture_atlas.get_atlas_texture());
}
mesh_renderer->set_alpha_blending_mode(false);
release();
@ -283,42 +328,87 @@ void ChunkManager::request_chunk(const math::Vector3i& pos) {
release();
}
Chunk& ChunkManager::get_chunk(const math::Vector3i& pos) {
void ChunkManager::add_node(uint16_t node, const math::Vector3i& node_pos) {
ClientChunk* const chunk = static_cast<ClientChunk*>(get_chunk_with_node(node_pos));
chunk->get_update_guard().lock();
chunk->add_node(node, node_pos_to_local_node_pos(node_pos));
generate_node_column_sun_lightmap(math::Vector2i{node_pos[0], node_pos[2]});
if(chunk->is_modified()) {
chunk->get_update_guard().unlock();
return;
}
chunk->set_modified(true);
m_updated_chunks.push_back(chunk);
chunk->get_update_guard().unlock();
}
Chunk* ChunkManager::get_chunk(const math::Vector3i& pos) {
acquire();
const math::Vector3i local_chunk_pos = pos-m_loaded_area_offset;
for(unsigned char i = 0; i<3; i++) {
if(local_chunk_pos[i]<0 || local_chunk_pos[i]>=VIEW_AREA_SIZE) {
release();
return m_dummy_chunk;
return &m_dummy_chunk;
}
}
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]];
ClientChunk* 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;
return chunk;
}
void ChunkManager::unload_chunk_animated(Chunk* chunk) {
if(!chunk->has_mesh()) {
delete chunk;
return;
}
void ChunkManager::unload_chunk_animated(ClientChunk* chunk) {
chunk->begin_animated_unload();
m_unloaded_chunks.push_back(chunk);
}
void ChunkManager::unload_all_chunks() {
while(!m_updated_chunks.empty());
for(size_t i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
if(m_loaded_chunks[i]) {
m_loaded_chunks[i]->get_update_guard().lock();
delete m_loaded_chunks[i];
m_loaded_chunks[i] = nullptr;
}
}
for(Chunk* chunk : m_unloaded_chunks)
for(ClientChunk* chunk : m_unloaded_chunks) {
chunk->get_update_guard().lock();
delete chunk;
}
m_unloaded_chunks.clear();
}
void ChunkManager::mesh_update_thread_func() {
while(m_mesh_update_thread_running) {
if(!m_pending_chunks.empty() || m_updated_chunks.empty())
continue;
for(unsigned i = 0; i<m_updated_chunks.size(); i++) {
ClientChunk* const chunk = m_updated_chunks[i];
if(!chunk->get_update_guard().try_lock())
continue;
for(unsigned char i = 0; i < engine::NodeSide::NODE_SIDE_COUNT; i++) {
const engine::NodeSide node_side = static_cast<engine::NodeSide>(i);
if(!chunk->get_neighbour(node_side)) {
const ClientChunk* const client_chunk = static_cast<const ClientChunk*>(get_chunk(chunk->get_pos()+engine::get_node_side_offset(node_side)));
chunk->set_neighbour(node_side, client_chunk);
}
}
if(chunk->sun_lightmap_needs_update())
generate_column_sun_lightmap(math::Vector2i{chunk->get_pos()[0], chunk->get_pos()[2]});
if(chunk->has_mesh()) {
chunk->generate_mesh(m_engine, m_content_registry);
m_updated_chunks.erase(m_updated_chunks.begin()+i);
i--;
}
chunk->get_update_guard().unlock();
}
}
}

View File

@ -28,7 +28,10 @@ SOFTWARE.
#include "common/thread_safe.hpp"
#include "common/world/chunk_manager_base.hpp"
#include "common/world/chunk.hpp"
#include <atomic>
#include <thread>
#include "game/world/client_chunk.hpp"
namespace polygun::engine {
class TextureAtlas;
@ -49,38 +52,46 @@ namespace polygun::world {
namespace polygun::world {
class ChunkManager final : public utils::ThreadSafe, public world::ChunkManagerBase {
public:
ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture_atlas);
ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture_atlas, engine::ContentRegistry& content_registry);
~ChunkManager();
void on_packet(network::NetworkPacket& packet);
void on_local_player_move(const LocalPlayer& player);
void update_chunks(float delta, engine::ContentRegistry& content_registry);
void update_chunks(float delta);
void generate_column_sun_lightmap(const math::Vector2i& pos);
void generate_node_column_sun_lightmap(const math::Vector2i& pos);
void render();
void try_place(const math::Vector3i& node_pos, uint16_t node);
void try_fill_node(const math::Vector3i& from, const math::Vector3i& to, uint16_t node);
void request_chunk(const math::Vector3i& pos);
unsigned get_updated_chunks() const { return m_updated_chunks; }
unsigned get_updated_chunks() const { return m_updated_chunks.size(); }
void set_network_manager(network::NetworkManager* manager) { m_network_manager = manager; }
virtual Chunk& get_chunk(const math::Vector3i& pos) override;
virtual void add_node(uint16_t node, const math::Vector3i& node_pos) override;
virtual Chunk* get_chunk(const math::Vector3i& pos) override;
private:
engine::Engine* m_engine;
engine::TextureAtlas& m_texture_atlas;
engine::ContentRegistry& m_content_registry;
math::Vector3i m_loaded_area_offset;
std::vector<math::Vector3i> m_pending_chunks;
Chunk** m_loaded_chunks;
std::vector<Chunk*> m_unloaded_chunks;
Chunk m_dummy_chunk;
unsigned m_updated_chunks;
ClientChunk** m_loaded_chunks;
std::vector<ClientChunk*> m_unloaded_chunks;
std::vector<ClientChunk*> m_updated_chunks;
ClientChunk m_dummy_chunk;
renderer::Shader* m_greedy_chunk_shader;
renderer::Shader* m_chunk_shader;
network::NetworkManager* m_network_manager;
std::atomic_bool m_mesh_update_thread_running;
std::unique_ptr<std::thread> m_mesh_update_thread;
private:
void unload_chunk_animated(Chunk* chunk);
void unload_chunk_animated(ClientChunk* chunk);
void unload_all_chunks();
void mesh_update_thread_func();
};
}

View File

@ -0,0 +1,183 @@
/*
PolyGun
Copyright (c) 2024 mrkubax10 <mrkubax10@onet.pl>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "game/world/client_chunk.hpp"
#include <cstring>
#include "game/engine/engine.hpp"
#include "game/engine/game_config.hpp"
#include "game/engine/greedy_meshing.hpp"
#include "game/engine/normal_meshing.hpp"
#include "game/engine/thread_helper.hpp"
#include "game/renderer/mesh.hpp"
using namespace polygun::world;
ClientChunk::ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size) :
Chunk(),
m_chunk_manager(chunk_manager),
m_update_guard(new std::mutex),
m_mesh(nullptr),
m_mesh_create_callback_index(0),
m_texture_unit_size(texture_unit_size),
m_loading_opacity(0.0f),
m_sun_lightmap_needs_update(true),
m_unloaded(false),
m_copy(false),
m_neighbours(),
m_chunk_lightmap(),
m_chunk_sun_lightmap()
{
m_mesh_create_callback_index = engine::ThreadHelper::get().invoke_on_main_thread([this, engine]() {
m_mesh = engine->get_master_renderer()->create_mesh();
});
memset(m_neighbours, 0, sizeof(m_neighbours));
memset(m_chunk_lightmap, 0, sizeof(m_chunk_lightmap));
memset(m_chunk_sun_lightmap, 0xFF, sizeof(m_chunk_sun_lightmap));
}
ClientChunk::ClientChunk(const ClientChunk& other) :
Chunk(),
m_chunk_manager(other.m_chunk_manager),
m_update_guard(other.m_update_guard),
m_mesh(other.m_mesh),
m_mesh_create_callback_index(),
m_texture_unit_size(other.m_texture_unit_size),
m_loading_opacity(other.m_loading_opacity),
m_sun_lightmap_needs_update(other.m_sun_lightmap_needs_update),
m_unloaded(other.m_unloaded),
m_copy(true),
m_neighbours(),
m_chunk_lightmap(),
m_chunk_sun_lightmap()
{
memcpy(m_neighbours, other.m_neighbours, sizeof(m_neighbours));
memcpy(m_chunk_lightmap, other.m_chunk_lightmap, sizeof(m_chunk_lightmap));
memcpy(m_chunk_sun_lightmap, other.m_chunk_sun_lightmap, sizeof(m_chunk_sun_lightmap));
}
ClientChunk::~ClientChunk() {
if(m_copy)
return;
if(m_mesh)
delete m_mesh;
else
engine::ThreadHelper::get().cancel_main_thread_callback(m_mesh_create_callback_index);
delete m_update_guard;
}
void ClientChunk::render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture) {
mesh_renderer->render_chunk_mesh(m_mesh, atlas_texture, m_loading_opacity);
}
void ClientChunk::update(float delta) {
if(m_unloaded && m_loading_opacity>0)
m_loading_opacity-=delta*2.0f;
else if(m_loading_opacity<1.0f)
m_loading_opacity+=delta*2.0f;
}
void ClientChunk::generate_mesh(engine::Engine* engine, engine::ContentRegistry& content_registry) {
// if mesh wasn't created yet, skip generating mesh
if(!m_mesh)
return;
switch(engine::GameConfig::get().m_meshing_mode) {
case engine::GameConfig::MeshingMode::MESHING_MODE_GREEDY:
if(engine->get_master_renderer()->supports_greedy_meshing()) {
engine::greedy_meshing meshing;
std::vector<float> vertices;
engine::vertices_indices mesh = meshing.generate_mesh(meshing.merge(*this), *this);
for (size_t i = 0; i < mesh.vertices.size(); i++) {
vertices.push_back(mesh.vertices[i][0]);
vertices.push_back(mesh.vertices[i][1]);
vertices.push_back(mesh.vertices[i][2]);
}
engine::ThreadHelper::get().invoke_on_main_thread([this, vertices, mesh] {
m_mesh->load_vertices(vertices);
m_mesh->load_indices(mesh.indices);
m_mesh->add_custom_buffer(mesh.node_ids.data(), mesh.node_ids.size()*sizeof(float), 1, misc::VarType::VAR_TYPE_FLOAT);
});
break;
}
[[fallthrough]];
case engine::GameConfig::MeshingMode::MESHING_MODE_NORMAL: {
std::vector<float> vertices;
std::vector<unsigned> indices;
std::vector<float> uvs;
std::vector<float> colors;
engine::normal_meshing::generate_mesh(*this, content_registry, m_texture_unit_size, vertices, indices, uvs, colors);
engine::ThreadHelper::get().invoke_on_main_thread([this, vertices, indices, uvs, colors] {
m_mesh->load_vertices(vertices);
m_mesh->load_uvs(uvs);
m_mesh->load_indices(indices);
m_mesh->load_colors(colors);
});
break;
}
}
m_modified = false;
}
void ClientChunk::set_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side, uint8_t val) {
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3+side/2;
const unsigned char offset_in_byte = (side%2)*4;
m_chunk_lightmap[index] &= offset_in_byte==4?0xF:0xF0;
m_chunk_lightmap[index] |= (val<<offset_in_byte)&(0xF<<offset_in_byte);
}
void ClientChunk::set_sunlight_value(const math::Vector3i& pos, math::RGBColor val) {
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3;
for(unsigned char i = 0; i<3; i++)
m_chunk_sun_lightmap[index+i] = val[i];
}
uint8_t ClientChunk::get_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side) const {
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3+side/2;
const unsigned char offset_in_byte = (side%2)*4;
return (m_chunk_lightmap[index]>>offset_in_byte)&0xF;
}
polygun::math::RGBColor ClientChunk::get_sunlight_value(math::Vector3i pos, engine::NodeSide side) const {
pos+=engine::get_node_side_offset(side);
const ClientChunk* queried_chunk = this;
for(unsigned char i = 0; i<3; i++) {
if(pos[i]<0 || pos[i]>=Chunk::CHUNK_SIZE) {
queried_chunk = m_neighbours[side];
pos[i] = pos[i]%Chunk::CHUNK_SIZE+(pos[i]>0?0:Chunk::CHUNK_SIZE);
break;
}
}
const size_t index = (pos[2]*CHUNK_SIZE*CHUNK_SIZE+pos[1]*CHUNK_SIZE+pos[0])*3;
math::RGBColor result;
for(unsigned char i = 0; i<3; i++)
result[i] = queried_chunk->m_chunk_sun_lightmap[index+i];
return result;
}
polygun::math::RGBColor ClientChunk::get_light_value(const math::Vector3i& pos, engine::NodeSide side) const {
//return std::max(get_emitted_light_value(pos,side), get_sunlight_value(pos, side));
return get_sunlight_value(pos, side);
}

View File

@ -0,0 +1,90 @@
/*
PolyGun
Copyright (c) 2024 mrkubax10 <mrkubax10@onet.pl>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef POLYGUN_WORLD_CLIENT_CHUNK_HPP
#define POLYGUN_WORLD_CLIENT_CHUNK_HPP
#include "common/world/chunk.hpp"
#include <mutex>
#include "game/engine/node_side.hpp"
namespace polygun::renderer {
class Mesh;
class MeshRenderer;
class Texture;
}
namespace polygun::engine {
class ContentRegistry;
class Engine;
}
namespace polygun::world {
class ChunkManager;
// Note: Instances of this class shouldn't be copied, otherwise memory after free errors may happen.
class ClientChunk final : public Chunk {
public:
ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size);
ClientChunk(const ClientChunk& other);
virtual ~ClientChunk() override;
void render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture);
void update(float delta);
void generate_mesh(engine::Engine* engine, engine::ContentRegistry& content_registry);
void begin_animated_unload() { m_unloaded = true; }
void set_sun_lightmap_needs_update(bool val) { m_sun_lightmap_needs_update = val; }
void set_neighbour(engine::NodeSide side, const ClientChunk* chunk) { m_neighbours[side] = chunk; }
const ClientChunk* get_neighbour(engine::NodeSide side) const { return m_neighbours[side]; }
void set_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side, uint8_t val);
void set_sunlight_value(const math::Vector3i& pos, math::RGBColor val);
uint8_t get_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
math::RGBColor get_sunlight_value(math::Vector3i pos, engine::NodeSide side) const;
math::RGBColor get_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
std::mutex& get_update_guard() { return *m_update_guard; }
bool has_mesh() const { return m_mesh; }
float get_loading_opacity() const { return m_loading_opacity; }
bool sun_lightmap_needs_update() const { return m_sun_lightmap_needs_update; }
private:
ChunkManager* m_chunk_manager;
std::mutex* m_update_guard;
renderer::Mesh* m_mesh;
unsigned m_mesh_create_callback_index;
float m_texture_unit_size;
float m_loading_opacity;
bool m_sun_lightmap_needs_update;
bool m_unloaded;
bool m_copy;
const ClientChunk* m_neighbours[6];
// Each light value is in 0-15 range so 2 node faces are stored in one byte
uint8_t m_chunk_lightmap[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE*3];
uint8_t m_chunk_sun_lightmap[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE*3];
};
}
#endif // POLYGUN_WORLD_CLIENT_CHUNK_HPP

View File

@ -121,13 +121,13 @@ void ChunkManager::update() {
m_last_write = time(nullptr);
}
polygun::world::Chunk& ChunkManager::get_chunk(const math::Vector3i& pos) {
polygun::world::Chunk* ChunkManager::get_chunk(const math::Vector3i& pos) {
std::vector<std::unique_ptr<world::Chunk>>::iterator it = std::find_if(m_loaded_chunks.begin(), m_loaded_chunks.end(), [pos](const std::unique_ptr<world::Chunk>& element) {
return element->get_pos()==pos;
});
if(it!=m_loaded_chunks.end()) {
(*it)->mark_access();
return **it;
return it->get();
}
uint32_t dummy;
@ -141,13 +141,13 @@ polygun::world::Chunk& ChunkManager::get_chunk(const math::Vector3i& pos) {
const uint64_t data_offset = utils::bytes_to_uint64(temp);
load_chunk(pos[0], pos[1], pos[2], compression_mode, data_size, data_offset);
LOG_VERBOSE("Loaded chunk at position %d %d %d (offset in map file: %lu)", pos[0], pos[1], pos[2], data_offset);
return *m_loaded_chunks.back();
return m_loaded_chunks.back().get();
}
std::unique_ptr<world::Chunk> chunk = std::make_unique<world::Chunk>();
chunk->set_pos(pos);
m_loaded_chunks.push_back(std::move(chunk));
return *m_loaded_chunks.back();
return m_loaded_chunks.back().get();
}
void ChunkManager::load_header() {
@ -351,4 +351,4 @@ void ChunkManager::close_map() {
void ChunkManager::reset_rw_mode() {
// Use dummy fseek to reset file R/W mode
fseek(m_map_file, 0, SEEK_CUR);
}
}

View File

@ -41,7 +41,7 @@ namespace polygun::server {
void load_map_from_file(const std::string& name);
void update();
virtual world::Chunk& get_chunk(const math::Vector3i& pos) override;
virtual world::Chunk* get_chunk(const math::Vector3i& pos) override;
private:
Server& m_server;

View File

@ -381,8 +381,8 @@ void Server::handle_chunk_request(polygun::network::NetworkPacket& packet, Clien
const math::Rect3D chunk_rect(pos.convert<float>(), math::Vector3f(world::Chunk::CHUNK_SIZE));
const math::Rect3D player_view_rect = math::Rect3D::with_center(client->get_position()/math::Vector3f(world::Chunk::CHUNK_SIZE), math::Vector3f(5));
if(player_view_rect.overlaps(chunk_rect)) {
const world::Chunk& chunk = m_chunk_manager.get_chunk(pos);
out_packet.write(&chunk);
const world::Chunk* const chunk = m_chunk_manager.get_chunk(pos);
out_packet.write(chunk);
}
else {
// Note: Maybe warn player for possible cheating/ddosing server with chunk requests outside of viewing range