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 +++++++++++++++++----------------- game/client/world/chunk_mesher.hh | 36 +- game/client/world/chunk_quad.hh | 82 +-- game/client/world/chunk_renderer.cc | 408 +++++++------- game/client/world/chunk_renderer.hh | 16 +- game/client/world/chunk_vbo.hh | 44 +- game/client/world/chunk_visibility.cc | 180 +++---- game/client/world/chunk_visibility.hh | 12 +- game/client/world/outline.cc | 300 +++++------ game/client/world/outline.hh | 32 +- game/client/world/player_target.cc | 138 ++--- game/client/world/player_target.hh | 36 +- game/client/world/skybox.cc | 22 +- game/client/world/skybox.hh | 22 +- game/client/world/voxel_anims.cc | 66 +-- game/client/world/voxel_anims.hh | 26 +- game/client/world/voxel_atlas.cc | 370 ++++++------- game/client/world/voxel_atlas.hh | 56 +- game/client/world/voxel_sounds.cc | 172 +++--- game/client/world/voxel_sounds.hh | 38 +- 20 files changed, 1512 insertions(+), 1512 deletions(-) (limited to 'game/client/world') 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); + } + } +} diff --git a/game/client/world/chunk_mesher.hh b/game/client/world/chunk_mesher.hh index cb0c7c5..1ef5035 100644 --- a/game/client/world/chunk_mesher.hh +++ b/game/client/world/chunk_mesher.hh @@ -1,18 +1,18 @@ -#pragma once - -#include "client/world/chunk_vbo.hh" - -namespace world -{ -struct ChunkMesh final { - std::vector quad_nb; - std::vector quad_b; -}; -} // namespace world - -namespace world::chunk_mesher -{ -void init(void); -void shutdown(void); -void update(void); -} // namespace world::chunk_mesher +#pragma once + +#include "client/world/chunk_vbo.hh" + +namespace world +{ +struct ChunkMesh final { + std::vector quad_nb; + std::vector quad_b; +}; +} // namespace world + +namespace world::chunk_mesher +{ +void init(void); +void shutdown(void); +void update(void); +} // namespace world::chunk_mesher diff --git a/game/client/world/chunk_quad.hh b/game/client/world/chunk_quad.hh index d160b7e..c15bb7a 100644 --- a/game/client/world/chunk_quad.hh +++ b/game/client/world/chunk_quad.hh @@ -1,41 +1,41 @@ -#pragma once - -#include "core/math/constexpr.hh" - -#include "shared/world/voxel_registry.hh" - -namespace world -{ -// [0] XXXXXXXXYYYYYYYYZZZZZZZZWWWWHHHH -// [1] FFFFTTTTTTTTTTTAAAAA------------ -using ChunkQuad = std::array; -} // namespace world - -namespace world -{ -constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, voxel_facing facing, - std::size_t texture, std::size_t frames) -{ - ChunkQuad result = {}; - result[0] = 0x00000000; - result[1] = 0x00000000; - - // [0] XXXXXXXXYYYYYYYYZZZZZZZZ-------- - result[0] |= (0x000000FFU & static_cast(position.x * 16.0f)) << 24U; - result[0] |= (0x000000FFU & static_cast(position.y * 16.0f)) << 16U; - result[0] |= (0x000000FFU & static_cast(position.z * 16.0f)) << 8U; - - // [0] ------------------------WWWWHHHH - result[0] |= (0x0000000FU & static_cast(size.x * 16.0f - 1.0f)) << 4U; - result[0] |= (0x0000000FU & static_cast(size.y * 16.0f - 1.0f)); - - // [1] FFFF---------------------------- - result[1] |= (0x0000000FU & static_cast(facing)) << 28U; - - // [1] ----TTTTTTTTTTTAAAAA------------ - result[1] |= (0x000007FFU & static_cast(texture)) << 17U; - result[1] |= (0x0000001FU & static_cast(frames)) << 12U; - - return result; -} -} // namespace world +#pragma once + +#include "core/math/constexpr.hh" + +#include "shared/world/voxel_registry.hh" + +namespace world +{ +// [0] XXXXXXXXYYYYYYYYZZZZZZZZWWWWHHHH +// [1] FFFFTTTTTTTTTTTAAAAA------------ +using ChunkQuad = std::array; +} // namespace world + +namespace world +{ +constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, voxel_facing facing, + std::size_t texture, std::size_t frames) +{ + ChunkQuad result = {}; + result[0] = 0x00000000; + result[1] = 0x00000000; + + // [0] XXXXXXXXYYYYYYYYZZZZZZZZ-------- + result[0] |= (0x000000FFU & static_cast(position.x * 16.0f)) << 24U; + result[0] |= (0x000000FFU & static_cast(position.y * 16.0f)) << 16U; + result[0] |= (0x000000FFU & static_cast(position.z * 16.0f)) << 8U; + + // [0] ------------------------WWWWHHHH + result[0] |= (0x0000000FU & static_cast(size.x * 16.0f - 1.0f)) << 4U; + result[0] |= (0x0000000FU & static_cast(size.y * 16.0f - 1.0f)); + + // [1] FFFF---------------------------- + result[1] |= (0x0000000FU & static_cast(facing)) << 28U; + + // [1] ----TTTTTTTTTTTAAAAA------------ + result[1] |= (0x000007FFU & static_cast(texture)) << 17U; + result[1] |= (0x0000001FU & static_cast(frames)) << 12U; + + return result; +} +} // namespace world diff --git a/game/client/world/chunk_renderer.cc b/game/client/world/chunk_renderer.cc index 23ce4ec..1b195d7 100644 --- a/game/client/world/chunk_renderer.cc +++ b/game/client/world/chunk_renderer.cc @@ -1,204 +1,204 @@ -#include "client/pch.hh" - -#include "client/world/chunk_renderer.hh" - -#include "core/config/boolean.hh" -#include "core/config/number.hh" - -#include "core/io/config_map.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/dimension.hh" - -#include "shared/coord.hh" - -#include "client/entity/camera.hh" - -#include "client/gui/settings.hh" - -#include "client/world/chunk_mesher.hh" -#include "client/world/chunk_quad.hh" -#include "client/world/outline.hh" -#include "client/world/skybox.hh" -#include "client/world/voxel_anims.hh" -#include "client/world/voxel_atlas.hh" - -#include "client/game.hh" -#include "client/globals.hh" -#include "client/program.hh" -#include "client/toggles.hh" - -// ONLY TOUCH THESE IF THE RESPECTIVE SHADER -// VARIANT MACRO DECLARATIONS LAYOUT CHANGED AS WELL -constexpr static unsigned int WORLD_CURVATURE = 0U; -constexpr static unsigned int WORLD_FOG = 1U; - -static config::Boolean depth_sort_chunks(true); - -static GL_Program quad_program; -static std::size_t u_quad_vproj_matrix; -static std::size_t u_quad_world_position; -static std::size_t u_quad_timings; -static std::size_t u_quad_fog_color; -static std::size_t u_quad_view_distance; -static std::size_t u_quad_textures; -static GLuint quad_vaobj; -static GLuint quad_vbo; - -void world::chunk_renderer::init(void) -{ - globals::client_config.add_value("chunk_renderer.depth_sort_chunks", depth_sort_chunks); - - settings::add_checkbox(5, depth_sort_chunks, settings_location::VIDEO, "chunk_renderer.depth_sort_chunks", false); - - if(!quad_program.setup("shaders/chunk_quad.vert", "shaders/chunk_quad.frag")) { - spdlog::critical("chunk_renderer: quad_program: setup failed"); - std::terminate(); - } - - u_quad_vproj_matrix = quad_program.add_uniform("u_ViewProjMatrix"); - u_quad_world_position = quad_program.add_uniform("u_WorldPosition"); - u_quad_timings = quad_program.add_uniform("u_Timings"); - u_quad_fog_color = quad_program.add_uniform("u_FogColor"); - u_quad_view_distance = quad_program.add_uniform("u_ViewDistance"); - u_quad_textures = quad_program.add_uniform("u_Textures"); - - const glm::fvec3 vertices[4] = { - glm::fvec3(1.0f, 0.0f, 1.0f), - glm::fvec3(1.0f, 0.0f, 0.0f), - glm::fvec3(0.0f, 0.0f, 1.0f), - glm::fvec3(0.0f, 0.0f, 0.0f), - }; - - glGenVertexArrays(1, &quad_vaobj); - glBindVertexArray(quad_vaobj); - - glGenBuffers(1, &quad_vbo); - glBindBuffer(GL_ARRAY_BUFFER, quad_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribDivisor(0, 0); - glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); -} - -void world::chunk_renderer::shutdown(void) -{ - glDeleteBuffers(1, &quad_vbo); - glDeleteVertexArrays(1, &quad_vaobj); - quad_program.destroy(); -} - -void world::chunk_renderer::render(void) -{ - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glLineWidth(1.0f); - - if(toggles::get(TOGGLE_WIREFRAME)) { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } - else { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - - quad_program.set_variant_vert(WORLD_CURVATURE, client_game::world_curvature.get_value()); - quad_program.set_variant_vert(WORLD_FOG, client_game::fog_mode.get_value()); - quad_program.set_variant_frag(WORLD_FOG, client_game::fog_mode.get_value()); - - if(!quad_program.update()) { - spdlog::critical("chunk_renderer: quad_program: update failed"); - quad_program.destroy(); - std::terminate(); - } - - GLuint timings[3]; - timings[0] = globals::window_frametime; - timings[1] = globals::window_frametime_avg; - timings[2] = world::voxel_anims::frame; - - const auto group = globals::dimension->chunks.group(entt::get); - - if(depth_sort_chunks.get_value()) { - // FIXME: speed! sorting every frame doesn't look - // like a good idea. Can we store the group elsewhere and - // still have all the up-to-date chunk things inside? - group.sort([](entt::entity ea, entt::entity eb) { - const auto dir_a = globals::dimension->chunks.get(ea).cpos - entity::camera::position_chunk; - const auto dir_b = globals::dimension->chunks.get(eb).cpos - entity::camera::position_chunk; - - const auto da = dir_a[0] * dir_a[0] + dir_a[1] * dir_a[1] + dir_a[2] * dir_a[2]; - const auto db = dir_b[0] * dir_b[0] + dir_b[1] * dir_b[1] + dir_b[2] * dir_b[2]; - - return da > db; - }); - } - - for(std::size_t plane_id = 0; plane_id < world::voxel_atlas::plane_count(); ++plane_id) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D_ARRAY, world::voxel_atlas::plane_texture(plane_id)); - - glBindVertexArray(quad_vaobj); - - glUseProgram(quad_program.handle); - glUniformMatrix4fv(quad_program.uniforms[u_quad_vproj_matrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); - glUniform3uiv(quad_program.uniforms[u_quad_timings].location, 1, timings); - glUniform4fv(quad_program.uniforms[u_quad_fog_color].location, 1, glm::value_ptr(world::skybox::fog_color)); - glUniform1f(quad_program.uniforms[u_quad_view_distance].location, entity::camera::view_distance.get_value() * CHUNK_SIZE); - glUniform1i(quad_program.uniforms[u_quad_textures].location, 0); // GL_TEXTURE0 - - glDisable(GL_BLEND); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CCW); - - for(const auto [entity, chunk, mesh] : group.each()) { - if(plane_id < mesh.quad_nb.size() && mesh.quad_nb[plane_id].handle && mesh.quad_nb[plane_id].size) { - const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); - glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); - - glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_nb[plane_id].handle); - - glEnableVertexAttribArray(1); - glVertexAttribDivisor(1, 1); - glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, sizeof(ChunkQuad), nullptr); - - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, mesh.quad_nb[plane_id].size); - - globals::num_drawcalls += 1; - globals::num_triangles += 2 * mesh.quad_nb[plane_id].size; - } - } - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - for(const auto [entity, chunk, mesh] : group.each()) { - if(plane_id < mesh.quad_b.size() && mesh.quad_b[plane_id].handle && mesh.quad_b[plane_id].size) { - const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); - glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); - - glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_b[plane_id].handle); - - glEnableVertexAttribArray(1); - glVertexAttribDivisor(1, 1); - glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, sizeof(ChunkQuad), nullptr); - - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, mesh.quad_b[plane_id].size); - - globals::num_drawcalls += 1; - globals::num_triangles += 2 * mesh.quad_b[plane_id].size; - } - } - } - - if(toggles::get(TOGGLE_CHUNK_AABB)) { - world::outline::prepare(); - - for(const auto [entity, chunk, mesh] : group.each()) { - const auto size = glm::fvec3(CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); - world::outline::cube(chunk.cpos, glm::fvec3(0.0f, 0.0f, 0.0f), size, 1.0f, glm::fvec4(1.0f, 1.0f, 0.0f, 1.0f)); - } - } -} +#include "client/pch.hh" + +#include "client/world/chunk_renderer.hh" + +#include "core/config/boolean.hh" +#include "core/config/number.hh" + +#include "core/io/config_map.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" + +#include "shared/coord.hh" + +#include "client/entity/camera.hh" + +#include "client/gui/settings.hh" + +#include "client/world/chunk_mesher.hh" +#include "client/world/chunk_quad.hh" +#include "client/world/outline.hh" +#include "client/world/skybox.hh" +#include "client/world/voxel_anims.hh" +#include "client/world/voxel_atlas.hh" + +#include "client/game.hh" +#include "client/globals.hh" +#include "client/program.hh" +#include "client/toggles.hh" + +// ONLY TOUCH THESE IF THE RESPECTIVE SHADER +// VARIANT MACRO DECLARATIONS LAYOUT CHANGED AS WELL +constexpr static unsigned int WORLD_CURVATURE = 0U; +constexpr static unsigned int WORLD_FOG = 1U; + +static config::Boolean depth_sort_chunks(true); + +static GL_Program quad_program; +static std::size_t u_quad_vproj_matrix; +static std::size_t u_quad_world_position; +static std::size_t u_quad_timings; +static std::size_t u_quad_fog_color; +static std::size_t u_quad_view_distance; +static std::size_t u_quad_textures; +static GLuint quad_vaobj; +static GLuint quad_vbo; + +void world::chunk_renderer::init(void) +{ + globals::client_config.add_value("chunk_renderer.depth_sort_chunks", depth_sort_chunks); + + settings::add_checkbox(5, depth_sort_chunks, settings_location::VIDEO, "chunk_renderer.depth_sort_chunks", false); + + if(!quad_program.setup("shaders/chunk_quad.vert", "shaders/chunk_quad.frag")) { + spdlog::critical("chunk_renderer: quad_program: setup failed"); + std::terminate(); + } + + u_quad_vproj_matrix = quad_program.add_uniform("u_ViewProjMatrix"); + u_quad_world_position = quad_program.add_uniform("u_WorldPosition"); + u_quad_timings = quad_program.add_uniform("u_Timings"); + u_quad_fog_color = quad_program.add_uniform("u_FogColor"); + u_quad_view_distance = quad_program.add_uniform("u_ViewDistance"); + u_quad_textures = quad_program.add_uniform("u_Textures"); + + const glm::fvec3 vertices[4] = { + glm::fvec3(1.0f, 0.0f, 1.0f), + glm::fvec3(1.0f, 0.0f, 0.0f), + glm::fvec3(0.0f, 0.0f, 1.0f), + glm::fvec3(0.0f, 0.0f, 0.0f), + }; + + glGenVertexArrays(1, &quad_vaobj); + glBindVertexArray(quad_vaobj); + + glGenBuffers(1, &quad_vbo); + glBindBuffer(GL_ARRAY_BUFFER, quad_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribDivisor(0, 0); + glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); +} + +void world::chunk_renderer::shutdown(void) +{ + glDeleteBuffers(1, &quad_vbo); + glDeleteVertexArrays(1, &quad_vaobj); + quad_program.destroy(); +} + +void world::chunk_renderer::render(void) +{ + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glLineWidth(1.0f); + + if(toggles::get(TOGGLE_WIREFRAME)) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } + else { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + quad_program.set_variant_vert(WORLD_CURVATURE, client_game::world_curvature.get_value()); + quad_program.set_variant_vert(WORLD_FOG, client_game::fog_mode.get_value()); + quad_program.set_variant_frag(WORLD_FOG, client_game::fog_mode.get_value()); + + if(!quad_program.update()) { + spdlog::critical("chunk_renderer: quad_program: update failed"); + quad_program.destroy(); + std::terminate(); + } + + GLuint timings[3]; + timings[0] = globals::window_frametime; + timings[1] = globals::window_frametime_avg; + timings[2] = world::voxel_anims::frame; + + const auto group = globals::dimension->chunks.group(entt::get); + + if(depth_sort_chunks.get_value()) { + // FIXME: speed! sorting every frame doesn't look + // like a good idea. Can we store the group elsewhere and + // still have all the up-to-date chunk things inside? + group.sort([](entt::entity ea, entt::entity eb) { + const auto dir_a = globals::dimension->chunks.get(ea).cpos - entity::camera::position_chunk; + const auto dir_b = globals::dimension->chunks.get(eb).cpos - entity::camera::position_chunk; + + const auto da = dir_a[0] * dir_a[0] + dir_a[1] * dir_a[1] + dir_a[2] * dir_a[2]; + const auto db = dir_b[0] * dir_b[0] + dir_b[1] * dir_b[1] + dir_b[2] * dir_b[2]; + + return da > db; + }); + } + + for(std::size_t plane_id = 0; plane_id < world::voxel_atlas::plane_count(); ++plane_id) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, world::voxel_atlas::plane_texture(plane_id)); + + glBindVertexArray(quad_vaobj); + + glUseProgram(quad_program.handle); + glUniformMatrix4fv(quad_program.uniforms[u_quad_vproj_matrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); + glUniform3uiv(quad_program.uniforms[u_quad_timings].location, 1, timings); + glUniform4fv(quad_program.uniforms[u_quad_fog_color].location, 1, glm::value_ptr(world::skybox::fog_color)); + glUniform1f(quad_program.uniforms[u_quad_view_distance].location, entity::camera::view_distance.get_value() * CHUNK_SIZE); + glUniform1i(quad_program.uniforms[u_quad_textures].location, 0); // GL_TEXTURE0 + + glDisable(GL_BLEND); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + + for(const auto [entity, chunk, mesh] : group.each()) { + if(plane_id < mesh.quad_nb.size() && mesh.quad_nb[plane_id].handle && mesh.quad_nb[plane_id].size) { + const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); + glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); + + glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_nb[plane_id].handle); + + glEnableVertexAttribArray(1); + glVertexAttribDivisor(1, 1); + glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, sizeof(ChunkQuad), nullptr); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, mesh.quad_nb[plane_id].size); + + globals::num_drawcalls += 1; + globals::num_triangles += 2 * mesh.quad_nb[plane_id].size; + } + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for(const auto [entity, chunk, mesh] : group.each()) { + if(plane_id < mesh.quad_b.size() && mesh.quad_b[plane_id].handle && mesh.quad_b[plane_id].size) { + const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); + glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); + + glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_b[plane_id].handle); + + glEnableVertexAttribArray(1); + glVertexAttribDivisor(1, 1); + glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, sizeof(ChunkQuad), nullptr); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, mesh.quad_b[plane_id].size); + + globals::num_drawcalls += 1; + globals::num_triangles += 2 * mesh.quad_b[plane_id].size; + } + } + } + + if(toggles::get(TOGGLE_CHUNK_AABB)) { + world::outline::prepare(); + + for(const auto [entity, chunk, mesh] : group.each()) { + const auto size = glm::fvec3(CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); + world::outline::cube(chunk.cpos, glm::fvec3(0.0f, 0.0f, 0.0f), size, 1.0f, glm::fvec4(1.0f, 1.0f, 0.0f, 1.0f)); + } + } +} diff --git a/game/client/world/chunk_renderer.hh b/game/client/world/chunk_renderer.hh index 2b73225..88b381f 100644 --- a/game/client/world/chunk_renderer.hh +++ b/game/client/world/chunk_renderer.hh @@ -1,8 +1,8 @@ -#pragma once - -namespace world::chunk_renderer -{ -void init(void); -void shutdown(void); -void render(void); -} // namespace world::chunk_renderer +#pragma once + +namespace world::chunk_renderer +{ +void init(void); +void shutdown(void); +void render(void); +} // namespace world::chunk_renderer diff --git a/game/client/world/chunk_vbo.hh b/game/client/world/chunk_vbo.hh index 175b34f..9fedf0d 100644 --- a/game/client/world/chunk_vbo.hh +++ b/game/client/world/chunk_vbo.hh @@ -1,22 +1,22 @@ -#pragma once - -namespace world -{ -class ChunkVBO final { -public: - std::size_t size; - GLuint handle; - -public: - inline ~ChunkVBO(void) - { - // The ChunkVBO structure is meant to be a part - // of the ChunkMesh component within the EnTT registry; - // When the registry is cleaned or a chunk is removed, components - // are expected to be safely disposed of so we need a destructor; - if(handle) { - glDeleteBuffers(1, &handle); - } - } -}; -} // namespace world +#pragma once + +namespace world +{ +class ChunkVBO final { +public: + std::size_t size; + GLuint handle; + +public: + inline ~ChunkVBO(void) + { + // The ChunkVBO structure is meant to be a part + // of the ChunkMesh component within the EnTT registry; + // When the registry is cleaned or a chunk is removed, components + // are expected to be safely disposed of so we need a destructor; + if(handle) { + glDeleteBuffers(1, &handle); + } + } +}; +} // namespace world diff --git a/game/client/world/chunk_visibility.cc b/game/client/world/chunk_visibility.cc index 871c04b..e8828ee 100644 --- a/game/client/world/chunk_visibility.cc +++ b/game/client/world/chunk_visibility.cc @@ -1,90 +1,90 @@ -#include "client/pch.hh" - -#include "client/world/chunk_visibility.hh" - -#include "core/config/number.hh" - -#include "core/math/vectors.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/chunk_aabb.hh" -#include "shared/world/dimension.hh" - -#include "shared/protocol.hh" - -#include "client/entity/camera.hh" - -#include "client/globals.hh" -#include "client/session.hh" - -// Sending a somewhat large amount of network packets -// can easily overwhelm both client, server and the network -// channel created between the two. To prevent this from happening -// we throttle the client's ever increasing itch for new chunks -constexpr static unsigned int MAX_CHUNKS_REQUESTS_PER_FRAME = 16U; - -static world::ChunkAABB current_view_box; -static world::ChunkAABB previous_view_box; -static std::vector requests; - -static void update_requests(void) -{ - requests.clear(); - - for(auto cx = current_view_box.min.x; cx != current_view_box.max.x; cx += 1) - for(auto cy = current_view_box.min.y; cy != current_view_box.max.y; cy += 1) - for(auto cz = current_view_box.min.z; cz != current_view_box.max.z; cz += 1) { - auto cpos = chunk_pos(cx, cy, cz); - - if(!globals::dimension->find_chunk(cpos)) { - requests.push_back(cpos); - } - } - - std::sort(requests.begin(), requests.end(), [](const chunk_pos& cpos_a, const chunk_pos& cpos_b) { - auto da = math::distance2(cpos_a, entity::camera::position_chunk); - auto db = math::distance2(cpos_b, entity::camera::position_chunk); - return da > db; - }); -} - -void world::chunk_visibility::update_late(void) -{ - current_view_box.min = entity::camera::position_chunk - static_cast(entity::camera::view_distance.get_value()); - current_view_box.max = entity::camera::position_chunk + static_cast(entity::camera::view_distance.get_value()); - - if(!session::is_ingame()) { - // This makes sure the previous view box - // is always different from the current one - previous_view_box.min = chunk_pos(INT32_MIN, INT32_MIN, INT32_MIN); - previous_view_box.max = chunk_pos(INT32_MAX, INT32_MAX, INT32_MAX); - return; - } - - if((current_view_box.min != previous_view_box.min) || (current_view_box.max != previous_view_box.max)) { - update_requests(); - } - - for(unsigned int i = 0U; i < MAX_CHUNKS_REQUESTS_PER_FRAME; ++i) { - if(requests.empty()) { - // Done sending requests - break; - } - - protocol::RequestChunk packet; - packet.cpos = requests.back(); - protocol::send(session::peer, protocol::encode(packet)); - - requests.pop_back(); - } - - auto view = globals::dimension->chunks.view(); - - for(const auto [entity, chunk] : view.each()) { - if(!current_view_box.contains(chunk.cpos)) { - globals::dimension->remove_chunk(entity); - } - } - - previous_view_box = current_view_box; -} +#include "client/pch.hh" + +#include "client/world/chunk_visibility.hh" + +#include "core/config/number.hh" + +#include "core/math/vectors.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/chunk_aabb.hh" +#include "shared/world/dimension.hh" + +#include "shared/protocol.hh" + +#include "client/entity/camera.hh" + +#include "client/globals.hh" +#include "client/session.hh" + +// Sending a somewhat large amount of network packets +// can easily overwhelm both client, server and the network +// channel created between the two. To prevent this from happening +// we throttle the client's ever increasing itch for new chunks +constexpr static unsigned int MAX_CHUNKS_REQUESTS_PER_FRAME = 16U; + +static world::ChunkAABB current_view_box; +static world::ChunkAABB previous_view_box; +static std::vector requests; + +static void update_requests(void) +{ + requests.clear(); + + for(auto cx = current_view_box.min.x; cx != current_view_box.max.x; cx += 1) + for(auto cy = current_view_box.min.y; cy != current_view_box.max.y; cy += 1) + for(auto cz = current_view_box.min.z; cz != current_view_box.max.z; cz += 1) { + auto cpos = chunk_pos(cx, cy, cz); + + if(!globals::dimension->find_chunk(cpos)) { + requests.push_back(cpos); + } + } + + std::sort(requests.begin(), requests.end(), [](const chunk_pos& cpos_a, const chunk_pos& cpos_b) { + auto da = math::distance2(cpos_a, entity::camera::position_chunk); + auto db = math::distance2(cpos_b, entity::camera::position_chunk); + return da > db; + }); +} + +void world::chunk_visibility::update_late(void) +{ + current_view_box.min = entity::camera::position_chunk - static_cast(entity::camera::view_distance.get_value()); + current_view_box.max = entity::camera::position_chunk + static_cast(entity::camera::view_distance.get_value()); + + if(!session::is_ingame()) { + // This makes sure the previous view box + // is always different from the current one + previous_view_box.min = chunk_pos(INT32_MIN, INT32_MIN, INT32_MIN); + previous_view_box.max = chunk_pos(INT32_MAX, INT32_MAX, INT32_MAX); + return; + } + + if((current_view_box.min != previous_view_box.min) || (current_view_box.max != previous_view_box.max)) { + update_requests(); + } + + for(unsigned int i = 0U; i < MAX_CHUNKS_REQUESTS_PER_FRAME; ++i) { + if(requests.empty()) { + // Done sending requests + break; + } + + protocol::RequestChunk packet; + packet.cpos = requests.back(); + protocol::send(session::peer, protocol::encode(packet)); + + requests.pop_back(); + } + + auto view = globals::dimension->chunks.view(); + + for(const auto [entity, chunk] : view.each()) { + if(!current_view_box.contains(chunk.cpos)) { + globals::dimension->remove_chunk(entity); + } + } + + previous_view_box = current_view_box; +} diff --git a/game/client/world/chunk_visibility.hh b/game/client/world/chunk_visibility.hh index 8d1f3cd..e9b2d89 100644 --- a/game/client/world/chunk_visibility.hh +++ b/game/client/world/chunk_visibility.hh @@ -1,6 +1,6 @@ -#pragma once - -namespace world::chunk_visibility -{ -void update_late(void); -} // namespace world::chunk_visibility +#pragma once + +namespace world::chunk_visibility +{ +void update_late(void); +} // namespace world::chunk_visibility diff --git a/game/client/world/outline.cc b/game/client/world/outline.cc index 396d297..62f9624 100644 --- a/game/client/world/outline.cc +++ b/game/client/world/outline.cc @@ -1,150 +1,150 @@ -#include "client/pch.hh" - -#include "client/world/outline.hh" - -#include "core/config/boolean.hh" -#include "core/config/number.hh" - -#include "shared/coord.hh" - -#include "client/entity/camera.hh" - -#include "client/const.hh" -#include "client/game.hh" -#include "client/program.hh" - -// ONLY TOUCH THESE IF THE RESPECTIVE SHADER -// VARIANT MACRO DECLARATIONS LAYOUT CHANGED AS WELL -constexpr static unsigned int WORLD_CURVATURE = 0U; - -static GL_Program program; -static std::size_t u_vpmatrix; -static std::size_t u_worldpos; -static std::size_t u_viewdist; -static std::size_t u_modulate; -static std::size_t u_scale; - -static GLuint vaobj; -static GLuint cube_vbo; -static GLuint line_vbo; - -void world::outline::init(void) -{ - if(!program.setup("shaders/outline.vert", "shaders/outline.frag")) { - spdlog::critical("outline: program setup failed"); - std::terminate(); - } - - u_vpmatrix = program.add_uniform("u_ViewProjMatrix"); - u_worldpos = program.add_uniform("u_WorldPosition"); - u_viewdist = program.add_uniform("u_ViewDistance"); - u_modulate = program.add_uniform("u_Modulate"); - u_scale = program.add_uniform("u_Scale"); - - const glm::fvec3 cube_vertices[24] = { - glm::fvec3(0.0f, 0.0f, 0.0f), - glm::fvec3(0.0f, 1.0f, 0.0f), - glm::fvec3(0.0f, 1.0f, 0.0f), - glm::fvec3(1.0f, 1.0f, 0.0f), - glm::fvec3(1.0f, 1.0f, 0.0f), - glm::fvec3(1.0f, 0.0f, 0.0f), - glm::fvec3(1.0f, 0.0f, 0.0f), - glm::fvec3(0.0f, 0.0f, 0.0f), - - glm::fvec3(0.0f, 0.0f, 1.0f), - glm::fvec3(0.0f, 1.0f, 1.0f), - glm::fvec3(0.0f, 1.0f, 1.0f), - glm::fvec3(1.0f, 1.0f, 1.0f), - glm::fvec3(1.0f, 1.0f, 1.0f), - glm::fvec3(1.0f, 0.0f, 1.0f), - glm::fvec3(1.0f, 0.0f, 1.0f), - glm::fvec3(0.0f, 0.0f, 1.0f), - - glm::fvec3(0.0f, 0.0f, 0.0f), - glm::fvec3(0.0f, 0.0f, 1.0f), - glm::fvec3(0.0f, 1.0f, 0.0f), - glm::fvec3(0.0f, 1.0f, 1.0f), - glm::fvec3(1.0f, 0.0f, 0.0f), - glm::fvec3(1.0f, 0.0f, 1.0f), - glm::fvec3(1.0f, 1.0f, 0.0f), - glm::fvec3(1.0f, 1.0f, 1.0f), - }; - - glGenBuffers(1, &cube_vbo); - glBindBuffer(GL_ARRAY_BUFFER, cube_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW); - - const glm::fvec3 line_vertices[2] = { - glm::fvec3(0.0f, 0.0f, 0.0f), - glm::fvec3(1.0f, 1.0f, 1.0f), - }; - - glGenBuffers(1, &line_vbo); - glBindBuffer(GL_ARRAY_BUFFER, line_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(line_vertices), line_vertices, GL_STATIC_DRAW); - - glGenVertexArrays(1, &vaobj); - - glBindVertexArray(vaobj); - glEnableVertexAttribArray(0); - glVertexAttribDivisor(0, 0); -} - -void world::outline::shutdown(void) -{ - glDeleteVertexArrays(1, &vaobj); - glDeleteBuffers(1, &line_vbo); - glDeleteBuffers(1, &cube_vbo); - program.destroy(); -} - -void world::outline::prepare(void) -{ - program.set_variant_vert(WORLD_CURVATURE, client_game::world_curvature.get_value()); - - if(!program.update()) { - spdlog::critical("outline_renderer: program update failed"); - std::terminate(); - } - - glDisable(GL_CULL_FACE); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - glUseProgram(program.handle); - glUniformMatrix4fv(program.uniforms[u_vpmatrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); - glUniform1f(program.uniforms[u_viewdist].location, CHUNK_SIZE * entity::camera::view_distance.get_value()); - - glBindVertexArray(vaobj); - glEnableVertexAttribArray(0); - glVertexAttribDivisor(0, 0); -} - -void world::outline::cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) -{ - auto patch_cpos = cpos - entity::camera::position_chunk; - - glLineWidth(thickness); - - glUniform3fv(program.uniforms[u_worldpos].location, 1, glm::value_ptr(coord::to_fvec3(patch_cpos, fpos))); - glUniform4fv(program.uniforms[u_modulate].location, 1, glm::value_ptr(color)); - glUniform3fv(program.uniforms[u_scale].location, 1, glm::value_ptr(size)); - - glBindBuffer(GL_ARRAY_BUFFER, cube_vbo); - glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); - glDrawArrays(GL_LINES, 0, 24); -} - -void world::outline::line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) -{ - auto patch_cpos = cpos - entity::camera::position_chunk; - - glLineWidth(thickness); - - glUniform3fv(program.uniforms[u_worldpos].location, 1, glm::value_ptr(coord::to_fvec3(patch_cpos, fpos))); - glUniform4fv(program.uniforms[u_modulate].location, 1, glm::value_ptr(color)); - glUniform3fv(program.uniforms[u_scale].location, 1, glm::value_ptr(size)); - - glBindBuffer(GL_ARRAY_BUFFER, line_vbo); - glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); - glDrawArrays(GL_LINES, 0, 2); -} +#include "client/pch.hh" + +#include "client/world/outline.hh" + +#include "core/config/boolean.hh" +#include "core/config/number.hh" + +#include "shared/coord.hh" + +#include "client/entity/camera.hh" + +#include "client/const.hh" +#include "client/game.hh" +#include "client/program.hh" + +// ONLY TOUCH THESE IF THE RESPECTIVE SHADER +// VARIANT MACRO DECLARATIONS LAYOUT CHANGED AS WELL +constexpr static unsigned int WORLD_CURVATURE = 0U; + +static GL_Program program; +static std::size_t u_vpmatrix; +static std::size_t u_worldpos; +static std::size_t u_viewdist; +static std::size_t u_modulate; +static std::size_t u_scale; + +static GLuint vaobj; +static GLuint cube_vbo; +static GLuint line_vbo; + +void world::outline::init(void) +{ + if(!program.setup("shaders/outline.vert", "shaders/outline.frag")) { + spdlog::critical("outline: program setup failed"); + std::terminate(); + } + + u_vpmatrix = program.add_uniform("u_ViewProjMatrix"); + u_worldpos = program.add_uniform("u_WorldPosition"); + u_viewdist = program.add_uniform("u_ViewDistance"); + u_modulate = program.add_uniform("u_Modulate"); + u_scale = program.add_uniform("u_Scale"); + + const glm::fvec3 cube_vertices[24] = { + glm::fvec3(0.0f, 0.0f, 0.0f), + glm::fvec3(0.0f, 1.0f, 0.0f), + glm::fvec3(0.0f, 1.0f, 0.0f), + glm::fvec3(1.0f, 1.0f, 0.0f), + glm::fvec3(1.0f, 1.0f, 0.0f), + glm::fvec3(1.0f, 0.0f, 0.0f), + glm::fvec3(1.0f, 0.0f, 0.0f), + glm::fvec3(0.0f, 0.0f, 0.0f), + + glm::fvec3(0.0f, 0.0f, 1.0f), + glm::fvec3(0.0f, 1.0f, 1.0f), + glm::fvec3(0.0f, 1.0f, 1.0f), + glm::fvec3(1.0f, 1.0f, 1.0f), + glm::fvec3(1.0f, 1.0f, 1.0f), + glm::fvec3(1.0f, 0.0f, 1.0f), + glm::fvec3(1.0f, 0.0f, 1.0f), + glm::fvec3(0.0f, 0.0f, 1.0f), + + glm::fvec3(0.0f, 0.0f, 0.0f), + glm::fvec3(0.0f, 0.0f, 1.0f), + glm::fvec3(0.0f, 1.0f, 0.0f), + glm::fvec3(0.0f, 1.0f, 1.0f), + glm::fvec3(1.0f, 0.0f, 0.0f), + glm::fvec3(1.0f, 0.0f, 1.0f), + glm::fvec3(1.0f, 1.0f, 0.0f), + glm::fvec3(1.0f, 1.0f, 1.0f), + }; + + glGenBuffers(1, &cube_vbo); + glBindBuffer(GL_ARRAY_BUFFER, cube_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW); + + const glm::fvec3 line_vertices[2] = { + glm::fvec3(0.0f, 0.0f, 0.0f), + glm::fvec3(1.0f, 1.0f, 1.0f), + }; + + glGenBuffers(1, &line_vbo); + glBindBuffer(GL_ARRAY_BUFFER, line_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(line_vertices), line_vertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &vaobj); + + glBindVertexArray(vaobj); + glEnableVertexAttribArray(0); + glVertexAttribDivisor(0, 0); +} + +void world::outline::shutdown(void) +{ + glDeleteVertexArrays(1, &vaobj); + glDeleteBuffers(1, &line_vbo); + glDeleteBuffers(1, &cube_vbo); + program.destroy(); +} + +void world::outline::prepare(void) +{ + program.set_variant_vert(WORLD_CURVATURE, client_game::world_curvature.get_value()); + + if(!program.update()) { + spdlog::critical("outline_renderer: program update failed"); + std::terminate(); + } + + glDisable(GL_CULL_FACE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glUseProgram(program.handle); + glUniformMatrix4fv(program.uniforms[u_vpmatrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); + glUniform1f(program.uniforms[u_viewdist].location, CHUNK_SIZE * entity::camera::view_distance.get_value()); + + glBindVertexArray(vaobj); + glEnableVertexAttribArray(0); + glVertexAttribDivisor(0, 0); +} + +void world::outline::cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) +{ + auto patch_cpos = cpos - entity::camera::position_chunk; + + glLineWidth(thickness); + + glUniform3fv(program.uniforms[u_worldpos].location, 1, glm::value_ptr(coord::to_fvec3(patch_cpos, fpos))); + glUniform4fv(program.uniforms[u_modulate].location, 1, glm::value_ptr(color)); + glUniform3fv(program.uniforms[u_scale].location, 1, glm::value_ptr(size)); + + glBindBuffer(GL_ARRAY_BUFFER, cube_vbo); + glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); + glDrawArrays(GL_LINES, 0, 24); +} + +void world::outline::line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) +{ + auto patch_cpos = cpos - entity::camera::position_chunk; + + glLineWidth(thickness); + + glUniform3fv(program.uniforms[u_worldpos].location, 1, glm::value_ptr(coord::to_fvec3(patch_cpos, fpos))); + glUniform4fv(program.uniforms[u_modulate].location, 1, glm::value_ptr(color)); + glUniform3fv(program.uniforms[u_scale].location, 1, glm::value_ptr(size)); + + glBindBuffer(GL_ARRAY_BUFFER, line_vbo); + glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); + glDrawArrays(GL_LINES, 0, 2); +} diff --git a/game/client/world/outline.hh b/game/client/world/outline.hh index 2456a32..bd69ecc 100644 --- a/game/client/world/outline.hh +++ b/game/client/world/outline.hh @@ -1,16 +1,16 @@ -#pragma once - -#include "shared/types.hh" - -namespace world::outline -{ -void init(void); -void shutdown(void); -void prepare(void); -} // namespace world::outline - -namespace world::outline -{ -void cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); -void line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); -} // namespace world::outline +#pragma once + +#include "shared/types.hh" + +namespace world::outline +{ +void init(void); +void shutdown(void); +void prepare(void); +} // namespace world::outline + +namespace world::outline +{ +void cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); +void line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); +} // namespace world::outline diff --git a/game/client/world/player_target.cc b/game/client/world/player_target.cc index a398eed..f0550c0 100644 --- a/game/client/world/player_target.cc +++ b/game/client/world/player_target.cc @@ -1,69 +1,69 @@ -#include "client/pch.hh" - -#include "client/world/player_target.hh" - -#include "shared/world/dimension.hh" -#include "shared/world/ray_dda.hh" - -#include "shared/coord.hh" - -#include "client/entity/camera.hh" -#include "client/world/outline.hh" - -#include "client/game.hh" -#include "client/globals.hh" -#include "client/session.hh" - -constexpr static float MAX_REACH = 16.0f; - -voxel_id world::player_target::voxel; -voxel_pos world::player_target::coord; -voxel_pos world::player_target::normal; -const world::VoxelInfo* world::player_target::info; - -void world::player_target::init(void) -{ - world::player_target::voxel = NULL_VOXEL_ID; - world::player_target::coord = voxel_pos(); - world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; -} - -void world::player_target::update(void) -{ - if(session::is_ingame()) { - RayDDA ray(globals::dimension, entity::camera::position_chunk, entity::camera::position_local, entity::camera::direction); - - do { - world::player_target::voxel = ray.step(); - - if(world::player_target::voxel != NULL_VOXEL_ID) { - world::player_target::coord = ray.vpos; - world::player_target::normal = ray.vnormal; - world::player_target::info = world::voxel_registry::find(world::player_target::voxel); - break; - } - - world::player_target::coord = voxel_pos(); - world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; - } while(ray.distance < MAX_REACH); - } - else { - world::player_target::voxel = NULL_VOXEL_ID; - world::player_target::coord = voxel_pos(); - world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; - } -} - -void world::player_target::render(void) -{ - if((world::player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) { - auto cpos = coord::to_chunk(world::player_target::coord); - auto fpos = coord::to_local(world::player_target::coord); - - world::outline::prepare(); - world::outline::cube(cpos, glm::fvec3(fpos), glm::fvec3(1.0f), 2.0f, glm::fvec4(0.0f, 0.0f, 0.0f, 1.0f)); - } -} +#include "client/pch.hh" + +#include "client/world/player_target.hh" + +#include "shared/world/dimension.hh" +#include "shared/world/ray_dda.hh" + +#include "shared/coord.hh" + +#include "client/entity/camera.hh" +#include "client/world/outline.hh" + +#include "client/game.hh" +#include "client/globals.hh" +#include "client/session.hh" + +constexpr static float MAX_REACH = 16.0f; + +voxel_id world::player_target::voxel; +voxel_pos world::player_target::coord; +voxel_pos world::player_target::normal; +const world::VoxelInfo* world::player_target::info; + +void world::player_target::init(void) +{ + world::player_target::voxel = NULL_VOXEL_ID; + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; +} + +void world::player_target::update(void) +{ + if(session::is_ingame()) { + RayDDA ray(globals::dimension, entity::camera::position_chunk, entity::camera::position_local, entity::camera::direction); + + do { + world::player_target::voxel = ray.step(); + + if(world::player_target::voxel != NULL_VOXEL_ID) { + world::player_target::coord = ray.vpos; + world::player_target::normal = ray.vnormal; + world::player_target::info = world::voxel_registry::find(world::player_target::voxel); + break; + } + + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; + } while(ray.distance < MAX_REACH); + } + else { + world::player_target::voxel = NULL_VOXEL_ID; + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; + } +} + +void world::player_target::render(void) +{ + if((world::player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) { + auto cpos = coord::to_chunk(world::player_target::coord); + auto fpos = coord::to_local(world::player_target::coord); + + world::outline::prepare(); + world::outline::cube(cpos, glm::fvec3(fpos), glm::fvec3(1.0f), 2.0f, glm::fvec4(0.0f, 0.0f, 0.0f, 1.0f)); + } +} diff --git a/game/client/world/player_target.hh b/game/client/world/player_target.hh index f137a1b..f0db9be 100644 --- a/game/client/world/player_target.hh +++ b/game/client/world/player_target.hh @@ -1,18 +1,18 @@ -#pragma once - -#include "shared/world/voxel_registry.hh" - -namespace world::player_target -{ -extern voxel_id voxel; -extern voxel_pos coord; -extern voxel_pos normal; -extern const VoxelInfo* info; -} // namespace world::player_target - -namespace world::player_target -{ -void init(void); -void update(void); -void render(void); -} // namespace world::player_target +#pragma once + +#include "shared/world/voxel_registry.hh" + +namespace world::player_target +{ +extern voxel_id voxel; +extern voxel_pos coord; +extern voxel_pos normal; +extern const VoxelInfo* info; +} // namespace world::player_target + +namespace world::player_target +{ +void init(void); +void update(void); +void render(void); +} // namespace world::player_target diff --git a/game/client/world/skybox.cc b/game/client/world/skybox.cc index 5e52fa4..cfff141 100644 --- a/game/client/world/skybox.cc +++ b/game/client/world/skybox.cc @@ -1,11 +1,11 @@ -#include "client/pch.hh" - -#include "client/world/skybox.hh" - -glm::fvec3 world::skybox::fog_color; - -void world::skybox::init(void) -{ - // https://convertingcolors.com/hex-color-B1F3FF.html - world::skybox::fog_color = glm::fvec3(0.690f, 0.950f, 1.000f); -} +#include "client/pch.hh" + +#include "client/world/skybox.hh" + +glm::fvec3 world::skybox::fog_color; + +void world::skybox::init(void) +{ + // https://convertingcolors.com/hex-color-B1F3FF.html + world::skybox::fog_color = glm::fvec3(0.690f, 0.950f, 1.000f); +} diff --git a/game/client/world/skybox.hh b/game/client/world/skybox.hh index 40113cd..39a514b 100644 --- a/game/client/world/skybox.hh +++ b/game/client/world/skybox.hh @@ -1,11 +1,11 @@ -#pragma once - -namespace world::skybox -{ -extern glm::fvec3 fog_color; -} // namespace world::skybox - -namespace world::skybox -{ -void init(void); -} // namespace world::skybox +#pragma once + +namespace world::skybox +{ +extern glm::fvec3 fog_color; +} // namespace world::skybox + +namespace world::skybox +{ +void init(void); +} // namespace world::skybox diff --git a/game/client/world/voxel_anims.cc b/game/client/world/voxel_anims.cc index e4d9cf6..185d463 100644 --- a/game/client/world/voxel_anims.cc +++ b/game/client/world/voxel_anims.cc @@ -1,33 +1,33 @@ -#include "client/pch.hh" - -#include "client/world/voxel_anims.hh" - -#include "core/config/number.hh" - -#include "core/io/config_map.hh" - -#include "core/math/constexpr.hh" - -#include "client/globals.hh" - -static config::Unsigned base_framerate(16U, 1U, 16U); - -std::uint64_t world::voxel_anims::nextframe = 0U; -std::uint32_t world::voxel_anims::frame = 0U; - -void world::voxel_anims::init(void) -{ - globals::client_config.add_value("voxel_anims.base_framerate", base_framerate); - - world::voxel_anims::nextframe = 0U; - world::voxel_anims::frame = 0U; -} - -void world::voxel_anims::update(void) -{ - if(globals::curtime >= world::voxel_anims::nextframe) { - world::voxel_anims::nextframe = globals::curtime - + static_cast(1000000.0 / static_cast(base_framerate.get_value())); - world::voxel_anims::frame += 1U; - } -} +#include "client/pch.hh" + +#include "client/world/voxel_anims.hh" + +#include "core/config/number.hh" + +#include "core/io/config_map.hh" + +#include "core/math/constexpr.hh" + +#include "client/globals.hh" + +static config::Unsigned base_framerate(16U, 1U, 16U); + +std::uint64_t world::voxel_anims::nextframe = 0U; +std::uint32_t world::voxel_anims::frame = 0U; + +void world::voxel_anims::init(void) +{ + globals::client_config.add_value("voxel_anims.base_framerate", base_framerate); + + world::voxel_anims::nextframe = 0U; + world::voxel_anims::frame = 0U; +} + +void world::voxel_anims::update(void) +{ + if(globals::curtime >= world::voxel_anims::nextframe) { + world::voxel_anims::nextframe = globals::curtime + + static_cast(1000000.0 / static_cast(base_framerate.get_value())); + world::voxel_anims::frame += 1U; + } +} diff --git a/game/client/world/voxel_anims.hh b/game/client/world/voxel_anims.hh index 0d8a0d0..e4fe73e 100644 --- a/game/client/world/voxel_anims.hh +++ b/game/client/world/voxel_anims.hh @@ -1,13 +1,13 @@ -#pragma once - -namespace world::voxel_anims -{ -extern std::uint64_t nextframe; -extern std::uint32_t frame; -} // namespace world::voxel_anims - -namespace world::voxel_anims -{ -void init(void); -void update(void); -} // namespace world::voxel_anims +#pragma once + +namespace world::voxel_anims +{ +extern std::uint64_t nextframe; +extern std::uint32_t frame; +} // namespace world::voxel_anims + +namespace world::voxel_anims +{ +void init(void); +void update(void); +} // namespace world::voxel_anims diff --git a/game/client/world/voxel_atlas.cc b/game/client/world/voxel_atlas.cc index 512a06a..a01db12 100644 --- a/game/client/world/voxel_atlas.cc +++ b/game/client/world/voxel_atlas.cc @@ -1,185 +1,185 @@ -#include "client/pch.hh" - -#include "client/world/voxel_atlas.hh" - -#include "core/math/constexpr.hh" -#include "core/math/crc64.hh" - -#include "core/resource/image.hh" -#include "core/resource/resource.hh" - -struct AtlasPlane final { - std::unordered_map lookup; - std::vector strips; - std::size_t layer_count_max; - std::size_t layer_count; - std::size_t plane_id; - GLuint gl_texture; -}; - -static int atlas_width; -static int atlas_height; -static std::size_t atlas_count; -static std::vector planes; - -// Certain animated and varied voxels just double their -// textures (see the "default" texture part in VoxelInfoBuilder::build) -// so there could either be six UNIQUE atlas strips or only one -// https://crypto.stackexchange.com/questions/55162/best-way-to-hash-two-values-into-one -static std::size_t vector_hash(const std::vector& strings) -{ - std::size_t source = 0; - for(const std::string& str : strings) - source += math::crc64(str); - return math::crc64(&source, sizeof(source)); -} - -static void plane_setup(AtlasPlane& plane) -{ - glGenTextures(1, &plane.gl_texture); - glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, atlas_width, atlas_height, plane.layer_count_max, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); -} - -static world::AtlasStrip* plane_lookup(AtlasPlane& plane, std::size_t hash_value) -{ - const auto it = plane.lookup.find(hash_value); - - if(it != plane.lookup.cend()) { - return &plane.strips[it->second]; - } - - return nullptr; -} - -static world::AtlasStrip* plane_new_strip(AtlasPlane& plane, const std::vector& paths, std::size_t hash_value) -{ - world::AtlasStrip strip = {}; - strip.offset = plane.layer_count; - strip.plane = plane.plane_id; - - glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); - - for(std::size_t i = 0; i < paths.size(); ++i) { - if(auto image = resource::load(paths[i].c_str(), IMAGE_LOAD_FLIP)) { - if((image->size.x != atlas_width) || (image->size.y != atlas_height)) { - spdlog::warn("atlas: {}: size mismatch", paths[i]); - continue; - } - - const std::size_t offset = strip.offset + i; - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, offset, image->size.x, image->size.y, 1, GL_RGBA, GL_UNSIGNED_BYTE, - image->pixels); - } - } - - plane.layer_count += paths.size(); - - const std::size_t index = plane.strips.size(); - plane.lookup.emplace(hash_value, index); - plane.strips.push_back(std::move(strip)); - return &plane.strips[index]; -} - -void world::voxel_atlas::create(int width, int height, std::size_t count) -{ - GLint max_plane_layers; - - atlas_width = 1 << math::log2(width); - atlas_height = 1 << math::log2(height); - - // Clipping this at OpenGL 4.5 limit of 2048 is important due to - // how voxel quad meshes are packed in memory: each texture index is - // confined in 11 bits so having bigger atlas planes makes no sense; - glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_plane_layers); - max_plane_layers = math::clamp(max_plane_layers, 256, 2048); - - for(long i = count; i > 0L; i -= max_plane_layers) { - AtlasPlane plane = {}; - plane.plane_id = planes.size(); - plane.layer_count_max = math::min(max_plane_layers, i); - plane.layer_count = 0; - - const std::size_t save_id = plane.plane_id; - planes.push_back(std::move(plane)); - plane_setup(planes[save_id]); - } - - spdlog::debug("voxel_atlas: count={}", count); - spdlog::debug("voxel_atlas: atlas_size=[{}x{}]", atlas_width, atlas_height); - spdlog::debug("voxel_atlas: max_plane_layers={}", max_plane_layers); -} - -void world::voxel_atlas::destroy(void) -{ - for(const AtlasPlane& plane : planes) - glDeleteTextures(1, &plane.gl_texture); - atlas_width = 0; - atlas_height = 0; - planes.clear(); -} - -std::size_t world::voxel_atlas::plane_count(void) -{ - return planes.size(); -} - -GLuint world::voxel_atlas::plane_texture(std::size_t plane_id) -{ - if(plane_id < planes.size()) { - return planes[plane_id].gl_texture; - } - else { - return 0; - } -} - -void world::voxel_atlas::generate_mipmaps(void) -{ - for(const AtlasPlane& plane : planes) { - glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); - } -} - -world::AtlasStrip* world::voxel_atlas::find_or_load(const std::vector& paths) -{ - const std::size_t hash_value = vector_hash(paths); - - for(AtlasPlane& plane : planes) { - if(AtlasStrip* strip = plane_lookup(plane, hash_value)) { - return strip; - } - - continue; - } - - for(AtlasPlane& plane : planes) { - if((plane.layer_count + paths.size()) <= plane.layer_count_max) { - return plane_new_strip(plane, paths, hash_value); - } - - continue; - } - - return nullptr; -} - -world::AtlasStrip* world::voxel_atlas::find(const std::vector& paths) -{ - const std::size_t hash_value = vector_hash(paths); - - for(AtlasPlane& plane : planes) { - if(AtlasStrip* strip = plane_lookup(plane, hash_value)) { - return strip; - } - - continue; - } - - return nullptr; -} +#include "client/pch.hh" + +#include "client/world/voxel_atlas.hh" + +#include "core/math/constexpr.hh" +#include "core/math/crc64.hh" + +#include "core/resource/image.hh" +#include "core/resource/resource.hh" + +struct AtlasPlane final { + std::unordered_map lookup; + std::vector strips; + std::size_t layer_count_max; + std::size_t layer_count; + std::size_t plane_id; + GLuint gl_texture; +}; + +static int atlas_width; +static int atlas_height; +static std::size_t atlas_count; +static std::vector planes; + +// Certain animated and varied voxels just double their +// textures (see the "default" texture part in VoxelInfoBuilder::build) +// so there could either be six UNIQUE atlas strips or only one +// https://crypto.stackexchange.com/questions/55162/best-way-to-hash-two-values-into-one +static std::size_t vector_hash(const std::vector& strings) +{ + std::size_t source = 0; + for(const std::string& str : strings) + source += math::crc64(str); + return math::crc64(&source, sizeof(source)); +} + +static void plane_setup(AtlasPlane& plane) +{ + glGenTextures(1, &plane.gl_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, atlas_width, atlas_height, plane.layer_count_max, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); +} + +static world::AtlasStrip* plane_lookup(AtlasPlane& plane, std::size_t hash_value) +{ + const auto it = plane.lookup.find(hash_value); + + if(it != plane.lookup.cend()) { + return &plane.strips[it->second]; + } + + return nullptr; +} + +static world::AtlasStrip* plane_new_strip(AtlasPlane& plane, const std::vector& paths, std::size_t hash_value) +{ + world::AtlasStrip strip = {}; + strip.offset = plane.layer_count; + strip.plane = plane.plane_id; + + glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); + + for(std::size_t i = 0; i < paths.size(); ++i) { + if(auto image = resource::load(paths[i].c_str(), IMAGE_LOAD_FLIP)) { + if((image->size.x != atlas_width) || (image->size.y != atlas_height)) { + spdlog::warn("atlas: {}: size mismatch", paths[i]); + continue; + } + + const std::size_t offset = strip.offset + i; + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, offset, image->size.x, image->size.y, 1, GL_RGBA, GL_UNSIGNED_BYTE, + image->pixels); + } + } + + plane.layer_count += paths.size(); + + const std::size_t index = plane.strips.size(); + plane.lookup.emplace(hash_value, index); + plane.strips.push_back(std::move(strip)); + return &plane.strips[index]; +} + +void world::voxel_atlas::create(int width, int height, std::size_t count) +{ + GLint max_plane_layers; + + atlas_width = 1 << math::log2(width); + atlas_height = 1 << math::log2(height); + + // Clipping this at OpenGL 4.5 limit of 2048 is important due to + // how voxel quad meshes are packed in memory: each texture index is + // confined in 11 bits so having bigger atlas planes makes no sense; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_plane_layers); + max_plane_layers = math::clamp(max_plane_layers, 256, 2048); + + for(long i = count; i > 0L; i -= max_plane_layers) { + AtlasPlane plane = {}; + plane.plane_id = planes.size(); + plane.layer_count_max = math::min(max_plane_layers, i); + plane.layer_count = 0; + + const std::size_t save_id = plane.plane_id; + planes.push_back(std::move(plane)); + plane_setup(planes[save_id]); + } + + spdlog::debug("voxel_atlas: count={}", count); + spdlog::debug("voxel_atlas: atlas_size=[{}x{}]", atlas_width, atlas_height); + spdlog::debug("voxel_atlas: max_plane_layers={}", max_plane_layers); +} + +void world::voxel_atlas::destroy(void) +{ + for(const AtlasPlane& plane : planes) + glDeleteTextures(1, &plane.gl_texture); + atlas_width = 0; + atlas_height = 0; + planes.clear(); +} + +std::size_t world::voxel_atlas::plane_count(void) +{ + return planes.size(); +} + +GLuint world::voxel_atlas::plane_texture(std::size_t plane_id) +{ + if(plane_id < planes.size()) { + return planes[plane_id].gl_texture; + } + else { + return 0; + } +} + +void world::voxel_atlas::generate_mipmaps(void) +{ + for(const AtlasPlane& plane : planes) { + glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + } +} + +world::AtlasStrip* world::voxel_atlas::find_or_load(const std::vector& paths) +{ + const std::size_t hash_value = vector_hash(paths); + + for(AtlasPlane& plane : planes) { + if(AtlasStrip* strip = plane_lookup(plane, hash_value)) { + return strip; + } + + continue; + } + + for(AtlasPlane& plane : planes) { + if((plane.layer_count + paths.size()) <= plane.layer_count_max) { + return plane_new_strip(plane, paths, hash_value); + } + + continue; + } + + return nullptr; +} + +world::AtlasStrip* world::voxel_atlas::find(const std::vector& paths) +{ + const std::size_t hash_value = vector_hash(paths); + + for(AtlasPlane& plane : planes) { + if(AtlasStrip* strip = plane_lookup(plane, hash_value)) { + return strip; + } + + continue; + } + + return nullptr; +} diff --git a/game/client/world/voxel_atlas.hh b/game/client/world/voxel_atlas.hh index 70e8a1e..4ae6381 100644 --- a/game/client/world/voxel_atlas.hh +++ b/game/client/world/voxel_atlas.hh @@ -1,28 +1,28 @@ -#pragma once - -namespace world -{ -struct AtlasStrip final { - std::size_t offset; - std::size_t plane; -}; -} // namespace world - -namespace world::voxel_atlas -{ -void create(int width, int height, std::size_t count); -void destroy(void); -} // namespace world::voxel_atlas - -namespace world::voxel_atlas -{ -std::size_t plane_count(void); -GLuint plane_texture(std::size_t plane_id); -void generate_mipmaps(void); -} // namespace world::voxel_atlas - -namespace world::voxel_atlas -{ -AtlasStrip* find_or_load(const std::vector& paths); -AtlasStrip* find(const std::vector& paths); -} // namespace world::voxel_atlas +#pragma once + +namespace world +{ +struct AtlasStrip final { + std::size_t offset; + std::size_t plane; +}; +} // namespace world + +namespace world::voxel_atlas +{ +void create(int width, int height, std::size_t count); +void destroy(void); +} // namespace world::voxel_atlas + +namespace world::voxel_atlas +{ +std::size_t plane_count(void); +GLuint plane_texture(std::size_t plane_id); +void generate_mipmaps(void); +} // namespace world::voxel_atlas + +namespace world::voxel_atlas +{ +AtlasStrip* find_or_load(const std::vector& paths); +AtlasStrip* find(const std::vector& paths); +} // namespace world::voxel_atlas diff --git a/game/client/world/voxel_sounds.cc b/game/client/world/voxel_sounds.cc index 71ea1fc..481e615 100644 --- a/game/client/world/voxel_sounds.cc +++ b/game/client/world/voxel_sounds.cc @@ -1,86 +1,86 @@ -#include "client/pch.hh" - -#include "client/world/voxel_sounds.hh" - -#include "client/resource/sound_effect.hh" - -constexpr static std::size_t NUM_SURFACES = static_cast(world::voxel_surface::COUNT); - -static std::vector> footsteps_sounds[NUM_SURFACES]; -static std::mt19937_64 randomizer; - -static void add_footsteps_effect(world::voxel_surface surface, std::string_view name) -{ - if(auto effect = resource::load(name)) { - auto surface_index = static_cast(surface); - footsteps_sounds[surface_index].push_back(effect); - } -} - -static resource_ptr get_footsteps_effect(world::voxel_surface surface) -{ - auto surface_index = static_cast(surface); - - if(surface_index >= NUM_SURFACES) { - // Surface index out of range - return nullptr; - } - - const auto& sounds = footsteps_sounds[surface_index]; - - if(sounds.empty()) { - // No sounds for this surface - return nullptr; - } - - auto dist = std::uniform_int_distribution(0, sounds.size() - 1); - return sounds.at(dist(randomizer)); -} - -void world::voxel_sounds::init(void) -{ - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default1.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default2.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default3.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default4.wav"); - - add_footsteps_effect(voxel_surface::DIRT, "sounds/surface/dirt1.wav"); - - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass1.wav"); - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass2.wav"); - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass3.wav"); - - add_footsteps_effect(voxel_surface::GRAVEL, "sounds/surface/gravel1.wav"); - - add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand1.wav"); - add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand2.wav"); - - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood1.wav"); - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood2.wav"); - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood3.wav"); -} - -void world::voxel_sounds::shutdown(void) -{ - for(std::size_t i = 0; i < NUM_SURFACES; ++i) { - footsteps_sounds[i].clear(); - } -} - -resource_ptr world::voxel_sounds::get_footsteps(voxel_surface surface) -{ - if(auto effect = get_footsteps_effect(surface)) { - return effect; - } - - if(auto effect = get_footsteps_effect(voxel_surface::DEFAULT)) { - return effect; - } - - return nullptr; -} - -resource_ptr world::voxel_sounds::get_placebreak(voxel_surface surface) -{ - return nullptr; -} +#include "client/pch.hh" + +#include "client/world/voxel_sounds.hh" + +#include "client/resource/sound_effect.hh" + +constexpr static std::size_t NUM_SURFACES = static_cast(world::voxel_surface::COUNT); + +static std::vector> footsteps_sounds[NUM_SURFACES]; +static std::mt19937_64 randomizer; + +static void add_footsteps_effect(world::voxel_surface surface, std::string_view name) +{ + if(auto effect = resource::load(name)) { + auto surface_index = static_cast(surface); + footsteps_sounds[surface_index].push_back(effect); + } +} + +static resource_ptr get_footsteps_effect(world::voxel_surface surface) +{ + auto surface_index = static_cast(surface); + + if(surface_index >= NUM_SURFACES) { + // Surface index out of range + return nullptr; + } + + const auto& sounds = footsteps_sounds[surface_index]; + + if(sounds.empty()) { + // No sounds for this surface + return nullptr; + } + + auto dist = std::uniform_int_distribution(0, sounds.size() - 1); + return sounds.at(dist(randomizer)); +} + +void world::voxel_sounds::init(void) +{ + add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default1.wav"); + add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default2.wav"); + add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default3.wav"); + add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default4.wav"); + + add_footsteps_effect(voxel_surface::DIRT, "sounds/surface/dirt1.wav"); + + add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass1.wav"); + add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass2.wav"); + add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass3.wav"); + + add_footsteps_effect(voxel_surface::GRAVEL, "sounds/surface/gravel1.wav"); + + add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand1.wav"); + add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand2.wav"); + + add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood1.wav"); + add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood2.wav"); + add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood3.wav"); +} + +void world::voxel_sounds::shutdown(void) +{ + for(std::size_t i = 0; i < NUM_SURFACES; ++i) { + footsteps_sounds[i].clear(); + } +} + +resource_ptr world::voxel_sounds::get_footsteps(voxel_surface surface) +{ + if(auto effect = get_footsteps_effect(surface)) { + return effect; + } + + if(auto effect = get_footsteps_effect(voxel_surface::DEFAULT)) { + return effect; + } + + return nullptr; +} + +resource_ptr world::voxel_sounds::get_placebreak(voxel_surface surface) +{ + return nullptr; +} diff --git a/game/client/world/voxel_sounds.hh b/game/client/world/voxel_sounds.hh index b09b475..09f5e2e 100644 --- a/game/client/world/voxel_sounds.hh +++ b/game/client/world/voxel_sounds.hh @@ -1,19 +1,19 @@ -#pragma once - -#include "core/resource/resource.hh" - -#include "shared/world/voxel_registry.hh" - -struct SoundEffect; - -namespace world::voxel_sounds -{ -void init(void); -void shutdown(void); -} // namespace world::voxel_sounds - -namespace world::voxel_sounds -{ -resource_ptr get_footsteps(voxel_surface surface); -resource_ptr get_placebreak(voxel_surface surface); -} // namespace world::voxel_sounds +#pragma once + +#include "core/resource/resource.hh" + +#include "shared/world/voxel_registry.hh" + +struct SoundEffect; + +namespace world::voxel_sounds +{ +void init(void); +void shutdown(void); +} // namespace world::voxel_sounds + +namespace world::voxel_sounds +{ +resource_ptr get_footsteps(voxel_surface surface); +resource_ptr get_placebreak(voxel_surface surface); +} // namespace world::voxel_sounds -- cgit