From d0fbd68055e3f4a796330cc8acc6c0954b5327ff Mon Sep 17 00:00:00 2001 From: untodesu Date: Thu, 11 Sep 2025 15:48:53 +0500 Subject: Run clang-format across the project --- game/client/world/chunk_mesher.cc | 968 +++++++++++++++++++------------------- 1 file changed, 484 insertions(+), 484 deletions(-) (limited to 'game/client/world/chunk_mesher.cc') diff --git a/game/client/world/chunk_mesher.cc b/game/client/world/chunk_mesher.cc index e2f1e10..a8ee817 100644 --- a/game/client/world/chunk_mesher.cc +++ b/game/client/world/chunk_mesher.cc @@ -1,484 +1,484 @@ -#include "client/pch.hh" - -#include "client/world/chunk_mesher.hh" - -#include "core/math/crc64.hh" - -#include "core/threading.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/dimension.hh" -#include "shared/world/voxel_registry.hh" - -#include "shared/coord.hh" - -#include "client/world/chunk_quad.hh" -#include "client/world/voxel_atlas.hh" - -#include "client/globals.hh" -#include "client/session.hh" - -using QuadBuilder = std::vector; - -using CachedChunkCoord = unsigned short; -constexpr static CachedChunkCoord CPOS_ITSELF = 0x0000; -constexpr static CachedChunkCoord CPOS_NORTH = 0x0001; -constexpr static CachedChunkCoord CPOS_SOUTH = 0x0002; -constexpr static CachedChunkCoord CPOS_EAST = 0x0003; -constexpr static CachedChunkCoord CPOS_WEST = 0x0004; -constexpr static CachedChunkCoord CPOS_TOP = 0x0005; -constexpr static CachedChunkCoord CPOS_BOTTOM = 0x0006; -constexpr static const size_t NUM_CACHED_CPOS = 7; - -static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chunk_pos& cpos) -{ - static const CachedChunkCoord nx[3] = { CPOS_WEST, 0, CPOS_EAST }; - static const CachedChunkCoord ny[3] = { CPOS_BOTTOM, 0, CPOS_TOP }; - static const CachedChunkCoord nz[3] = { CPOS_NORTH, 0, CPOS_SOUTH }; - - if(pivot != cpos) { - chunk_pos delta = pivot - cpos; - delta[0] = math::clamp(delta[0], -1, 1); - delta[1] = math::clamp(delta[1], -1, 1); - delta[2] = math::clamp(delta[2], -1, 1); - - if(delta[0]) { - return nx[delta[0] + 1]; - } - else if(delta[1]) { - return ny[delta[1] + 1]; - } - else { - return nz[delta[2] + 1]; - } - } - - return CPOS_ITSELF; -} - -static world::voxel_facing get_facing(world::voxel_face face, world::voxel_type type) -{ - if(type == world::voxel_type::CROSS) { - switch(face) { - case world::voxel_face::CROSS_NESW: - return world::voxel_facing::NESW; - case world::voxel_face::CROSS_NWSE: - return world::voxel_facing::NWSE; - default: - return world::voxel_facing::NORTH; - } - } - - switch(face) { - case world::voxel_face::CUBE_NORTH: - return world::voxel_facing::NORTH; - case world::voxel_face::CUBE_SOUTH: - return world::voxel_facing::SOUTH; - case world::voxel_face::CUBE_EAST: - return world::voxel_facing::EAST; - case world::voxel_face::CUBE_WEST: - return world::voxel_facing::WEST; - case world::voxel_face::CUBE_TOP: - return world::voxel_facing::UP; - case world::voxel_face::CUBE_BOTTOM: - return world::voxel_facing::DOWN; - default: - return world::voxel_facing::NORTH; - } -} - -class GL_MeshingTask final : public Task { -public: - explicit GL_MeshingTask(entt::entity entity, const chunk_pos& cpos); - virtual ~GL_MeshingTask(void) = default; - virtual void process(void) override; - virtual void finalize(void) override; - -private: - bool vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const; - void push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face); - void push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, - std::size_t entropy); - void make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy); - void cache_chunk(const chunk_pos& cpos); - -private: - std::array m_cache; - std::vector m_quads_b; // blending - std::vector m_quads_s; // solid - entt::entity m_entity; - chunk_pos m_cpos; -}; - -GL_MeshingTask::GL_MeshingTask(entt::entity entity, const chunk_pos& cpos) -{ - m_entity = entity; - m_cpos = cpos; - - cache_chunk(m_cpos); - cache_chunk(m_cpos + DIR_NORTH); - cache_chunk(m_cpos + DIR_SOUTH); - cache_chunk(m_cpos + DIR_EAST); - cache_chunk(m_cpos + DIR_WEST); - cache_chunk(m_cpos + DIR_DOWN); - cache_chunk(m_cpos + DIR_UP); -} - -void GL_MeshingTask::process(void) -{ - m_quads_b.resize(world::voxel_atlas::plane_count()); - m_quads_s.resize(world::voxel_atlas::plane_count()); - - const auto& voxels = m_cache.at(CPOS_ITSELF); - - for(std::size_t i = 0; i < CHUNK_VOLUME; ++i) { - if(m_status == task_status::CANCELLED) { - m_quads_b.clear(); - m_quads_s.clear(); - return; - } - - const auto voxel = voxels[i]; - const auto lpos = coord::to_local(i); - - const auto info = world::voxel_registry::find(voxel); - - if(info == nullptr) { - // Either a NULL_VOXEL_ID or something went - // horribly wrong and we don't what this is - continue; - } - - world::voxel_vis vis = 0; - - if(vis_test(voxel, info, lpos + DIR_NORTH)) { - vis |= world::VIS_NORTH; - } - - if(vis_test(voxel, info, lpos + DIR_SOUTH)) { - vis |= world::VIS_SOUTH; - } - - if(vis_test(voxel, info, lpos + DIR_EAST)) { - vis |= world::VIS_EAST; - } - - if(vis_test(voxel, info, lpos + DIR_WEST)) { - vis |= world::VIS_WEST; - } - - if(vis_test(voxel, info, lpos + DIR_UP)) { - vis |= world::VIS_UP; - } - - if(vis_test(voxel, info, lpos + DIR_DOWN)) { - vis |= world::VIS_DOWN; - } - - const auto vpos = coord::to_voxel(m_cpos, lpos); - const auto entropy_src = vpos[0] * vpos[1] * vpos[2]; - const auto entropy = math::crc64(&entropy_src, sizeof(entropy_src)); - - // FIXME: handle different voxel types - make_cube(voxel, info, lpos, vis, entropy); - } -} - -void GL_MeshingTask::finalize(void) -{ - if(!globals::dimension || !globals::dimension->chunks.valid(m_entity)) { - // We either disconnected or something - // else happened that invalidated the entity - return; - } - - auto& component = globals::dimension->chunks.emplace_or_replace(m_entity); - - const std::size_t plane_count_nb = m_quads_s.size(); - const std::size_t plane_count_b = m_quads_b.size(); - - bool has_no_submeshes_b = true; - bool has_no_submeshes_nb = true; - - component.quad_nb.resize(plane_count_nb); - component.quad_b.resize(plane_count_b); - - for(std::size_t plane = 0; plane < plane_count_nb; ++plane) { - auto& builder = m_quads_s[plane]; - auto& buffer = component.quad_nb[plane]; - - if(builder.empty()) { - if(buffer.handle) { - glDeleteBuffers(1, &buffer.handle); - buffer.handle = 0; - buffer.size = 0; - } - } - else { - if(!buffer.handle) { - glGenBuffers(1, &buffer.handle); - } - - glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); - glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); - buffer.size = builder.size(); - has_no_submeshes_nb = false; - } - } - - for(std::size_t plane = 0; plane < plane_count_b; ++plane) { - auto& builder = m_quads_b[plane]; - auto& buffer = component.quad_b[plane]; - - if(builder.empty()) { - if(buffer.handle) { - glDeleteBuffers(1, &buffer.handle); - buffer.handle = 0; - buffer.size = 0; - } - } - else { - if(!buffer.handle) { - glGenBuffers(1, &buffer.handle); - } - - glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); - glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); - buffer.size = builder.size(); - has_no_submeshes_b = false; - } - } - - if(has_no_submeshes_b && has_no_submeshes_nb) { - globals::dimension->chunks.remove(m_entity); - } -} - -bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const -{ - const auto pvpos = coord::to_voxel(m_cpos, lpos); - const auto pcpos = coord::to_chunk(pvpos); - const auto plpos = coord::to_local(pvpos); - const auto index = coord::to_index(plpos); - - const auto cached_cpos = get_cached_cpos(m_cpos, pcpos); - const auto& voxels = m_cache.at(cached_cpos); - const auto neighbour = voxels[index]; - - bool result; - - if(neighbour == NULL_VOXEL_ID) { - result = true; - } - else if(neighbour == voxel) { - result = false; - } - else if(auto neighbour_info = world::voxel_registry::find(neighbour)) { - if(neighbour_info->blending != info->blending) { - // Voxel types that use blending are semi-transparent; - // this means they're rendered using a different setup - // and they must have visible faces with opaque voxels - result = neighbour_info->blending; - } - else { - result = false; - } - } - else { - result = false; - } - - return result; -} - -void GL_MeshingTask::push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face) -{ - const world::voxel_facing facing = get_facing(face, info->type); - const world::VoxelTexture& vtex = info->textures[static_cast(face)]; - - if(info->blending) { - m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); - } - else { - m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); - } -} - -void GL_MeshingTask::push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, - std::size_t entropy) -{ - const world::voxel_facing facing = get_facing(face, info->type); - const world::VoxelTexture& vtex = info->textures[static_cast(face)]; - const std::size_t entropy_mod = entropy % vtex.paths.size(); - - if(info->blending) { - m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); - } - else { - m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); - } -} - -void GL_MeshingTask::make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, - std::size_t entropy) -{ - const glm::fvec3 fpos = glm::fvec3(lpos); - const glm::fvec2 fsize = glm::fvec2(1.0f, 1.0f); - - if(info->animated) { - if(vis & world::VIS_NORTH) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_NORTH); - } - - if(vis & world::VIS_SOUTH) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_SOUTH); - } - - if(vis & world::VIS_EAST) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_EAST); - } - - if(vis & world::VIS_WEST) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_WEST); - } - - if(vis & world::VIS_UP) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_TOP); - } - - if(vis & world::VIS_DOWN) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM); - } - } - else { - if(vis & world::VIS_NORTH) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_NORTH, entropy); - } - - if(vis & world::VIS_SOUTH) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_SOUTH, entropy); - } - - if(vis & world::VIS_EAST) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_EAST, entropy); - } - - if(vis & world::VIS_WEST) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_WEST, entropy); - } - - if(vis & world::VIS_UP) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_TOP, entropy); - } - - if(vis & world::VIS_DOWN) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM, entropy); - } - } -} - -void GL_MeshingTask::cache_chunk(const chunk_pos& cpos) -{ - const auto index = get_cached_cpos(m_cpos, cpos); - - if(const auto chunk = globals::dimension->find_chunk(cpos)) { - m_cache[index] = chunk->get_voxels(); - return; - } -} - -// Bogus internal flag component -struct NeedsMeshingComponent final {}; - -static void on_chunk_create(const world::ChunkCreateEvent& event) -{ - const std::array neighbours = { - event.cpos + DIR_NORTH, - event.cpos + DIR_SOUTH, - event.cpos + DIR_EAST, - event.cpos + DIR_WEST, - event.cpos + DIR_UP, - event.cpos + DIR_DOWN, - }; - - globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); - - for(const chunk_pos& cpos : neighbours) { - if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { - globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); - continue; - } - } -} - -static void on_chunk_update(const world::ChunkUpdateEvent& event) -{ - const std::array neighbours = { - event.cpos + DIR_NORTH, - event.cpos + DIR_SOUTH, - event.cpos + DIR_EAST, - event.cpos + DIR_WEST, - event.cpos + DIR_UP, - event.cpos + DIR_DOWN, - }; - - globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); - - for(const chunk_pos& cpos : neighbours) { - if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { - globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); - continue; - } - } -} - -static void on_voxel_set(const world::VoxelSetEvent& event) -{ - globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); - - std::vector neighbours; - - for(int dim = 0; dim < 3; dim += 1) { - chunk_pos offset = chunk_pos(0, 0, 0); - offset[dim] = 1; - - if(event.lpos[dim] == 0) { - neighbours.push_back(event.cpos - offset); - continue; - } - - if(event.lpos[dim] == (CHUNK_SIZE - 1)) { - neighbours.push_back(event.cpos + offset); - continue; - } - } - - for(const chunk_pos& cpos : neighbours) { - if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { - globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); - continue; - } - } -} - -void world::chunk_mesher::init(void) -{ - globals::dispatcher.sink().connect<&on_chunk_create>(); - globals::dispatcher.sink().connect<&on_chunk_update>(); - globals::dispatcher.sink().connect<&on_voxel_set>(); -} - -void world::chunk_mesher::shutdown(void) -{ -} - -void world::chunk_mesher::update(void) -{ - if(session::is_ingame()) { - const auto group = globals::dimension->chunks.group(entt::get); - for(const auto [entity, chunk] : group.each()) { - globals::dimension->chunks.remove(entity); - threading::submit(entity, chunk.cpos); - } - } -} +#include "client/pch.hh" + +#include "client/world/chunk_mesher.hh" + +#include "core/math/crc64.hh" + +#include "core/threading.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" +#include "shared/world/voxel_registry.hh" + +#include "shared/coord.hh" + +#include "client/world/chunk_quad.hh" +#include "client/world/voxel_atlas.hh" + +#include "client/globals.hh" +#include "client/session.hh" + +using QuadBuilder = std::vector; + +using CachedChunkCoord = unsigned short; +constexpr static CachedChunkCoord CPOS_ITSELF = 0x0000; +constexpr static CachedChunkCoord CPOS_NORTH = 0x0001; +constexpr static CachedChunkCoord CPOS_SOUTH = 0x0002; +constexpr static CachedChunkCoord CPOS_EAST = 0x0003; +constexpr static CachedChunkCoord CPOS_WEST = 0x0004; +constexpr static CachedChunkCoord CPOS_TOP = 0x0005; +constexpr static CachedChunkCoord CPOS_BOTTOM = 0x0006; +constexpr static const size_t NUM_CACHED_CPOS = 7; + +static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chunk_pos& cpos) +{ + static const CachedChunkCoord nx[3] = { CPOS_WEST, 0, CPOS_EAST }; + static const CachedChunkCoord ny[3] = { CPOS_BOTTOM, 0, CPOS_TOP }; + static const CachedChunkCoord nz[3] = { CPOS_NORTH, 0, CPOS_SOUTH }; + + if(pivot != cpos) { + chunk_pos delta = pivot - cpos; + delta[0] = math::clamp(delta[0], -1, 1); + delta[1] = math::clamp(delta[1], -1, 1); + delta[2] = math::clamp(delta[2], -1, 1); + + if(delta[0]) { + return nx[delta[0] + 1]; + } + else if(delta[1]) { + return ny[delta[1] + 1]; + } + else { + return nz[delta[2] + 1]; + } + } + + return CPOS_ITSELF; +} + +static world::voxel_facing get_facing(world::voxel_face face, world::voxel_type type) +{ + if(type == world::voxel_type::CROSS) { + switch(face) { + case world::voxel_face::CROSS_NESW: + return world::voxel_facing::NESW; + case world::voxel_face::CROSS_NWSE: + return world::voxel_facing::NWSE; + default: + return world::voxel_facing::NORTH; + } + } + + switch(face) { + case world::voxel_face::CUBE_NORTH: + return world::voxel_facing::NORTH; + case world::voxel_face::CUBE_SOUTH: + return world::voxel_facing::SOUTH; + case world::voxel_face::CUBE_EAST: + return world::voxel_facing::EAST; + case world::voxel_face::CUBE_WEST: + return world::voxel_facing::WEST; + case world::voxel_face::CUBE_TOP: + return world::voxel_facing::UP; + case world::voxel_face::CUBE_BOTTOM: + return world::voxel_facing::DOWN; + default: + return world::voxel_facing::NORTH; + } +} + +class GL_MeshingTask final : public Task { +public: + explicit GL_MeshingTask(entt::entity entity, const chunk_pos& cpos); + virtual ~GL_MeshingTask(void) = default; + virtual void process(void) override; + virtual void finalize(void) override; + +private: + bool vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const; + void push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face); + void push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, + std::size_t entropy); + void make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy); + void cache_chunk(const chunk_pos& cpos); + +private: + std::array m_cache; + std::vector m_quads_b; // blending + std::vector m_quads_s; // solid + entt::entity m_entity; + chunk_pos m_cpos; +}; + +GL_MeshingTask::GL_MeshingTask(entt::entity entity, const chunk_pos& cpos) +{ + m_entity = entity; + m_cpos = cpos; + + cache_chunk(m_cpos); + cache_chunk(m_cpos + DIR_NORTH); + cache_chunk(m_cpos + DIR_SOUTH); + cache_chunk(m_cpos + DIR_EAST); + cache_chunk(m_cpos + DIR_WEST); + cache_chunk(m_cpos + DIR_DOWN); + cache_chunk(m_cpos + DIR_UP); +} + +void GL_MeshingTask::process(void) +{ + m_quads_b.resize(world::voxel_atlas::plane_count()); + m_quads_s.resize(world::voxel_atlas::plane_count()); + + const auto& voxels = m_cache.at(CPOS_ITSELF); + + for(std::size_t i = 0; i < CHUNK_VOLUME; ++i) { + if(m_status == task_status::CANCELLED) { + m_quads_b.clear(); + m_quads_s.clear(); + return; + } + + const auto voxel = voxels[i]; + const auto lpos = coord::to_local(i); + + const auto info = world::voxel_registry::find(voxel); + + if(info == nullptr) { + // Either a NULL_VOXEL_ID or something went + // horribly wrong and we don't what this is + continue; + } + + world::voxel_vis vis = 0; + + if(vis_test(voxel, info, lpos + DIR_NORTH)) { + vis |= world::VIS_NORTH; + } + + if(vis_test(voxel, info, lpos + DIR_SOUTH)) { + vis |= world::VIS_SOUTH; + } + + if(vis_test(voxel, info, lpos + DIR_EAST)) { + vis |= world::VIS_EAST; + } + + if(vis_test(voxel, info, lpos + DIR_WEST)) { + vis |= world::VIS_WEST; + } + + if(vis_test(voxel, info, lpos + DIR_UP)) { + vis |= world::VIS_UP; + } + + if(vis_test(voxel, info, lpos + DIR_DOWN)) { + vis |= world::VIS_DOWN; + } + + const auto vpos = coord::to_voxel(m_cpos, lpos); + const auto entropy_src = vpos[0] * vpos[1] * vpos[2]; + const auto entropy = math::crc64(&entropy_src, sizeof(entropy_src)); + + // FIXME: handle different voxel types + make_cube(voxel, info, lpos, vis, entropy); + } +} + +void GL_MeshingTask::finalize(void) +{ + if(!globals::dimension || !globals::dimension->chunks.valid(m_entity)) { + // We either disconnected or something + // else happened that invalidated the entity + return; + } + + auto& component = globals::dimension->chunks.emplace_or_replace(m_entity); + + const std::size_t plane_count_nb = m_quads_s.size(); + const std::size_t plane_count_b = m_quads_b.size(); + + bool has_no_submeshes_b = true; + bool has_no_submeshes_nb = true; + + component.quad_nb.resize(plane_count_nb); + component.quad_b.resize(plane_count_b); + + for(std::size_t plane = 0; plane < plane_count_nb; ++plane) { + auto& builder = m_quads_s[plane]; + auto& buffer = component.quad_nb[plane]; + + if(builder.empty()) { + if(buffer.handle) { + glDeleteBuffers(1, &buffer.handle); + buffer.handle = 0; + buffer.size = 0; + } + } + else { + if(!buffer.handle) { + glGenBuffers(1, &buffer.handle); + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); + glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); + buffer.size = builder.size(); + has_no_submeshes_nb = false; + } + } + + for(std::size_t plane = 0; plane < plane_count_b; ++plane) { + auto& builder = m_quads_b[plane]; + auto& buffer = component.quad_b[plane]; + + if(builder.empty()) { + if(buffer.handle) { + glDeleteBuffers(1, &buffer.handle); + buffer.handle = 0; + buffer.size = 0; + } + } + else { + if(!buffer.handle) { + glGenBuffers(1, &buffer.handle); + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); + glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); + buffer.size = builder.size(); + has_no_submeshes_b = false; + } + } + + if(has_no_submeshes_b && has_no_submeshes_nb) { + globals::dimension->chunks.remove(m_entity); + } +} + +bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const +{ + const auto pvpos = coord::to_voxel(m_cpos, lpos); + const auto pcpos = coord::to_chunk(pvpos); + const auto plpos = coord::to_local(pvpos); + const auto index = coord::to_index(plpos); + + const auto cached_cpos = get_cached_cpos(m_cpos, pcpos); + const auto& voxels = m_cache.at(cached_cpos); + const auto neighbour = voxels[index]; + + bool result; + + if(neighbour == NULL_VOXEL_ID) { + result = true; + } + else if(neighbour == voxel) { + result = false; + } + else if(auto neighbour_info = world::voxel_registry::find(neighbour)) { + if(neighbour_info->blending != info->blending) { + // Voxel types that use blending are semi-transparent; + // this means they're rendered using a different setup + // and they must have visible faces with opaque voxels + result = neighbour_info->blending; + } + else { + result = false; + } + } + else { + result = false; + } + + return result; +} + +void GL_MeshingTask::push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face) +{ + const world::voxel_facing facing = get_facing(face, info->type); + const world::VoxelTexture& vtex = info->textures[static_cast(face)]; + + if(info->blending) { + m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); + } + else { + m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); + } +} + +void GL_MeshingTask::push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, + std::size_t entropy) +{ + const world::voxel_facing facing = get_facing(face, info->type); + const world::VoxelTexture& vtex = info->textures[static_cast(face)]; + const std::size_t entropy_mod = entropy % vtex.paths.size(); + + if(info->blending) { + m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); + } + else { + m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); + } +} + +void GL_MeshingTask::make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, + std::size_t entropy) +{ + const glm::fvec3 fpos = glm::fvec3(lpos); + const glm::fvec2 fsize = glm::fvec2(1.0f, 1.0f); + + if(info->animated) { + if(vis & world::VIS_NORTH) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_NORTH); + } + + if(vis & world::VIS_SOUTH) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_SOUTH); + } + + if(vis & world::VIS_EAST) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_EAST); + } + + if(vis & world::VIS_WEST) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_WEST); + } + + if(vis & world::VIS_UP) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_TOP); + } + + if(vis & world::VIS_DOWN) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM); + } + } + else { + if(vis & world::VIS_NORTH) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_NORTH, entropy); + } + + if(vis & world::VIS_SOUTH) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_SOUTH, entropy); + } + + if(vis & world::VIS_EAST) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_EAST, entropy); + } + + if(vis & world::VIS_WEST) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_WEST, entropy); + } + + if(vis & world::VIS_UP) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_TOP, entropy); + } + + if(vis & world::VIS_DOWN) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM, entropy); + } + } +} + +void GL_MeshingTask::cache_chunk(const chunk_pos& cpos) +{ + const auto index = get_cached_cpos(m_cpos, cpos); + + if(const auto chunk = globals::dimension->find_chunk(cpos)) { + m_cache[index] = chunk->get_voxels(); + return; + } +} + +// Bogus internal flag component +struct NeedsMeshingComponent final {}; + +static void on_chunk_create(const world::ChunkCreateEvent& event) +{ + const std::array neighbours = { + event.cpos + DIR_NORTH, + event.cpos + DIR_SOUTH, + event.cpos + DIR_EAST, + event.cpos + DIR_WEST, + event.cpos + DIR_UP, + event.cpos + DIR_DOWN, + }; + + globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); + + for(const chunk_pos& cpos : neighbours) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { + globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); + continue; + } + } +} + +static void on_chunk_update(const world::ChunkUpdateEvent& event) +{ + const std::array neighbours = { + event.cpos + DIR_NORTH, + event.cpos + DIR_SOUTH, + event.cpos + DIR_EAST, + event.cpos + DIR_WEST, + event.cpos + DIR_UP, + event.cpos + DIR_DOWN, + }; + + globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); + + for(const chunk_pos& cpos : neighbours) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { + globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); + continue; + } + } +} + +static void on_voxel_set(const world::VoxelSetEvent& event) +{ + globals::dimension->chunks.emplace_or_replace(event.chunk->get_entity()); + + std::vector neighbours; + + for(int dim = 0; dim < 3; dim += 1) { + chunk_pos offset = chunk_pos(0, 0, 0); + offset[dim] = 1; + + if(event.lpos[dim] == 0) { + neighbours.push_back(event.cpos - offset); + continue; + } + + if(event.lpos[dim] == (CHUNK_SIZE - 1)) { + neighbours.push_back(event.cpos + offset); + continue; + } + } + + for(const chunk_pos& cpos : neighbours) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { + globals::dimension->chunks.emplace_or_replace(chunk->get_entity()); + continue; + } + } +} + +void world::chunk_mesher::init(void) +{ + globals::dispatcher.sink().connect<&on_chunk_create>(); + globals::dispatcher.sink().connect<&on_chunk_update>(); + globals::dispatcher.sink().connect<&on_voxel_set>(); +} + +void world::chunk_mesher::shutdown(void) +{ +} + +void world::chunk_mesher::update(void) +{ + if(session::is_ingame()) { + const auto group = globals::dimension->chunks.group(entt::get); + for(const auto [entity, chunk] : group.each()) { + globals::dimension->chunks.remove(entity); + threading::submit(entity, chunk.cpos); + } + } +} -- cgit