pathfinding.h 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #pragma once
  2. #include <array>
  3. #include <atomic>
  4. #include <condition_variable>
  5. #include <cstdint>
  6. #include <future>
  7. #include <mutex>
  8. #include <queue>
  9. #include <thread>
  10. #include <vector>
  11. namespace Game::Systems {
  12. class BuildingCollisionRegistry;
  13. struct Point {
  14. int x = 0;
  15. int y = 0;
  16. constexpr Point() = default;
  17. constexpr Point(int x_, int y_) : x(x_), y(y_) {}
  18. constexpr auto operator==(const Point &other) const -> bool {
  19. return x == other.x && y == other.y;
  20. }
  21. };
  22. struct DirtyRegion {
  23. int min_x;
  24. int max_x;
  25. int min_z;
  26. int max_z;
  27. DirtyRegion(int x1, int x2, int z1, int z2)
  28. : min_x(x1), max_x(x2), min_z(z1), max_z(z2) {}
  29. };
  30. class Pathfinding {
  31. public:
  32. Pathfinding(int width, int height);
  33. ~Pathfinding();
  34. void set_grid_offset(float offset_x, float offset_z);
  35. auto get_grid_offset_x() const -> float { return m_grid_offset_x; }
  36. auto get_grid_offset_z() const -> float { return m_grid_offset_z; }
  37. void set_obstacle(int x, int y, bool is_obstacle);
  38. auto is_walkable(int x, int y) const -> bool;
  39. auto is_walkable_with_radius(int x, int y, float unit_radius) const -> bool;
  40. void update_building_obstacles();
  41. void mark_obstacles_dirty();
  42. void mark_region_dirty(int min_x, int max_x, int min_z, int max_z);
  43. void mark_building_region_dirty(float center_x, float center_z, float width,
  44. float depth);
  45. auto find_path(const Point &start, const Point &end) -> std::vector<Point>;
  46. auto find_path(const Point &start, const Point &end,
  47. float unit_radius) -> std::vector<Point>;
  48. auto find_path_async(const Point &start,
  49. const Point &end) -> std::future<std::vector<Point>>;
  50. void submit_path_request(std::uint64_t request_id, const Point &start,
  51. const Point &end);
  52. void submit_path_request(std::uint64_t request_id, const Point &start,
  53. const Point &end, float unit_radius);
  54. struct PathResult {
  55. std::uint64_t request_id;
  56. std::vector<Point> path;
  57. };
  58. auto fetch_completed_paths() -> std::vector<PathResult>;
  59. static auto find_nearest_walkable_point(const Point &point,
  60. int max_search_radius,
  61. const Pathfinding &pathfinder,
  62. float unit_radius = 0.0F) -> Point;
  63. private:
  64. auto find_path_internal(const Point &start,
  65. const Point &end) -> std::vector<Point>;
  66. auto find_path_internal(const Point &start, const Point &end,
  67. float unit_radius) -> std::vector<Point>;
  68. static auto calculate_heuristic(const Point &a, const Point &b) -> int;
  69. void ensure_working_buffers();
  70. auto next_generation() -> std::uint32_t;
  71. void reset_generations();
  72. auto to_index(int x, int y) const -> int { return y * m_width + x; }
  73. auto to_index(const Point &p) const -> int { return to_index(p.x, p.y); }
  74. auto to_point(int index) const -> Point {
  75. return {index % m_width, index / m_width};
  76. }
  77. auto is_closed(int index, std::uint32_t generation) const -> bool;
  78. void set_closed(int index, std::uint32_t generation);
  79. auto get_g_cost(int index, std::uint32_t generation) const -> int;
  80. void set_g_cost(int index, std::uint32_t generation, int cost);
  81. auto has_parent(int index, std::uint32_t generation) const -> bool;
  82. auto get_parent(int index, std::uint32_t generation) const -> int;
  83. void set_parent(int index, std::uint32_t generation, int parent_index);
  84. auto collect_neighbors(const Point &point,
  85. std::array<Point, 8> &buffer) const -> std::size_t;
  86. void build_path(int start_index, int end_index, std::uint32_t generation,
  87. int expected_length, std::vector<Point> &out_path) const;
  88. struct QueueNode {
  89. int index;
  90. int f_cost;
  91. int g_cost;
  92. };
  93. static auto heap_less(const QueueNode &lhs, const QueueNode &rhs) -> bool;
  94. void push_open_node(const QueueNode &node);
  95. auto pop_open_node() -> QueueNode;
  96. void worker_loop();
  97. void process_dirty_regions();
  98. void update_region(int min_x, int max_x, int min_z, int max_z);
  99. int m_width, m_height;
  100. std::vector<std::vector<std::uint8_t>> m_obstacles;
  101. float m_grid_cell_size{1.0F};
  102. float m_grid_offset_x{0.0F}, m_grid_offset_z{0.0F};
  103. std::atomic<bool> m_obstacles_dirty;
  104. mutable std::mutex m_mutex;
  105. std::atomic<bool> m_stop_worker{false};
  106. std::thread m_worker_thread;
  107. std::mutex m_request_mutex;
  108. std::condition_variable m_request_condition;
  109. struct PathRequest {
  110. std::uint64_t request_id{};
  111. Point start;
  112. Point end;
  113. float unit_radius{0.0F};
  114. };
  115. std::queue<PathRequest> m_request_queue;
  116. std::mutex m_result_mutex;
  117. std::queue<PathResult> m_result_queue;
  118. mutable std::vector<std::uint32_t> m_closed_generation;
  119. mutable std::vector<std::uint32_t> m_g_cost_generation;
  120. mutable std::vector<int> m_g_cost_values;
  121. mutable std::vector<std::uint32_t> m_parent_generation;
  122. mutable std::vector<int> m_parent_values;
  123. mutable std::vector<QueueNode> m_open_heap;
  124. mutable std::uint32_t m_generation_counter{0};
  125. std::mutex m_dirty_mutex;
  126. std::vector<DirtyRegion> m_dirty_regions;
  127. bool m_full_update_required{true};
  128. };
  129. } // namespace Game::Systems