diff options
| -rw-r--r-- | src/core/version.cc | 4 | ||||
| -rw-r--r-- | src/game/client/gui/metrics.cc | 4 | ||||
| -rw-r--r-- | src/game/client/toggles.cc | 2 | ||||
| -rw-r--r-- | src/game/shared/entity/collision.cc | 148 | ||||
| -rw-r--r-- | src/game/shared/world/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/game/shared/world/ray_aabb.cc | 64 | ||||
| -rw-r--r-- | src/game/shared/world/ray_aabb.hh | 30 |
7 files changed, 181 insertions, 73 deletions
diff --git a/src/core/version.cc b/src/core/version.cc index 74c67c4..3683941 100644 --- a/src/core/version.cc +++ b/src/core/version.cc @@ -8,8 +8,8 @@ const unsigned short version::minor = 0; const unsigned short version::patch = 1; // clang-format on -const std::string_view version::commit = "33eb183a"; +const std::string_view version::commit = "7c2df3b5"; const std::string_view version::branch = "devel"; const std::string_view version::triplet = "16.0.1"; -const std::string_view version::full = "16.0.1-33eb183a"; +const std::string_view version::full = "16.0.1-7c2df3b5"; diff --git a/src/game/client/gui/metrics.cc b/src/game/client/gui/metrics.cc index 51b1ffa..f39434a 100644 --- a/src/game/client/gui/metrics.cc +++ b/src/game/client/gui/metrics.cc @@ -48,9 +48,9 @@ void metrics::layout(void) auto text_color = ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); auto shadow_color = ImGui::GetColorU32(ImVec4(0.1f, 0.1f, 0.1f, 1.0f)); - auto font_size = 4.0f; + auto font_size = 8.0f * globals::gui_scale; auto position = ImVec2(8.0f, 8.0f); - auto y_step = 1.5f * globals::gui_scale * font_size; + auto y_step = 1.5f * font_size; // Draw version auto version_line = std::format("Voxelius {}", version::full); diff --git a/src/game/client/toggles.cc b/src/game/client/toggles.cc index 60b2f25..5381993 100644 --- a/src/game/client/toggles.cc +++ b/src/game/client/toggles.cc @@ -101,7 +101,7 @@ void toggles::init(void) toggle_infos[TOGGLE_CHUNK_AABB].description = "chunk Borders"; toggle_infos[TOGGLE_CHUNK_AABB].glfw_keycode = GLFW_KEY_G; - toggle_infos[TOGGLE_CHUNK_AABB].is_enabled = false; + toggle_infos[TOGGLE_CHUNK_AABB].is_enabled = true; toggle_infos[TOGGLE_METRICS_UI].description = std::string_view(); toggle_infos[TOGGLE_METRICS_UI].glfw_keycode = GLFW_KEY_V; diff --git a/src/game/shared/entity/collision.cc b/src/game/shared/entity/collision.cc index af23047..1be1ded 100644 --- a/src/game/shared/entity/collision.cc +++ b/src/game/shared/entity/collision.cc @@ -18,35 +18,41 @@ static int vgrid_collide(const Dimension* dimension, int d, Collision& collision, Transform& transform, Velocity& velocity, VoxelMaterial& touch_surface) { - const auto move = globals::fixed_frametime * velocity.value[d]; - const auto move_sign = math::sign<int>(move); + auto movespeed = globals::fixed_frametime * velocity.value[d]; + auto movesign = math::sign<int>(movespeed); - const auto& ref_aabb = collision.aabb; - const auto current_aabb = ref_aabb.push(transform.local); + auto& ref_aabb = collision.aabb; + auto ref_center = 0.5f * ref_aabb.min + 0.5f * ref_aabb.max; + auto ref_halfsize = 0.5f * ref_aabb.max - 0.5f * ref_aabb.min; - auto next_aabb = math::AABBf(current_aabb); - next_aabb.min[d] += move; - next_aabb.max[d] += move; + auto curr_aabb = ref_aabb.push(transform.local); + + math::AABBf next_aabb(curr_aabb); + next_aabb.min[d] += movespeed; + next_aabb.max[d] += movespeed; + + auto next_center = 0.5f * next_aabb.min + 0.5f * next_aabb.max; + + auto csg_aabb = curr_aabb.combine(next_aabb); local_pos lpos_min; - lpos_min.x = static_cast<local_pos::value_type>(glm::floor(next_aabb.min.x)); - lpos_min.y = static_cast<local_pos::value_type>(glm::floor(next_aabb.min.y)); - lpos_min.z = static_cast<local_pos::value_type>(glm::floor(next_aabb.min.z)); + lpos_min.x = static_cast<local_pos::value_type>(glm::floor(csg_aabb.min.x)); + lpos_min.y = static_cast<local_pos::value_type>(glm::floor(csg_aabb.min.y)); + lpos_min.z = static_cast<local_pos::value_type>(glm::floor(csg_aabb.min.z)); local_pos lpos_max; - lpos_max.x = static_cast<local_pos::value_type>(glm::ceil(next_aabb.max.x)); - lpos_max.y = static_cast<local_pos::value_type>(glm::ceil(next_aabb.max.y)); - lpos_max.z = static_cast<local_pos::value_type>(glm::ceil(next_aabb.max.z)); + lpos_max.x = static_cast<local_pos::value_type>(glm::ceil(csg_aabb.max.x)); + lpos_max.y = static_cast<local_pos::value_type>(glm::ceil(csg_aabb.max.y)); + lpos_max.z = static_cast<local_pos::value_type>(glm::ceil(csg_aabb.max.z)); - // Other axes - const int u = (d + 1) % 3; - const int v = (d + 2) % 3; + auto u = (d + 1) % 3; + auto v = (d + 2) % 3; local_pos::value_type ddir; local_pos::value_type dmin; local_pos::value_type dmax; - if(move < 0.0f) { + if(movespeed < 0.0f) { ddir = local_pos::value_type(+1); dmin = lpos_min[d]; dmax = lpos_max[d]; @@ -57,13 +63,14 @@ static int vgrid_collide(const Dimension* dimension, int d, Collision& collision dmax = lpos_min[d]; } - VoxelTouch latch_touch = VTOUCH_NONE; - glm::fvec3 latch_values = glm::fvec3(0.0f, 0.0f, 0.0f); - VoxelMaterial latch_surface = VMAT_UNKNOWN; - math::AABBf latch_vbox; + auto closest_dist = std::numeric_limits<float>::infinity(); + auto closest_touch = VTOUCH_NONE; + auto closest_surface = VMAT_UNKNOWN; + auto closest_tvalues = ZERO_VEC3<float>; + math::AABBf closest_vbox; for(auto i = dmin; i != dmax; i += ddir) { - for(auto j = lpos_min[u]; j < lpos_max[u]; ++j) + for(auto j = lpos_min[u]; j < lpos_max[u]; ++j) { for(auto k = lpos_min[v]; k < lpos_max[v]; ++k) { local_pos lpos; lpos[d] = i; @@ -73,79 +80,84 @@ static int vgrid_collide(const Dimension* dimension, int d, Collision& collision auto vpos = coord::to_voxel(transform.chunk, lpos); auto voxel = dimension->get_voxel(vpos); - if(voxel == nullptr) { - // Don't collide with something - // that we assume to be nothing - continue; + if(voxel == nullptr || voxel->is_touch_type<VTOUCH_NONE>()) { + continue; // non-collidable } - math::AABBf vbox(voxel->get_collision().push(lpos)); + auto vbox = voxel->get_collision().push(lpos); + auto vbox_center = 0.5f * vbox.min + 0.5f * vbox.max; - if(!next_aabb.intersect(vbox)) { - // No intersection between the voxel - // and the entity's collision hull - continue; + if(!csg_aabb.intersect(vbox)) { + continue; // no intersection } - if(voxel->is_touch_type<VTOUCH_SOLID>()) { - // Solid touch type makes a collision - // response whenever it is encountered - velocity.value[d] = 0.0f; - touch_surface = voxel->get_surface_material(); - return move_sign; - } + auto distance = glm::abs(vbox_center[d] - next_center[d]); - // In case of other touch types, they - // are latched and the last ever touch - // type is then responded to - if(voxel->get_touch_type() != VTOUCH_NONE) { - latch_touch = voxel->get_touch_type(); - latch_values = voxel->get_touch_values(); - latch_surface = voxel->get_surface_material(); - latch_vbox = vbox; - continue; + if(distance < closest_dist) { + closest_dist = distance; + closest_touch = voxel->get_touch_type(); + closest_surface = voxel->get_surface_material(); + closest_tvalues = voxel->get_touch_values(); + closest_vbox = vbox; } } + } } - if(latch_touch != VTOUCH_NONE) { - if(latch_touch == VTOUCH_BOUNCE) { - const auto move_distance = glm::abs(current_aabb.min[d] - next_aabb.min[d]); - const auto threshold = 2.0f * globals::fixed_frametime; + if(std::isfinite(closest_dist)) { + auto snap_to_closest_vbox = false; - if(move_distance > threshold) { - velocity.value[d] *= -latch_values[d]; + if(closest_touch == VTOUCH_BOUNCE) { + auto threshold = 2.0f * static_cast<float>(globals::fixed_frametime); + auto travel_distance = glm::abs(curr_aabb.min[d] - next_aabb.min[d]); + + if(travel_distance > threshold) { + velocity.value[d] = -velocity.value[d] * closest_tvalues[d]; } else { velocity.value[d] = 0.0f; } - touch_surface = latch_surface; + snap_to_closest_vbox = true; + } + else if(closest_touch == VTOUCH_SINK) { + constexpr auto threshold = 0.01f; - return move_sign; + if(velocity.value[d] < threshold) { + velocity.value[d] = 0.0f; + } + else { + velocity.value[d] *= closest_tvalues[d]; + } + } + else { + velocity.value[d] = 0.0f; + snap_to_closest_vbox = true; } - if(latch_touch == VTOUCH_SINK) { - velocity.value[d] *= latch_values[d]; - touch_surface = latch_surface; - return move_sign; + if(snap_to_closest_vbox) { + auto vbox_center = 0.5f * closest_vbox.min[d] + 0.5f * closest_vbox.max[d]; + auto vbox_halfsize = 0.5f * closest_vbox.max[d] - 0.5f * closest_vbox.min[d]; + + if(movesign < 0) { + transform.local[d] = vbox_center + vbox_halfsize + ref_halfsize[d] - ref_center[d] + 0.01f; + } + else { + transform.local[d] = vbox_center - vbox_halfsize - ref_halfsize[d] - ref_center[d] - 0.01f; + } } + + touch_surface = closest_surface; + + return movesign; } + touch_surface = VMAT_UNKNOWN; return 0; } void Collision::fixed_update(Dimension* dimension) { - // FIXME: this isn't particularly accurate considering - // some voxels might be passable and some other voxels - // might apply some slowing factor; what I might do in the - // future is to add a specific value to the voxel registry - // entries that would specify the amount of force we apply - // to prevent player movement inside a specific voxel, plus - // we shouldn't treat all voxels as full cubes if we want - // to support slabs, stairs and non-full liquid voxels in the future - auto group = dimension->entities.group<Collision>(entt::get<Transform, Velocity>); for(auto [entity, collision, transform, velocity] : group.each()) { diff --git a/src/game/shared/world/CMakeLists.txt b/src/game/shared/world/CMakeLists.txt index db3f370..baa9b71 100644 --- a/src/game/shared/world/CMakeLists.txt +++ b/src/game/shared/world/CMakeLists.txt @@ -10,6 +10,8 @@ target_sources(shared PRIVATE "${CMAKE_CURRENT_LIST_DIR}/item_registry.hh" "${CMAKE_CURRENT_LIST_DIR}/item.cc" "${CMAKE_CURRENT_LIST_DIR}/item.hh" + "${CMAKE_CURRENT_LIST_DIR}/ray_aabb.cc" + "${CMAKE_CURRENT_LIST_DIR}/ray_aabb.hh" "${CMAKE_CURRENT_LIST_DIR}/ray_dda.cc" "${CMAKE_CURRENT_LIST_DIR}/ray_dda.hh" "${CMAKE_CURRENT_LIST_DIR}/voxel_registry.cc" diff --git a/src/game/shared/world/ray_aabb.cc b/src/game/shared/world/ray_aabb.cc new file mode 100644 index 0000000..f6f2638 --- /dev/null +++ b/src/game/shared/world/ray_aabb.cc @@ -0,0 +1,64 @@ +#include "shared/pch.hh" + +#include "shared/world/ray_aabb.hh" + +RayAABB::RayAABB(const glm::fvec3& start, const glm::fvec3& dir) noexcept +{ + reset(start, dir); +} + +void RayAABB::reset(const glm::fvec3& start, const glm::fvec3& dir) noexcept +{ + assert(std::isfinite(start.x)); + assert(std::isfinite(start.y)); + assert(std::isfinite(start.z)); + assert(std::isfinite(dir.x)); + assert(std::isfinite(dir.y)); + assert(std::isfinite(dir.z)); + + m_start_pos = start; + m_direction = dir; +} + +bool RayAABB::intersect(const math::AABBf& aabb, float& distance, glm::fvec3& surface) const noexcept +{ + // Adapted from https://github.com/gdbooks/3DCollisions/blob/master/Chapter3/raycast_aabb.md + + constexpr static std::array<glm::fvec3, 6> TNORMALS = { + glm::fvec3(-1.0f, 0.0f, 0.0f), + glm::fvec3(1.0f, 0.0f, 0.0f), + glm::fvec3(0.0f, -1.0f, 0.0f), + glm::fvec3(0.0f, 1.0f, 0.0f), + glm::fvec3(0.0f, 0.0f, -1.0f), + glm::fvec3(0.0f, 0.0f, 1.0f), + }; + + std::array<float, 6> tvalues; + tvalues[0] = (aabb.min.x - m_start_pos.x) / m_direction.x; + tvalues[1] = (aabb.max.x - m_start_pos.x) / m_direction.x; + tvalues[2] = (aabb.min.y - m_start_pos.y) / m_direction.y; + tvalues[3] = (aabb.max.y - m_start_pos.y) / m_direction.y; + tvalues[4] = (aabb.min.z - m_start_pos.z) / m_direction.z; + tvalues[5] = (aabb.max.z - m_start_pos.z) / m_direction.z; + + static_assert(tvalues.size() == TNORMALS.size()); + + auto tmin = std::max(std::max(std::min(tvalues[0], tvalues[1]), std::min(tvalues[2], tvalues[3])), std::min(tvalues[4], tvalues[5])); + auto tmax = std::min(std::min(std::max(tvalues[0], tvalues[1]), std::max(tvalues[2], tvalues[3])), std::max(tvalues[4], tvalues[5])); + + if(tmax < 0.0f || tmin > tmax) { + distance = std::numeric_limits<float>::quiet_NaN(); + return false; // no intersection + } + + for(std::size_t i = 0U; i < tvalues.size(); ++i) { + if(tvalues[i] == tmin) { + surface = TNORMALS[i]; + break; + } + } + + distance = tmin < 0.0f ? tmax : tmin; + + return true; +} diff --git a/src/game/shared/world/ray_aabb.hh b/src/game/shared/world/ray_aabb.hh new file mode 100644 index 0000000..49c0846 --- /dev/null +++ b/src/game/shared/world/ray_aabb.hh @@ -0,0 +1,30 @@ +#pragma once + +#include "core/math/aabb.hh" + +class RayAABB final { +public: + RayAABB(void) = default; + explicit RayAABB(const glm::fvec3& start, const glm::fvec3& dir) noexcept; + + constexpr const glm::fvec3& start_pos(void) const noexcept; + constexpr const glm::fvec3& direction(void) const noexcept; + + void reset(const glm::fvec3& start, const glm::fvec3& dir) noexcept; + + bool intersect(const math::AABBf& aabb, float& distance, glm::fvec3& surface) const noexcept; + +private: + glm::fvec3 m_start_pos; + glm::fvec3 m_direction; +}; + +constexpr const glm::fvec3& RayAABB::start_pos(void) const noexcept +{ + return m_start_pos; +} + +constexpr const glm::fvec3& RayAABB::direction(void) const noexcept +{ + return m_direction; +} |
