diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2024-02-04 11:05:17 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2024-02-04 11:05:17 +0100 |
commit | 7ed869ef78775ef34bb7db7832e53ab24f689688 (patch) | |
tree | c8340212c3c3a07f00e62174d682890752806c19 | |
parent | 9acc880b83724009eb55feaf9d4808d048966046 (diff) |
a
-rw-r--r-- | editor/tests/raycast-test.cpp | 337 | ||||
-rw-r--r-- | src/chunk.hpp | 17 | ||||
-rw-r--r-- | src/collision.hpp | 21 | ||||
-rw-r--r-- | src/raycast-diag.hpp | 26 | ||||
-rw-r--r-- | src/raycast.cpp | 321 | ||||
-rw-r--r-- | src/raycast.hpp | 34 |
6 files changed, 411 insertions, 345 deletions
diff --git a/editor/tests/raycast-test.cpp b/editor/tests/raycast-test.cpp index c814269d..e6799de4 100644 --- a/editor/tests/raycast-test.cpp +++ b/editor/tests/raycast-test.cpp @@ -5,102 +5,18 @@ #include "../imgui-raii.hpp" #include "src/critter.hpp" #include "src/world.hpp" -#include "src/RTree-search.hpp" +#include "src/raycast-diag.hpp" #include <cinttypes> #include <array> #include <vector> -#include <Corrade/Containers/StructuredBindings.h> -#include <Magnum/Math/Functions.h> -#include <Magnum/Math/Vector2.h> #include <Magnum/Math/Color.h> namespace floormat::tests { namespace { -using namespace imgui; - -template<typename T> constexpr inline auto tile_size = Math::Vector2<T>{iTILE_SIZE2}; -template<typename T> constexpr inline auto chunk_size = Math::Vector2<T>{TILE_MAX_DIM} * tile_size<T>; - -using RTree = std::decay_t<decltype(*std::declval<struct chunk>().rtree())>; -using Rect = typename RTree::Rect; - -template<class T> constexpr inline T sign_(auto&& x) { - constexpr auto zero = std::decay_t<decltype(x)>{0}; - return T(x > zero) - T(x < zero); -} - -constexpr Vector2 pt_to_vec(point from, point pt) -{ - auto V = Vector2{}; - V += (Vector2(pt.chunk()) - Vector2(from.chunk())) * chunk_size<float>; - V += (Vector2(pt.local()) - Vector2(from.local())) * tile_size<float>; - V += (Vector2(pt.offset()) - Vector2(from.offset())); - return V; -} - -struct aabb_result -{ - float tmin; - bool result; -}; - -template<typename T> -std::array<uint8_t, 2> ray_aabb_signs(Math::Vector2<T> ray_dir_inv_norm) -{ - bool signs[2]; - for (unsigned d = 0; d < 2; ++d) - signs[d] = std::signbit(ray_dir_inv_norm[d]); - return { signs[0], signs[1] }; -} - -// https://tavianator.com/2022/ray_box_boundary.html -// https://www.researchgate.net/figure/The-slab-method-for-ray-intersection-detection-15_fig3_283515372 -aabb_result ray_aabb_intersection(Vector2 ray_origin, Vector2 ray_dir_inv_norm, - std::array<Vector2, 2> box_minmax, std::array<uint8_t, 2> signs) -{ - using Math::min; - using Math::max; - - float tmin = 0, tmax = 16777216; - - for (unsigned d = 0; d < 2; ++d) - { - auto bmin = box_minmax[signs[d]][d]; - auto bmax = box_minmax[!signs[d]][d]; - float dmin = (bmin - ray_origin[d]) * ray_dir_inv_norm[d]; - float dmax = (bmax - ray_origin[d]) * ray_dir_inv_norm[d]; - - tmin = max(dmin, tmin); - tmax = min(dmax, tmax); - } - - return { tmin, tmin < tmax }; -} - -struct bbox -{ - point center; - Vector2ui size; -}; - -struct diag_s -{ - std::vector<bbox> path{}; - Vector2 V, dir, dir_inv_norm; - Vector2ui size; - //unsigned short_steps, long_steps; - float tmin; -}; - -struct result_s -{ - point from, to, collision; - collision_data collider; - bool has_result : 1 = false, - success : 1 = false; -}; +using namespace floormat::imgui; +using namespace floormat::rc; struct pending_s { @@ -109,52 +25,6 @@ struct pending_s bool exists : 1 = false; }; -struct chunk_neighbors -{ - chunk* array[3][3]; -}; - -auto get_chunk_neighbors(class world& w, chunk_coords_ ch) -{ - chunk_neighbors nbs; - for (int j = 0; j < 3; j++) - for (int i = 0; i < 3; i++) - nbs.array[i][j] = w.at(ch + Vector2i(i - 1, j - 1)); - return nbs; -} - -constexpr Vector2i chunk_offsets[3][3] = { - { - { -chunk_size<int>.x(), -chunk_size<int>.y() }, - { -chunk_size<int>.x(), 0 }, - { -chunk_size<int>.x(), chunk_size<int>.y() }, - }, - { - { 0, -chunk_size<int>.y() }, - { 0, 0 }, - { 0, chunk_size<int>.y() }, - }, - { - { chunk_size<int>.x(), -chunk_size<int>.y() }, - { chunk_size<int>.x(), 0 }, - { chunk_size<int>.x(), chunk_size<int>.y() }, - }, -}; -//static_assert(chunk_offsets[0][0] == Vector2i(-1024, -1024)); -//static_assert(chunk_offsets[2][0] == Vector2i(1024, -1024)); - -template<typename T> -constexpr bool within_chunk_bounds(Math::Vector2<T> p0, Math::Vector2<T> p1) -{ - constexpr auto max_bb_size = Math::Vector2<T>{T{0xff}, T{0xff}}; - constexpr auto half_bb = (max_bb_size + Math::Vector2{T{1}}) / T{2}; - constexpr auto start = -half_bb, end = chunk_size<T> + half_bb; - - return !(start.x() > p1.x() || end.x() < p0.x() || - start.y() > p1.y() || end.y() < p0.y()); -} -template bool within_chunk_bounds<int>(Math::Vector2<int> p0, Math::Vector2<int> p1); - void print_coord(auto&& buf, Vector3i c, Vector2i l, Vector2i p) { std::snprintf(buf, std::size(buf), "(ch %dx%d) <%dx%d> {%dx%d px}", c.x(), c.y(), l.x(), l.y(), p.x(), p.y()); @@ -181,203 +51,11 @@ void do_column(StringView name) ImGui::TableNextColumn(); } -template<bool EnableDiagnostics> -result_s do_raycasting(std::conditional_t<EnableDiagnostics, diag_s*, std::nullptr_t> diag, - app& a, point from, point to, object_id self) -{ - result_s result; - if constexpr(EnableDiagnostics) - fm_assert(diag != nullptr); - fm_assert(from.chunk3().z == to.chunk3().z); - - using Math::max; - using Math::min; - using Math::abs; - using Math::ceil; - - constexpr float eps = 1e-6f; - constexpr float inv_eps = 1/eps; - constexpr int fuzz = 2; - constexpr auto fuzz2 = 0.5f; - - result.has_result = false; - - auto& w = a.main().world(); - auto V = pt_to_vec(from, to); - auto ray_len = V.length(); - auto dir = V.normalized(); - - if (abs(dir.x()) < eps && abs(dir.y()) < eps) [[unlikely]] - dir = {eps, eps}; - - unsigned long_axis, short_axis; - - if (abs(dir.y()) > abs(dir.x())) - { - long_axis = 1; - short_axis = 0; - } - else - { - long_axis = 0; - short_axis = 1; - } - - auto long_len = max(1u, (unsigned)ceil(abs(V[long_axis]))), - short_len = max(1u, (unsigned)ceil(abs(V[short_axis]))); - auto nsteps = 1u; - nsteps = max(nsteps, (short_len+tile_size<unsigned>.x()-1)/tile_size<unsigned>.x()); - nsteps = max(nsteps, (long_len+chunk_size<unsigned>.x()-1)/chunk_size<unsigned>.x()); - auto size_ = Vector2ui{}; - size_[short_axis] = (short_len+nsteps*2-1) / nsteps; - size_[long_axis] = (long_len+nsteps-1) / nsteps; - - auto dir_inv_norm = Vector2( - abs(dir.x()) < eps ? std::copysign(inv_eps, dir.x()) : 1 / dir.x(), - abs(dir.y()) < eps ? std::copysign(inv_eps, dir.y()) : 1 / dir.y() - ); - auto signs = ray_aabb_signs(dir_inv_norm); - - result = { - .from = from, - .to = to, - .collision = {}, - .collider = { - .tag = (uint64_t)collision_type::none, - .pass = (uint64_t)pass_mode::pass, - .data = ((uint64_t)1 << collision_data_BITS)-1, - }, - .has_result = true, - .success = false, - }; - if constexpr(EnableDiagnostics) - { - *diag = { - .V = V, - .dir = dir, - .dir_inv_norm = dir_inv_norm, - .size = size_, - .tmin = 0, - }; - diag->path.clear(); - diag->path.reserve(nsteps+1); - } - - float min_tmin = FLT_MAX; - bool b = true; - - //Debug{} << "------"; - for (unsigned k = 0; b && k <= nsteps; k++) - { - auto pos_ = ceil(abs(V * (float)k/(float)nsteps)); - auto pos = Vector2i{(int)std::copysign(pos_.x(), V.x()), (int)std::copysign(pos_.y(), V.y())}; - auto size = size_; - - if (k == 0) - { - for (auto axis : { long_axis, short_axis }) - { - auto sign = sign_<int>(V[axis]); - pos[axis] += (int)(size[axis]/4) * sign; - size[axis] -= size[axis]/2; - } - } - else if (k == nsteps) - { - constexpr auto add = (tile_size<unsigned>.x()+1)/2, - min_size = tile_size<unsigned>.x() + add; - if (size[long_axis] > min_size) - { - auto sign = sign_<int>(V[long_axis]); - auto off = (int)(size[long_axis]/2) - (int)add; - fm_debug_assert(off >= 0); - pos[long_axis] -= off/2 * sign; - size[long_axis] -= (unsigned)off; - } - } - - pos -= Vector2i(fuzz); - size += Vector2ui(fuzz)*2; - - Vector2 origin; - min_tmin = FLT_MAX; - b = true; - - const auto do_check_collider = [&](uint64_t data, const Rect& r) - { - auto x = std::bit_cast<collision_data>(data); - if (x.data == self || x.pass == (uint64_t)pass_mode::pass) - return; - //Debug{} << "item" << x.data << Vector2(r.m_min[0], r.m_min[1]) << Vector2(r.m_max[0], r.m_max[1]); - auto ret = ray_aabb_intersection(origin, dir_inv_norm, - {{{r.m_min[0]-fuzz2, r.m_min[1]-fuzz2}, - {r.m_max[0]+fuzz2, r.m_max[1]+fuzz2}}}, - signs); - if (!ret.result) - return; - if (ret.tmin > ray_len) [[unlikely]] - return; - if (ret.tmin < min_tmin) [[likely]] - { - min_tmin = ret.tmin; - result.collision = object::normalize_coords(from, Vector2i(dir * min_tmin)); - result.collider = x; - b = false; - } - }; - - auto center = object::normalize_coords(from, pos); - - if constexpr(EnableDiagnostics) - diag->path.push_back(bbox{center, size}); - - auto last_ch = from.chunk3(); - auto nbs = get_chunk_neighbors(w, from.chunk3()); - - if (center.chunk3() != last_ch) [[unlikely]] - { - last_ch = center.chunk3(); - nbs = get_chunk_neighbors(w, center.chunk3()); - } - - auto pt = Vector2i(center.local()) * tile_size<int> + Vector2i(center.offset()); - - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - auto* c = nbs.array[i][j]; - if (!c) - continue; - auto* r = c->rtree(); - auto off = chunk_offsets[i][j]; - auto pt0 = pt - Vector2i(size/2), pt1 = pt0 + Vector2i(size); - auto pt0_ = pt0 - off, pt1_ = pt1 - off; - auto [fmin, fmax] = Math::minmax(Vector2(pt0_)-Vector2(fuzz2), Vector2(pt1_)+Vector2(fuzz2)); - if (!within_chunk_bounds(fmin, fmax)) continue; - auto ch_off = (center.chunk() - from.chunk() + Vector2i(i-1, j-1)) * chunk_size<int>; - origin = Vector2((Vector2i(from.local()) * tile_size<int>) + Vector2i(from.offset()) - ch_off); - //Debug{} << "search" << fmin << fmax << Vector3i(c->coord()); - r->Search(fmin.data(), fmax.data(), [&](uint64_t data, const Rect& r) { - do_check_collider(data, r); - return true; - }); - } - } - } - if constexpr(EnableDiagnostics) - diag->tmin = b ? 0 : min_tmin; - result.success = b; - return result; -} - -} // namespace - struct raycast_test : base_test { - result_s result; + raycast_result_s result; pending_s pending; - diag_s diag; + raycast_diag_s diag; ~raycast_test() noexcept override; @@ -400,7 +78,6 @@ struct raycast_test : base_test return true; } } - return false; } @@ -579,13 +256,15 @@ struct raycast_test : base_test fm_warn("raycast: wrong Z value"); return; } - result = do_raycasting<true>(&diag, a, pending.from, pending.to, pending.self); + result = raycast_with_diag(diag, a.main().world(), pending.from, pending.to, pending.self); } } }; raycast_test::~raycast_test() noexcept = default; +} // namespace + Pointer<base_test> tests_data::make_test_raycast() { return Pointer<raycast_test>{InPlaceInit}; } } // namespace floormat::tests diff --git a/src/chunk.hpp b/src/chunk.hpp index 6833ab03..75fe4bc7 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -5,6 +5,7 @@ #include "src/RTree-fwd.h" #include "global-coords.hpp" #include "wall-defs.hpp" +#include "collision.hpp" #include <type_traits> #include <array> #include <Corrade/Containers/Array.h> @@ -20,22 +21,6 @@ struct object_proto; class tile_iterator; class tile_const_iterator; -enum class collision : unsigned char { - view, shoot, move, -}; - -enum class collision_type : unsigned char { - none, object, scenery, geometry, -}; - -constexpr inline size_t collision_data_BITS = 60; - -struct collision_data final { - uint64_t tag : 2; - uint64_t pass : 2; - uint64_t data : collision_data_BITS; -}; - struct chunk final { friend struct tile_ref; diff --git a/src/collision.hpp b/src/collision.hpp new file mode 100644 index 00000000..e5605f4e --- /dev/null +++ b/src/collision.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace floormat { + +enum class collision : unsigned char { + view, shoot, move, +}; + +enum class collision_type : unsigned char { + none, object, scenery, geometry, +}; + +constexpr inline size_t collision_data_BITS = 60; + +struct collision_data final { + uint64_t tag : 2; + uint64_t pass : 2; + uint64_t data : collision_data_BITS; +}; + +} // namespace floormat diff --git a/src/raycast-diag.hpp b/src/raycast-diag.hpp new file mode 100644 index 00000000..12d027bb --- /dev/null +++ b/src/raycast-diag.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "raycast.hpp" +#include <Corrade/Containers/Array.h> + +namespace floormat::detail_rc { + +struct bbox +{ + point center; + Vector2ui size; +}; + +} // namespace floormat::detail_rc + +namespace floormat::rc { + +struct raycast_diag_s +{ + Array<floormat::detail_rc::bbox> path{}; + Vector2 V, dir, dir_inv_norm; + Vector2ui size; + //unsigned short_steps, long_steps; + float tmin; +}; + +} // namespace floormat::rc diff --git a/src/raycast.cpp b/src/raycast.cpp new file mode 100644 index 00000000..bdd348fb --- /dev/null +++ b/src/raycast.cpp @@ -0,0 +1,321 @@ +#include "raycast-diag.hpp" +#include "pass-mode.hpp" +#include "src/world.hpp" +#include "src/object.hpp" +#include "src/RTree-search.hpp" +#include <float.h> +#include <Corrade/Containers/StructuredBindings.h> +#include <Corrade/Containers/GrowableArray.h> +#include <Magnum/Math/Functions.h> +#include <Magnum/Math/Vector2.h> + +namespace floormat::rc { + +namespace { + +template<typename T> constexpr inline auto tile_size = Math::Vector2<T>{iTILE_SIZE2}; +template<typename T> constexpr inline auto chunk_size = Math::Vector2<T>{TILE_MAX_DIM} * tile_size<T>; + +using floormat::detail_rc::bbox; +using RTree = std::decay_t<decltype(*std::declval<struct chunk>().rtree())>; +using Rect = typename RTree::Rect; + +template<class T> constexpr inline T sign_(auto&& x) { + constexpr auto zero = std::decay_t<decltype(x)>{0}; + return T(x > zero) - T(x < zero); +} + +constexpr Vector2 pt_to_vec(point from, point pt) +{ + auto V = Vector2{}; + V += (Vector2(pt.chunk()) - Vector2(from.chunk())) * chunk_size<float>; + V += (Vector2(pt.local()) - Vector2(from.local())) * tile_size<float>; + V += (Vector2(pt.offset()) - Vector2(from.offset())); + return V; +} + +struct aabb_result +{ + float tmin; + bool result; +}; + +template<typename T> +std::array<uint8_t, 2> ray_aabb_signs(Math::Vector2<T> ray_dir_inv_norm) +{ + bool signs[2]; + for (unsigned d = 0; d < 2; ++d) + signs[d] = std::signbit(ray_dir_inv_norm[d]); + return { signs[0], signs[1] }; +} + +// https://tavianator.com/2022/ray_box_boundary.html +// https://www.researchgate.net/figure/The-slab-method-for-ray-intersection-detection-15_fig3_283515372 +aabb_result ray_aabb_intersection(Vector2 ray_origin, Vector2 ray_dir_inv_norm, + std::array<Vector2, 2> box_minmax, std::array<uint8_t, 2> signs) +{ + using Math::min; + using Math::max; + + float tmin = 0, tmax = 16777216; + + for (unsigned d = 0; d < 2; ++d) + { + auto bmin = box_minmax[signs[d]][d]; + auto bmax = box_minmax[!signs[d]][d]; + float dmin = (bmin - ray_origin[d]) * ray_dir_inv_norm[d]; + float dmax = (bmax - ray_origin[d]) * ray_dir_inv_norm[d]; + + tmin = max(dmin, tmin); + tmax = min(dmax, tmax); + } + + return { tmin, tmin < tmax }; +} + +struct chunk_neighbors +{ + chunk* array[3][3]; +}; + +auto get_chunk_neighbors(class world& w, chunk_coords_ ch) +{ + chunk_neighbors nbs; + for (int j = 0; j < 3; j++) + for (int i = 0; i < 3; i++) + nbs.array[i][j] = w.at(ch + Vector2i(i - 1, j - 1)); + return nbs; +} + +constexpr Vector2i chunk_offsets[3][3] = { + { + { -chunk_size<int>.x(), -chunk_size<int>.y() }, + { -chunk_size<int>.x(), 0 }, + { -chunk_size<int>.x(), chunk_size<int>.y() }, + }, + { + { 0, -chunk_size<int>.y() }, + { 0, 0 }, + { 0, chunk_size<int>.y() }, + }, + { + { chunk_size<int>.x(), -chunk_size<int>.y() }, + { chunk_size<int>.x(), 0 }, + { chunk_size<int>.x(), chunk_size<int>.y() }, + }, +}; +//static_assert(chunk_offsets[0][0] == Vector2i(-1024, -1024)); +//static_assert(chunk_offsets[2][0] == Vector2i(1024, -1024)); + +template<typename T> +constexpr bool within_chunk_bounds(Math::Vector2<T> p0, Math::Vector2<T> p1) +{ + constexpr auto max_bb_size = Math::Vector2<T>{T{0xff}, T{0xff}}; + constexpr auto half_bb = (max_bb_size + Math::Vector2{T{1}}) / T{2}; + constexpr auto start = -half_bb, end = chunk_size<T> + half_bb; + + return !(start.x() > p1.x() || end.x() < p0.x() || + start.y() > p1.y() || end.y() < p0.y()); +} +template bool within_chunk_bounds<int>(Math::Vector2<int> p0, Math::Vector2<int> p1); + +template<bool EnableDiagnostics> +raycast_result_s do_raycasting(std::conditional_t<EnableDiagnostics, raycast_diag_s&, std::nullptr_t> diag, + world& w, point from, point to, object_id self) +{ + raycast_result_s result; + fm_assert(from.chunk3().z == to.chunk3().z); + + using Math::max; + using Math::min; + using Math::abs; + using Math::ceil; + + constexpr float eps = 1e-6f; + constexpr float inv_eps = 1/eps; + constexpr int fuzz = 2; + constexpr auto fuzz2 = 0.5f; + + result.has_result = false; + + auto V = pt_to_vec(from, to); + auto ray_len = V.length(); + auto dir = V.normalized(); + + if (abs(dir.x()) < eps && abs(dir.y()) < eps) [[unlikely]] + dir = {eps, eps}; + + unsigned long_axis, short_axis; + + if (abs(dir.y()) > abs(dir.x())) + { + long_axis = 1; + short_axis = 0; + } + else + { + long_axis = 0; + short_axis = 1; + } + + auto long_len = max(1u, (unsigned)ceil(abs(V[long_axis]))), + short_len = max(1u, (unsigned)ceil(abs(V[short_axis]))); + auto nsteps = 1u; + nsteps = max(nsteps, (short_len+tile_size<unsigned>.x()-1)/tile_size<unsigned>.x()); + nsteps = max(nsteps, (long_len+chunk_size<unsigned>.x()-1)/chunk_size<unsigned>.x()); + auto size_ = Vector2ui{}; + size_[short_axis] = (short_len+nsteps*2-1) / nsteps; + size_[long_axis] = (long_len+nsteps-1) / nsteps; + + auto dir_inv_norm = Vector2( + abs(dir.x()) < eps ? std::copysign(inv_eps, dir.x()) : 1 / dir.x(), + abs(dir.y()) < eps ? std::copysign(inv_eps, dir.y()) : 1 / dir.y() + ); + auto signs = ray_aabb_signs(dir_inv_norm); + + result = { + .from = from, + .to = to, + .collision = {}, + .collider = { + .tag = (uint64_t)collision_type::none, + .pass = (uint64_t)pass_mode::pass, + .data = ((uint64_t)1 << collision_data_BITS)-1, + }, + .has_result = true, + .success = false, + }; + if constexpr(EnableDiagnostics) + { + diag = { + .V = V, + .dir = dir, + .dir_inv_norm = dir_inv_norm, + .size = size_, + .tmin = 0, + }; + arrayResize(diag.path, 0); + arrayReserve(diag.path, nsteps+1); + } + + float min_tmin = FLT_MAX; + bool b = true; + + //Debug{} << "------"; + for (unsigned k = 0; b && k <= nsteps; k++) + { + auto pos_ = ceil(abs(V * (float)k/(float)nsteps)); + auto pos = Vector2i{(int)std::copysign(pos_.x(), V.x()), (int)std::copysign(pos_.y(), V.y())}; + auto size = size_; + + if (k == 0) + { + for (auto axis : { long_axis, short_axis }) + { + auto sign = sign_<int>(V[axis]); + pos[axis] += (int)(size[axis]/4) * sign; + size[axis] -= size[axis]/2; + } + } + else if (k == nsteps) + { + constexpr auto add = (tile_size<unsigned>.x()+1)/2, + min_size = tile_size<unsigned>.x() + add; + if (size[long_axis] > min_size) + { + auto sign = sign_<int>(V[long_axis]); + auto off = (int)(size[long_axis]/2) - (int)add; + fm_debug_assert(off >= 0); + pos[long_axis] -= off/2 * sign; + size[long_axis] -= (unsigned)off; + } + } + + pos -= Vector2i(fuzz); + size += Vector2ui(fuzz)*2; + + Vector2 origin; + min_tmin = FLT_MAX; + b = true; + + const auto do_check_collider = [&](uint64_t data, const Rect& r) + { + auto x = std::bit_cast<collision_data>(data); + if (x.data == self || x.pass == (uint64_t)pass_mode::pass) + return; + //Debug{} << "item" << x.data << Vector2(r.m_min[0], r.m_min[1]) << Vector2(r.m_max[0], r.m_max[1]); + auto ret = ray_aabb_intersection(origin, dir_inv_norm, + {{{r.m_min[0]-fuzz2, r.m_min[1]-fuzz2}, + {r.m_max[0]+fuzz2, r.m_max[1]+fuzz2}}}, + signs); + if (!ret.result) + return; + if (ret.tmin > ray_len) [[unlikely]] + return; + if (ret.tmin < min_tmin) [[likely]] + { + min_tmin = ret.tmin; + result.collision = object::normalize_coords(from, Vector2i(dir * min_tmin)); + result.collider = x; + b = false; + } + }; + + auto center = object::normalize_coords(from, pos); + + if constexpr(EnableDiagnostics) + arrayAppend(diag.path, bbox{center, size}); + + auto last_ch = from.chunk3(); + auto nbs = get_chunk_neighbors(w, from.chunk3()); + + if (center.chunk3() != last_ch) [[unlikely]] + { + last_ch = center.chunk3(); + nbs = get_chunk_neighbors(w, center.chunk3()); + } + + auto pt = Vector2i(center.local()) * tile_size<int> + Vector2i(center.offset()); + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + auto* c = nbs.array[i][j]; + if (!c) + continue; + auto* r = c->rtree(); + auto off = chunk_offsets[i][j]; + auto pt0 = pt - Vector2i(size/2), pt1 = pt0 + Vector2i(size); + auto pt0_ = pt0 - off, pt1_ = pt1 - off; + auto [fmin, fmax] = Math::minmax(Vector2(pt0_)-Vector2(fuzz2), Vector2(pt1_)+Vector2(fuzz2)); + if (!within_chunk_bounds(fmin, fmax)) continue; + auto ch_off = (center.chunk() - from.chunk() + Vector2i(i-1, j-1)) * chunk_size<int>; + origin = Vector2((Vector2i(from.local()) * tile_size<int>) + Vector2i(from.offset()) - ch_off); + //Debug{} << "search" << fmin << fmax << Vector3i(c->coord()); + r->Search(fmin.data(), fmax.data(), [&](uint64_t data, const Rect& r) { + do_check_collider(data, r); + return true; + }); + } + } + } + if constexpr(EnableDiagnostics) + diag.tmin = b ? 0 : min_tmin; + result.success = b; + return result; +} + +} // namespace + +raycast_result_s raycast(world& w, point from, point to, object_id self) +{ + return do_raycasting<false>(nullptr, w, from, to, self); +} + +raycast_result_s raycast_with_diag(raycast_diag_s& diag, world& w, point from, point to, object_id self) +{ + return do_raycasting<true>(diag, w, from, to, self); +} + +} // namespace floormat::rc diff --git a/src/raycast.hpp b/src/raycast.hpp new file mode 100644 index 00000000..acdddd33 --- /dev/null +++ b/src/raycast.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "src/object-id.hpp" +#include "src/collision.hpp" +#include "src/point.hpp" + +namespace floormat { + +class world; + +} // namespace floormat + +namespace floormat::rc { + +struct raycast_diag_s; + +struct raycast_result_s +{ + point from, to, collision; + collision_data collider; + bool has_result : 1 = false, + success : 1 = false; +}; + +[[nodiscard]] raycast_result_s raycast(world& w, point from, point to, object_id self); +[[nodiscard]] raycast_result_s raycast_with_diag(raycast_diag_s& diag, world& w, point from, point to, object_id self); + +} // namespace floormat::rc + +namespace floormat { + +using floormat::rc::raycast; +using floormat::rc::raycast_with_diag; + +} // namespace floormat |