From cb5a754a61fbf9be2ca90d0ddd9a81b04cdeb619 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 12 May 2024 18:59:36 +0200 Subject: [PATCH 01/68] gamestate: Map class to store terrain and pathfinder. --- libopenage/gamestate/CMakeLists.txt | 1 + libopenage/gamestate/map.cpp | 35 ++++++++++++++ libopenage/gamestate/map.h | 74 +++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 libopenage/gamestate/map.cpp create mode 100644 libopenage/gamestate/map.h diff --git a/libopenage/gamestate/CMakeLists.txt b/libopenage/gamestate/CMakeLists.txt index a94cd93514..3f0b57bf6b 100644 --- a/libopenage/gamestate/CMakeLists.txt +++ b/libopenage/gamestate/CMakeLists.txt @@ -5,6 +5,7 @@ add_sources(libopenage game_state.cpp game.cpp manager.cpp + map.cpp player.cpp simulation.cpp terrain_chunk.cpp diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp new file mode 100644 index 0000000000..9561e599ac --- /dev/null +++ b/libopenage/gamestate/map.cpp @@ -0,0 +1,35 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#include "map.h" + +#include "gamestate/terrain.h" +#include "pathfinding/pathfinder.h" + + +namespace openage::gamestate { +Map::Map(const std::shared_ptr &terrain) : + terrain{terrain}, + pathfinder{std::make_shared()} { + // TODO: Initialize pathfinder with terrain path costs +} + +Map::Map(const std::shared_ptr &terrain, + const std::shared_ptr &pathfinder) : + terrain{terrain}, + pathfinder{pathfinder} { + // TODO: Initialize pathfinder with terrain path costs +} + +const util::Vector2s &Map::get_size() const { + return this->terrain->get_size(); +} + +const std::shared_ptr &Map::get_terrain() const { + return this->terrain; +} + +const std::shared_ptr &Map::get_pathfinder() const { + return this->pathfinder; +} + +} // namespace openage::gamestate diff --git a/libopenage/gamestate/map.h b/libopenage/gamestate/map.h new file mode 100644 index 0000000000..78a79e9d67 --- /dev/null +++ b/libopenage/gamestate/map.h @@ -0,0 +1,74 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "util/vector.h" + + +namespace openage { +namespace path { +class Pathfinder; +} // namespace path + +namespace gamestate { +class Terrain; + +class Map { +public: + /** + * Create a new map from existing terrain. + * + * Initializes the pathfinder with the terrain path costs. + * + * @param terrain Terrain object. + */ + Map(const std::shared_ptr &terrain); + + /** + * Create a new map from existing terrain and pathfinder. + * + * @param terrain Terrain. + * @param pathfinder Pathfinder. + */ + Map(const std::shared_ptr &terrain, + const std::shared_ptr &pathfinder); + + ~Map() = default; + + /** + * Get the size of the map. + * + * @return Map size (width x height). + */ + const util::Vector2s &get_size() const; + + /** + * Get the terrain of the map. + * + * @return Terrain. + */ + const std::shared_ptr &get_terrain() const; + + /** + * Get the pathfinder for the map. + * + * @return Pathfinder. + */ + const std::shared_ptr &get_pathfinder() const; + +private: + /** + * Terrain. + */ + std::shared_ptr terrain; + + /** + * Pathfinder. + */ + std::shared_ptr pathfinder; +}; + +} // namespace gamestate +} // namespace openage From 7584db49829b4a2f4b8195f676c3b0c88804c8ed Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 12 May 2024 19:00:16 +0200 Subject: [PATCH 02/68] gamestate: Verify terrain chunk size. --- libopenage/gamestate/terrain.cpp | 67 +++++++++++++++++++++++++++++++- libopenage/gamestate/terrain.h | 50 ++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index 9e40230ffe..b29a2fe541 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the openage authors. See copying.md for legal info. +// Copyright 2018-2024 the openage authors. See copying.md for legal info. #include "terrain.h" @@ -6,6 +6,8 @@ #include #include +#include "error/error.h" + #include "gamestate/terrain_chunk.h" #include "renderer/render_factory.h" @@ -18,6 +20,59 @@ Terrain::Terrain() : // TODO: Get actual size of terrain. } +Terrain::Terrain(const util::Vector2s &size, + const std::vector> &&chunks) : + size{size}, + chunks{std::move(chunks)} { + // Check if chunk is correct + coord::tile_delta current_offset{0, 0}; + util::Vector2s all_chunks_size{0, 0}; + if (this->chunks.size() > 0) { + all_chunks_size = this->chunks[0]->get_size(); + } + + for (const auto &chunk : this->chunks) { + auto chunk_size = chunk->get_size(); + current_offset.ne += chunk_size[0]; + + // Wrap around to the next row + if (current_offset.ne == size[0]) { + current_offset.ne = 0; + current_offset.se += chunk_size[1]; + } + + // Check terrain boundaries + if (current_offset.ne > size[0]) { + throw error::Error{ERR << "Width of chunk " << chunk->get_offset() + << " exceeds terrain width: " << chunk_size[0] + << " (max width: " << size[0] << ")"}; + } + else if (current_offset.se > size[1]) { + throw error::Error{ERR << "Height of chunk " << chunk->get_offset() + << " exceeds terrain height: " << chunk_size[1] + << " (max height: " << size[1] << ")"}; + } + // Check if chunk size is correct + else if (chunk_size != chunk_size) [[unlikely]] { + throw error::Error{ERR << "Chunk size of chunk " << chunk->get_offset() + << " is not equal to the first chunk size: " << chunk_size + << " (expected: " << all_chunks_size << ")"}; + } + + // Check chunk delta + auto chunk_offset = chunk->get_offset(); + if (current_offset != chunk_offset) [[unlikely]] { + throw error::Error{ERR << "Chunk offset of chunk " << chunk->get_offset() + << " does not match position in vector: " << chunk_offset + << " (expected: " << current_offset << ")"}; + } + } +} + +const util::Vector2s &Terrain::get_size() const { + return this->size; +} + void Terrain::add_chunk(const std::shared_ptr &chunk) { this->chunks.push_back(chunk); } @@ -26,6 +81,16 @@ const std::vector> &Terrain::get_chunks() const { return this->chunks; } +const std::shared_ptr &Terrain::get_chunk(size_t idx) const { + return this->chunks.at(idx); +} + +const std::shared_ptr &Terrain::get_chunk(const coord::chunk &pos) const { + size_t index = pos.ne + pos.se * this->size[0]; + + return this->get_chunk(index); +} + void Terrain::attach_renderer(const std::shared_ptr &render_factory) { for (auto &chunk : this->get_chunks()) { auto render_entity = render_factory->add_terrain_render_entity(chunk->get_size(), diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h index 97ffcbdffd..0dbf6ba7dd 100644 --- a/libopenage/gamestate/terrain.h +++ b/libopenage/gamestate/terrain.h @@ -6,8 +6,10 @@ #include #include +#include "coord/chunk.h" #include "util/vector.h" + namespace openage { namespace renderer { class RenderFactory; @@ -27,6 +29,13 @@ class Terrain { Terrain(); ~Terrain() = default; + /** + * Get the size of the terrain (in tiles). + * + * @return Terrain tile size (width x height). + */ + const util::Vector2s &get_size() const; + /** * Add a chunk to the terrain. * @@ -41,6 +50,24 @@ class Terrain { */ const std::vector> &get_chunks() const; + /** + * Get a specific chunk of the terrain. + * + * @param idx Index of the chunk. + * + * @return Terrain chunk. + */ + const std::shared_ptr &get_chunk(size_t idx) const; + + /** + * Get a specific chunk of the terrain. + * + * @param pos Position of the chunk in the terrain. + * + * @return Terrain chunk. + */ + const std::shared_ptr &get_chunk(const coord::chunk &pos) const; + /** * Attach a renderer which enables graphical display. * @@ -54,16 +81,33 @@ class Terrain { */ void attach_renderer(const std::shared_ptr &render_factory); +protected: + /** + * Create a new terrain. + * + * Chunks must conform to these constraints: + * - All chunks that are not last in a row OR columns must have the same size (width x height). + * - The last chunk in a row may have a different width. + * - The last chunk in a column may have a different height. + * + * @param size Total size of the terrain in tiles (width x height). + * @param chunks Terrain chunks. + */ + Terrain(const util::Vector2s &size, + const std::vector> &&chunks); + private: /** - * Total size of the map - * origin is the left corner - * x = top left edge; y = top right edge + * Total size of the terrain in tiles (width x height). + * + * Origin is the top left corner (left corner with camera projection). */ util::Vector2s size; /** * Subdivision of the main terrain entity. + * + * Ordered in rows from left to right, top to bottom. */ std::vector> chunks; }; From 1ddc053ff716813ef9e8788f3607efe34b7123c3 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 12 May 2024 19:13:04 +0200 Subject: [PATCH 03/68] gamestate: Get path costs from nyan API. --- libopenage/gamestate/api/terrain.cpp | 16 +++++++++++++++- libopenage/gamestate/api/terrain.h | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libopenage/gamestate/api/terrain.cpp b/libopenage/gamestate/api/terrain.cpp index e69edee5fc..180d82425f 100644 --- a/libopenage/gamestate/api/terrain.cpp +++ b/libopenage/gamestate/api/terrain.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "terrain.h" @@ -21,4 +21,18 @@ const std::string APITerrain::get_terrain_path(const nyan::Object &terrain) { return resolve_file_path(terrain, terrain_path); } +const std::unordered_map APITerrain::get_path_costs(const nyan::Object &terrain) { + std::unordered_map result; + + nyan::dict_t path_costs = terrain.get_dict("Terrain.path_costs"); + for (const auto &pair : path_costs) { + auto key = std::dynamic_pointer_cast(pair.first.get_ptr()); + auto value = std::dynamic_pointer_cast(pair.second.get_ptr()); + + result.emplace(key->get_name(), value->get()); + } + + return result; +} + } // namespace openage::gamestate::api diff --git a/libopenage/gamestate/api/terrain.h b/libopenage/gamestate/api/terrain.h index 0046a47bdc..d049bce279 100644 --- a/libopenage/gamestate/api/terrain.h +++ b/libopenage/gamestate/api/terrain.h @@ -30,6 +30,15 @@ class APITerrain { * @return Relative path to the terrain file. */ static const std::string get_terrain_path(const nyan::Object &terrain); + + /** + * Get the path costs of a terrain. + * + * @param terrain \p Terrain nyan object (type == \p engine.util.terrain.Terrain). + * + * @return Path costs for the cost fields of the pathfinder. + */ + static const std::unordered_map get_path_costs(const nyan::Object &terrain); }; } // namespace openage::gamestate::api From c6e6c8bedf65b3e36847cb6903685d2fd51e5296 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 12 May 2024 23:50:10 +0200 Subject: [PATCH 04/68] gamestate: Generate pathfinding grids from nyan terrain defs. --- libopenage/gamestate/map.cpp | 58 ++++++++++++++++++++++---- libopenage/gamestate/map.h | 9 ---- libopenage/gamestate/terrain.cpp | 26 +++++++++--- libopenage/gamestate/terrain.h | 15 +++++++ libopenage/gamestate/terrain_chunk.cpp | 12 ++++++ libopenage/gamestate/terrain_chunk.h | 25 +++++++++++ 6 files changed, 123 insertions(+), 22 deletions(-) diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index 9561e599ac..b7a9fa6bb5 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -2,22 +2,66 @@ #include "map.h" +#include + +#include "gamestate/api/terrain.h" #include "gamestate/terrain.h" +#include "gamestate/terrain_chunk.h" +#include "pathfinding/cost_field.h" +#include "pathfinding/grid.h" #include "pathfinding/pathfinder.h" +#include "pathfinding/sector.h" namespace openage::gamestate { Map::Map(const std::shared_ptr &terrain) : terrain{terrain}, pathfinder{std::make_shared()} { - // TODO: Initialize pathfinder with terrain path costs -} + // Get grid types + // TODO: This should probably not be dependent on the existing tiles, but + // on all defined nyan PathType objects + std::unordered_map path_types; + size_t grid_idx = 0; + auto chunk_size = this->terrain->get_chunk(0)->get_size(); + auto side_length = std::max(chunk_size[0], chunk_size[0]); + util::Vector2s grid_size{this->terrain->get_row_size(), this->terrain->get_column_size()}; + for (const auto &chunk : this->terrain->get_chunks()) { + for (const auto &tile : chunk->get_tiles()) { + if (tile.terrain != std::nullopt) { + auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); -Map::Map(const std::shared_ptr &terrain, - const std::shared_ptr &pathfinder) : - terrain{terrain}, - pathfinder{pathfinder} { - // TODO: Initialize pathfinder with terrain path costs + for (const auto &path_cost : path_costs) { + if (not path_types.contains(path_cost.first)) { + auto grid = std::make_shared(grid_idx, grid_size, side_length); + this->pathfinder->add_grid(grid); + + path_types.emplace(path_cost.first, grid_idx); + grid_idx += 1; + } + } + } + } + } + + // Set path costs + for (size_t chunk_idx = 0; chunk_idx < this->terrain->get_chunks().size(); ++chunk_idx) { + auto chunk_terrain = this->terrain->get_chunk(chunk_idx); + for (size_t tile_idx = 0; tile_idx < chunk_terrain->get_tiles().size(); ++tile_idx) { + auto tile = chunk_terrain->get_tile(tile_idx); + if (tile.terrain != std::nullopt) { + auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); + + for (const auto &path_cost : path_costs) { + auto grid_id = path_types.at(path_cost.first); + auto grid = this->pathfinder->get_grid(grid_id); + + auto sector = grid->get_sector(chunk_idx); + auto cost_field = sector->get_cost_field(); + cost_field->set_cost(tile_idx, path_cost.second); + } + } + } + } } const util::Vector2s &Map::get_size() const { diff --git a/libopenage/gamestate/map.h b/libopenage/gamestate/map.h index 78a79e9d67..b8037c13d5 100644 --- a/libopenage/gamestate/map.h +++ b/libopenage/gamestate/map.h @@ -26,15 +26,6 @@ class Map { */ Map(const std::shared_ptr &terrain); - /** - * Create a new map from existing terrain and pathfinder. - * - * @param terrain Terrain. - * @param pathfinder Pathfinder. - */ - Map(const std::shared_ptr &terrain, - const std::shared_ptr &pathfinder); - ~Map() = default; /** diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index b29a2fe541..8e52aad2c5 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -40,6 +40,16 @@ Terrain::Terrain(const util::Vector2s &size, current_offset.ne = 0; current_offset.se += chunk_size[1]; } + // Check if chunk size is correct + else if (chunk_size != chunk_size) [[unlikely]] { + throw error::Error{ERR << "Chunk size of chunk " << chunk->get_offset() + << " is not equal to the first chunk size: " << chunk_size + << " (expected: " << all_chunks_size << ")"}; + } + else if (chunk_size[0] != chunk_size[1]) { + throw error::Error{ERR << "Chunk size of chunk " << chunk->get_offset() + << " is not square: " << chunk_size}; + } // Check terrain boundaries if (current_offset.ne > size[0]) { @@ -52,12 +62,6 @@ Terrain::Terrain(const util::Vector2s &size, << " exceeds terrain height: " << chunk_size[1] << " (max height: " << size[1] << ")"}; } - // Check if chunk size is correct - else if (chunk_size != chunk_size) [[unlikely]] { - throw error::Error{ERR << "Chunk size of chunk " << chunk->get_offset() - << " is not equal to the first chunk size: " << chunk_size - << " (expected: " << all_chunks_size << ")"}; - } // Check chunk delta auto chunk_offset = chunk->get_offset(); @@ -73,6 +77,16 @@ const util::Vector2s &Terrain::get_size() const { return this->size; } +size_t Terrain::get_row_size() const { + auto chunk_size = this->chunks[0]->get_size(); + return this->size[0] / chunk_size[0]; +} + +size_t Terrain::get_column_size() const { + auto chunk_size = this->chunks[0]->get_size(); + return this->size[1] / chunk_size[1]; +} + void Terrain::add_chunk(const std::shared_ptr &chunk) { this->chunks.push_back(chunk); } diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h index 0dbf6ba7dd..8e5b03159c 100644 --- a/libopenage/gamestate/terrain.h +++ b/libopenage/gamestate/terrain.h @@ -36,6 +36,20 @@ class Terrain { */ const util::Vector2s &get_size() const; + /** + * Get the size of a row in the terrain. + * + * @return Row size (width). + */ + size_t get_row_size() const; + + /** + * Get the size of a column in the terrain. + * + * @return Column size (height). + */ + size_t get_column_size() const; + /** * Add a chunk to the terrain. * @@ -87,6 +101,7 @@ class Terrain { * * Chunks must conform to these constraints: * - All chunks that are not last in a row OR columns must have the same size (width x height). + * - All chunks that are not last in a row OR columns must be square (width == height). * - The last chunk in a row may have a different width. * - The last chunk in a column may have a different height. * diff --git a/libopenage/gamestate/terrain_chunk.cpp b/libopenage/gamestate/terrain_chunk.cpp index e988260333..d863db9f67 100644 --- a/libopenage/gamestate/terrain_chunk.cpp +++ b/libopenage/gamestate/terrain_chunk.cpp @@ -30,6 +30,18 @@ const coord::tile_delta &TerrainChunk::get_offset() const { return this->offset; } +const std::vector &TerrainChunk::get_tiles() const { + return this->tiles; +} + +const TerrainTile &TerrainChunk::get_tile(size_t idx) const { + return this->tiles.at(idx); +} + +const TerrainTile &TerrainChunk::get_tile(const coord::tile &pos) const { + return this->tiles.at(pos.ne + pos.se * this->size[0]); +} + void TerrainChunk::render_update(const time::time_t &time) { if (this->render_entity != nullptr) { // TODO: Update individual tiles instead of the whole chunk diff --git a/libopenage/gamestate/terrain_chunk.h b/libopenage/gamestate/terrain_chunk.h index 684d3af357..574e63da4d 100644 --- a/libopenage/gamestate/terrain_chunk.h +++ b/libopenage/gamestate/terrain_chunk.h @@ -48,6 +48,31 @@ class TerrainChunk { */ const coord::tile_delta &get_offset() const; + /** + * Get the tiles of this terrain chunk. + * + * @return Terrain tiles. + */ + const std::vector &get_tiles() const; + + /** + * Get the tile at the given index. + * + * @param idx Index of the tile. + * + * @return Terrain tile. + */ + const TerrainTile &get_tile(size_t idx) const; + + /** + * Get the tile at the given position. + * + * @param pos Position of the tile. + * + * @return Terrain tile. + */ + const TerrainTile &get_tile(const coord::tile &pos) const; + /** * Update the render entity. * From 3d24ca4e1ab071c21c93d45562f99564ac487fa9 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 13 May 2024 00:45:27 +0200 Subject: [PATCH 05/68] gamestate: Fix nyan object value pointer cast. --- libopenage/gamestate/api/terrain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenage/gamestate/api/terrain.cpp b/libopenage/gamestate/api/terrain.cpp index 180d82425f..ad5e300b47 100644 --- a/libopenage/gamestate/api/terrain.cpp +++ b/libopenage/gamestate/api/terrain.cpp @@ -26,7 +26,7 @@ const std::unordered_map APITerrain::get_path_costs(const nya nyan::dict_t path_costs = terrain.get_dict("Terrain.path_costs"); for (const auto &pair : path_costs) { - auto key = std::dynamic_pointer_cast(pair.first.get_ptr()); + auto key = std::dynamic_pointer_cast(pair.first.get_ptr()); auto value = std::dynamic_pointer_cast(pair.second.get_ptr()); result.emplace(key->get_name(), value->get()); From cdf83d848dba0006e523be2e5735244124bb4d28 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 13 May 2024 00:46:12 +0200 Subject: [PATCH 06/68] gamestate: Add map to state. --- libopenage/gamestate/game.cpp | 16 +++++++--------- libopenage/gamestate/game_state.cpp | 10 +++++----- libopenage/gamestate/game_state.h | 18 +++++++++--------- libopenage/gamestate/terrain_factory.cpp | 5 +++-- libopenage/gamestate/terrain_factory.h | 3 ++- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp index 8e3497bfbc..db50f8cc72 100644 --- a/libopenage/gamestate/game.cpp +++ b/libopenage/gamestate/game.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the openage authors. See copying.md for legal info. +// Copyright 2018-2024 the openage authors. See copying.md for legal info. #include "game.h" @@ -13,6 +13,7 @@ #include "assets/modpack.h" #include "gamestate/entity_factory.h" #include "gamestate/game_state.h" +#include "gamestate/map.h" #include "gamestate/terrain.h" #include "gamestate/terrain_factory.h" #include "gamestate/universe.h" @@ -53,7 +54,7 @@ const std::shared_ptr &Game::get_state() const { void Game::attach_renderer(const std::shared_ptr &render_factory) { this->universe->attach_renderer(render_factory); - this->state->get_terrain()->attach_renderer(render_factory); + this->state->get_map()->get_terrain()->attach_renderer(render_factory); } void Game::load_data(const std::shared_ptr &mod_manager) { @@ -127,8 +128,6 @@ void Game::load_path(const util::Path &base_dir, } void Game::generate_terrain(const std::shared_ptr &terrain_factory) { - auto terrain = terrain_factory->add_terrain(); - auto chunk0 = terrain_factory->add_chunk(this->state, util::Vector2s{10, 10}, coord::tile_delta{0, 0}); @@ -141,12 +140,11 @@ void Game::generate_terrain(const std::shared_ptr &terrain_facto auto chunk3 = terrain_factory->add_chunk(this->state, util::Vector2s{10, 10}, coord::tile_delta{10, 10}); - terrain->add_chunk(chunk0); - terrain->add_chunk(chunk1); - terrain->add_chunk(chunk2); - terrain->add_chunk(chunk3); - this->state->set_terrain(terrain); + auto terrain = terrain_factory->add_terrain({20, 20}, {chunk0, chunk1, chunk2, chunk3}); + + auto map = std::make_shared(terrain); + this->state->set_map(map); } } // namespace openage::gamestate diff --git a/libopenage/gamestate/game_state.cpp b/libopenage/gamestate/game_state.cpp index 9de7c69bff..4bf0aaa348 100644 --- a/libopenage/gamestate/game_state.cpp +++ b/libopenage/gamestate/game_state.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "game_state.h" @@ -37,8 +37,8 @@ void GameState::add_player(const std::shared_ptr &player) { this->players[player->get_id()] = player; } -void GameState::set_terrain(const std::shared_ptr &terrain) { - this->terrain = terrain; +void GameState::set_map(const std::shared_ptr &map) { + this->map = map; } const std::shared_ptr &GameState::get_game_entity(entity_id_t id) const { @@ -59,8 +59,8 @@ const std::shared_ptr &GameState::get_player(player_id_t id) const { return this->players.at(id); } -const std::shared_ptr &GameState::get_terrain() const { - return this->terrain; +const std::shared_ptr &GameState::get_map() const { + return this->map; } const std::shared_ptr &GameState::get_mod_manager() const { diff --git a/libopenage/gamestate/game_state.h b/libopenage/gamestate/game_state.h index bf1531bd55..98cdbc187a 100644 --- a/libopenage/gamestate/game_state.h +++ b/libopenage/gamestate/game_state.h @@ -26,8 +26,8 @@ class EventLoop; namespace gamestate { class GameEntity; +class Map; class Player; -class Terrain; /** * State of the game. @@ -70,11 +70,11 @@ class GameState : public openage::event::State { void add_player(const std::shared_ptr &player); /** - * Set the terrain of the current game. + * Set the map of the current game. * - * @param terrain Terrain object. + * @param terrain Map object. */ - void set_terrain(const std::shared_ptr &terrain); + void set_map(const std::shared_ptr &map); /** * Get a game entity by its ID. @@ -102,11 +102,11 @@ class GameState : public openage::event::State { const std::shared_ptr &get_player(player_id_t id) const; /** - * Get the terrain of the current game. + * Get the map of the current game. * - * @return Terrain object. + * @return Map object. */ - const std::shared_ptr &get_terrain() const; + const std::shared_ptr &get_map() const; /** * TODO: Only for testing. @@ -131,9 +131,9 @@ class GameState : public openage::event::State { std::unordered_map> players; /** - * Terrain of the current game. + * Map of the current game. */ - std::shared_ptr terrain; + std::shared_ptr map; /** * TODO: Only for testing diff --git a/libopenage/gamestate/terrain_factory.cpp b/libopenage/gamestate/terrain_factory.cpp index 17b5d452cb..55c70b34cf 100644 --- a/libopenage/gamestate/terrain_factory.cpp +++ b/libopenage/gamestate/terrain_factory.cpp @@ -170,9 +170,10 @@ static const std::vector> layout_chunks{ }; -std::shared_ptr TerrainFactory::add_terrain() { +std::shared_ptr TerrainFactory::add_terrain(const util::Vector2s &size, + std::vector> &&chunks) { // TODO: Replace this with a proper terrain generator. - auto terrain = std::make_shared(); + auto terrain = std::make_shared(size, std::move(chunks)); return terrain; } diff --git a/libopenage/gamestate/terrain_factory.h b/libopenage/gamestate/terrain_factory.h index ab419acc30..f5ce40b283 100644 --- a/libopenage/gamestate/terrain_factory.h +++ b/libopenage/gamestate/terrain_factory.h @@ -36,7 +36,8 @@ class TerrainFactory { * * @return New terrain object. */ - std::shared_ptr add_terrain(); + std::shared_ptr add_terrain(const util::Vector2s &size, + std::vector> &&chunks); /** * Create a new empty terrain chunk. From 94e1afb2c799873b9827f21bbc3db99f351ad27d Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 13 May 2024 00:46:33 +0200 Subject: [PATCH 07/68] gamestate: Fix access of Terrain constructor. --- libopenage/gamestate/terrain.cpp | 19 ++++++++++--------- libopenage/gamestate/terrain.h | 31 +++++++++++++++---------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index 8e52aad2c5..8625c0b727 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -21,7 +21,7 @@ Terrain::Terrain() : } Terrain::Terrain(const util::Vector2s &size, - const std::vector> &&chunks) : + std::vector> &&chunks) : size{size}, chunks{std::move(chunks)} { // Check if chunk is correct @@ -33,6 +33,15 @@ Terrain::Terrain(const util::Vector2s &size, for (const auto &chunk : this->chunks) { auto chunk_size = chunk->get_size(); + + // Check chunk delta + auto chunk_offset = chunk->get_offset(); + if (current_offset != chunk_offset) [[unlikely]] { + throw error::Error{ERR << "Chunk offset of chunk " << chunk->get_offset() + << " does not match position in vector: " << chunk_offset + << " (expected: " << current_offset << ")"}; + } + current_offset.ne += chunk_size[0]; // Wrap around to the next row @@ -62,14 +71,6 @@ Terrain::Terrain(const util::Vector2s &size, << " exceeds terrain height: " << chunk_size[1] << " (max height: " << size[1] << ")"}; } - - // Check chunk delta - auto chunk_offset = chunk->get_offset(); - if (current_offset != chunk_offset) [[unlikely]] { - throw error::Error{ERR << "Chunk offset of chunk " << chunk->get_offset() - << " does not match position in vector: " << chunk_offset - << " (expected: " << current_offset << ")"}; - } } } diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h index 8e5b03159c..1a5db1bf95 100644 --- a/libopenage/gamestate/terrain.h +++ b/libopenage/gamestate/terrain.h @@ -29,6 +29,21 @@ class Terrain { Terrain(); ~Terrain() = default; + /** + * Create a new terrain. + * + * Chunks must conform to these constraints: + * - All chunks that are not last in a row OR columns must have the same size (width x height). + * - All chunks that are not last in a row OR columns must be square (width == height). + * - The last chunk in a row may have a different width. + * - The last chunk in a column may have a different height. + * + * @param size Total size of the terrain in tiles (width x height). + * @param chunks Terrain chunks. + */ + Terrain(const util::Vector2s &size, + std::vector> &&chunks); + /** * Get the size of the terrain (in tiles). * @@ -95,22 +110,6 @@ class Terrain { */ void attach_renderer(const std::shared_ptr &render_factory); -protected: - /** - * Create a new terrain. - * - * Chunks must conform to these constraints: - * - All chunks that are not last in a row OR columns must have the same size (width x height). - * - All chunks that are not last in a row OR columns must be square (width == height). - * - The last chunk in a row may have a different width. - * - The last chunk in a column may have a different height. - * - * @param size Total size of the terrain in tiles (width x height). - * @param chunks Terrain chunks. - */ - Terrain(const util::Vector2s &size, - const std::vector> &&chunks); - private: /** * Total size of the terrain in tiles (width x height). From 6d2a2006e7d97279eb28a12f9f5024a8d1f42e9b Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 20 May 2024 03:44:59 +0200 Subject: [PATCH 08/68] gamestate: Connect portals in pathfinder grid. --- libopenage/gamestate/map.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index b7a9fa6bb5..a21a96a01f 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -62,6 +62,12 @@ Map::Map(const std::shared_ptr &terrain) : } } } + + // Connect sectors with portals + for (const auto &path_type : path_types) { + auto grid = this->pathfinder->get_grid(path_type.second); + grid->init_portals(); + } } const util::Vector2s &Map::get_size() const { From ae363878fbf669f9b98c6be423865e2d62de190f Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 20 May 2024 04:38:24 +0200 Subject: [PATCH 09/68] gamestate: Record mapping of nyan path grid -> grid ID. --- libopenage/gamestate/map.cpp | 18 +++++++++++------- libopenage/gamestate/map.h | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index a21a96a01f..dbe460ca36 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -2,7 +2,7 @@ #include "map.h" -#include +#include #include "gamestate/api/terrain.h" #include "gamestate/terrain.h" @@ -16,11 +16,11 @@ namespace openage::gamestate { Map::Map(const std::shared_ptr &terrain) : terrain{terrain}, - pathfinder{std::make_shared()} { + pathfinder{std::make_shared()}, + grid_lookup{} { // Get grid types // TODO: This should probably not be dependent on the existing tiles, but // on all defined nyan PathType objects - std::unordered_map path_types; size_t grid_idx = 0; auto chunk_size = this->terrain->get_chunk(0)->get_size(); auto side_length = std::max(chunk_size[0], chunk_size[0]); @@ -31,11 +31,11 @@ Map::Map(const std::shared_ptr &terrain) : auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); for (const auto &path_cost : path_costs) { - if (not path_types.contains(path_cost.first)) { + if (not this->grid_lookup.contains(path_cost.first)) { auto grid = std::make_shared(grid_idx, grid_size, side_length); this->pathfinder->add_grid(grid); - path_types.emplace(path_cost.first, grid_idx); + this->grid_lookup.emplace(path_cost.first, grid_idx); grid_idx += 1; } } @@ -52,7 +52,7 @@ Map::Map(const std::shared_ptr &terrain) : auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); for (const auto &path_cost : path_costs) { - auto grid_id = path_types.at(path_cost.first); + auto grid_id = this->grid_lookup.at(path_cost.first); auto grid = this->pathfinder->get_grid(grid_id); auto sector = grid->get_sector(chunk_idx); @@ -64,7 +64,7 @@ Map::Map(const std::shared_ptr &terrain) : } // Connect sectors with portals - for (const auto &path_type : path_types) { + for (const auto &path_type : this->grid_lookup) { auto grid = this->pathfinder->get_grid(path_type.second); grid->init_portals(); } @@ -82,4 +82,8 @@ const std::shared_ptr &Map::get_pathfinder() const { return this->pathfinder; } +path::grid_id_t Map::get_grid_id(const nyan::Object &path_grid) const { + return this->grid_lookup.at(path_grid.get_name()); +} + } // namespace openage::gamestate diff --git a/libopenage/gamestate/map.h b/libopenage/gamestate/map.h index b8037c13d5..d6ac1188c1 100644 --- a/libopenage/gamestate/map.h +++ b/libopenage/gamestate/map.h @@ -3,7 +3,11 @@ #pragma once #include +#include +#include + +#include "pathfinding/types.h" #include "util/vector.h" @@ -49,6 +53,15 @@ class Map { */ const std::shared_ptr &get_pathfinder() const; + /** + * Get the grid ID associated with a nyan path grid object. + * + * @param path_grid Path grid object. + * + * @return Grid ID. + */ + path::grid_id_t get_grid_id(const nyan::Object &path_grid) const; + private: /** * Terrain. @@ -59,6 +72,11 @@ class Map { * Pathfinder. */ std::shared_ptr pathfinder; + + /** + * Lookup table for mapping path grid objects in nyan to grid indices. + */ + std::unordered_map grid_lookup; }; } // namespace gamestate From 672efb1a4a4ea1b4370cf9dd1ad08f147891f1d4 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 20 May 2024 17:51:33 +0200 Subject: [PATCH 10/68] gamestate: Use pathfinder in Move system. --- libopenage/gamestate/map.cpp | 4 +- libopenage/gamestate/map.h | 4 +- libopenage/gamestate/system/activity.cpp | 14 +-- libopenage/gamestate/system/activity.h | 8 +- libopenage/gamestate/system/move.cpp | 121 +++++++++++++++++------ libopenage/gamestate/system/move.h | 5 + 6 files changed, 113 insertions(+), 43 deletions(-) diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index dbe460ca36..beb02b5e8a 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -82,8 +82,8 @@ const std::shared_ptr &Map::get_pathfinder() const { return this->pathfinder; } -path::grid_id_t Map::get_grid_id(const nyan::Object &path_grid) const { - return this->grid_lookup.at(path_grid.get_name()); +path::grid_id_t Map::get_grid_id(const nyan::fqon_t &path_grid) const { + return this->grid_lookup.at(path_grid); } } // namespace openage::gamestate diff --git a/libopenage/gamestate/map.h b/libopenage/gamestate/map.h index d6ac1188c1..4033373023 100644 --- a/libopenage/gamestate/map.h +++ b/libopenage/gamestate/map.h @@ -56,11 +56,11 @@ class Map { /** * Get the grid ID associated with a nyan path grid object. * - * @param path_grid Path grid object. + * @param path_grid Path grid object fqon. * * @return Grid ID. */ - path::grid_id_t get_grid_id(const nyan::Object &path_grid) const; + path::grid_id_t get_grid_id(const nyan::fqon_t &path_grid) const; private: /** diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index 49371fba30..7ef1d574b4 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "activity.h" @@ -79,7 +79,7 @@ void Activity::advance(const time::time_t &start_time, case activity::node_t::TASK_SYSTEM: { auto node = std::static_pointer_cast(current_node); auto task = node->get_system_id(); - event_wait_time = Activity::handle_subsystem(entity, start_time, task); + event_wait_time = Activity::handle_subsystem(start_time, entity, state, task); auto next_id = node->get_next(); current_node = node->next(next_id); } break; @@ -120,18 +120,20 @@ void Activity::advance(const time::time_t &start_time, activity_component->set_node(start_time, current_node); } -const time::time_t Activity::handle_subsystem(const std::shared_ptr &entity, - const time::time_t &start_time, +const time::time_t Activity::handle_subsystem(const time::time_t &start_time, + const std::shared_ptr &entity, + const std::shared_ptr &state, system_id_t system_id) { switch (system_id) { case system_id_t::IDLE: return Idle::idle(entity, start_time); break; case system_id_t::MOVE_COMMAND: - return Move::move_command(entity, start_time); + return Move::move_command(entity, state, start_time); break; case system_id_t::MOVE_DEFAULT: - return Move::move_default(entity, {1, 1, 1}, start_time); + // TODO: replace destination value with a parameter + return Move::move_default(entity, state, {1, 1, 1}, start_time); break; default: throw Error{ERR << "Unhandled subsystem " << static_cast(system_id)}; diff --git a/libopenage/gamestate/system/activity.h b/libopenage/gamestate/system/activity.h index a5c38e6f49..11dcce68ca 100644 --- a/libopenage/gamestate/system/activity.h +++ b/libopenage/gamestate/system/activity.h @@ -40,14 +40,16 @@ class Activity { /** * Run a built-in engine subsystem. * - * @param entity Game entity. * @param start_time Start time of change. + * @param entity Game entity. + * @param state Game state. * @param system_id ID of the subsystem to run. * * @return Runtime of the change in simulation time. */ - static const time::time_t handle_subsystem(const std::shared_ptr &entity, - const time::time_t &start_time, + static const time::time_t handle_subsystem(const time::time_t &start_time, + const std::shared_ptr &entity, + const std::shared_ptr &state, system_id_t system_id); }; diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index 3ebd02085b..9607658ca2 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "move.h" @@ -24,11 +24,52 @@ #include "gamestate/component/internal/position.h" #include "gamestate/component/types.h" #include "gamestate/game_entity.h" +#include "gamestate/game_state.h" +#include "gamestate/map.h" +#include "pathfinding/path.h" +#include "pathfinding/pathfinder.h" #include "util/fixed_point.h" namespace openage::gamestate::system { + + +std::vector find_path(const std::shared_ptr &pathfinder, + path::grid_id_t grid_id, + const coord::phys3 &start, + const coord::phys3 &end) { + auto start_tile = start.to_tile(); + auto end_tile = end.to_tile(); + + // Search for a path between the start and end tiles + path::PathRequest request{ + grid_id, + start_tile, + end_tile, + }; + auto tile_path = pathfinder->get_path(request); + + // Get the waypoints of the path + std::vector path; + path.reserve(tile_path.waypoints.size()); + + // Start poition is first waypoint + path.push_back(start); + + // Pathfinder waypoints contain start and end tile; we can ignore them + for (size_t i = 1; i < tile_path.waypoints.size() - 1; ++i) { + auto tile = tile_path.waypoints.at(i); + path.push_back(tile.to_phys3()); + } + + // End position is last waypoint + path.push_back(end); + + return path; +} + const time::time_t Move::move_command(const std::shared_ptr &entity, + const std::shared_ptr &state, const time::time_t &start_time) { auto command_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); @@ -40,11 +81,12 @@ const time::time_t Move::move_command(const std::shared_ptrget_target(), start_time); + return Move::move_default(entity, state, command->get_target(), start_time); } const time::time_t Move::move_default(const std::shared_ptr &entity, + const std::shared_ptr &state, const coord::phys3 &destination, const time::time_t &start_time) { if (not entity->has_component(component::component_t::MOVE)) [[unlikely]] { @@ -61,6 +103,7 @@ const time::time_t Move::move_default(const std::shared_ptrget_component(component::component_t::MOVE)); auto move_ability = move_component->get_ability(); auto move_speed = move_ability.get("Move.speed"); + auto move_path_grid = move_ability.get("Move.path_type"); auto pos_component = std::dynamic_pointer_cast( entity->get_component(component::component_t::POSITION)); @@ -70,38 +113,56 @@ const time::time_t Move::move_default(const std::shared_ptris_infinite_positive()) { - auto angle_diff = new_angle - current_angle; - if (angle_diff < 0) { - // get the positive difference - angle_diff = angle_diff * -1; - } - if (angle_diff > 180) { - // always use the smaller angle - angle_diff = angle_diff - 360; - angle_diff = angle_diff * -1; + // Find path + auto map = state->get_map(); + auto pathfinder = map->get_pathfinder(); + auto grid_id = map->get_grid_id(move_path_grid->get_name()); + auto waypoints = find_path(pathfinder, grid_id, current_pos, destination); + + // use waypoints for movement + double total_time = 0; + pos_component->set_position(start_time, current_pos); + auto prev_angle = current_angle; + for (size_t i = 1; i < waypoints.size(); ++i) { + auto prev_waypoint = waypoints[i - 1]; + auto cur_waypoint = waypoints[i]; + + auto path_vector = cur_waypoint - prev_waypoint; + auto path_angle = path_vector.to_angle(); + + // rotation + if (not turn_speed->is_infinite_positive()) { + auto angle_diff = path_angle - current_angle; + if (angle_diff < 0) { + // get the positive difference + angle_diff = angle_diff * -1; + } + if (angle_diff > 180) { + // always use the smaller angle + angle_diff = angle_diff - 360; + angle_diff = angle_diff * -1; + } + + // Set an intermediate position keyframe to halt the game entity + // until the rotation is done + double turn_time = angle_diff.to_double() / turn_speed->get(); + total_time += turn_time; + pos_component->set_position(start_time + total_time, prev_waypoint); } + pos_component->set_angle(start_time + total_time, path_angle); - turn_time = angle_diff.to_double() / turn_speed->get(); - } - pos_component->set_angle(start_time + turn_time, new_angle); + // movement + double move_time = 0; + if (not move_speed->is_infinite_positive()) { + auto distance = path_vector.length(); + move_time = distance / move_speed->get(); + } + total_time += move_time; - // movement - double move_time = 0; - if (not move_speed->is_infinite_positive()) { - auto distance = path.length(); - move_time = distance / move_speed->get(); + pos_component->set_position(start_time + total_time, cur_waypoint); } - pos_component->set_position(start_time, current_pos); - pos_component->set_position(start_time + turn_time + move_time, destination); - + // properties auto ability = move_component->get_ability(); if (api::APIAbility::check_property(ability, api::ability_property_t::ANIMATED)) { auto property = api::APIAbility::get_property(ability, api::ability_property_t::ANIMATED); @@ -113,7 +174,7 @@ const time::time_t Move::move_default(const std::shared_ptr &entity, + const std::shared_ptr &state, const time::time_t &start_time); /** * Move a game entity to a destination. * * @param entity Game entity. + * @param state Game state. * @param destination Destination coordinates. * @param start_time Start time of change. * * @return Runtime of the change in simulation time. */ static const time::time_t move_default(const std::shared_ptr &entity, + const std::shared_ptr &state, const coord::phys3 &destination, const time::time_t &start_time); }; From a2ed206989f14e88d9c204a2350daf70dfc7a26d Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 20 May 2024 22:59:56 +0200 Subject: [PATCH 11/68] path: Block diagonal flow direction if adjactent horizontal/vertical cells are blocked. --- libopenage/pathfinding/flow_field.cpp | 70 +++++++++++++++++---------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 0aa8daf84b..d8674a87f4 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -2,6 +2,8 @@ #include "flow_field.h" +#include + #include "error/error.h" #include "log/log.h" @@ -76,61 +78,77 @@ void FlowField::build(const std::shared_ptr &integration_field this->cells[idx] = this->cells[idx] | FLOW_WAVEFRONT_BLOCKED_MASK; } + // Store which of the non-diagonal directions are unreachable. + std::bitset<4> directions_unreachable; + // Find the neighbor with the smallest cost. flow_dir_t direction = static_cast(this->cells[idx] & FLOW_DIR_MASK); auto smallest_cost = INTEGRATED_COST_UNREACHABLE; if (y > 0) { auto cost = integrate_cells[idx - this->size].cost; - if (cost < smallest_cost) { - smallest_cost = cost; - direction = flow_dir_t::NORTH; + if (cost == INTEGRATED_COST_UNREACHABLE) { + directions_unreachable[0] = true; } - } - if (x < this->size - 1 && y > 0) { - auto cost = integrate_cells[idx - this->size + 1].cost; - if (cost < smallest_cost) { + else if (cost < smallest_cost) { smallest_cost = cost; - direction = flow_dir_t::NORTH_EAST; + direction = flow_dir_t::NORTH; } } if (x < this->size - 1) { auto cost = integrate_cells[idx + 1].cost; - if (cost < smallest_cost) { - smallest_cost = cost; - direction = flow_dir_t::EAST; + if (cost == INTEGRATED_COST_UNREACHABLE) { + directions_unreachable[1] = true; } - } - if (x < this->size - 1 && y < this->size - 1) { - auto cost = integrate_cells[idx + this->size + 1].cost; - if (cost < smallest_cost) { + else if (cost < smallest_cost) { smallest_cost = cost; - direction = flow_dir_t::SOUTH_EAST; + direction = flow_dir_t::EAST; } } if (y < this->size - 1) { auto cost = integrate_cells[idx + this->size].cost; - if (cost < smallest_cost) { - smallest_cost = cost; - direction = flow_dir_t::SOUTH; + if (cost == INTEGRATED_COST_UNREACHABLE) { + directions_unreachable[2] = true; } - } - if (x > 0 && y < this->size - 1) { - auto cost = integrate_cells[idx + this->size - 1].cost; - if (cost < smallest_cost) { + else if (cost < smallest_cost) { smallest_cost = cost; - direction = flow_dir_t::SOUTH_WEST; + direction = flow_dir_t::SOUTH; } } if (x > 0) { auto cost = integrate_cells[idx - 1].cost; - if (cost < smallest_cost) { + if (cost == INTEGRATED_COST_UNREACHABLE) { + directions_unreachable[3] = true; + } + else if (cost < smallest_cost) { smallest_cost = cost; direction = flow_dir_t::WEST; } } + + if (x < this->size - 1 && y > 0) { + auto cost = integrate_cells[idx - this->size + 1].cost; + if (cost < smallest_cost and not(directions_unreachable[0] and directions_unreachable[1])) { + smallest_cost = cost; + direction = flow_dir_t::NORTH_EAST; + } + } + if (x < this->size - 1 && y < this->size - 1) { + auto cost = integrate_cells[idx + this->size + 1].cost; + if (cost < smallest_cost and not(directions_unreachable[1] and directions_unreachable[2])) { + smallest_cost = cost; + direction = flow_dir_t::SOUTH_EAST; + } + } + if (x > 0 && y < this->size - 1) { + auto cost = integrate_cells[idx + this->size - 1].cost; + if (cost < smallest_cost and not(directions_unreachable[2] and directions_unreachable[3])) { + smallest_cost = cost; + direction = flow_dir_t::SOUTH_WEST; + } + } if (x > 0 && y > 0) { auto cost = integrate_cells[idx - this->size - 1].cost; - if (cost < smallest_cost) { + if (cost < smallest_cost and not(directions_unreachable[3] and directions_unreachable[0])) { smallest_cost = cost; direction = flow_dir_t::NORTH_WEST; } From 68443c66ac52b6ad6b5be800b7e3c15b92948c37 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 00:46:12 +0200 Subject: [PATCH 12/68] path: Only use reachable start/target portal nodes for high-level path. --- libopenage/pathfinding/flow_field.cpp | 20 +++--- libopenage/pathfinding/pathfinder.cpp | 98 +++++++++++++++++++++------ libopenage/pathfinding/pathfinder.h | 7 +- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index d8674a87f4..7198ae5fbe 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -125,30 +125,34 @@ void FlowField::build(const std::shared_ptr &integration_field } } - if (x < this->size - 1 && y > 0) { + if (x < this->size - 1 and y > 0 + and not(directions_unreachable[0] and directions_unreachable[1])) { auto cost = integrate_cells[idx - this->size + 1].cost; - if (cost < smallest_cost and not(directions_unreachable[0] and directions_unreachable[1])) { + if (cost < smallest_cost) { smallest_cost = cost; direction = flow_dir_t::NORTH_EAST; } } - if (x < this->size - 1 && y < this->size - 1) { + if (x < this->size - 1 and y < this->size - 1 + and not(directions_unreachable[1] and directions_unreachable[2])) { auto cost = integrate_cells[idx + this->size + 1].cost; - if (cost < smallest_cost and not(directions_unreachable[1] and directions_unreachable[2])) { + if (cost < smallest_cost) { smallest_cost = cost; direction = flow_dir_t::SOUTH_EAST; } } - if (x > 0 && y < this->size - 1) { + if (x > 0 and y < this->size - 1 + and not(directions_unreachable[2] and directions_unreachable[3])) { auto cost = integrate_cells[idx + this->size - 1].cost; - if (cost < smallest_cost and not(directions_unreachable[2] and directions_unreachable[3])) { + if (cost < smallest_cost) { smallest_cost = cost; direction = flow_dir_t::SOUTH_WEST; } } - if (x > 0 && y > 0) { + if (x > 0 and y > 0 + and not(directions_unreachable[3] and directions_unreachable[0])) { auto cost = integrate_cells[idx - this->size - 1].cost; - if (cost < smallest_cost and not(directions_unreachable[3] and directions_unreachable[0])) { + if (cost < smallest_cost) { smallest_cost = cost; direction = flow_dir_t::NORTH_WEST; } diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 7e09278f7c..ca830ca075 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -5,6 +5,7 @@ #include "coord/phys.h" #include "pathfinding/flow_field.h" #include "pathfinding/grid.h" +#include "pathfinding/integration_field.h" #include "pathfinding/integrator.h" #include "pathfinding/portal.h" #include "pathfinding/sector.h" @@ -18,31 +19,84 @@ Pathfinder::Pathfinder() : } const Path Pathfinder::get_path(const PathRequest &request) { - // High-level pathfinding - // Find the portals to use to get from the start to the target - auto portal_path = this->portal_a_star(request); - - // Low-level pathfinding - // Find the path within the sectors auto grid = this->grids.at(request.grid_id); auto sector_size = grid->get_sector_size(); + auto start_sector_x = request.start.ne / sector_size; + auto start_sector_y = request.start.se / sector_size; + auto start_sector = grid->get_sector(start_sector_x, start_sector_y); + auto target_sector_x = request.target.ne / sector_size; auto target_sector_y = request.target.se / sector_size; auto target_sector = grid->get_sector(target_sector_x, target_sector_y); + // Integrate the target field coord::tile_t target_x = request.target.ne % sector_size; coord::tile_t target_y = request.target.se % sector_size; auto target = coord::tile{target_x, target_y}; + auto target_integration_field = this->integrator->integrate(target_sector->get_cost_field(), target); + + if (target_sector == start_sector) { + auto start_x = request.start.ne % sector_size; + auto start_y = request.start.se % sector_size; + + if (target_integration_field->get_cell(coord::tile{start_x, start_y}).cost != INTEGRATED_COST_UNREACHABLE) { + // Exit early if the start and target are in the same sector + // and are reachable from within the same sector + auto flow_field = this->integrator->build(target_integration_field); + auto flow_field_waypoints = this->get_waypoints({std::make_pair(target_sector->get_id(), flow_field)}, request); + + std::vector waypoints{request.start}; + waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); + return Path{request.grid_id, waypoints}; + } + } + + // Check which portals are reachable from the target field + std::unordered_set target_portal_ids; + for (auto &portal : target_sector->get_portals()) { + auto center_cell = portal->get_entry_center(target_sector->get_id()); + + if (target_integration_field->get_cell(center_cell).cost != INTEGRATED_COST_UNREACHABLE) { + target_portal_ids.insert(portal->get_id()); + } + } + + // Check which portals are reachable from the start field + coord::tile_t start_x = request.start.ne % sector_size; + coord::tile_t start_y = request.start.se % sector_size; + auto start = coord::tile{start_x, start_y}; + auto start_integration_field = this->integrator->integrate(start_sector->get_cost_field(), start); + + std::unordered_set start_portal_ids; + for (auto &portal : start_sector->get_portals()) { + auto center_cell = portal->get_entry_center(start_sector->get_id()); + + if (start_integration_field->get_cell(center_cell).cost != INTEGRATED_COST_UNREACHABLE) { + start_portal_ids.insert(portal->get_id()); + } + } + + // ASDF + + // High-level pathfinding + // Find the portals to use to get from the start to the target + auto portal_path = this->portal_a_star(request, target_portal_ids, start_portal_ids); + + // Low-level pathfinding + // Find the path within the sectors - auto sector_fields = this->integrator->build(target_sector->get_cost_field(), target); - auto prev_integration_field = sector_fields.first; + // Build flow field for the target sector + auto prev_integration_field = target_integration_field; + auto prev_flow_field = this->integrator->build(prev_integration_field); auto prev_sector_id = target_sector->get_id(); + Integrator::build_return_t sector_fields{prev_integration_field, prev_flow_field}; + std::vector>> flow_fields; flow_fields.reserve(portal_path.size() + 1); - flow_fields.push_back(std::make_pair(target_sector->get_id(), sector_fields.second)); + for (auto &portal : portal_path) { auto prev_sector = grid->get_sector(prev_sector_id); auto next_sector_id = portal->get_exit_sector(prev_sector_id); @@ -77,7 +131,9 @@ void Pathfinder::add_grid(const std::shared_ptr &grid) { this->grids[grid->get_id()] = grid; } -const std::vector> Pathfinder::portal_a_star(const PathRequest &request) const { +const std::vector> Pathfinder::portal_a_star(const PathRequest &request, + const std::unordered_set &target_portal_ids, + const std::unordered_set &start_portal_ids) const { std::vector> result; auto grid = this->grids.at(request.grid_id); @@ -87,15 +143,6 @@ const std::vector> Pathfinder::portal_a_star(const PathR auto start_sector_y = request.start.se / sector_size; auto start_sector = grid->get_sector(start_sector_x, start_sector_y); - auto target_sector_x = request.target.ne / sector_size; - auto target_sector_y = request.target.se / sector_size; - auto target_sector = grid->get_sector(target_sector_x, target_sector_y); - - if (start_sector == target_sector) { - // exit early if the start and target are in the same sector - return result; - } - // path node storage, always provides cheapest next node. heap_t node_candidates; @@ -106,8 +153,13 @@ const std::vector> Pathfinder::portal_a_star(const PathR // TODO: Determine this cost for each portal // const int distance_cost = 1; - // start nodes: all portals in the start sector + // create start nodes for (auto &portal : start_sector->get_portals()) { + if (not start_portal_ids.contains(portal->get_id())) { + // only consider portals that are reachable from the start cell + continue; + } + auto portal_node = std::make_shared(portal, start_sector->get_id(), nullptr); auto sector_pos = grid->get_sector(portal->get_exit_sector(start_sector->get_id()))->get_position(); @@ -133,8 +185,10 @@ const std::vector> Pathfinder::portal_a_star(const PathR current_node->was_best = true; - // check if the current node is the target - if (current_node->portal->get_exit_sector(current_node->entry_sector) == target_sector->get_id()) { + // check if the current node is a portal in the target sector that can + // be reached from the target cell + auto exit_portal_id = current_node->portal->get_id(); + if (target_portal_ids.contains(exit_portal_id)) { auto backtrace = current_node->generate_backtrace(); for (auto &node : backtrace) { result.push_back(node->portal); diff --git a/libopenage/pathfinding/pathfinder.h b/libopenage/pathfinding/pathfinder.h index a4df0350d8..0d4d3dfa7c 100644 --- a/libopenage/pathfinding/pathfinder.h +++ b/libopenage/pathfinding/pathfinder.h @@ -4,6 +4,7 @@ #include #include +#include #include "coord/tile.h" #include "datastructure/pairing_heap.h" @@ -66,10 +67,14 @@ class Pathfinder { * High-level pathfinder. Uses A* to find the path through the portals of sectors. * * @param request Pathfinding request. + * @param target_portal_ids IDs of portals that can be reached from the target cell. + * @param start_portal_ids IDs of portals that can be reached from the start cell. * * @return Portals to traverse in order to reach the target. */ - const std::vector> portal_a_star(const PathRequest &request) const; + const std::vector> portal_a_star(const PathRequest &request, + const std::unordered_set &target_portal_ids, + const std::unordered_set &start_portal_ids) const; /** * Low-level pathfinder. Uses flow fields to find the path through the sectors. From 596aecb831c4250718ce695525af0b21f1efb581 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 01:56:31 +0200 Subject: [PATCH 13/68] path: Store whether a path has been found. --- libopenage/gamestate/system/move.cpp | 5 ++++ libopenage/pathfinding/path.h | 17 +++++++----- libopenage/pathfinding/pathfinder.cpp | 37 ++++++++++++++++++++------- libopenage/pathfinding/pathfinder.h | 8 +++--- libopenage/pathfinding/types.h | 10 ++++++++ 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index 9607658ca2..158beea6ac 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -49,6 +49,11 @@ std::vector find_path(const std::shared_ptr &pat }; auto tile_path = pathfinder->get_path(request); + if (tile_path.status != path::PathResult::FOUND) { + // No path found + return {}; + } + // Get the waypoints of the path std::vector path; path.reserve(tile_path.waypoints.size()); diff --git a/libopenage/pathfinding/path.h b/libopenage/pathfinding/path.h index 8d64f5914f..d1c61ac325 100644 --- a/libopenage/pathfinding/path.h +++ b/libopenage/pathfinding/path.h @@ -5,6 +5,7 @@ #include #include "coord/tile.h" +#include "pathfinding/types.h" namespace openage::path { @@ -13,11 +14,11 @@ namespace openage::path { * Path request for the pathfinder. */ struct PathRequest { - // ID of the grid to use for pathfinding. + /// ID of the grid to use for pathfinding. size_t grid_id; - // Start position of the path. + /// Start position of the path. coord::tile start; - // Target position of the path. + /// Target position of the path. coord::tile target; }; @@ -25,11 +26,13 @@ struct PathRequest { * Path found by the pathfinder. */ struct Path { - // ID of the grid to used for pathfinding. + /// ID of the grid to used for pathfinding. size_t grid_id; - // Waypoints of the path. - // First waypoint is the start position of the path request. - // Last waypoint is the target position of the path request. + /// Status + PathResult status; + /// Waypoints of the path. + /// First waypoint is the start position of the path request. + /// Last waypoint is the target position of the path request. std::vector waypoints; }; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index ca830ca075..e16528afcf 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -48,7 +48,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { std::vector waypoints{request.start}; waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); - return Path{request.grid_id, waypoints}; + + log::log(INFO << "Path found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path is within the same sector."); + return Path{request.grid_id, PathResult::FOUND, waypoints}; } } @@ -77,11 +80,18 @@ const Path Pathfinder::get_path(const PathRequest &request) { } } - // ASDF + if (target_portal_ids.empty() or start_portal_ids.empty()) { + // Exit early if no portals are reachable from the start or target + log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "No portals are reachable from the start or target."); + return Path{request.grid_id, PathResult::NOT_FOUND, {}}; + } // High-level pathfinding // Find the portals to use to get from the start to the target - auto portal_path = this->portal_a_star(request, target_portal_ids, start_portal_ids); + auto portal_result = this->portal_a_star(request, target_portal_ids, start_portal_ids); + auto portal_status = portal_result.first; + auto portal_path = portal_result.second; // Low-level pathfinding // Find the path within the sectors @@ -120,7 +130,13 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto flow_field_waypoints = this->get_waypoints(flow_fields, request); waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); - return Path{request.grid_id, waypoints}; + if (portal_status == PathResult::NOT_FOUND) { + log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + } + else { + log::log(INFO << "Path found (start = " << request.start << "; target = " << request.target << ")"); + } + return Path{request.grid_id, portal_status, waypoints}; } const std::shared_ptr &Pathfinder::get_grid(grid_id_t id) const { @@ -131,9 +147,9 @@ void Pathfinder::add_grid(const std::shared_ptr &grid) { this->grids[grid->get_id()] = grid; } -const std::vector> Pathfinder::portal_a_star(const PathRequest &request, - const std::unordered_set &target_portal_ids, - const std::unordered_set &start_portal_ids) const { +const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &request, + const std::unordered_set &target_portal_ids, + const std::unordered_set &start_portal_ids) const { std::vector> result; auto grid = this->grids.at(request.grid_id); @@ -193,7 +209,8 @@ const std::vector> Pathfinder::portal_a_star(const PathR for (auto &node : backtrace) { result.push_back(node->portal); } - return result; + log::log(INFO << "Portal path found with " << result.size() << " portal traversals."); + return std::make_pair(PathResult::FOUND, result); } // check if the current node is the closest to the target @@ -248,7 +265,9 @@ const std::vector> Pathfinder::portal_a_star(const PathR result.push_back(node->portal); } - return result; + log::log(INFO << "Portal path not found."); + log::log(DBG << "Closest portal: " << closest_node->portal->get_id()); + return std::make_pair(PathResult::NOT_FOUND, result); } const std::vector Pathfinder::get_waypoints(const std::vector>> &flow_fields, diff --git a/libopenage/pathfinding/pathfinder.h b/libopenage/pathfinding/pathfinder.h index 0d4d3dfa7c..706e222318 100644 --- a/libopenage/pathfinding/pathfinder.h +++ b/libopenage/pathfinding/pathfinder.h @@ -63,6 +63,8 @@ class Pathfinder { const Path get_path(const PathRequest &request); private: + using portal_star_t = std::pair>>; + /** * High-level pathfinder. Uses A* to find the path through the portals of sectors. * @@ -72,9 +74,9 @@ class Pathfinder { * * @return Portals to traverse in order to reach the target. */ - const std::vector> portal_a_star(const PathRequest &request, - const std::unordered_set &target_portal_ids, - const std::unordered_set &start_portal_ids) const; + const portal_star_t portal_a_star(const PathRequest &request, + const std::unordered_set &target_portal_ids, + const std::unordered_set &start_portal_ids) const; /** * Low-level pathfinder. Uses flow fields to find the path through the sectors. diff --git a/libopenage/pathfinding/types.h b/libopenage/pathfinding/types.h index 11190f6551..b90e932323 100644 --- a/libopenage/pathfinding/types.h +++ b/libopenage/pathfinding/types.h @@ -8,6 +8,16 @@ namespace openage::path { +/** + * Path result type. + */ +enum class PathResult { + /// Path was found. + FOUND, + /// Path was not found. + NOT_FOUND, +}; + /** * Movement cost in the cost field. * From 564497e9e28f4067f4129955dec549e16df2a67c Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 02:12:49 +0200 Subject: [PATCH 14/68] path: Prevent duplicate waypoints at beginning of path. --- libopenage/pathfinding/pathfinder.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index e16528afcf..48eeb7d83e 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -46,7 +46,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto flow_field = this->integrator->build(target_integration_field); auto flow_field_waypoints = this->get_waypoints({std::make_pair(target_sector->get_id(), flow_field)}, request); - std::vector waypoints{request.start}; + std::vector waypoints{}; + if (flow_field_waypoints.at(0) != request.start) { + waypoints.push_back(request.start); + } waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); log::log(INFO << "Path found (start = " << request.start << "; target = " << request.target << ")"); @@ -126,8 +129,11 @@ const Path Pathfinder::get_path(const PathRequest &request) { std::reverse(flow_fields.begin(), flow_fields.end()); // traverse the flow fields to get the waypoints - std::vector waypoints{request.start}; auto flow_field_waypoints = this->get_waypoints(flow_fields, request); + std::vector waypoints{}; + if (flow_field_waypoints.at(0) != request.start) { + waypoints.push_back(request.start); + } waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); if (portal_status == PathResult::NOT_FOUND) { From f239a6f9950b17db6630757ba6eb420a02329316 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 22:32:23 +0200 Subject: [PATCH 15/68] path: LOS integration with portal. --- libopenage/pathfinding/integration_field.cpp | 37 ++++++++++++++++++++ libopenage/pathfinding/integration_field.h | 17 +++++++++ 2 files changed, 54 insertions(+) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index bc2745879f..ff10e0e507 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -33,6 +33,43 @@ const integrated_t &IntegrationField::get_cell(size_t idx) const { return this->cells.at(idx); } +std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, + sector_id_t other_sector_id, + const std::shared_ptr &portal) { + ENSURE(cost_field->get_size() == this->get_size(), + "cost field size " + << cost_field->get_size() << "x" << cost_field->get_size() + << " does not match integration field size " + << this->get_size() << "x" << this->get_size()); + + // Integrate the cost of the cells on the exit side (this) of the portal + std::vector start_cells; + auto exit_start = portal->get_exit_start(other_sector_id); + auto exit_end = portal->get_exit_end(other_sector_id); + auto entry_start = portal->get_entry_start(other_sector_id); + auto entry_end = portal->get_entry_end(other_sector_id); + + auto x_diff = exit_start.ne - entry_start.ne; + auto y_diff = exit_start.se - entry_start.se; + + for (auto y = exit_start.se; y <= exit_end.se; ++y) { + for (auto x = exit_start.ne; x <= exit_end.ne; ++x) { + // every portal cell is a target cell + auto target_idx = x + y * this->size; + + auto source_idx = x - x_diff + (y - y_diff) * this->size; + + // Set the cost of all target cells to the start value + this->cells[target_idx].cost = INTEGRATED_COST_START; + this->cells[target_idx].flags = this->cells[source_idx].flags; + + start_cells.push_back(target_idx); + } + } + + // TODO: Call main LOS integration function +} + std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, const coord::tile &target) { ENSURE(cost_field->get_size() == this->get_size(), diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index 183fb9cff6..c7c2bf5283 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -69,6 +69,23 @@ class IntegrationField { std::vector integrate_los(const std::shared_ptr &cost_field, const coord::tile &target); + /** + * Calculate the line-of-sight integration flags starting from a portal to another + * integration field. + * + * Returns a list of cells that are flagged as "wavefront blocked". These cells + * can be used as a starting point for the cost integration. + * + * @param cost_field Cost field to integrate. + * @param other_sector_id Sector ID of the other integration field. + * @param portal Portal connecting the two fields. + * + * @return Cells flagged as "wavefront blocked". + */ + std::vector integrate_los(const std::shared_ptr &cost_field, + sector_id_t other_sector_id, + const std::shared_ptr &portal); + /** * Calculate the cost integration field starting from a target cell. * From 0763deb77f279e3b1cdab428f26920f35a635273 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 23:21:21 +0200 Subject: [PATCH 16/68] path: Change all relative coordinates to delta types. Prevents accidental confusion between relative and absolute coordinates on grid. --- libopenage/coord/chunk.cpp | 16 +++++- libopenage/coord/chunk.h | 4 ++ libopenage/pathfinding/cost_field.cpp | 4 +- libopenage/pathfinding/cost_field.h | 10 ++-- libopenage/pathfinding/demo/demo_0.cpp | 49 ++++++++-------- libopenage/pathfinding/demo/demo_1.cpp | 2 +- libopenage/pathfinding/flow_field.cpp | 4 +- libopenage/pathfinding/flow_field.h | 10 ++-- libopenage/pathfinding/integration_field.cpp | 22 +++---- libopenage/pathfinding/integration_field.h | 24 ++++---- libopenage/pathfinding/integrator.cpp | 4 +- libopenage/pathfinding/integrator.h | 6 +- libopenage/pathfinding/pathfinder.cpp | 30 +++++----- libopenage/pathfinding/pathfinder.h | 11 ++-- libopenage/pathfinding/portal.cpp | 24 ++++---- libopenage/pathfinding/portal.h | 60 +++++++++++--------- libopenage/pathfinding/sector.cpp | 24 ++++---- libopenage/pathfinding/sector.h | 4 +- libopenage/pathfinding/tests.cpp | 4 +- 19 files changed, 169 insertions(+), 143 deletions(-) diff --git a/libopenage/coord/chunk.cpp b/libopenage/coord/chunk.cpp index fd061586b7..19cb606295 100644 --- a/libopenage/coord/chunk.cpp +++ b/libopenage/coord/chunk.cpp @@ -1,8 +1,20 @@ -// Copyright 2016-2018 the openage authors. See copying.md for legal info. +// Copyright 2016-2024 the openage authors. See copying.md for legal info. #include "chunk.h" +#include "coord/tile.h" + + namespace openage { namespace coord { -}} // namespace openage::coord +tile_delta chunk_delta::to_tile(size_t tiles_per_chunk) const { + return tile_delta{this->ne * tiles_per_chunk, this->se * tiles_per_chunk}; +} + +tile chunk::to_tile(size_t tiles_per_chunk) const { + return tile{this->ne * tiles_per_chunk, this->se * tiles_per_chunk}; +} + +} // namespace coord +} // namespace openage diff --git a/libopenage/coord/chunk.h b/libopenage/coord/chunk.h index a894cc56c1..0b5a1d50c0 100644 --- a/libopenage/coord/chunk.h +++ b/libopenage/coord/chunk.h @@ -12,10 +12,14 @@ namespace coord { struct chunk_delta : CoordNeSeRelative { using CoordNeSeRelative::CoordNeSeRelative; + + tile_delta to_tile(size_t tiles_per_chunk) const; }; struct chunk : CoordNeSeAbsolute { using CoordNeSeAbsolute::CoordNeSeAbsolute; + + tile to_tile(size_t tiles_per_chunk) const; }; struct chunk3_delta : CoordNeSeUpRelative { diff --git a/libopenage/pathfinding/cost_field.cpp b/libopenage/pathfinding/cost_field.cpp index 248a260d8e..e85fecc3ed 100644 --- a/libopenage/pathfinding/cost_field.cpp +++ b/libopenage/pathfinding/cost_field.cpp @@ -21,7 +21,7 @@ size_t CostField::get_size() const { return this->size; } -cost_t CostField::get_cost(const coord::tile &pos) const { +cost_t CostField::get_cost(const coord::tile_delta &pos) const { return this->cells.at(pos.ne + pos.se * this->size); } @@ -29,7 +29,7 @@ cost_t CostField::get_cost(size_t idx) const { return this->cells.at(idx); } -void CostField::set_cost(const coord::tile &pos, cost_t cost) { +void CostField::set_cost(const coord::tile_delta &pos, cost_t cost) { this->cells[pos.ne + pos.se * this->size] = cost; } diff --git a/libopenage/pathfinding/cost_field.h b/libopenage/pathfinding/cost_field.h index ffb27bdec8..8b805c524a 100644 --- a/libopenage/pathfinding/cost_field.h +++ b/libopenage/pathfinding/cost_field.h @@ -10,7 +10,7 @@ namespace openage { namespace coord { -struct tile; +struct tile_delta; } // namespace coord namespace path { @@ -37,10 +37,10 @@ class CostField { /** * Get the cost at a specified position. * - * @param pos Coordinates of the cell. + * @param pos Coordinates of the cell (relative to field origin). * @return Cost at the specified position. */ - cost_t get_cost(const coord::tile &pos) const; + cost_t get_cost(const coord::tile_delta &pos) const; /** * Get the cost at a specified position. @@ -53,10 +53,10 @@ class CostField { /** * Set the cost at a specified position. * - * @param pos Coordinates of the cell. + * @param pos Coordinates of the cell (relative to field origin). * @param cost Cost to set. */ - void set_cost(const coord::tile &pos, cost_t cost); + void set_cost(const coord::tile_delta &pos, cost_t cost); /** * Set the cost at a specified position. diff --git a/libopenage/pathfinding/demo/demo_0.cpp b/libopenage/pathfinding/demo/demo_0.cpp index bcf4cca945..d90d5ef39d 100644 --- a/libopenage/pathfinding/demo/demo_0.cpp +++ b/libopenage/pathfinding/demo/demo_0.cpp @@ -33,15 +33,15 @@ void path_demo_0(const util::Path &path) { // Cost field with some obstacles auto cost_field = std::make_shared(field_length); - cost_field->set_cost(coord::tile{0, 0}, COST_IMPASSABLE); - cost_field->set_cost(coord::tile{1, 0}, 254); - cost_field->set_cost(coord::tile{4, 3}, 128); - cost_field->set_cost(coord::tile{5, 3}, 128); - cost_field->set_cost(coord::tile{6, 3}, 128); - cost_field->set_cost(coord::tile{4, 4}, 128); - cost_field->set_cost(coord::tile{5, 4}, 128); - cost_field->set_cost(coord::tile{6, 4}, 128); - cost_field->set_cost(coord::tile{1, 7}, COST_IMPASSABLE); + cost_field->set_cost(coord::tile_delta{0, 0}, COST_IMPASSABLE); + cost_field->set_cost(coord::tile_delta{1, 0}, 254); + cost_field->set_cost(coord::tile_delta{4, 3}, 128); + cost_field->set_cost(coord::tile_delta{5, 3}, 128); + cost_field->set_cost(coord::tile_delta{6, 3}, 128); + cost_field->set_cost(coord::tile_delta{4, 4}, 128); + cost_field->set_cost(coord::tile_delta{5, 4}, 128); + cost_field->set_cost(coord::tile_delta{6, 4}, 128); + cost_field->set_cost(coord::tile_delta{1, 7}, COST_IMPASSABLE); log::log(INFO << "Created cost field"); // Create an integration field from the cost field @@ -49,7 +49,7 @@ void path_demo_0(const util::Path &path) { log::log(INFO << "Created integration field"); // Set cell (7, 7) to be the initial target cell - auto wavefront_blocked = integration_field->integrate_los(cost_field, coord::tile{7, 7}); + auto wavefront_blocked = integration_field->integrate_los(cost_field, coord::tile_delta{7, 7}); integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); log::log(INFO << "Calculated integration field for target cell (7, 7)"); @@ -92,7 +92,8 @@ void path_demo_0(const util::Path &path) { // Recalculate the integration field and the flow field integration_field->reset(); - auto wavefront_blocked = integration_field->integrate_los(cost_field, coord::tile{grid_x, grid_y}); + auto wavefront_blocked = integration_field->integrate_los(cost_field, + coord::tile_delta{grid_x, grid_y}); integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); log::log(INFO << "Calculated integration field for target cell (" << grid_x << ", " << grid_y << ")"); @@ -275,7 +276,7 @@ void RenderManager0::show_vectors(const std::shared_ptr &field) this->vector_pass->clear_renderables(); for (size_t y = 0; y < field->get_size(); ++y) { for (size_t x = 0; x < field->get_size(); ++x) { - auto cell = field->get_cell(coord::tile(x, y)); + auto cell = field->get_cell(coord::tile_delta(x, y)); if (cell & FLOW_PATHABLE_MASK and not(cell & FLOW_LOS_MASK)) { Eigen::Affine3f arrow_model = Eigen::Affine3f::Identity(); arrow_model.translate(Eigen::Vector3f(y + 0.5, 0, -1.0f * x - 0.5)); @@ -566,19 +567,19 @@ renderer::resources::MeshData RenderManager0::get_cost_field_mesh(const std::sha // for each vertex, compare the surrounding tiles std::vector surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cost(coord::tile((i - 1) / resolution, (j - 1) / resolution)); + auto cost = field->get_cost(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cost(coord::tile((i - 1) / resolution, j / resolution)); + auto cost = field->get_cost(coord::tile_delta((i - 1) / resolution, j / resolution)); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cost(coord::tile(i / resolution, j / resolution)); + auto cost = field->get_cost(coord::tile_delta(i / resolution, j / resolution)); surround.push_back(cost); } if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cost(coord::tile(i / resolution, (j - 1) / resolution)); + auto cost = field->get_cost(coord::tile_delta(i / resolution, (j - 1) / resolution)); surround.push_back(cost); } // use the cost of the most expensive surrounding tile @@ -652,19 +653,19 @@ renderer::resources::MeshData RenderManager0::get_integration_field_mesh(const s // for each vertex, compare the surrounding tiles std::vector surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile((i - 1) / resolution, (j - 1) / resolution)).cost; + auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)).cost; surround.push_back(cost); } if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile((i - 1) / resolution, j / resolution)).cost; + auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, j / resolution)).cost; surround.push_back(cost); } if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile(i / resolution, j / resolution)).cost; + auto cost = field->get_cell(coord::tile_delta(i / resolution, j / resolution)).cost; surround.push_back(cost); } if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile(i / resolution, (j - 1) / resolution)).cost; + auto cost = field->get_cell(coord::tile_delta(i / resolution, (j - 1) / resolution)).cost; surround.push_back(cost); } // use the cost of the most expensive surrounding tile @@ -738,19 +739,19 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha // for each vertex, compare the surrounding tiles std::vector surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile((i - 1) / resolution, (j - 1) / resolution)); + auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile((i - 1) / resolution, j / resolution)); + auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, j / resolution)); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile(i / resolution, j / resolution)); + auto cost = field->get_cell(coord::tile_delta(i / resolution, j / resolution)); surround.push_back(cost); } if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile(i / resolution, (j - 1) / resolution)); + auto cost = field->get_cell(coord::tile_delta(i / resolution, (j - 1) / resolution)); surround.push_back(cost); } // use the cost of the most expensive surrounding tile diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index 21aaa2c9d8..907caeb3d5 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -449,7 +449,7 @@ void RenderManager1::create_impassible_tiles(const std::shared_ptr & auto cost_field = sector->get_cost_field(); for (size_t y = 0; y < sector_size; y++) { for (size_t x = 0; x < sector_size; x++) { - auto cost = cost_field->get_cost(coord::tile(x, y)); + auto cost = cost_field->get_cost(coord::tile_delta(x, y)); if (cost == COST_IMPASSABLE) { std::array tile_data{ -1.0f + x * tile_offset_width + sector_size * sector_x * tile_offset_width, diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 7198ae5fbe..56fccc155c 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -30,11 +30,11 @@ size_t FlowField::get_size() const { return this->size; } -flow_t FlowField::get_cell(const coord::tile &pos) const { +flow_t FlowField::get_cell(const coord::tile_delta &pos) const { return this->cells.at(pos.ne + pos.se * this->size); } -flow_dir_t FlowField::get_dir(const coord::tile &pos) const { +flow_dir_t FlowField::get_dir(const coord::tile_delta &pos) const { return static_cast(this->get_cell(pos) & FLOW_DIR_MASK); } diff --git a/libopenage/pathfinding/flow_field.h b/libopenage/pathfinding/flow_field.h index 1c1adcfe33..6ae3882c5d 100644 --- a/libopenage/pathfinding/flow_field.h +++ b/libopenage/pathfinding/flow_field.h @@ -13,7 +13,7 @@ namespace openage { namespace coord { -struct tile; +struct tile_delta; } // namespace coord namespace path { @@ -46,20 +46,20 @@ class FlowField { /** * Get the flow field value at a specified position. * - * @param pos Coordinates of the cell. + * @param pos Coordinates of the cell (relative to field origin). * * @return Flowfield value at the specified position. */ - flow_t get_cell(const coord::tile &pos) const; + flow_t get_cell(const coord::tile_delta &pos) const; /** * Get the flow field direction at a specified position. * - * @param pos Coordinates of the cell. + * @param pos Coordinates of the cell (relative to field origin). * * @return Flowfield direction at the specified position. */ - flow_dir_t get_dir(const coord::tile &pos) const; + flow_dir_t get_dir(const coord::tile_delta &pos) const; /** * Build the flow field. diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index ff10e0e507..2a7fe7df7d 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -25,7 +25,7 @@ size_t IntegrationField::get_size() const { return this->size; } -const integrated_t &IntegrationField::get_cell(const coord::tile &pos) const { +const integrated_t &IntegrationField::get_cell(const coord::tile_delta &pos) const { return this->cells.at(pos.ne + pos.se * this->size); } @@ -71,7 +71,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr IntegrationField::integrate_los(const std::shared_ptr &cost_field, - const coord::tile &target) { + const coord::tile_delta &target) { ENSURE(cost_field->get_size() == this->get_size(), "cost field size " << cost_field->get_size() << "x" << cost_field->get_size() @@ -135,7 +135,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_los_corners(cost_field, target, coord::tile(x, y)); + auto corners = this->get_los_corners(cost_field, target, coord::tile_delta(x, y)); for (auto &corner : corners) { // draw a line from the corner to the edge of the field // to get the cells blocked by the corner @@ -186,7 +186,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, - const coord::tile &target) { + const coord::tile_delta &target) { ENSURE(cost_field->get_size() == this->get_size(), "cost field size " << cost_field->get_size() << "x" << cost_field->get_size() @@ -326,8 +326,8 @@ void IntegrationField::update_neighbor(size_t idx, } std::vector> IntegrationField::get_los_corners(const std::shared_ptr &cost_field, - const coord::tile &target, - const coord::tile &blocker) { + const coord::tile_delta &target, + const coord::tile_delta &blocker) { std::vector> corners; // Get the cost of the blocking cell's neighbors @@ -345,16 +345,16 @@ std::vector> IntegrationField::get_los_corners(const std::sh // Get neighbor costs (if they exist) if (blocker.se > 0) { - top_cost = cost_field->get_cost(coord::tile{blocker.ne, blocker.se - 1}); + top_cost = cost_field->get_cost(coord::tile_delta{blocker.ne, blocker.se - 1}); } if (blocker.ne > 0) { - left_cost = cost_field->get_cost(coord::tile{blocker.ne - 1, blocker.se}); + left_cost = cost_field->get_cost(coord::tile_delta{blocker.ne - 1, blocker.se}); } if (static_cast(blocker.se) < this->size - 1) { - bottom_cost = cost_field->get_cost(coord::tile{blocker.ne, blocker.se + 1}); + bottom_cost = cost_field->get_cost(coord::tile_delta{blocker.ne, blocker.se + 1}); } if (static_cast(blocker.ne) < this->size - 1) { - right_cost = cost_field->get_cost(coord::tile{blocker.ne + 1, blocker.se}); + right_cost = cost_field->get_cost(coord::tile_delta{blocker.ne + 1, blocker.se}); } // Check which corners are blocking LOS @@ -460,7 +460,7 @@ std::vector> IntegrationField::get_los_corners(const std::sh return corners; } -std::vector IntegrationField::bresenhams_line(const coord::tile &target, +std::vector IntegrationField::bresenhams_line(const coord::tile_delta &target, int corner_x, int corner_y) { std::vector cells; diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index c7c2bf5283..fc5f087404 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -13,7 +13,7 @@ namespace openage { namespace coord { -struct tile; +struct tile_delta; } // namespace coord namespace path { @@ -42,10 +42,10 @@ class IntegrationField { /** * Get the integration value at a specified position. * - * @param pos Coordinates of the cell. + * @param pos Coordinates of the cell (relative to field origin). * @return Integration value at the specified position. */ - const integrated_t &get_cell(const coord::tile &pos) const; + const integrated_t &get_cell(const coord::tile_delta &pos) const; /** * Get the integration value at a specified position. @@ -62,12 +62,12 @@ class IntegrationField { * can be used as a starting point for the cost integration. * * @param cost_field Cost field to integrate. - * @param target Coordinates of the target cell. + * @param target Coordinates of the target cell (relative to field origin). * * @return Cells flagged as "wavefront blocked". */ std::vector integrate_los(const std::shared_ptr &cost_field, - const coord::tile &target); + const coord::tile_delta &target); /** * Calculate the line-of-sight integration flags starting from a portal to another @@ -93,7 +93,7 @@ class IntegrationField { * @param target Coordinates of the target cell. */ void integrate_cost(const std::shared_ptr &cost_field, - const coord::tile &target); + const coord::tile_delta &target); /** * Calculate the cost integration field starting from a portal to another @@ -150,14 +150,14 @@ class IntegrationField { * Get the LOS corners around a cell. * * @param cost_field Cost field to integrate. - * @param target Cell coordinates of the target. - * @param blocker Cell coordinates of the cell blocking LOS. + * @param target Cell coordinates of the target (relative to field origin). + * @param blocker Cell coordinates of the cell blocking LOS (relative to field origin). * * @return Field coordinates of the LOS corners. */ std::vector> get_los_corners(const std::shared_ptr &cost_field, - const coord::tile &target, - const coord::tile &blocker); + const coord::tile_delta &target, + const coord::tile_delta &blocker); /** * Get the cells in a bresenham's line between the corner cell and the field edge. @@ -167,13 +167,13 @@ class IntegrationField { * the cells between two arbitrary points. We do this because the intersection * point with the field edge is unknown. * - * @param target Cell coordinates of the target. + * @param target Cell coordinates of the target (relative to field origin). * @param corner_x X field coordinate edge of the LOS corner. * @param corner_y Y field coordinate edge of the LOS corner. * * @return Cell indices of the LOS line. */ - std::vector bresenhams_line(const coord::tile &target, + std::vector bresenhams_line(const coord::tile_delta &target, int corner_x, int corner_y); diff --git a/libopenage/pathfinding/integrator.cpp b/libopenage/pathfinding/integrator.cpp index 28f8a829ca..4ffdedff15 100644 --- a/libopenage/pathfinding/integrator.cpp +++ b/libopenage/pathfinding/integrator.cpp @@ -10,7 +10,7 @@ namespace openage::path { std::shared_ptr Integrator::integrate(const std::shared_ptr &cost_field, - const coord::tile &target) { + const coord::tile_delta &target) { auto integration_field = std::make_shared(cost_field->get_size()); auto wavefront_blocked = integration_field->integrate_los(cost_field, target); @@ -46,7 +46,7 @@ std::shared_ptr Integrator::build(const std::shared_ptr &cost_field, - const coord::tile &target) { + const coord::tile_delta &target) { auto integration_field = this->integrate(cost_field, target); auto flow_field = this->build(integration_field); diff --git a/libopenage/pathfinding/integrator.h b/libopenage/pathfinding/integrator.h index 027f627274..ef9c2780cd 100644 --- a/libopenage/pathfinding/integrator.h +++ b/libopenage/pathfinding/integrator.h @@ -10,7 +10,7 @@ namespace openage { namespace coord { -struct tile; +struct tile_delta; } // namespace coord namespace path { @@ -36,7 +36,7 @@ class Integrator { * @return Integration field. */ std::shared_ptr integrate(const std::shared_ptr &cost_field, - const coord::tile &target); + const coord::tile_delta &target); /** * Integrate the cost field from a portal. @@ -86,7 +86,7 @@ class Integrator { * @return Flow field. */ build_return_t build(const std::shared_ptr &cost_field, - const coord::tile &target); + const coord::tile_delta &target); /** * Build the integration field and flow field from a portal. diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 48eeb7d83e..300bc7d92d 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -2,6 +2,7 @@ #include "pathfinder.h" +#include "coord/chunk.h" #include "coord/phys.h" #include "pathfinding/flow_field.h" #include "pathfinding/grid.h" @@ -33,14 +34,14 @@ const Path Pathfinder::get_path(const PathRequest &request) { // Integrate the target field coord::tile_t target_x = request.target.ne % sector_size; coord::tile_t target_y = request.target.se % sector_size; - auto target = coord::tile{target_x, target_y}; + auto target = coord::tile_delta{target_x, target_y}; auto target_integration_field = this->integrator->integrate(target_sector->get_cost_field(), target); if (target_sector == start_sector) { auto start_x = request.start.ne % sector_size; auto start_y = request.start.se % sector_size; - if (target_integration_field->get_cell(coord::tile{start_x, start_y}).cost != INTEGRATED_COST_UNREACHABLE) { + if (target_integration_field->get_cell(coord::tile_delta{start_x, start_y}).cost != INTEGRATED_COST_UNREACHABLE) { // Exit early if the start and target are in the same sector // and are reachable from within the same sector auto flow_field = this->integrator->build(target_integration_field); @@ -71,7 +72,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { // Check which portals are reachable from the start field coord::tile_t start_x = request.start.ne % sector_size; coord::tile_t start_y = request.start.se % sector_size; - auto start = coord::tile{start_x, start_y}; + auto start = coord::tile_delta{start_x, start_y}; auto start_integration_field = this->integrator->integrate(start_sector->get_cost_field(), start); std::unordered_set start_portal_ids; @@ -184,9 +185,9 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req auto portal_node = std::make_shared(portal, start_sector->get_id(), nullptr); - auto sector_pos = grid->get_sector(portal->get_exit_sector(start_sector->get_id()))->get_position(); + auto sector_pos = grid->get_sector(portal->get_exit_sector(start_sector->get_id()))->get_position().to_tile(sector_size); auto portal_pos = portal->get_exit_center(start_sector->get_id()); - auto portal_abs_pos = portal_pos + coord::tile_delta(sector_pos.ne * sector_size, sector_pos.se * sector_size); + auto portal_abs_pos = sector_pos + portal_pos; auto heuristic_cost = Pathfinder::heuristic_cost(portal_abs_pos, request.target); portal_node->current_cost = 0; @@ -233,7 +234,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req continue; } - // Get distance cost from current node to exit + // Get distance cost (from current node to exit node) auto distance_cost = Pathfinder::distance_cost( current_node->portal->get_exit_center(current_node->entry_sector), exit->portal->get_entry_center(exit->entry_sector)); @@ -243,9 +244,12 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req if (not_visited or tentative_cost < exit->current_cost) { if (not_visited) { - // calculate the heuristic cost + // Get heuristic cost (from exit node to target cell) + auto exit_sector = grid->get_sector(exit->portal->get_exit_sector(exit->entry_sector)); + auto exit_sector_pos = exit_sector->get_position().to_tile(sector_size); + auto exit_portal_pos = exit->portal->get_exit_center(exit->entry_sector); exit->heuristic_cost = Pathfinder::heuristic_cost( - exit->portal->get_exit_center(exit->entry_sector), + exit_sector_pos + exit_portal_pos, request.target); } @@ -291,7 +295,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(coord::tile{current_x, current_y}); + flow_dir_t current_direction = flow_fields.at(0).second->get_dir(coord::tile_delta{current_x, current_y}); for (size_t i = 0; i < flow_fields.size(); ++i) { auto sector = grid->get_sector(flow_fields.at(i).first); auto flow_field = flow_fields.at(i).second; @@ -301,7 +305,7 @@ const std::vector Pathfinder::get_waypoints(const std::vector(sector_size) and current_x >= 0 and current_y >= 0) { - auto cell = flow_field->get_cell(coord::tile{current_x, current_y}); + auto cell = flow_field->get_cell(coord::tile_delta{current_x, current_y}); if (cell & FLOW_LOS_MASK) { // check if we reached an LOS cell auto sector_pos = sector->get_position(); @@ -313,7 +317,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(coord::tile(current_x, current_y)); + auto cell_direction = flow_field->get_dir(coord::tile_delta(current_x, current_y)); if (cell_direction != current_direction) { // add the current cell as a waypoint auto sector_pos = sector->get_position(); @@ -412,8 +416,8 @@ int Pathfinder::heuristic_cost(const coord::tile &portal_pos, return delta.length(); } -int Pathfinder::distance_cost(const coord::tile &portal1_pos, - const coord::tile &portal2_pos) { +int Pathfinder::distance_cost(const coord::tile_delta &portal1_pos, + const coord::tile_delta &portal2_pos) { auto delta = portal2_pos.to_phys2() - portal1_pos.to_phys2(); return delta.length(); diff --git a/libopenage/pathfinding/pathfinder.h b/libopenage/pathfinding/pathfinder.h index 706e222318..1809b7524d 100644 --- a/libopenage/pathfinding/pathfinder.h +++ b/libopenage/pathfinding/pathfinder.h @@ -92,8 +92,9 @@ class Pathfinder { /** * Calculate the heuristic cost between a portal and a target cell. * - * @param portal_pos Position of the portal. This should be the center of the portal exit. - * @param target_pos Position of the target cell. + * @param portal_pos Position of the portal (absolute on the grid). + * This should be the center of the portal exit. + * @param target_pos Position of the target cell (absolute on the grid). * * @return Heuristic cost between the cells. */ @@ -102,12 +103,12 @@ class Pathfinder { /** * Calculate the distance cost between two portals. * - * @param portal1_pos Center of the first portal. - * @param portal2_pos Center of the second portal. + * @param portal1_pos Center of the first portal (relative to sector origin). + * @param portal2_pos Center of the second portal (relative to sector origin). * * @return Distance cost between the portal centers. */ - static int distance_cost(const coord::tile &portal1_pos, const coord::tile &portal2_pos); + static int distance_cost(const coord::tile_delta &portal1_pos, const coord::tile_delta &portal2_pos); /** * Grids managed by this pathfinder. diff --git a/libopenage/pathfinding/portal.cpp b/libopenage/pathfinding/portal.cpp index da3581047f..9b8f64cd51 100644 --- a/libopenage/pathfinding/portal.cpp +++ b/libopenage/pathfinding/portal.cpp @@ -11,8 +11,8 @@ Portal::Portal(portal_id_t id, sector_id_t sector0, sector_id_t sector1, PortalDirection direction, - const coord::tile &cell_start, - const coord::tile &cell_end) : + const coord::tile_delta &cell_start, + const coord::tile_delta &cell_end) : id{id}, sector0{sector0}, sector1{sector1}, @@ -65,7 +65,7 @@ sector_id_t Portal::get_exit_sector(sector_id_t entry_sector) const { return this->sector0; } -const coord::tile Portal::get_entry_start(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_entry_start(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -75,7 +75,7 @@ const coord::tile Portal::get_entry_start(sector_id_t entry_sector) const { return this->get_sector1_start(); } -const coord::tile Portal::get_entry_center(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_entry_center(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -89,7 +89,7 @@ const coord::tile Portal::get_entry_center(sector_id_t entry_sector) const { return {(start.ne + end.ne) / 2, (start.se + end.se) / 2}; } -const coord::tile Portal::get_entry_end(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_entry_end(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -99,7 +99,7 @@ const coord::tile Portal::get_entry_end(sector_id_t entry_sector) const { return this->get_sector1_end(); } -const coord::tile Portal::get_exit_start(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_exit_start(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -109,7 +109,7 @@ const coord::tile Portal::get_exit_start(sector_id_t entry_sector) const { return this->get_sector0_start(); } -const coord::tile Portal::get_exit_center(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_exit_center(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -123,7 +123,7 @@ const coord::tile Portal::get_exit_center(sector_id_t entry_sector) const { return {(start.ne + end.ne) / 2, (start.se + end.se) / 2}; } -const coord::tile Portal::get_exit_end(sector_id_t entry_sector) const { +const coord::tile_delta Portal::get_exit_end(sector_id_t entry_sector) const { ENSURE(entry_sector == this->sector0 || entry_sector == this->sector1, "Invalid entry sector"); if (entry_sector == this->sector0) { @@ -137,22 +137,22 @@ PortalDirection Portal::get_direction() const { return this->direction; } -const coord::tile &Portal::get_sector0_start() const { +const coord::tile_delta &Portal::get_sector0_start() const { return this->cell_start; } -const coord::tile &Portal::get_sector0_end() const { +const coord::tile_delta &Portal::get_sector0_end() const { return this->cell_end; } -const coord::tile Portal::get_sector1_start() const { +const coord::tile_delta Portal::get_sector1_start() const { if (this->direction == PortalDirection::NORTH_SOUTH) { return {this->cell_start.ne, 0}; } return {0, this->cell_start.se}; } -const coord::tile Portal::get_sector1_end() const { +const coord::tile_delta Portal::get_sector1_end() const { if (this->direction == PortalDirection::NORTH_SOUTH) { return {this->cell_end.ne, 0}; } diff --git a/libopenage/pathfinding/portal.h b/libopenage/pathfinding/portal.h index 2487e449c7..6593eea852 100644 --- a/libopenage/pathfinding/portal.h +++ b/libopenage/pathfinding/portal.h @@ -49,15 +49,15 @@ class Portal { * @param sector1 Second sector connected by the portal. * Must be south or west on the grid in relation to sector 0. * @param direction Direction of the portal from sector 0 to sector 1. - * @param cell_start Start cell coordinate in sector 0. - * @param cell_end End cell coordinate in sector 0. + * @param cell_start Start cell coordinate in sector 0 (relative to sector origin). + * @param cell_end End cell coordinate in sector 0 (relative to sector origin). */ Portal(portal_id_t id, sector_id_t sector0, sector_id_t sector1, PortalDirection direction, - const coord::tile &cell_start, - const coord::tile &cell_end); + const coord::tile_delta &cell_start, + const coord::tile_delta &cell_end); ~Portal() = default; @@ -110,54 +110,54 @@ class Portal { * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the start of the portal in the entry sector. + * @return Cell coordinates of the start of the portal in the entry sector (relative to sector origin). */ - const coord::tile get_entry_start(sector_id_t entry_sector) const; + const coord::tile_delta get_entry_start(sector_id_t entry_sector) const; /** * Get the cell coordinates of the center of the portal in the entry sector. * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the center of the portal in the entry sector. + * @return Cell coordinates of the center of the portal in the entry sector (relative to sector origin). */ - const coord::tile get_entry_center(sector_id_t entry_sector) const; + const coord::tile_delta get_entry_center(sector_id_t entry_sector) const; /** * Get the cell coordinates of the start of the portal in the entry sector. * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the start of the portal in the entry sector. + * @return Cell coordinates of the start of the portal in the entry sector (relative to sector origin). */ - const coord::tile get_entry_end(sector_id_t entry_sector) const; + const coord::tile_delta get_entry_end(sector_id_t entry_sector) const; /** * Get the cell coordinates of the start of the portal in the exit sector. * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the start of the portal in the exit sector. + * @return Cell coordinates of the start of the portal in the exit sector (relative to sector origin). */ - const coord::tile get_exit_start(sector_id_t entry_sector) const; + const coord::tile_delta get_exit_start(sector_id_t entry_sector) const; /** * Get the cell coordinates of the center of the portal in the exit sector. * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the center of the portal in the exit sector. + * @return Cell coordinates of the center of the portal in the exit sector (relative to sector origin). */ - const coord::tile get_exit_center(sector_id_t entry_sector) const; + const coord::tile_delta get_exit_center(sector_id_t entry_sector) const; /** * Get the cell coordinates of the end of the portal in the exit sector. * * @param entry_sector Sector from which the portal is entered. * - * @return Cell coordinates of the end of the portal in the exit sector. + * @return Cell coordinates of the end of the portal in the exit sector (relative to sector origin). */ - const coord::tile get_exit_end(sector_id_t entry_sector) const; + const coord::tile_delta get_exit_end(sector_id_t entry_sector) const; /** * Get the direction of the portal from sector 0 to sector 1. @@ -170,30 +170,30 @@ class Portal { /** * Get the start cell coordinates of the portal. * - * @return Start cell coordinates of the portal. + * @return Start cell coordinates of the portal (relative to sector origin). */ - const coord::tile &get_sector0_start() const; + const coord::tile_delta &get_sector0_start() const; /** * Get the end cell coordinates of the portal. * - * @return End cell coordinates of the portal. + * @return End cell coordinates of the portal (relative to sector origin). */ - const coord::tile &get_sector0_end() const; + const coord::tile_delta &get_sector0_end() const; /** * Get the start cell coordinates of the portal. * - * @return Start cell coordinates of the portal. + * @return Start cell coordinates of the portal (relative to sector origin). */ - const coord::tile get_sector1_start() const; + const coord::tile_delta get_sector1_start() const; /** * Get the end cell coordinates of the portal. * - * @return End cell coordinates of the portal. + * @return End cell coordinates of the portal (relative to sector origin). */ - const coord::tile get_sector1_end() const; + const coord::tile_delta get_sector1_end() const; /** * ID of the portal. @@ -230,13 +230,17 @@ class Portal { PortalDirection direction; /** - * Start cell coordinate. + * Start cell coordinate in sector 0 (relative to sector origin). + * + * Coordinates for sector 1 are calculated on-the-fly using the direction. */ - coord::tile cell_start; + coord::tile_delta cell_start; /** - * End cell coordinate. + * End cell coordinate in sector 0 (relative to sector origin). + * + * Coordinates for sector 1 are calculated on-the-fly using the direction. */ - coord::tile cell_end; + coord::tile_delta cell_end; }; } // namespace openage::path diff --git a/libopenage/pathfinding/sector.cpp b/libopenage/pathfinding/sector.cpp index cf6ffaa755..f50dc209bf 100644 --- a/libopenage/pathfinding/sector.cpp +++ b/libopenage/pathfinding/sector.cpp @@ -61,17 +61,17 @@ std::vector> Sector::find_portals(const std::shared_ptr< size_t end = 0; bool passable_edge = false; for (size_t i = 0; i < this->cost_field->get_size(); ++i) { - auto coord_this = coord::tile{0, 0}; - auto coord_other = coord::tile{0, 0}; + auto coord_this = coord::tile_delta{0, 0}; + auto coord_other = coord::tile_delta{0, 0}; if (direction == PortalDirection::NORTH_SOUTH) { // right edge; top to bottom - coord_this = coord::tile(i, this->cost_field->get_size() - 1); - coord_other = coord::tile(i, 0); + coord_this = coord::tile_delta(i, this->cost_field->get_size() - 1); + coord_other = coord::tile_delta(i, 0); } else if (direction == PortalDirection::EAST_WEST) { // bottom edge; east to west - coord_this = coord::tile(this->cost_field->get_size() - 1, i); - coord_other = coord::tile(0, i); + coord_this = coord::tile_delta(this->cost_field->get_size() - 1, i); + coord_other = coord::tile_delta(0, i); } if (this->cost_field->get_cost(coord_this) != COST_IMPASSABLE @@ -94,17 +94,17 @@ std::vector> Sector::find_portals(const std::shared_ptr< if (passable_edge) { // create a new portal - auto coord_start = coord::tile{0, 0}; - auto coord_end = coord::tile{0, 0}; + auto coord_start = coord::tile_delta{0, 0}; + auto coord_end = coord::tile_delta{0, 0}; if (direction == PortalDirection::NORTH_SOUTH) { // right edge; top to bottom - coord_start = coord::tile(start, this->cost_field->get_size() - 1); - coord_end = coord::tile(end, this->cost_field->get_size() - 1); + coord_start = coord::tile_delta(start, this->cost_field->get_size() - 1); + coord_end = coord::tile_delta(end, this->cost_field->get_size() - 1); } else if (direction == PortalDirection::EAST_WEST) { // bottom edge; east to west - coord_start = coord::tile(this->cost_field->get_size() - 1, start); - coord_end = coord::tile(this->cost_field->get_size() - 1, end); + coord_start = coord::tile_delta(this->cost_field->get_size() - 1, start); + coord_end = coord::tile_delta(this->cost_field->get_size() - 1, end); } result.push_back( diff --git a/libopenage/pathfinding/sector.h b/libopenage/pathfinding/sector.h index f39f1904a9..a4ba94fa52 100644 --- a/libopenage/pathfinding/sector.h +++ b/libopenage/pathfinding/sector.h @@ -57,7 +57,7 @@ class Sector { /** * Get the position of this sector in the grid. * - * @return Position of the sector. + * @return Position of the sector (absolute on the grid). */ const coord::chunk &get_position() const; @@ -110,7 +110,7 @@ class Sector { sector_id_t id; /** - * Position of the sector in the grid. + * Position of the sector (absolute on the grid). */ coord::chunk position; diff --git a/libopenage/pathfinding/tests.cpp b/libopenage/pathfinding/tests.cpp index 2c9775ca36..12c96cc30e 100644 --- a/libopenage/pathfinding/tests.cpp +++ b/libopenage/pathfinding/tests.cpp @@ -28,7 +28,7 @@ void flow_field() { // Test the different field types { auto integration_field = std::make_shared(3); - integration_field->integrate_cost(cost_field, coord::tile{2, 2}); + integration_field->integrate_cost(cost_field, coord::tile_delta{2, 2}); auto int_cells = integration_field->get_cells(); // The integration field should look like: @@ -85,7 +85,7 @@ void flow_field() { auto integrator = std::make_shared(); // Build the flow field - auto flow_field = integrator->build(cost_field, coord::tile{2, 2}).second; + auto flow_field = integrator->build(cost_field, coord::tile_delta{2, 2}).second; auto ff_cells = flow_field->get_cells(); // The flow field for targeting (2, 2) hould look like this: From 258c5922d135ae30ab07cbef5b58ba3b1068f7d5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 23:49:49 +0200 Subject: [PATCH 17/68] path: Use x,y cell position instead of coord types for most operations. --- libopenage/pathfinding/cost_field.cpp | 8 +++++ libopenage/pathfinding/cost_field.h | 18 ++++++++++ libopenage/pathfinding/demo/demo_0.cpp | 16 ++++----- libopenage/pathfinding/demo/demo_1.cpp | 2 +- libopenage/pathfinding/flow_field.cpp | 16 +++++++++ libopenage/pathfinding/flow_field.h | 38 ++++++++++++++++++++ libopenage/pathfinding/integration_field.cpp | 12 ++++--- libopenage/pathfinding/integration_field.h | 9 +++++ libopenage/pathfinding/pathfinder.cpp | 6 ++-- 9 files changed, 109 insertions(+), 16 deletions(-) diff --git a/libopenage/pathfinding/cost_field.cpp b/libopenage/pathfinding/cost_field.cpp index e85fecc3ed..3299084548 100644 --- a/libopenage/pathfinding/cost_field.cpp +++ b/libopenage/pathfinding/cost_field.cpp @@ -25,6 +25,10 @@ cost_t CostField::get_cost(const coord::tile_delta &pos) const { return this->cells.at(pos.ne + pos.se * this->size); } +cost_t CostField::get_cost(size_t x, size_t y) const { + return this->cells.at(x + y * this->size); +} + cost_t CostField::get_cost(size_t idx) const { return this->cells.at(idx); } @@ -33,6 +37,10 @@ void CostField::set_cost(const coord::tile_delta &pos, cost_t cost) { this->cells[pos.ne + pos.se * this->size] = cost; } +void CostField::set_cost(size_t x, size_t y, cost_t cost) { + this->cells[x + y * this->size] = cost; +} + void CostField::set_cost(size_t idx, cost_t cost) { this->cells[idx] = cost; } diff --git a/libopenage/pathfinding/cost_field.h b/libopenage/pathfinding/cost_field.h index 8b805c524a..a1fc87b046 100644 --- a/libopenage/pathfinding/cost_field.h +++ b/libopenage/pathfinding/cost_field.h @@ -42,6 +42,15 @@ class CostField { */ cost_t get_cost(const coord::tile_delta &pos) const; + /** + * Get the cost at a specified position. + * + * @param x X-coordinate of the cell. + * @param y Y-coordinate of the cell. + * @return Cost at the specified position. + */ + cost_t get_cost(size_t x, size_t y) const; + /** * Get the cost at a specified position. * @@ -58,6 +67,15 @@ class CostField { */ void set_cost(const coord::tile_delta &pos, cost_t cost); + /** + * Set the cost at a specified position. + * + * @param x X-coordinate of the cell. + * @param y Y-coordinate of the cell. + * @param cost Cost to set. + */ + void set_cost(size_t x, size_t y, cost_t cost); + /** * Set the cost at a specified position. * diff --git a/libopenage/pathfinding/demo/demo_0.cpp b/libopenage/pathfinding/demo/demo_0.cpp index d90d5ef39d..8e844d942d 100644 --- a/libopenage/pathfinding/demo/demo_0.cpp +++ b/libopenage/pathfinding/demo/demo_0.cpp @@ -567,19 +567,19 @@ renderer::resources::MeshData RenderManager0::get_cost_field_mesh(const std::sha // for each vertex, compare the surrounding tiles std::vector surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cost(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)); + auto cost = field->get_cost((i - 1) / resolution, (j - 1) / resolution); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cost(coord::tile_delta((i - 1) / resolution, j / resolution)); + auto cost = field->get_cost((i - 1) / resolution, j / resolution); surround.push_back(cost); } if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cost(coord::tile_delta(i / resolution, j / resolution)); + auto cost = field->get_cost(i / resolution, j / resolution); surround.push_back(cost); } if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cost(coord::tile_delta(i / resolution, (j - 1) / resolution)); + auto cost = field->get_cost(i / resolution, (j - 1) / resolution); surround.push_back(cost); } // use the cost of the most expensive surrounding tile @@ -653,19 +653,19 @@ renderer::resources::MeshData RenderManager0::get_integration_field_mesh(const s // for each vertex, compare the surrounding tiles std::vector surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)).cost; + auto cost = field->get_cell((i - 1) / resolution, (j - 1) / resolution).cost; surround.push_back(cost); } if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, j / resolution)).cost; + auto cost = field->get_cell((i - 1) / resolution, j / resolution).cost; surround.push_back(cost); } if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile_delta(i / resolution, j / resolution)).cost; + auto cost = field->get_cell(i / resolution, j / resolution).cost; surround.push_back(cost); } if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile_delta(i / resolution, (j - 1) / resolution)).cost; + auto cost = field->get_cell(i / resolution, (j - 1) / resolution).cost; surround.push_back(cost); } // use the cost of the most expensive surrounding tile diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index 907caeb3d5..d7daaf4675 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -449,7 +449,7 @@ void RenderManager1::create_impassible_tiles(const std::shared_ptr & auto cost_field = sector->get_cost_field(); for (size_t y = 0; y < sector_size; y++) { for (size_t x = 0; x < sector_size; x++) { - auto cost = cost_field->get_cost(coord::tile_delta(x, y)); + auto cost = cost_field->get_cost(x, y); if (cost == COST_IMPASSABLE) { std::array tile_data{ -1.0f + x * tile_offset_width + sector_size * sector_x * tile_offset_width, diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 56fccc155c..bb6c2d6b6e 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -34,10 +34,26 @@ flow_t FlowField::get_cell(const coord::tile_delta &pos) const { return this->cells.at(pos.ne + pos.se * this->size); } +flow_t FlowField::get_cell(size_t x, size_t y) const { + return this->cells.at(x + y * this->size); +} + +flow_t FlowField::get_cell(size_t idx) const { + return this->cells.at(idx); +} + flow_dir_t FlowField::get_dir(const coord::tile_delta &pos) const { return static_cast(this->get_cell(pos) & FLOW_DIR_MASK); } +flow_dir_t FlowField::get_dir(size_t x, size_t y) const { + return static_cast(this->get_cell(x, y) & FLOW_DIR_MASK); +} + +flow_dir_t FlowField::get_dir(size_t idx) const { + return static_cast(this->get_cell(idx) & FLOW_DIR_MASK); +} + void FlowField::build(const std::shared_ptr &integration_field, const std::unordered_set &target_cells) { ENSURE(integration_field->get_size() == this->get_size(), diff --git a/libopenage/pathfinding/flow_field.h b/libopenage/pathfinding/flow_field.h index 6ae3882c5d..4a055d3cb1 100644 --- a/libopenage/pathfinding/flow_field.h +++ b/libopenage/pathfinding/flow_field.h @@ -52,6 +52,25 @@ class FlowField { */ flow_t get_cell(const coord::tile_delta &pos) const; + /** + * Get the flow field value at a specified position. + * + * @param x X-coordinate of the cell. + * @param y Y-coordinate of the cell. + * + * @return Flowfield value at the specified position. + */ + flow_t get_cell(size_t x, size_t y) const; + + /** + * Get the flow field direction at a specified position. + * + * @param idx Index of the cell. + * + * @return Flowfield value at the specified position. + */ + flow_t get_cell(size_t idx) const; + /** * Get the flow field direction at a specified position. * @@ -61,6 +80,25 @@ class FlowField { */ flow_dir_t get_dir(const coord::tile_delta &pos) const; + /** + * Get the flow field direction at a specified position. + * + * @param x X-coordinate of the cell. + * @param y Y-coordinate of the cell. + * + * @return Flowfield direction at the specified position. + */ + flow_dir_t get_dir(size_t x, size_t y) const; + + /** + * Get the flow field direction at a specified position. + * + * @param idx Index of the cell. + * + * @return Flowfield direction at the specified position. + */ + flow_dir_t get_dir(size_t idx) const; + /** * Build the flow field. * diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 2a7fe7df7d..0219dd17f3 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -29,6 +29,10 @@ const integrated_t &IntegrationField::get_cell(const coord::tile_delta &pos) con return this->cells.at(pos.ne + pos.se * this->size); } +const integrated_t &IntegrationField::get_cell(size_t x, size_t y) const { + return this->cells.at(x + y * this->size); +} + const integrated_t &IntegrationField::get_cell(size_t idx) const { return this->cells.at(idx); } @@ -345,16 +349,16 @@ std::vector> IntegrationField::get_los_corners(const std::sh // Get neighbor costs (if they exist) if (blocker.se > 0) { - top_cost = cost_field->get_cost(coord::tile_delta{blocker.ne, blocker.se - 1}); + top_cost = cost_field->get_cost(blocker.ne, blocker.se - 1); } if (blocker.ne > 0) { - left_cost = cost_field->get_cost(coord::tile_delta{blocker.ne - 1, blocker.se}); + left_cost = cost_field->get_cost(blocker.ne - 1, blocker.se); } if (static_cast(blocker.se) < this->size - 1) { - bottom_cost = cost_field->get_cost(coord::tile_delta{blocker.ne, blocker.se + 1}); + bottom_cost = cost_field->get_cost(blocker.ne, blocker.se + 1); } if (static_cast(blocker.ne) < this->size - 1) { - right_cost = cost_field->get_cost(coord::tile_delta{blocker.ne + 1, blocker.se}); + right_cost = cost_field->get_cost(blocker.ne + 1, blocker.se); } // Check which corners are blocking LOS diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index fc5f087404..40e6780c8f 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -47,6 +47,15 @@ class IntegrationField { */ const integrated_t &get_cell(const coord::tile_delta &pos) const; + /** + * Get the integration value at a specified position. + * + * @param x X-coordinate of the cell. + * @param y Y-coordinate of the cell. + * @return Integration value at the specified position. + */ + const integrated_t &get_cell(size_t x, size_t y) const; + /** * Get the integration value at a specified position. * diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 300bc7d92d..22662c9e9d 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -41,7 +41,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto start_x = request.start.ne % sector_size; auto start_y = request.start.se % sector_size; - if (target_integration_field->get_cell(coord::tile_delta{start_x, start_y}).cost != INTEGRATED_COST_UNREACHABLE) { + if (target_integration_field->get_cell(start_x, start_y).cost != INTEGRATED_COST_UNREACHABLE) { // Exit early if the start and target are in the same sector // and are reachable from within the same sector auto flow_field = this->integrator->build(target_integration_field); @@ -295,7 +295,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(coord::tile_delta{current_x, current_y}); + flow_dir_t current_direction = flow_fields.at(0).second->get_dir(current_x, current_y); for (size_t i = 0; i < flow_fields.size(); ++i) { auto sector = grid->get_sector(flow_fields.at(i).first); auto flow_field = flow_fields.at(i).second; @@ -305,7 +305,7 @@ const std::vector Pathfinder::get_waypoints(const std::vector(sector_size) and current_x >= 0 and current_y >= 0) { - auto cell = flow_field->get_cell(coord::tile_delta{current_x, current_y}); + auto cell = flow_field->get_cell(current_x, current_y); if (cell & FLOW_LOS_MASK) { // check if we reached an LOS cell auto sector_pos = sector->get_position(); From 66709b4f913735e09a4ac3c4049967d2770bdef7 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 21 May 2024 23:55:18 +0200 Subject: [PATCH 18/68] path: Fix crash in demo 1 when path is not found. --- libopenage/pathfinding/demo/demo_1.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index d7daaf4675..801eea045d 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -135,8 +135,10 @@ void path_demo_1(const util::Path &path) { log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " ps"); - // Create renderables for the waypoints of the path - render_manager->create_waypoint_tiles(path_result); + if (path_result.status == PathResult::FOUND) { + // Create renderables for the waypoints of the path + render_manager->create_waypoint_tiles(path_result); + } } else if (ev.button() == Qt::LeftButton) { // Set start cell start = coord::tile{grid_x, grid_y}; @@ -153,8 +155,10 @@ void path_demo_1(const util::Path &path) { log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " ps"); - // Create renderables for the waypoints of the path - render_manager->create_waypoint_tiles(path_result); + if (path_result.status == PathResult::FOUND) { + // Create renderables for the waypoints of the path + render_manager->create_waypoint_tiles(path_result); + } } } }); From d26915041abd837200b7acfe32046a2408dddb99 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 22 May 2024 01:08:23 +0200 Subject: [PATCH 19/68] path: Flag in integration field. Speeds up flow field creation by ~30%. --- libopenage/pathfinding/definitions.h | 7 +++++++ libopenage/pathfinding/flow_field.cpp | 14 +++----------- libopenage/pathfinding/flow_field.h | 5 +---- libopenage/pathfinding/integration_field.cpp | 2 ++ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/libopenage/pathfinding/definitions.h b/libopenage/pathfinding/definitions.h index d61c5fe43c..4e184406cd 100644 --- a/libopenage/pathfinding/definitions.h +++ b/libopenage/pathfinding/definitions.h @@ -52,6 +52,11 @@ constexpr integrated_flags_t INTEGRATE_LOS_MASK = 0x01; */ constexpr integrated_flags_t INTEGRATE_WAVEFRONT_BLOCKED_MASK = 0x02; +/** + * Target flag in an integrated_flags_t value. + */ +constexpr integrated_flags_t INTEGRATE_TARGET_MASK = 0x40; + /** * Initial value for a cell in the integration grid. */ @@ -80,6 +85,8 @@ constexpr flow_t FLOW_LOS_MASK = 0x20; /** * Wavefront blocked flag in a flow_t value. + * + * TODO: This flag is only used in demo 0 and can be removed. */ constexpr flow_t FLOW_WAVEFRONT_BLOCKED_MASK = 0x40; diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index bb6c2d6b6e..c005e6cf05 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -54,8 +54,7 @@ flow_dir_t FlowField::get_dir(size_t idx) const { return static_cast(this->get_cell(idx) & FLOW_DIR_MASK); } -void FlowField::build(const std::shared_ptr &integration_field, - const std::unordered_set &target_cells) { +void FlowField::build(const std::shared_ptr &integration_field) { ENSURE(integration_field->get_size() == this->get_size(), "integration field size " << integration_field->get_size() << "x" << integration_field->get_size() @@ -69,7 +68,7 @@ void FlowField::build(const std::shared_ptr &integration_field for (size_t x = 0; x < this->size; ++x) { size_t idx = y * this->size + x; - if (target_cells.contains(idx)) { + if (integrate_cells[idx].flags & INTEGRATE_TARGET_MASK) { // Ignore target cells continue; } @@ -204,9 +203,6 @@ void FlowField::build(const std::shared_ptr &integration_field // TODO: Compare integration values from other side of portal // auto &integrate_cells = integration_field->get_cells(); - // cells that are part of the portal - std::unordered_set portal_cells; - // set the direction for the flow field cells that are part of the portal if (direction == PortalDirection::NORTH_SOUTH) { bool other_is_north = entry_start.se > exit_start.se; @@ -216,7 +212,6 @@ void FlowField::build(const std::shared_ptr &integration_field auto idx = y * this->size + x; flow_cells[idx] = flow_cells[idx] | FLOW_PATHABLE_MASK; flow_cells[idx] = flow_cells[idx] | static_cast(flow_dir_t::NORTH); - portal_cells.insert(idx); } } else { @@ -225,7 +220,6 @@ void FlowField::build(const std::shared_ptr &integration_field auto idx = y * this->size + x; flow_cells[idx] = flow_cells[idx] | FLOW_PATHABLE_MASK; flow_cells[idx] = flow_cells[idx] | static_cast(flow_dir_t::SOUTH); - portal_cells.insert(idx); } } } @@ -237,7 +231,6 @@ void FlowField::build(const std::shared_ptr &integration_field auto idx = y * this->size + x; flow_cells[idx] = flow_cells[idx] | FLOW_PATHABLE_MASK; flow_cells[idx] = flow_cells[idx] | static_cast(flow_dir_t::EAST); - portal_cells.insert(idx); } } else { @@ -246,7 +239,6 @@ void FlowField::build(const std::shared_ptr &integration_field auto idx = y * this->size + x; flow_cells[idx] = flow_cells[idx] | FLOW_PATHABLE_MASK; flow_cells[idx] = flow_cells[idx] | static_cast(flow_dir_t::WEST); - portal_cells.insert(idx); } } } @@ -254,7 +246,7 @@ void FlowField::build(const std::shared_ptr &integration_field throw Error(ERR << "Invalid portal direction: " << static_cast(direction)); } - this->build(integration_field, portal_cells); + this->build(integration_field); } const std::vector &FlowField::get_cells() const { diff --git a/libopenage/pathfinding/flow_field.h b/libopenage/pathfinding/flow_field.h index 4a055d3cb1..51a7d9a90d 100644 --- a/libopenage/pathfinding/flow_field.h +++ b/libopenage/pathfinding/flow_field.h @@ -103,11 +103,8 @@ class FlowField { * Build the flow field. * * @param integration_field Integration field. - * @param target_cells Target cells of the flow field. These cells are ignored - * when building the field. */ - void build(const std::shared_ptr &integration_field, - const std::unordered_set &target_cells = {}); + void build(const std::shared_ptr &integration_field); /** * Build the flow field for a portal. diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 0219dd17f3..1d95c9225a 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -202,6 +202,7 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie // Move outwards from the target cell, updating the integration field this->cells[target_idx].cost = INTEGRATED_COST_START; + this->cells[target_idx].flags |= INTEGRATE_TARGET_MASK; this->integrate_cost(cost_field, {target_idx}); } @@ -225,6 +226,7 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie // Set the cost of all target cells to the start value this->cells[target_idx].cost = INTEGRATED_COST_START; + this->cells[target_idx].flags |= INTEGRATE_TARGET_MASK; start_cells.push_back(target_idx); // TODO: Transfer flags and cost from the other integration field From 60cf82a0f454233c2ace88ecb4acf8bde202d22b Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 22 May 2024 01:10:42 +0200 Subject: [PATCH 20/68] path: Fix time unit log (microseconds instead of picoseconds). --- libopenage/pathfinding/demo/demo_1.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index 801eea045d..3d00d31b0c 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -133,7 +133,7 @@ void path_demo_1(const util::Path &path) { path_result = pathfinder->get_path(new_path_request); timer.stop(); - log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " ps"); + log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " us"); if (path_result.status == PathResult::FOUND) { // Create renderables for the waypoints of the path @@ -153,7 +153,7 @@ void path_demo_1(const util::Path &path) { path_result = pathfinder->get_path(new_path_request); timer.stop(); - log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " ps"); + log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " us"); if (path_result.status == PathResult::FOUND) { // Create renderables for the waypoints of the path From 6c7a471ba71e532073325df81ff3d0716e8a201b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 06:30:02 +0200 Subject: [PATCH 21/68] coord: Make tile_per_chunk type-compatible with tile_t. --- libopenage/coord/chunk.cpp | 4 ++-- libopenage/coord/chunk.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libopenage/coord/chunk.cpp b/libopenage/coord/chunk.cpp index 19cb606295..62ab65be32 100644 --- a/libopenage/coord/chunk.cpp +++ b/libopenage/coord/chunk.cpp @@ -8,11 +8,11 @@ namespace openage { namespace coord { -tile_delta chunk_delta::to_tile(size_t tiles_per_chunk) const { +tile_delta chunk_delta::to_tile(tile_t tiles_per_chunk) const { return tile_delta{this->ne * tiles_per_chunk, this->se * tiles_per_chunk}; } -tile chunk::to_tile(size_t tiles_per_chunk) const { +tile chunk::to_tile(tile_t tiles_per_chunk) const { return tile{this->ne * tiles_per_chunk, this->se * tiles_per_chunk}; } diff --git a/libopenage/coord/chunk.h b/libopenage/coord/chunk.h index 0b5a1d50c0..158888c9a1 100644 --- a/libopenage/coord/chunk.h +++ b/libopenage/coord/chunk.h @@ -13,13 +13,13 @@ namespace coord { struct chunk_delta : CoordNeSeRelative { using CoordNeSeRelative::CoordNeSeRelative; - tile_delta to_tile(size_t tiles_per_chunk) const; + tile_delta to_tile(tile_t tiles_per_chunk) const; }; struct chunk : CoordNeSeAbsolute { using CoordNeSeAbsolute::CoordNeSeAbsolute; - tile to_tile(size_t tiles_per_chunk) const; + tile to_tile(tile_t tiles_per_chunk) const; }; struct chunk3_delta : CoordNeSeUpRelative { From 308c913aaae67301b65e013d11e37b03664efc25 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 12:26:58 +0200 Subject: [PATCH 22/68] gamestate: Fix setting recent angle position. --- libopenage/gamestate/component/internal/position.cpp | 4 ++-- libopenage/gamestate/system/move.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libopenage/gamestate/component/internal/position.cpp b/libopenage/gamestate/component/internal/position.cpp index 61611382cf..7c4b7bbb83 100644 --- a/libopenage/gamestate/component/internal/position.cpp +++ b/libopenage/gamestate/component/internal/position.cpp @@ -1,4 +1,4 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2024 the openage authors. See copying.md for legal info. #include "position.h" @@ -60,7 +60,7 @@ const curve::Segmented &Position::get_angles() const { void Position::set_angle(const time::time_t &time, const coord::phys_angle_t &angle) { auto old_angle = this->angle.get(time); - this->angle.set_insert_jump(time, old_angle, angle); + this->angle.set_last_jump(time, old_angle, angle); } } // namespace openage::gamestate::component diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index 158beea6ac..0031a2624d 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -127,7 +127,6 @@ const time::time_t Move::move_default(const std::shared_ptrset_position(start_time, current_pos); - auto prev_angle = current_angle; for (size_t i = 1; i < waypoints.size(); ++i) { auto prev_waypoint = waypoints[i - 1]; auto cur_waypoint = waypoints[i]; @@ -153,6 +152,9 @@ const time::time_t Move::move_default(const std::shared_ptrget(); total_time += turn_time; pos_component->set_position(start_time + total_time, prev_waypoint); + + // update current angle for next waypoint + current_angle = path_angle; } pos_component->set_angle(start_time + total_time, path_angle); From e7f7ed98be5473d7a3695ea4b132dcb923bbd137 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 12:52:48 +0200 Subject: [PATCH 23/68] coord: Reorder tiles and fix missing conversions. --- libopenage/coord/tile.cpp | 47 +++++++++++++++++++++++---------------- libopenage/coord/tile.h | 9 +++----- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/libopenage/coord/tile.cpp b/libopenage/coord/tile.cpp index e20868ff98..b1eb9593a4 100644 --- a/libopenage/coord/tile.cpp +++ b/libopenage/coord/tile.cpp @@ -8,55 +8,64 @@ namespace openage::coord { +phys2_delta tile_delta::to_phys2() const { + return phys2_delta{phys2::elem_t::from_int(this->ne), + phys2::elem_t::from_int(this->se)}; +} -tile3 tile::to_tile3(tile_t up) const { - return tile3(this->ne, this->se, up); +phys3_delta tile_delta::to_phys3(tile_t up) const { + return phys3_delta{phys3::elem_t::from_int(this->ne), + phys3::elem_t::from_int(this->se), + phys3::elem_t::from_int(up)}; } +tile3 tile::to_tile3(tile_t up) const { + return tile3{this->ne, this->se, up}; +} phys2 tile::to_phys2() const { - return phys2{phys3::elem_t::from_int(this->ne), phys3::elem_t::from_int(this->se)}; + return phys2{phys3::elem_t::from_int(this->ne), + phys3::elem_t::from_int(this->se)}; } - phys3 tile::to_phys3(tile_t up) const { return this->to_tile3(up).to_phys3(); } - chunk tile::to_chunk() const { return chunk{ static_cast(util::div(this->ne, tiles_per_chunk)), static_cast(util::div(this->se, tiles_per_chunk))}; } - tile_delta tile::get_pos_on_chunk() const { return tile_delta{ util::mod(this->ne, tiles_per_chunk), util::mod(this->se, tiles_per_chunk)}; } - -phys2 tile3::to_phys2() const { - return this->to_tile().to_phys2(); +tile_delta tile3_delta::to_tile() const { + return tile_delta{this->ne, this->se}; } - -phys3 tile3::to_phys3() const { - return phys3{ - phys3::elem_t::from_int(this->ne), - phys3::elem_t::from_int(this->se), - phys3::elem_t::from_int(this->up)}; +phys3_delta tile3_delta::to_phys3() const { + return phys3_delta{phys3::elem_t::from_int(this->ne), + phys3::elem_t::from_int(this->se), + phys3::elem_t::from_int(up)}; } +tile tile3::to_tile() const { + return tile{this->ne, this->se}; +} -phys2_delta tile_delta::to_phys2() const { - return phys2_delta(this->ne, this->se); +phys2 tile3::to_phys2() const { + return this->to_tile().to_phys2(); } -phys3_delta tile_delta::to_phys3(tile_t up) const { - return phys3_delta(this->ne, this->se, up); +phys3 tile3::to_phys3() const { + return phys3{phys3::elem_t::from_int(this->ne), + phys3::elem_t::from_int(this->se), + phys3::elem_t::from_int(this->up)}; } } // namespace openage::coord diff --git a/libopenage/coord/tile.h b/libopenage/coord/tile.h index e8e61a09c2..1fff2efa21 100644 --- a/libopenage/coord/tile.h +++ b/libopenage/coord/tile.h @@ -20,6 +20,7 @@ namespace coord { struct tile_delta : CoordNeSeRelative { using CoordNeSeRelative::CoordNeSeRelative; + // coordinate conversions phys2_delta to_phys2() const; phys3_delta to_phys3(tile_t up = 0) const; }; @@ -45,9 +46,7 @@ struct tile3_delta : CoordNeSeUpRelative { // coordinate conversions // simply discards the UP component of the coordinate delta. - constexpr tile_delta to_tile() const { - return tile_delta{this->ne, this->se}; - } + tile_delta to_tile() const; phys3_delta to_phys3() const; }; @@ -56,9 +55,7 @@ struct tile3 : CoordNeSeUpAbsolute { // coordinate conversions // simply discards the UP component of the coordinate. - constexpr tile to_tile() const { - return tile{this->ne, this->se}; - } + tile to_tile() const; phys2 to_phys2() const; phys3 to_phys3() const; }; From 58281773e4913792b75c19ec6f553193ef416e4d Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 12:58:39 +0200 Subject: [PATCH 24/68] gamestate: Use tile center for waypoints in path. --- libopenage/coord/tile.cpp | 24 +++++++++++++++++++++++- libopenage/coord/tile.h | 7 +++++++ libopenage/gamestate/system/move.cpp | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libopenage/coord/tile.cpp b/libopenage/coord/tile.cpp index b1eb9593a4..e447819799 100644 --- a/libopenage/coord/tile.cpp +++ b/libopenage/coord/tile.cpp @@ -1,4 +1,4 @@ -// Copyright 2016-2023 the openage authors. See copying.md for legal info. +// Copyright 2016-2024 the openage authors. See copying.md for legal info. #include "tile.h" @@ -32,6 +32,17 @@ phys3 tile::to_phys3(tile_t up) const { return this->to_tile3(up).to_phys3(); } +phys2 tile::to_phys2_center() const { + return phys2{phys3::elem_t::from_int(this->ne) + 0.5, + phys3::elem_t::from_int(this->se) + 0.5}; +} + +phys3 tile::to_phys3_center(tile_t up) const { + return phys3{phys3::elem_t::from_int(this->ne) + 0.5, + phys3::elem_t::from_int(this->se) + 0.5, + phys3::elem_t::from_int(up)}; +} + chunk tile::to_chunk() const { return chunk{ static_cast(util::div(this->ne, tiles_per_chunk)), @@ -68,4 +79,15 @@ phys3 tile3::to_phys3() const { phys3::elem_t::from_int(this->up)}; } +phys2 tile3::to_phys2_center() const { + return phys2{phys3::elem_t::from_int(this->ne) + 0.5, + phys3::elem_t::from_int(this->se) + 0.5}; +} + +phys3 tile3::to_phys3_center() const { + return phys3{phys3::elem_t::from_int(this->ne) + 0.5, + phys3::elem_t::from_int(this->se) + 0.5, + phys3::elem_t::from_int(this->up)}; +} + } // namespace openage::coord diff --git a/libopenage/coord/tile.h b/libopenage/coord/tile.h index 1fff2efa21..ec685b8002 100644 --- a/libopenage/coord/tile.h +++ b/libopenage/coord/tile.h @@ -35,8 +35,12 @@ struct tile : CoordNeSeAbsolute { * elevation. */ tile3 to_tile3(tile_t up = 0) const; + phys2 to_phys2() const; phys3 to_phys3(tile_t up = 0) const; + phys2 to_phys2_center() const; + phys3 to_phys3_center(tile_t up = 0) const; + chunk to_chunk() const; tile_delta get_pos_on_chunk() const; }; @@ -56,8 +60,11 @@ struct tile3 : CoordNeSeUpAbsolute { // coordinate conversions // simply discards the UP component of the coordinate. tile to_tile() const; + phys2 to_phys2() const; phys3 to_phys3() const; + phys2 to_phys2_center() const; + phys3 to_phys3_center() const; }; diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index 0031a2624d..3da45ceab3 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -64,7 +64,7 @@ std::vector find_path(const std::shared_ptr &pat // Pathfinder waypoints contain start and end tile; we can ignore them for (size_t i = 1; i < tile_path.waypoints.size() - 1; ++i) { auto tile = tile_path.waypoints.at(i); - path.push_back(tile.to_phys3()); + path.push_back(tile.to_phys3_center()); } // End position is last waypoint From d5d437239c38dd2d653b93011ad9529be15ccb66 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 22:14:14 +0200 Subject: [PATCH 25/68] path: Reset with std::fill. --- libopenage/pathfinding/flow_field.cpp | 13 ++++++++++--- libopenage/pathfinding/flow_field.h | 11 +++++++++++ libopenage/pathfinding/integration_field.cpp | 14 +++++++++++--- libopenage/pathfinding/integration_field.h | 11 +++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index c005e6cf05..3b52aeedcf 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -254,11 +254,18 @@ const std::vector &FlowField::get_cells() const { } void FlowField::reset() { - for (auto &cell : this->cells) { - cell = FLOW_INIT; - } + std::fill(this->cells.begin(), this->cells.end(), FLOW_INIT); log::log(DBG << "Flow field has been reset"); } +void FlowField::reset_dynamic_flags() { + flow_t mask = 0xFF & ~(FLOW_LOS_MASK | FLOW_WAVEFRONT_BLOCKED_MASK); + for (flow_t &cell : this->cells) { + cell = cell & mask; + } + + log::log(DBG << "Flow field dynamic flags have been reset"); +} + } // namespace openage::path diff --git a/libopenage/pathfinding/flow_field.h b/libopenage/pathfinding/flow_field.h index 51a7d9a90d..6d9aeaeb63 100644 --- a/libopenage/pathfinding/flow_field.h +++ b/libopenage/pathfinding/flow_field.h @@ -131,6 +131,17 @@ class FlowField { */ void reset(); + /** + * Reset all flags that are dependent on the path target location. These + * flags should be removed when the field is cached and reused for + * other targets. + * + * Relevant flags are: + * - FLOW_LOS_MASK + * - FLOW_WAVEFRONT_BLOCKED_MASK + */ + void reset_dynamic_flags(); + private: /** * Side length of the field. diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 1d95c9225a..0271f32f37 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -299,12 +299,20 @@ const std::vector &IntegrationField::get_cells() const { } void IntegrationField::reset() { - for (auto &cell : this->cells) { - cell = INTEGRATE_INIT; - } + std::fill(this->cells.begin(), this->cells.end(), INTEGRATE_INIT); + log::log(DBG << "Integration field has been reset"); } +void IntegrationField::reset_dynamic_flags() { + integrated_flags_t mask = 0xFF & ~(INTEGRATE_LOS_MASK | INTEGRATE_WAVEFRONT_BLOCKED_MASK); + for (integrated_t &cell : this->cells) { + cell.flags = cell.flags & mask; + } + + log::log(DBG << "Integration field dynamic flags have been reset"); +} + void IntegrationField::update_neighbor(size_t idx, cost_t cell_cost, integrated_cost_t integrated_cost, diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index 40e6780c8f..7cce6d2529 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -137,6 +137,17 @@ class IntegrationField { */ void reset(); + /** + * Reset all flags that are dependent on the path target location. These + * flags should be removed when the field is cached and reused for + * other targets. + * + * Relevant flags are: + * - INTEGRATE_LOS_MASK + * - INTEGRATE_WAVEFRONT_BLOCKED_MASK + */ + void reset_dynamic_flags(); + private: /** * Update a neigbor cell during the cost integration process. From d51ebba060888034bedb4f3d158d1ddb4cd4452d Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 25 May 2024 23:46:56 +0200 Subject: [PATCH 26/68] patg: Cache fields during pathing. --- libopenage/pathfinding/integrator.cpp | 26 ++++++++++++++++++++++++++ libopenage/pathfinding/integrator.h | 25 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/libopenage/pathfinding/integrator.cpp b/libopenage/pathfinding/integrator.cpp index 4ffdedff15..0802f00ee5 100644 --- a/libopenage/pathfinding/integrator.cpp +++ b/libopenage/pathfinding/integrator.cpp @@ -5,6 +5,7 @@ #include "pathfinding/cost_field.h" #include "pathfinding/flow_field.h" #include "pathfinding/integration_field.h" +#include "pathfinding/portal.h" namespace openage::path { @@ -22,6 +23,12 @@ std::shared_ptr Integrator::integrate(const std::shared_ptr Integrator::integrate(const std::shared_ptr &cost_field, sector_id_t other_sector_id, const std::shared_ptr &portal) { + auto cache_key = std::make_pair(portal->get_id(), other_sector_id); + auto cached = this->field_cache.find(cache_key); + if (cached != this->field_cache.end()) { + return cached->second.first; + } + auto integration_field = std::make_shared(cost_field->get_size()); integration_field->integrate_cost(cost_field, other_sector_id, portal); @@ -39,6 +46,12 @@ std::shared_ptr Integrator::build(const std::shared_ptr &other, sector_id_t other_sector_id, const std::shared_ptr &portal) { + auto cache_key = std::make_pair(portal->get_id(), other_sector_id); + auto cached = this->field_cache.find(cache_key); + if (cached != this->field_cache.end()) { + return cached->second.second; + } + auto flow_field = std::make_shared(integration_field->get_size()); flow_field->build(integration_field, other, other_sector_id, portal); @@ -57,9 +70,22 @@ Integrator::build_return_t Integrator::build(const std::shared_ptr &c const std::shared_ptr &other_integration_field, sector_id_t other_sector_id, const std::shared_ptr &portal) { + auto cache_key = std::make_pair(portal->get_id(), other_sector_id); + auto cached = this->field_cache.find(cache_key); + if (cached != this->field_cache.end()) { + return cached->second; + } + auto integration_field = this->integrate(cost_field, other_sector_id, portal); auto flow_field = this->build(integration_field, other_integration_field, other_sector_id, portal); + // Copy the fields to the cache. + std::shared_ptr cached_integration_field = std::make_shared(*integration_field); + cached_integration_field->reset_dynamic_flags(); + std::shared_ptr cached_flow_field = std::make_shared(*flow_field); + cached_flow_field->reset_dynamic_flags(); + this->field_cache[cache_key] = std::make_pair(cached_integration_field, cached_flow_field); + return std::make_pair(integration_field, flow_field); } diff --git a/libopenage/pathfinding/integrator.h b/libopenage/pathfinding/integrator.h index ef9c2780cd..660f8cbb7c 100644 --- a/libopenage/pathfinding/integrator.h +++ b/libopenage/pathfinding/integrator.h @@ -4,8 +4,10 @@ #include #include +#include #include "pathfinding/types.h" +#include "util/hash.h" namespace openage { @@ -102,7 +104,28 @@ class Integrator { const std::shared_ptr &portal); private: - // TODO: Cache created flow fields. + /** + * Hash function for the field cache. + */ + struct pair_hash { + template + std::size_t operator()(const std::pair &pair) const { + return util::hash_combine(std::hash{}(pair.first), std::hash{}(pair.second)); + } + }; + + /** + * Cache for already computed fields. + * + * Key is the portal ID and the sector ID from which the field was entered. Fields that are cached are + * cleared of dynamic flags, i.e. wavefront or LOS flags. These have to be recalculated + * when the field is reused. + */ + std::unordered_map, + std::pair, + std::shared_ptr>, + pair_hash> + field_cache; }; } // namespace path From e849754d33d6be80ba8f2cc8104f77f71320c81a Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 26 May 2024 00:31:52 +0200 Subject: [PATCH 27/68] path: Clear LOS next wave list before next loop iteration. --- libopenage/pathfinding/integration_field.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 0271f32f37..ce98114de2 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -183,6 +183,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr Date: Sun, 26 May 2024 00:56:33 +0200 Subject: [PATCH 28/68] path: Only add cells with blocked flag to wavefront list. --- libopenage/pathfinding/integration_field.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index ce98114de2..1b42de0ef2 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -136,6 +136,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = cost - 1 + cost_field->get_cost(idx); + // TODO: this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; } // check each neighbor for a corner @@ -154,8 +155,9 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[blocked_idx].flags &= ~INTEGRATE_LOS_MASK; + + wavefront_blocked.push_back(blocked_idx); } - wavefront_blocked.insert(wavefront_blocked.end(), blocked_cells.begin(), blocked_cells.end()); } continue; } From 805148cc76df3f5927c2bc1f7befd0d16bbbe192 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 26 May 2024 02:37:40 +0200 Subject: [PATCH 29/68] path: Add flag for found LOS cells instead of lookup set. --- libopenage/pathfinding/definitions.h | 7 ++++++- libopenage/pathfinding/integration_field.cpp | 12 ++++-------- libopenage/pathfinding/types.h | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libopenage/pathfinding/definitions.h b/libopenage/pathfinding/definitions.h index 4e184406cd..a54dff576a 100644 --- a/libopenage/pathfinding/definitions.h +++ b/libopenage/pathfinding/definitions.h @@ -55,7 +55,12 @@ constexpr integrated_flags_t INTEGRATE_WAVEFRONT_BLOCKED_MASK = 0x02; /** * Target flag in an integrated_flags_t value. */ -constexpr integrated_flags_t INTEGRATE_TARGET_MASK = 0x40; +constexpr integrated_flags_t INTEGRATE_TARGET_MASK = 0x04; + +/** + * Found flag in an integrated_flags_t value. + */ +constexpr integrated_flags_t INTEGRATE_FOUND_MASK = 0x80; /** * Initial value for a cell in the integration grid. diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 1b42de0ef2..3916c40120 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -88,10 +88,6 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrsize; - // Lookup table for cells that have been found - std::unordered_set found; - found.reserve(this->size * this->size); - // Cells that still have to be visited by the current wave std::deque current_wave; @@ -108,19 +104,19 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].flags & INTEGRATE_FOUND_MASK) { + // Skip cells that are already in the line of sight continue; } else if (this->cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // Stop at cells that are blocked by a LOS corner this->cells[idx].cost = cost - 1 + cost_field->get_cost(idx); - found.insert(idx); + this->cells[idx].flags |= INTEGRATE_FOUND_MASK; continue; } // Add the current cell to the found cells - found.insert(idx); + this->cells[idx].flags |= INTEGRATE_FOUND_MASK; // Get the x and y coordinates of the current cell auto x = idx % this->size; diff --git a/libopenage/pathfinding/types.h b/libopenage/pathfinding/types.h index b90e932323..bf5a7de473 100644 --- a/libopenage/pathfinding/types.h +++ b/libopenage/pathfinding/types.h @@ -51,6 +51,8 @@ struct integrated_t { /** * Flags. * + * Bit 0: Found flag. + * Bit 5: Target flag. * Bit 6: Wave front blocked flag. * Bit 7: Line of sight flag. */ From b48c68a23b01d82527dfdd6e7b099c722bd2be1b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 26 May 2024 03:25:05 +0200 Subject: [PATCH 30/68] path: Remove lookup set from cost integration pass. --- libopenage/pathfinding/integration_field.cpp | 20 ++++---------------- libopenage/pathfinding/integration_field.h | 4 +--- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 3916c40120..38639b89fe 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -238,10 +238,6 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie void IntegrationField::integrate_cost(const std::shared_ptr &cost_field, std::vector &&start_cells) { - // Lookup table for cells that are in the open list - std::unordered_set in_list; - in_list.reserve(this->size * this->size); - // Cells that still have to be visited // they may be visited multiple times std::deque open_list; @@ -281,15 +277,11 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie this->update_neighbor(neighbor_idx, cost_field->get_cost(neighbor_idx), integrated_current, - open_list, - in_list); + open_list); } // Clear the neighbors vector neighbors.clear(); - - // Remove the current cell from the open list lookup table - in_list.erase(idx); } } @@ -315,8 +307,7 @@ void IntegrationField::reset_dynamic_flags() { void IntegrationField::update_neighbor(size_t idx, cost_t cell_cost, integrated_cost_t integrated_cost, - std::deque &open_list, - std::unordered_set &in_list) { + std::deque &open_list) { ENSURE(cell_cost > COST_INIT, "cost field cell value must be non-zero"); // Check if the cell is impassable @@ -326,15 +317,12 @@ void IntegrationField::update_neighbor(size_t idx, } auto cost = integrated_cost + cell_cost; - if (cost < this->cells.at(idx).cost) { + if (cost < this->cells[idx].cost) { // If the new integration value is smaller than the current one, // update the cell and add it to the open list this->cells[idx].cost = cost; - if (not in_list.contains(idx)) { - in_list.insert(idx); - open_list.push_back(idx); - } + open_list.push_back(idx); } } diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index 7cce6d2529..ad0b879b83 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -156,15 +156,13 @@ class IntegrationField { * @param cell_cost Cost of the neighbor cell from the cost field. * @param integrated_cost Current integrated cost of the updating cell in the integration field. * @param open_list List of cells to be updated. - * @param in_list Set of cells that have been updated. * * @return New integration value of the cell. */ void update_neighbor(size_t idx, cost_t cell_cost, integrated_cost_t integrated_cost, - std::deque &open_list, - std::unordered_set &in_list); + std::deque &open_list); /** * Get the LOS corners around a cell. From 352996fe3fdc30cb0dc035dd8bddb03b10a74470 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 30 May 2024 14:02:45 +0200 Subject: [PATCH 31/68] path: Check if target cell is out of bounds for grid. --- libopenage/pathfinding/grid.h | 8 ++++---- libopenage/pathfinding/pathfinder.cpp | 13 +++++++++++++ libopenage/pathfinding/types.h | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libopenage/pathfinding/grid.h b/libopenage/pathfinding/grid.h index 83b4c2b721..8f34a7643c 100644 --- a/libopenage/pathfinding/grid.h +++ b/libopenage/pathfinding/grid.h @@ -49,16 +49,16 @@ class Grid { grid_id_t get_id() const; /** - * Get the size of the grid. + * Get the size of the grid (in number of sectors). * - * @return Size of the grid (width x height). + * @return Size of the grid (in number of sectors) (width x height). */ const util::Vector2s &get_size() const; /** - * Get the side length of the sectors on the grid. + * Get the side length of the sectors on the grid (in number of cells). * - * @return Sector side length. + * @return Sector side length (in number of cells). */ size_t get_sector_size() const; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 22662c9e9d..9da9b4a1c9 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -23,6 +23,19 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto grid = this->grids.at(request.grid_id); auto sector_size = grid->get_sector_size(); + // Check if the target is within the grid + auto grid_size = grid->get_size(); + auto grid_width = grid_size[0] * sector_size; + auto grid_height = grid_size[1] * sector_size; + if (request.target.ne < 0 + or request.target.se < 0 + or request.target.ne >= grid_width + or request.target.se >= grid_height) { + log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Target is out of bounds."); + return Path{request.grid_id, PathResult::OUT_OF_BOUNDS, {}}; + } + auto start_sector_x = request.start.ne / sector_size; auto start_sector_y = request.start.se / sector_size; auto start_sector = grid->get_sector(start_sector_x, start_sector_y); diff --git a/libopenage/pathfinding/types.h b/libopenage/pathfinding/types.h index bf5a7de473..3285d9d170 100644 --- a/libopenage/pathfinding/types.h +++ b/libopenage/pathfinding/types.h @@ -16,6 +16,8 @@ enum class PathResult { FOUND, /// Path was not found. NOT_FOUND, + /// Target is not on grid. + OUT_OF_BOUNDS, }; /** From 906e47a7bb64f63ab8d42c11017a23d8a6769a74 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 31 May 2024 13:51:04 +0200 Subject: [PATCH 32/68] path: Set PATHABLE and LOS flags for target cells in flow field. --- libopenage/pathfinding/flow_field.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 3b52aeedcf..2641b343fb 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -68,11 +68,6 @@ void FlowField::build(const std::shared_ptr &integration_field for (size_t x = 0; x < this->size; ++x) { size_t idx = y * this->size + x; - if (integrate_cells[idx].flags & INTEGRATE_TARGET_MASK) { - // Ignore target cells - continue; - } - if (integrate_cells[idx].cost == INTEGRATED_COST_UNREACHABLE) { // Cell cannot be used as path continue; @@ -80,24 +75,32 @@ void FlowField::build(const std::shared_ptr &integration_field if (integrate_cells[idx].flags & INTEGRATE_LOS_MASK) { // Cell is in line of sight - this->cells[idx] = this->cells[idx] | FLOW_LOS_MASK; + flow_cells[idx] |= FLOW_LOS_MASK; // we can skip calculating the flow direction as we can // move straight to the target from this cell - this->cells[idx] = this->cells[idx] | FLOW_PATHABLE_MASK; + flow_cells[idx] |= FLOW_PATHABLE_MASK; continue; } if (integrate_cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // Cell is blocked by a line-of-sight corner - this->cells[idx] = this->cells[idx] | FLOW_WAVEFRONT_BLOCKED_MASK; + flow_cells[idx] |= FLOW_WAVEFRONT_BLOCKED_MASK; + } + + if (integrate_cells[idx].flags & INTEGRATE_TARGET_MASK) { + // target cells are pathable + flow_cells[idx] |= FLOW_PATHABLE_MASK; + + // they also have a preset flow direction so we can skip here + continue; } // Store which of the non-diagonal directions are unreachable. std::bitset<4> directions_unreachable; // Find the neighbor with the smallest cost. - flow_dir_t direction = static_cast(this->cells[idx] & FLOW_DIR_MASK); + flow_dir_t direction = static_cast(flow_cells[idx] & FLOW_DIR_MASK); auto smallest_cost = INTEGRATED_COST_UNREACHABLE; if (y > 0) { auto cost = integrate_cells[idx - this->size].cost; @@ -174,10 +177,10 @@ void FlowField::build(const std::shared_ptr &integration_field } // Set the flow field cell to pathable. - flow_cells[idx] = flow_cells[idx] | FLOW_PATHABLE_MASK; + flow_cells[idx] |= FLOW_PATHABLE_MASK; // Set the flow field cell to the direction of the smallest cost. - flow_cells[idx] = flow_cells[idx] | static_cast(direction); + flow_cells[idx] |= static_cast(direction); } } } From 4029276f196bbd49297817cf3fc9529b229229d2 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 31 May 2024 14:23:45 +0200 Subject: [PATCH 33/68] path: Smooth LOS integration for target cells with cost > MIN_COST. --- libopenage/pathfinding/integration_field.cpp | 42 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 38639b89fe..6b88380929 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -97,10 +97,44 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_cost(target_idx) > COST_MIN) { + // Do a preliminary LOS integration wave for targets that have cost > min cost + // This avoids the bresenham's line algorithm calculations + // (which wouldn't return accurate results for blocker == target) + // and makes sure that sorrounding cells that are min cost are considered + // in line-of-sight. + + this->cells[target_idx].cost = cost; + this->cells[target_idx].flags |= INTEGRATE_FOUND_MASK; + // ASDF: Fix display in demo 0 + // this->cells[target_idx].flags |= INTEGRATE_LOS_MASK; + + // Add neighbors to current wave + if (target.se > 0) { + current_wave.push_back(target_idx - this->size); + } + if (target.ne > 0) { + current_wave.push_back(target_idx - 1); + } + if (target.se < static_cast(this->size - 1)) { + current_wave.push_back(target_idx + this->size); + } + if (target.ne < static_cast(this->size - 1)) { + current_wave.push_back(target_idx + 1); + } + + // Increment wave cost as we technically handled the first wave in this block + cost += 1; + } + else { + // Move outwards from the target cell, updating the integration field + current_wave.push_back(target_idx); + } + do { - while (!current_wave.empty()) { + while (not current_wave.empty()) { + // inner loop: handle a wave + auto idx = current_wave.front(); current_wave.pop_front(); @@ -183,7 +217,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr Date: Sat, 1 Jun 2024 04:09:56 +0200 Subject: [PATCH 34/68] path: Start LOS pass from configurable start cells. --- libopenage/pathfinding/integration_field.cpp | 201 ++++++++++++++----- libopenage/pathfinding/integration_field.h | 25 ++- 2 files changed, 174 insertions(+), 52 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 6b88380929..ab1dbb3c0b 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -37,45 +37,178 @@ const integrated_t &IntegrationField::get_cell(size_t idx) const { return this->cells.at(idx); } +std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, + const coord::tile_delta &target) { + ENSURE(cost_field->get_size() == this->get_size(), + "cost field size " + << cost_field->get_size() << "x" << cost_field->get_size() + << " does not match integration field size " + << this->get_size() << "x" << this->get_size()); + + ENSURE(target.ne >= 0 + and target.se >= 0 + and target.ne < static_cast(this->size) + and target.se < static_cast(this->size), + "target cell (" << target.ne << ", " << target.se << ") " + << "is out of bounds for integration field of size " + << this->size << "x" << this->size); + + std::vector start_cells; + integrated_cost_t start_cost = INTEGRATED_COST_START; + + // Target cell index + auto target_idx = target.ne + target.se * this->size; + + this->cells[target_idx].cost = start_cost; + this->cells[target_idx].flags |= INTEGRATE_TARGET_MASK; + + if (cost_field->get_cost(target_idx) > COST_MIN) { + // Do a preliminary LOS integration wave for targets that have cost > min cost + // This avoids the bresenham's line algorithm calculations + // (which wouldn't return accurate results for blocker == target) + // and makes sure that sorrounding cells that are min cost are considered + // in line-of-sight. + + this->cells[target_idx].flags |= INTEGRATE_FOUND_MASK; + // ASDF: Fix display in demo 0 + // this->cells[target_idx].flags |= INTEGRATE_LOS_MASK; + + // Add neighbors to current wave + if (target.se > 0) { + start_cells.push_back(target_idx - this->size); + } + if (target.ne > 0) { + start_cells.push_back(target_idx - 1); + } + if (target.se < static_cast(this->size - 1)) { + start_cells.push_back(target_idx + this->size); + } + if (target.ne < static_cast(this->size - 1)) { + start_cells.push_back(target_idx + 1); + } + + // Increment wave cost as we technically handled the first wave in this block + start_cost += 1; + } + else { + // Move outwards from the target cell, updating the integration field + start_cells.push_back(target_idx); + } + + return this->integrate_los(cost_field, target, start_cost, std::move(start_cells)); +} + std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, sector_id_t other_sector_id, - const std::shared_ptr &portal) { + const std::shared_ptr &portal, + const coord::tile_delta &target) { ENSURE(cost_field->get_size() == this->get_size(), "cost field size " << cost_field->get_size() << "x" << cost_field->get_size() << " does not match integration field size " << this->get_size() << "x" << this->get_size()); - // Integrate the cost of the cells on the exit side (this) of the portal + std::vector wavefront_blocked_portal; + std::vector start_cells; + auto exit_start = portal->get_exit_start(other_sector_id); auto exit_end = portal->get_exit_end(other_sector_id); auto entry_start = portal->get_entry_start(other_sector_id); - auto entry_end = portal->get_entry_end(other_sector_id); auto x_diff = exit_start.ne - entry_start.ne; auto y_diff = exit_start.se - entry_start.se; + // transfer masks for flags from the other side of the portal + // only LOS and wavefront blocked flags are relevant + integrated_flags_t transfer_mask = INTEGRATE_LOS_MASK | INTEGRATE_WAVEFRONT_BLOCKED_MASK; + + // every portal cell is a target cell for (auto y = exit_start.se; y <= exit_end.se; ++y) { for (auto x = exit_start.ne; x <= exit_end.ne; ++x) { - // every portal cell is a target cell + // cell index on the exit side of the portal auto target_idx = x + y * this->size; - auto source_idx = x - x_diff + (y - y_diff) * this->size; + // cell index on the entry side of the portal + auto entry_idx = x - x_diff + (y - y_diff) * this->size; // Set the cost of all target cells to the start value this->cells[target_idx].cost = INTEGRATED_COST_START; - this->cells[target_idx].flags = this->cells[source_idx].flags; + this->cells[target_idx].flags = this->cells[entry_idx].flags & transfer_mask; + + this->cells[target_idx].flags |= INTEGRATE_TARGET_MASK; + + if (not(this->cells[target_idx].flags & transfer_mask)) { + // If neither LOS nor wavefront blocked flags are set for the portal entry, + // the portal exit cell doesn't affect the LOS and we can skip further checks + continue; + } + + // Get the cost of the current cell + auto cell_cost = cost_field->get_cost(target_idx); + if (cell_cost > COST_MIN or this->cells[target_idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { + // cell blocks line of sight + + // set the blocked flag for the cell if it wasn't set already + this->cells[target_idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + wavefront_blocked_portal.push_back(target_idx); + + // set the found flag for the cell, so that the start costs + // are not changed in the main LOS integration + this->cells[target_idx].flags |= INTEGRATE_FOUND_MASK; + + // check each neighbor for a corner + auto corners = this->get_los_corners(cost_field, target, coord::tile_delta(x, y)); + for (auto &corner : corners) { + // draw a line from the corner to the edge of the field + // to get the cells blocked by the corner + auto blocked_cells = this->bresenhams_line(target, corner.first, corner.second); + for (auto &blocked_idx : blocked_cells) { + if (cost_field->get_cost(blocked_idx) > COST_MIN) { + // stop if blocked_idx is not min cost + // because this idx may create a new corner + break; + } + // set the blocked flag for the cell + this->cells[blocked_idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + + // clear los flag if it was set + this->cells[blocked_idx].flags &= ~INTEGRATE_LOS_MASK; + + wavefront_blocked_portal.push_back(blocked_idx); + } + } + continue; + } + + // the cell has the LOS flag and is added to the start cells start_cells.push_back(target_idx); } } - // TODO: Call main LOS integration function + if (start_cells.empty()) { + // Main LOS integration will not enter its loop + // so we can take a shortcut and just return the + // wavefront blocked cells we already found + return wavefront_blocked_portal; + } + + // Call main LOS integration function + auto wavefront_blocked_main = this->integrate_los(cost_field, + target, + INTEGRATED_COST_START, + std::move(start_cells)); + wavefront_blocked_main.insert(wavefront_blocked_main.end(), + wavefront_blocked_portal.begin(), + wavefront_blocked_portal.end()); + return wavefront_blocked_main; } std::vector IntegrationField::integrate_los(const std::shared_ptr &cost_field, - const coord::tile_delta &target) { + const coord::tile_delta &target, + integrated_cost_t start_cost, + std::vector &&start_wave) { ENSURE(cost_field->get_size() == this->get_size(), "cost field size " << cost_field->get_size() << "x" << cost_field->get_size() @@ -85,9 +218,6 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr wavefront_blocked; - // Target cell index - auto target_idx = target.ne + target.se * this->size; - // Cells that still have to be visited by the current wave std::deque current_wave; @@ -95,42 +225,10 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr next_wave; // Cost of the current wave - integrated_cost_t cost = INTEGRATED_COST_START; - - if (cost_field->get_cost(target_idx) > COST_MIN) { - // Do a preliminary LOS integration wave for targets that have cost > min cost - // This avoids the bresenham's line algorithm calculations - // (which wouldn't return accurate results for blocker == target) - // and makes sure that sorrounding cells that are min cost are considered - // in line-of-sight. - - this->cells[target_idx].cost = cost; - this->cells[target_idx].flags |= INTEGRATE_FOUND_MASK; - // ASDF: Fix display in demo 0 - // this->cells[target_idx].flags |= INTEGRATE_LOS_MASK; - - // Add neighbors to current wave - if (target.se > 0) { - current_wave.push_back(target_idx - this->size); - } - if (target.ne > 0) { - current_wave.push_back(target_idx - 1); - } - if (target.se < static_cast(this->size - 1)) { - current_wave.push_back(target_idx + this->size); - } - if (target.ne < static_cast(this->size - 1)) { - current_wave.push_back(target_idx + 1); - } - - // Increment wave cost as we technically handled the first wave in this block - cost += 1; - } - else { - // Move outwards from the target cell, updating the integration field - current_wave.push_back(target_idx); - } + integrated_cost_t wave_cost = start_cost; + // Add the start wave to the current wave + current_wave.insert(current_wave.end(), start_wave.begin(), start_wave.end()); do { while (not current_wave.empty()) { // inner loop: handle a wave @@ -144,7 +242,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // Stop at cells that are blocked by a LOS corner - this->cells[idx].cost = cost - 1 + cost_field->get_cost(idx); + this->cells[idx].cost = wave_cost - 1 + cost_field->get_cost(idx); this->cells[idx].flags |= INTEGRATE_FOUND_MASK; continue; } @@ -165,8 +263,9 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = cost - 1 + cost_field->get_cost(idx); - // TODO: this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + this->cells[idx].cost = wave_cost - 1 + cost_field->get_cost(idx); + // ASDF: Fix display in demo 0 + // this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; } // check each neighbor for a corner @@ -194,7 +293,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = cost; + this->cells[idx].cost = wave_cost; this->cells[idx].flags |= INTEGRATE_LOS_MASK; // Search the neighbors of the current cell @@ -213,7 +312,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr integrate_los(const std::shared_ptr &cost_field, sector_id_t other_sector_id, - const std::shared_ptr &portal); + const std::shared_ptr &portal, + const coord::tile_delta &target); + + /** + * Calculate the line-of-sight integration flags for a target cell. + * + * Returns a list of cells that are flagged as "wavefront blocked". These cells + * can be used as a starting point for the cost integration. + * + * @param cost_field Cost field to integrate. + * @param target Coordinates of the target cell (relative to field origin). + * @param start_cost Integration cost for the start wave. + * @param start_wave Cells used for the first LOS integration wave. The wavefront + * expands outwards from these cells. + * + * @return Cells flagged as "wavefront blocked". + */ + std::vector integrate_los(const std::shared_ptr &cost_field, + const coord::tile_delta &target, + integrated_cost_t start_cost, + std::vector &&start_wave); /** * Calculate the cost integration field starting from a target cell. From 52a6e862f2e8ad053ee4e1c2b27e0c3cb1025a3c Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 04:47:21 +0200 Subject: [PATCH 35/68] path: Make LOS pass optional. --- libopenage/pathfinding/integrator.cpp | 39 ++++++++++++++++++++++----- libopenage/pathfinding/integrator.h | 29 +++++++++++++++----- libopenage/pathfinding/pathfinder.cpp | 5 +++- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/libopenage/pathfinding/integrator.cpp b/libopenage/pathfinding/integrator.cpp index 0802f00ee5..ac551957ce 100644 --- a/libopenage/pathfinding/integrator.cpp +++ b/libopenage/pathfinding/integrator.cpp @@ -11,26 +11,47 @@ namespace openage::path { std::shared_ptr Integrator::integrate(const std::shared_ptr &cost_field, - const coord::tile_delta &target) { + const coord::tile_delta &target, + bool with_los) { auto integration_field = std::make_shared(cost_field->get_size()); - auto wavefront_blocked = integration_field->integrate_los(cost_field, target); - integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); + if (with_los) { + auto wavefront_blocked = integration_field->integrate_los(cost_field, target); + integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); + } + else { + integration_field->integrate_cost(cost_field, target); + } return integration_field; } std::shared_ptr Integrator::integrate(const std::shared_ptr &cost_field, sector_id_t other_sector_id, - const std::shared_ptr &portal) { + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los) { auto cache_key = std::make_pair(portal->get_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + // TODO: LOS pass? + return cached->second.first; } auto integration_field = std::make_shared(cost_field->get_size()); - integration_field->integrate_cost(cost_field, other_sector_id, portal); + + std::vector wavefront_blocked; + if (with_los) { + wavefront_blocked = integration_field->integrate_los(cost_field, other_sector_id, portal, target); + } + + if (wavefront_blocked.empty()) { + integration_field->integrate_cost(cost_field, other_sector_id, portal); + } + else { + integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); + } return integration_field; } @@ -69,14 +90,18 @@ Integrator::build_return_t Integrator::build(const std::shared_ptr &c Integrator::build_return_t Integrator::build(const std::shared_ptr &cost_field, const std::shared_ptr &other_integration_field, sector_id_t other_sector_id, - const std::shared_ptr &portal) { + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los) { auto cache_key = std::make_pair(portal->get_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + // TODO: LOS pass? + return cached->second; } - auto integration_field = this->integrate(cost_field, other_sector_id, portal); + auto integration_field = this->integrate(cost_field, other_sector_id, portal, target, with_los); auto flow_field = this->build(integration_field, other_integration_field, other_sector_id, portal); // Copy the fields to the cache. diff --git a/libopenage/pathfinding/integrator.h b/libopenage/pathfinding/integrator.h index 660f8cbb7c..995c451aba 100644 --- a/libopenage/pathfinding/integrator.h +++ b/libopenage/pathfinding/integrator.h @@ -32,26 +32,38 @@ class Integrator { /** * Integrate the cost field for a target. * + * This should be used for the field containing the target cell. + * The target coordinates must lie within the boundaries of the cost field. + * * @param cost_field Cost field. * @param target Coordinates of the target cell. + * @param with_los true if an LOS pass should be performed, else false. * * @return Integration field. */ std::shared_ptr integrate(const std::shared_ptr &cost_field, - const coord::tile_delta &target); + const coord::tile_delta &target, + bool with_los = true); /** * Integrate the cost field from a portal. * + * This should be used for the fields on the portal path that are not the target field. + * The target coordinates must be relative to the origin of the sector the cost field belongs to. + * * @param cost_field Cost field. * @param other_sector_id Sector ID of the other side of the portal. * @param portal Portal. + * @param target Coordinates of the target cell, relative to the integration field origin. + * @param with_los true if an LOS pass should be performed, else false. * * @return Integration field. */ std::shared_ptr integrate(const std::shared_ptr &cost_field, sector_id_t other_sector_id, - const std::shared_ptr &portal); + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los = true); /** * Build the flow field from an integration field. @@ -85,7 +97,7 @@ class Integrator { * @param cost_field Cost field. * @param target Coordinates of the target cell. * - * @return Flow field. + * @return Integration field and flow field. */ build_return_t build(const std::shared_ptr &cost_field, const coord::tile_delta &target); @@ -97,11 +109,17 @@ class Integrator { * @param other_integration_field Integration field of the other side of the portal. * @param other_sector_id Sector ID of the other side of the portal. * @param portal Portal. + * @param target Coordinates of the target cell, relative to the integration field origin. + * @param with_los true if an LOS pass should be performed, else false. + * + * @return Integration field and flow field. */ build_return_t build(const std::shared_ptr &cost_field, const std::shared_ptr &other_integration_field, sector_id_t other_sector_id, - const std::shared_ptr &portal); + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los = false); private: /** @@ -122,8 +140,7 @@ class Integrator { * when the field is reused. */ std::unordered_map, - std::pair, - std::shared_ptr>, + build_return_t, pair_hash> field_cache; }; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 9da9b4a1c9..e35d7719ae 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -129,10 +129,13 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto next_sector_id = portal->get_exit_sector(prev_sector_id); auto next_sector = grid->get_sector(next_sector_id); + coord::tile_delta target_delta = request.target - next_sector->get_position().to_tile(sector_size); + sector_fields = this->integrator->build(next_sector->get_cost_field(), prev_integration_field, prev_sector_id, - portal); + portal, + target_delta); flow_fields.push_back(std::make_pair(next_sector_id, sector_fields.second)); prev_integration_field = sector_fields.first; From 7d8025c6e18f0ae080a68a5e3577cf5297f20d9c Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 04:53:51 +0200 Subject: [PATCH 36/68] path: Simplify calculations for start/target coordinate deltas. --- libopenage/pathfinding/pathfinder.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index e35d7719ae..75b2591436 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -45,16 +45,14 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto target_sector = grid->get_sector(target_sector_x, target_sector_y); // Integrate the target field - coord::tile_t target_x = request.target.ne % sector_size; - coord::tile_t target_y = request.target.se % sector_size; - auto target = coord::tile_delta{target_x, target_y}; - auto target_integration_field = this->integrator->integrate(target_sector->get_cost_field(), target); + coord::tile_delta target_delta = request.target - target_sector->get_position().to_tile(sector_size); + auto target_integration_field = this->integrator->integrate(target_sector->get_cost_field(), + target_delta); if (target_sector == start_sector) { - auto start_x = request.start.ne % sector_size; - auto start_y = request.start.se % sector_size; + auto start = request.start - start_sector->get_position().to_tile(sector_size); - if (target_integration_field->get_cell(start_x, start_y).cost != INTEGRATED_COST_UNREACHABLE) { + if (target_integration_field->get_cell(start.ne, start.se).cost != INTEGRATED_COST_UNREACHABLE) { // Exit early if the start and target are in the same sector // and are reachable from within the same sector auto flow_field = this->integrator->build(target_integration_field); @@ -83,10 +81,9 @@ const Path Pathfinder::get_path(const PathRequest &request) { } // Check which portals are reachable from the start field - coord::tile_t start_x = request.start.ne % sector_size; - coord::tile_t start_y = request.start.se % sector_size; - auto start = coord::tile_delta{start_x, start_y}; - auto start_integration_field = this->integrator->integrate(start_sector->get_cost_field(), start); + coord::tile_delta start = request.start - start_sector->get_position().to_tile(sector_size); + auto start_integration_field = this->integrator->integrate(start_sector->get_cost_field(), + start); std::unordered_set start_portal_ids; for (auto &portal : start_sector->get_portals()) { @@ -129,7 +126,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto next_sector_id = portal->get_exit_sector(prev_sector_id); auto next_sector = grid->get_sector(next_sector_id); - coord::tile_delta target_delta = request.target - next_sector->get_position().to_tile(sector_size); + target_delta = request.target - next_sector->get_position().to_tile(sector_size); sector_fields = this->integrator->build(next_sector->get_cost_field(), prev_integration_field, From 36eafc475005a9c9b55f9e327e4844b871cca7fd Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 04:54:37 +0200 Subject: [PATCH 37/68] path: Skip LOS pass when checking start sector for exit portals. --- libopenage/pathfinding/pathfinder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 75b2591436..e3a1bd72ec 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -83,7 +83,8 @@ const Path Pathfinder::get_path(const PathRequest &request) { // Check which portals are reachable from the start field coord::tile_delta start = request.start - start_sector->get_position().to_tile(sector_size); auto start_integration_field = this->integrator->integrate(start_sector->get_cost_field(), - start); + start, + false); std::unordered_set start_portal_ids; for (auto &portal : start_sector->get_portals()) { From d328738d6e6fda47c8f104fabfd0d00a15fc8d77 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 05:47:54 +0200 Subject: [PATCH 38/68] path: Exit waypoinnt search if any LOS cell is found. --- libopenage/pathfinding/pathfinder.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index e3a1bd72ec..d2fef7b2b9 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -307,6 +307,8 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(current_x, current_y); @@ -323,20 +325,21 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_position(); - auto cell_pos = coord::tile(sector_pos.ne * sector_size, - sector_pos.se * sector_size) + auto cell_pos = sector_pos.to_tile(sector_size) + coord::tile_delta(current_x, current_y); waypoints.push_back(cell_pos); + reached_target = true; break; } + // ASDF: break if target cell is reached + // check if we need to change direction auto cell_direction = flow_field->get_dir(coord::tile_delta(current_x, current_y)); if (cell_direction != current_direction) { // add the current cell as a waypoint auto sector_pos = sector->get_position(); - auto cell_pos = coord::tile(sector_pos.ne * sector_size, - sector_pos.se * sector_size) + auto cell_pos = sector_pos.to_tile(sector_size) + coord::tile_delta(current_x, current_y); waypoints.push_back(cell_pos); current_direction = cell_direction; @@ -377,6 +380,10 @@ const std::vector Pathfinder::get_waypoints(const std::vector Pathfinder::get_waypoints(const std::vectorget_sector(flow_fields.back().first)->get_position(); - auto target_pos = coord::tile(sector_pos.ne * sector_size, sector_pos.se * sector_size) + auto target_pos = sector_pos.to_tile(sector_size) + coord::tile_delta{target_x, target_y}; waypoints.push_back(target_pos); From 0da61fa2d66fb05a1b365d482938bf6497d2164f Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 06:20:00 +0200 Subject: [PATCH 39/68] path: Calculate flow field directions for LOS cells to allow caching of field. --- libopenage/pathfinding/flow_field.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 2641b343fb..6c51306d4f 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -76,11 +76,6 @@ void FlowField::build(const std::shared_ptr &integration_field if (integrate_cells[idx].flags & INTEGRATE_LOS_MASK) { // Cell is in line of sight flow_cells[idx] |= FLOW_LOS_MASK; - - // we can skip calculating the flow direction as we can - // move straight to the target from this cell - flow_cells[idx] |= FLOW_PATHABLE_MASK; - continue; } if (integrate_cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { From d651de443a163f18972c54babb636ffd5b7b81e6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 06:29:25 +0200 Subject: [PATCH 40/68] path: Transfer flags for LOS/wavefront blocked from an integration field. --- libopenage/pathfinding/flow_field.cpp | 19 +++++++++++++++++++ libopenage/pathfinding/flow_field.h | 19 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 6c51306d4f..3b2dd3e1da 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -266,4 +266,23 @@ void FlowField::reset_dynamic_flags() { log::log(DBG << "Flow field dynamic flags have been reset"); } +void FlowField::transfer_dynamic_flags(const std::shared_ptr &integration_field) { + auto &integrate_cells = integration_field->get_cells(); + auto &flow_cells = this->cells; + + for (size_t idx = 0; idx < integrate_cells.size(); ++idx) { + if (integrate_cells[idx].flags & INTEGRATE_LOS_MASK) { + // Cell is in line of sight + flow_cells[idx] |= FLOW_LOS_MASK; + } + + if (integrate_cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { + // Cell is blocked by a line-of-sight corner + flow_cells[idx] |= FLOW_WAVEFRONT_BLOCKED_MASK; + } + } + + log::log(DBG << "Flow field dynamic flags have been transferred"); +} + } // namespace openage::path diff --git a/libopenage/pathfinding/flow_field.h b/libopenage/pathfinding/flow_field.h index 6d9aeaeb63..5dc91fed96 100644 --- a/libopenage/pathfinding/flow_field.h +++ b/libopenage/pathfinding/flow_field.h @@ -132,8 +132,9 @@ class FlowField { void reset(); /** - * Reset all flags that are dependent on the path target location. These - * flags should be removed when the field is cached and reused for + * Reset all flags that are dependent on the path target location. + * + * These flags should be removed when the field is cached and reused for * other targets. * * Relevant flags are: @@ -142,6 +143,20 @@ class FlowField { */ void reset_dynamic_flags(); + /** + * Transfer dynamic flags from an integration field. + * + * These flags should be transferred when the field is copied from cache. + * Flow field directions are not altered. + * + * Relevant flags are: + * - FLOW_LOS_MASK + * - FLOW_WAVEFRONT_BLOCKED_MASK + * + * @param integration_field Integration field. + */ + void transfer_dynamic_flags(const std::shared_ptr &integration_field); + private: /** * Side length of the field. From da34efce76cce05c8c5cdba8acf8050181a50bbe Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 06:43:25 +0200 Subject: [PATCH 41/68] path: Apply LOS flags when getting fields from cache. --- libopenage/pathfinding/integration_field.cpp | 7 +- libopenage/pathfinding/integration_field.h | 3 + libopenage/pathfinding/integrator.cpp | 70 ++++++++++++++++---- libopenage/pathfinding/integrator.h | 38 ++++++----- libopenage/pathfinding/pathfinder.cpp | 12 ++-- libopenage/pathfinding/tests.cpp | 2 +- 6 files changed, 92 insertions(+), 40 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index ab1dbb3c0b..fb62372d67 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -99,6 +99,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr IntegrationField::integrate_los(const std::shared_ptr &cost_field, + const std::shared_ptr &other, sector_id_t other_sector_id, const std::shared_ptr &portal, const coord::tile_delta &target) { @@ -119,6 +120,8 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_cells(); + // transfer masks for flags from the other side of the portal // only LOS and wavefront blocked flags are relevant integrated_flags_t transfer_mask = INTEGRATE_LOS_MASK | INTEGRATE_WAVEFRONT_BLOCKED_MASK; @@ -134,7 +137,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[target_idx].cost = INTEGRATED_COST_START; - this->cells[target_idx].flags = this->cells[entry_idx].flags & transfer_mask; + this->cells[target_idx].flags = other_cells[entry_idx].flags & transfer_mask; this->cells[target_idx].flags |= INTEGRATE_TARGET_MASK; @@ -429,7 +432,7 @@ void IntegrationField::reset() { } void IntegrationField::reset_dynamic_flags() { - integrated_flags_t mask = 0xFF & ~(INTEGRATE_LOS_MASK | INTEGRATE_WAVEFRONT_BLOCKED_MASK); + integrated_flags_t mask = 0xFF & ~(INTEGRATE_LOS_MASK | INTEGRATE_WAVEFRONT_BLOCKED_MASK | INTEGRATE_FOUND_MASK); for (integrated_t &cell : this->cells) { cell.flags = cell.flags & mask; } diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index fa9e910613..a8564eccd8 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -88,6 +88,7 @@ class IntegrationField { * can be used as a starting point for the cost integration. * * @param cost_field Cost field to integrate. + * @param other Integration field of the other sector. * @param other_sector_id Sector ID of the other integration field. * @param portal Portal connecting the two fields. * @param target Coordinates of the target cell (relative to field origin). @@ -95,6 +96,7 @@ class IntegrationField { * @return Cells flagged as "wavefront blocked". */ std::vector integrate_los(const std::shared_ptr &cost_field, + const std::shared_ptr &other, sector_id_t other_sector_id, const std::shared_ptr &portal, const coord::tile_delta &target); @@ -168,6 +170,7 @@ class IntegrationField { * Relevant flags are: * - INTEGRATE_LOS_MASK * - INTEGRATE_WAVEFRONT_BLOCKED_MASK + * - INTEGRATE_FOUND_MASK */ void reset_dynamic_flags(); diff --git a/libopenage/pathfinding/integrator.cpp b/libopenage/pathfinding/integrator.cpp index ac551957ce..990dab37dd 100644 --- a/libopenage/pathfinding/integrator.cpp +++ b/libopenage/pathfinding/integrator.cpp @@ -27,6 +27,7 @@ std::shared_ptr Integrator::integrate(const std::shared_ptr Integrator::integrate(const std::shared_ptr &cost_field, + const std::shared_ptr &other, sector_id_t other_sector_id, const std::shared_ptr &portal, const coord::tile_delta &target, @@ -34,22 +35,36 @@ std::shared_ptr Integrator::integrate(const std::shared_ptrget_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { - // TODO: LOS pass? + if (with_los) { + // Make a copy of the cached field to avoid modifying the cached field + auto integration_field = std::make_shared(*cached->second.first); + // Only integrate LOS; leave the rest of the field as is + integration_field->integrate_los(cost_field, other, other_sector_id, portal, target); + + return integration_field; + } return cached->second.first; } + // Create a new integration field auto integration_field = std::make_shared(cost_field->get_size()); + // LOS pass std::vector wavefront_blocked; if (with_los) { - wavefront_blocked = integration_field->integrate_los(cost_field, other_sector_id, portal, target); + wavefront_blocked = integration_field->integrate_los(cost_field, other, other_sector_id, portal, target); } + // Cost integration if (wavefront_blocked.empty()) { + // No LOS pass or no blocked cells + // use the portal as the target integration_field->integrate_cost(cost_field, other_sector_id, portal); } else { + // LOS pass was performed and some cells were blocked + // use the blocked cells as the start wave integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); } @@ -66,10 +81,21 @@ std::shared_ptr Integrator::build(const std::shared_ptr Integrator::build(const std::shared_ptr &integration_field, const std::shared_ptr &other, sector_id_t other_sector_id, - const std::shared_ptr &portal) { + const std::shared_ptr &portal, + bool with_los) { auto cache_key = std::make_pair(portal->get_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + if (with_los) { + // Make a copy of the cached flow field + auto flow_field = std::make_shared(*cached->second.second); + + // Transfer the LOS flags to the flow field + flow_field->transfer_dynamic_flags(integration_field); + + return flow_field; + } + return cached->second.second; } @@ -79,36 +105,52 @@ std::shared_ptr Integrator::build(const std::shared_ptr &cost_field, - const coord::tile_delta &target) { +Integrator::get_return_t Integrator::get(const std::shared_ptr &cost_field, + const coord::tile_delta &target) { auto integration_field = this->integrate(cost_field, target); auto flow_field = this->build(integration_field); return std::make_pair(integration_field, flow_field); } -Integrator::build_return_t Integrator::build(const std::shared_ptr &cost_field, - const std::shared_ptr &other_integration_field, - sector_id_t other_sector_id, - const std::shared_ptr &portal, - const coord::tile_delta &target, - bool with_los) { +Integrator::get_return_t Integrator::get(const std::shared_ptr &cost_field, + const std::shared_ptr &other, + sector_id_t other_sector_id, + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los) { auto cache_key = std::make_pair(portal->get_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { - // TODO: LOS pass? + if (with_los) { + // Make a copy of the cached integration field + auto integration_field = std::make_shared(*cached->second.first); + + // Only integrate LOS; leave the rest of the field as is + integration_field->integrate_los(cost_field, other, other_sector_id, portal, target); + + // Make a copy of the cached flow field + auto flow_field = std::make_shared(*cached->second.second); + + // Transfer the LOS flags to the flow field + flow_field->transfer_dynamic_flags(integration_field); + + return std::make_pair(integration_field, flow_field); + } return cached->second; } - auto integration_field = this->integrate(cost_field, other_sector_id, portal, target, with_los); - auto flow_field = this->build(integration_field, other_integration_field, other_sector_id, portal); + auto integration_field = this->integrate(cost_field, other, other_sector_id, portal, target, with_los); + auto flow_field = this->build(integration_field, other, other_sector_id, portal); // Copy the fields to the cache. std::shared_ptr cached_integration_field = std::make_shared(*integration_field); cached_integration_field->reset_dynamic_flags(); + std::shared_ptr cached_flow_field = std::make_shared(*flow_field); cached_flow_field->reset_dynamic_flags(); + this->field_cache[cache_key] = std::make_pair(cached_integration_field, cached_flow_field); return std::make_pair(integration_field, flow_field); diff --git a/libopenage/pathfinding/integrator.h b/libopenage/pathfinding/integrator.h index 995c451aba..c27d3c1eb1 100644 --- a/libopenage/pathfinding/integrator.h +++ b/libopenage/pathfinding/integrator.h @@ -37,7 +37,7 @@ class Integrator { * * @param cost_field Cost field. * @param target Coordinates of the target cell. - * @param with_los true if an LOS pass should be performed, else false. + * @param with_los If true an LOS pass is performed before cost integration. * * @return Integration field. */ @@ -52,14 +52,16 @@ class Integrator { * The target coordinates must be relative to the origin of the sector the cost field belongs to. * * @param cost_field Cost field. + * @param other Integration field of the other side of the portal. * @param other_sector_id Sector ID of the other side of the portal. * @param portal Portal. * @param target Coordinates of the target cell, relative to the integration field origin. - * @param with_los true if an LOS pass should be performed, else false. + * @param with_los If true an LOS pass is performed before cost integration. * * @return Integration field. */ std::shared_ptr integrate(const std::shared_ptr &cost_field, + const std::shared_ptr &other, sector_id_t other_sector_id, const std::shared_ptr &portal, const coord::tile_delta &target, @@ -81,45 +83,47 @@ class Integrator { * @param other Integration field of the other side of the portal. * @param other_sector_id Sector ID of the other side of the portal. * @param portal Portal. + * @param with_los If true LOS flags are calculated if the flow field is in cache. * * @return Flow field. */ std::shared_ptr build(const std::shared_ptr &integration_field, const std::shared_ptr &other, sector_id_t other_sector_id, - const std::shared_ptr &portal); + const std::shared_ptr &portal, + bool with_los = true); - using build_return_t = std::pair, std::shared_ptr>; + using get_return_t = std::pair, std::shared_ptr>; /** - * Build the integration field and flow field for a target. + * Get the integration field and flow field for a target. * * @param cost_field Cost field. * @param target Coordinates of the target cell. * * @return Integration field and flow field. */ - build_return_t build(const std::shared_ptr &cost_field, - const coord::tile_delta &target); + get_return_t get(const std::shared_ptr &cost_field, + const coord::tile_delta &target); /** - * Build the integration field and flow field from a portal. + * Get the integration field and flow field from a portal. * * @param cost_field Cost field. - * @param other_integration_field Integration field of the other side of the portal. + * @param other Integration field of the other side of the portal. * @param other_sector_id Sector ID of the other side of the portal. * @param portal Portal. * @param target Coordinates of the target cell, relative to the integration field origin. - * @param with_los true if an LOS pass should be performed, else false. + * @param with_los If true an LOS pass is performed before cost integration. * * @return Integration field and flow field. */ - build_return_t build(const std::shared_ptr &cost_field, - const std::shared_ptr &other_integration_field, - sector_id_t other_sector_id, - const std::shared_ptr &portal, - const coord::tile_delta &target, - bool with_los = false); + get_return_t get(const std::shared_ptr &cost_field, + const std::shared_ptr &other, + sector_id_t other_sector_id, + const std::shared_ptr &portal, + const coord::tile_delta &target, + bool with_los = true); private: /** @@ -140,7 +144,7 @@ class Integrator { * when the field is reused. */ std::unordered_map, - build_return_t, + get_return_t, pair_hash> field_cache; }; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index d2fef7b2b9..92304c5ffe 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -116,7 +116,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto prev_flow_field = this->integrator->build(prev_integration_field); auto prev_sector_id = target_sector->get_id(); - Integrator::build_return_t sector_fields{prev_integration_field, prev_flow_field}; + Integrator::get_return_t sector_fields{prev_integration_field, prev_flow_field}; std::vector>> flow_fields; flow_fields.reserve(portal_path.size() + 1); @@ -129,11 +129,11 @@ const Path Pathfinder::get_path(const PathRequest &request) { target_delta = request.target - next_sector->get_position().to_tile(sector_size); - sector_fields = this->integrator->build(next_sector->get_cost_field(), - prev_integration_field, - prev_sector_id, - portal, - target_delta); + sector_fields = this->integrator->get(next_sector->get_cost_field(), + prev_integration_field, + prev_sector_id, + portal, + target_delta); flow_fields.push_back(std::make_pair(next_sector_id, sector_fields.second)); prev_integration_field = sector_fields.first; diff --git a/libopenage/pathfinding/tests.cpp b/libopenage/pathfinding/tests.cpp index 12c96cc30e..2d54dd728d 100644 --- a/libopenage/pathfinding/tests.cpp +++ b/libopenage/pathfinding/tests.cpp @@ -85,7 +85,7 @@ void flow_field() { auto integrator = std::make_shared(); // Build the flow field - auto flow_field = integrator->build(cost_field, coord::tile_delta{2, 2}).second; + auto flow_field = integrator->get(cost_field, coord::tile_delta{2, 2}).second; auto ff_cells = flow_field->get_cells(); // The flow field for targeting (2, 2) hould look like this: From e67deef83f9020eb3ed6290fffc6a64691cfd412 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 06:52:31 +0200 Subject: [PATCH 42/68] path: Limit sectors where LOS is transferred between portals. This avoid edge cases where LOS flags would be set when LOS is actually blocked by an obstacle, e.g. a cell in a sector that is not visited. --- libopenage/pathfinding/pathfinder.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 92304c5ffe..5d6a84c5a9 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -122,22 +122,27 @@ const Path Pathfinder::get_path(const PathRequest &request) { flow_fields.reserve(portal_path.size() + 1); flow_fields.push_back(std::make_pair(target_sector->get_id(), sector_fields.second)); + int los_depth = 1; + for (auto &portal : portal_path) { auto prev_sector = grid->get_sector(prev_sector_id); auto next_sector_id = portal->get_exit_sector(prev_sector_id); auto next_sector = grid->get_sector(next_sector_id); target_delta = request.target - next_sector->get_position().to_tile(sector_size); + bool with_los = los_depth > 0; sector_fields = this->integrator->get(next_sector->get_cost_field(), prev_integration_field, prev_sector_id, portal, - target_delta); + target_delta, + with_los); flow_fields.push_back(std::make_pair(next_sector_id, sector_fields.second)); prev_integration_field = sector_fields.first; prev_sector_id = next_sector_id; + los_depth -= 1; } // reverse the flow fields so they are ordered from start to target From 0eba33c874dbf4129987271adfa1ebd413ff92c6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 1 Jun 2024 06:59:55 +0200 Subject: [PATCH 43/68] path: Fix time unit in demo 1. --- libopenage/pathfinding/demo/demo_1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index 3d00d31b0c..f9d7cbc92f 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -96,7 +96,7 @@ void path_demo_1(const util::Path &path) { Path path_result = pathfinder->get_path(path_request); timer.stop(); - log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " ps"); + log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " us"); // Create a renderer to display the grid and path auto qtapp = std::make_shared(); From 7a48ef0474cbeace1b363376166d5ba053b4e34f Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 00:05:29 +0200 Subject: [PATCH 44/68] path: Align bit positions for shared flags of flow/integration cell values. --- etc/gdb_pretty/printers.py | 6 ++++-- libopenage/pathfinding/definitions.h | 8 ++++---- libopenage/pathfinding/types.h | 14 ++++++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index 1a7803619f..de3b3cc1ce 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -333,8 +333,10 @@ def children(self): # Integrated flags INTEGRATED_FLAGS: dict = { - 0x01: 'LOS', - 0x02: 'WAVEFRONT_BLOCKED', + 0x01: 'TARGET', + 0x02: 'FOUND', + 0x20: 'LOS', + 0x40: 'WAVEFRONT_BLOCKED', } diff --git a/libopenage/pathfinding/definitions.h b/libopenage/pathfinding/definitions.h index a54dff576a..7391d9523e 100644 --- a/libopenage/pathfinding/definitions.h +++ b/libopenage/pathfinding/definitions.h @@ -45,22 +45,22 @@ constexpr integrated_cost_t INTEGRATED_COST_UNREACHABLE = std::numeric_limits Date: Sun, 2 Jun 2024 00:09:47 +0200 Subject: [PATCH 45/68] path: Use integration flags in flow field shader. --- .../pathfinding/demo_0_flow_field.frag.glsl | 19 ++++-- .../pathfinding/demo_0_flow_field.vert.glsl | 9 ++- libopenage/pathfinding/demo/demo_0.cpp | 63 ++++++++++++------- libopenage/pathfinding/demo/demo_0.h | 12 ++-- libopenage/pathfinding/pathfinder.cpp | 1 + 5 files changed, 70 insertions(+), 34 deletions(-) diff --git a/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl b/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl index b5ff4042a3..8dd4a2dd85 100644 --- a/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl +++ b/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl @@ -1,24 +1,33 @@ #version 330 -in float v_cost; +/// Flow field value +in float flow_val; + +/// Integration field flags +in float int_val; out vec4 outcol; +int WAVEFRONT_BLOCKED = 0x40; +int LINE_OF_SIGHT = 0x20; +int PATHABLE = 0x10; + void main() { - int cost = int(v_cost); - if (bool(cost & 0x40)) { + int flow_flags = int(flow_val) & 0xF0; + int int_flags = int(int_val); + if (bool(int_flags & WAVEFRONT_BLOCKED)) { // wavefront blocked outcol = vec4(0.9, 0.9, 0.9, 1.0); return; } - if (bool(cost & 0x20)) { + if (bool(flow_flags & LINE_OF_SIGHT)) { // line of sight outcol = vec4(1.0, 1.0, 1.0, 1.0); return; } - if (bool(cost & 0x10)) { + if (bool(flow_flags & PATHABLE)) { // pathable outcol = vec4(0.7, 0.7, 0.7, 1.0); return; diff --git a/assets/test/shaders/pathfinding/demo_0_flow_field.vert.glsl b/assets/test/shaders/pathfinding/demo_0_flow_field.vert.glsl index 2c2190e57c..03900a8daf 100644 --- a/assets/test/shaders/pathfinding/demo_0_flow_field.vert.glsl +++ b/assets/test/shaders/pathfinding/demo_0_flow_field.vert.glsl @@ -1,15 +1,18 @@ #version 330 layout(location=0) in vec3 position; -layout(location=1) in float cost; +layout(location=1) in float flow_cell; +layout(location=2) in float int_cell; uniform mat4 model; uniform mat4 view; uniform mat4 proj; -out float v_cost; +out float flow_val; +out float int_val; void main() { gl_Position = proj * view * model * vec4(position, 1.0); - v_cost = cost; + flow_val = flow_cell; + int_val = int_cell; } diff --git a/libopenage/pathfinding/demo/demo_0.cpp b/libopenage/pathfinding/demo/demo_0.cpp index 8e844d942d..c063445192 100644 --- a/libopenage/pathfinding/demo/demo_0.cpp +++ b/libopenage/pathfinding/demo/demo_0.cpp @@ -111,7 +111,7 @@ void path_demo_0(const util::Path &path) { render_manager->show_integration_field(integration_field); break; case RenderManager0::field_t::FLOW: - render_manager->show_flow_field(flow_field); + render_manager->show_flow_field(flow_field, integration_field); break; } @@ -137,7 +137,7 @@ void path_demo_0(const util::Path &path) { log::log(INFO << "Showing integration field"); } else if (ev.key() == Qt::Key_F3) { // Show flow field - render_manager->show_flow_field(flow_field); + render_manager->show_flow_field(flow_field, integration_field); current_field = RenderManager0::field_t::FLOW; log::log(INFO << "Showing flow field"); } @@ -241,7 +241,8 @@ void RenderManager0::show_integration_field(const std::shared_ptrfield_pass->set_renderables({renderable}); } -void RenderManager0::show_flow_field(const std::shared_ptr &field) { +void RenderManager0::show_flow_field(const std::shared_ptr &flow_field, + const std::shared_ptr &int_field) { Eigen::Matrix4f model = Eigen::Matrix4f::Identity(); auto unifs = this->flow_shader->new_uniform_input( "model", @@ -250,7 +251,7 @@ void RenderManager0::show_flow_field(const std::shared_ptr &fie this->camera->get_view_matrix(), "proj", this->camera->get_projection_matrix()); - auto mesh = get_flow_field_mesh(field, 4); + auto mesh = get_flow_field_mesh(flow_field, int_field, 4); auto geometry = this->renderer->add_mesh_geometry(mesh); renderer::Renderable renderable{ unifs, @@ -720,13 +721,14 @@ renderer::resources::MeshData RenderManager0::get_integration_field_mesh(const s return {std::move(vert_data), std::move(idx_data), info}; } -renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::shared_ptr &field, +renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::shared_ptr &flow_field, + const std::shared_ptr &int_field, size_t resolution) { // increase by 1 in every dimension because to get the vertex length // of each dimension util::Vector2s size{ - field->get_size() * resolution + 1, - field->get_size() * resolution + 1, + flow_field->get_size() * resolution + 1, + flow_field->get_size() * resolution + 1, }; auto vert_distance = 1.0f / resolution; @@ -737,25 +739,39 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha for (int i = 0; i < static_cast(size[0]); ++i) { for (int j = 0; j < static_cast(size[1]); ++j) { // for each vertex, compare the surrounding tiles - std::vector surround{}; + std::vector ff_surround{}; + std::vector int_surround{}; if (j - 1 >= 0 and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, (j - 1) / resolution)); - surround.push_back(cost); + auto ff_cost = flow_field->get_cell((i - 1) / resolution, (j - 1) / resolution); + ff_surround.push_back(ff_cost); + + auto int_flags = int_field->get_cell((i - 1) / resolution, (j - 1) / resolution).flags; + int_surround.push_back(int_flags); } - if (j < static_cast(field->get_size()) and i - 1 >= 0) { - auto cost = field->get_cell(coord::tile_delta((i - 1) / resolution, j / resolution)); - surround.push_back(cost); + if (j < static_cast(flow_field->get_size()) and i - 1 >= 0) { + auto ff_cost = flow_field->get_cell((i - 1) / resolution, j / resolution); + ff_surround.push_back(ff_cost); + + auto int_flags = int_field->get_cell((i - 1) / resolution, j / resolution).flags; + int_surround.push_back(int_flags); } - if (j < static_cast(field->get_size()) and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile_delta(i / resolution, j / resolution)); - surround.push_back(cost); + if (j < static_cast(flow_field->get_size()) and i < static_cast(flow_field->get_size())) { + auto ff_cost = flow_field->get_cell(i / resolution, j / resolution); + ff_surround.push_back(ff_cost); + + auto int_flags = int_field->get_cell(i / resolution, j / resolution).flags; + int_surround.push_back(int_flags); } - if (j - 1 >= 0 and i < static_cast(field->get_size())) { - auto cost = field->get_cell(coord::tile_delta(i / resolution, (j - 1) / resolution)); - surround.push_back(cost); + if (j - 1 >= 0 and i < static_cast(flow_field->get_size())) { + auto ff_cost = flow_field->get_cell(i / resolution, (j - 1) / resolution); + ff_surround.push_back(ff_cost); + + auto int_flags = int_field->get_cell(i / resolution, (j - 1) / resolution).flags; + int_surround.push_back(int_flags); } // use the cost of the most expensive surrounding tile - auto max_cost = *std::max_element(surround.begin(), surround.end()); + auto ff_max_cost = *std::max_element(ff_surround.begin(), ff_surround.end()); + auto int_max_flags = *std::max_element(int_surround.begin(), int_surround.end()); coord::scene3 v{ static_cast(i * vert_distance), @@ -766,7 +782,8 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha verts.push_back(world_v[0]); verts.push_back(world_v[1]); verts.push_back(world_v[2]); - verts.push_back(max_cost); + verts.push_back(ff_max_cost); + verts.push_back(int_max_flags); } } @@ -790,7 +807,9 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha } renderer::resources::VertexInputInfo info{ - {renderer::resources::vertex_input_t::V3F32, renderer::resources::vertex_input_t::F32}, + {renderer::resources::vertex_input_t::V3F32, + renderer::resources::vertex_input_t::F32, + renderer::resources::vertex_input_t::F32}, renderer::resources::vertex_layout_t::AOS, renderer::resources::vertex_primitive_t::TRIANGLES, renderer::resources::index_t::U16}; diff --git a/libopenage/pathfinding/demo/demo_0.h b/libopenage/pathfinding/demo/demo_0.h index 7a07ebfd7e..adf5651971 100644 --- a/libopenage/pathfinding/demo/demo_0.h +++ b/libopenage/pathfinding/demo/demo_0.h @@ -92,9 +92,11 @@ class RenderManager0 { /** * Draw a flow field to the screen. * - * @param field Flow field. + * @param flow_field Flow field. + * @param int_field Integration field. */ - void show_flow_field(const std::shared_ptr &field); + void show_flow_field(const std::shared_ptr &flow_field, + const std::shared_ptr &int_field); /** * Draw the steering vectors of a flow field to the screen. @@ -160,9 +162,11 @@ class RenderManager0 { /** * Create a mesh for the flow field. * - * @param field Flow field to visualize. + * @param flow_field Flow field to visualize. + * @param int_field Integration field. */ - static renderer::resources::MeshData get_flow_field_mesh(const std::shared_ptr &field, + static renderer::resources::MeshData get_flow_field_mesh(const std::shared_ptr &flow_field, + const std::shared_ptr &int_field, size_t resolution = 2); /** diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 5d6a84c5a9..559a761639 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -338,6 +338,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(coord::tile_delta(current_x, current_y)); From 4f843d88bca4a3d20117767566e381d033993685 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 01:57:48 +0200 Subject: [PATCH 46/68] path: Remove FLOW_WAVEFRONT_BLOCKED flag. --- .../pathfinding/demo_0_flow_field.frag.glsl | 4 ++-- etc/gdb_pretty/printers.py | 4 ++-- libopenage/pathfinding/definitions.h | 17 +++++----------- libopenage/pathfinding/demo/demo_0.cpp | 20 ++++++++++++------- libopenage/pathfinding/flow_field.cpp | 12 +---------- libopenage/pathfinding/integration_field.cpp | 6 ++---- libopenage/pathfinding/tests.cpp | 4 ++-- libopenage/pathfinding/types.h | 6 +++--- 8 files changed, 30 insertions(+), 43 deletions(-) diff --git a/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl b/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl index 8dd4a2dd85..b003945619 100644 --- a/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl +++ b/assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl @@ -8,7 +8,7 @@ in float int_val; out vec4 outcol; -int WAVEFRONT_BLOCKED = 0x40; +int WAVEFRONT_BLOCKED = 0x04; int LINE_OF_SIGHT = 0x20; int PATHABLE = 0x10; @@ -21,7 +21,7 @@ void main() { return; } - if (bool(flow_flags & LINE_OF_SIGHT)) { + if (bool(int_flags & LINE_OF_SIGHT)) { // line of sight outcol = vec4(1.0, 1.0, 1.0, 1.0); return; diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index de3b3cc1ce..fdab2f12bc 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -291,7 +291,7 @@ class PathFlowTypePrinter: FLOW_FLAGS: dict = { 0x10: 'PATHABLE', 0x20: 'LOS', - 0x40: 'WAVEFRONT_BLOCKED', + 0x40: 'UNUSED', 0x80: 'UNUSED', } @@ -335,8 +335,8 @@ def children(self): INTEGRATED_FLAGS: dict = { 0x01: 'TARGET', 0x02: 'FOUND', + 0x04: 'WAVEFRONT_BLOCKED', 0x20: 'LOS', - 0x40: 'WAVEFRONT_BLOCKED', } diff --git a/libopenage/pathfinding/definitions.h b/libopenage/pathfinding/definitions.h index 7391d9523e..0dc9594935 100644 --- a/libopenage/pathfinding/definitions.h +++ b/libopenage/pathfinding/definitions.h @@ -47,11 +47,6 @@ constexpr integrated_cost_t INTEGRATED_COST_UNREACHABLE = std::numeric_limits verts{}; auto vert_count = size[0] * size[1]; - verts.reserve(vert_count * 4); + verts.reserve(vert_count * 5); for (int i = 0; i < static_cast(size[0]); ++i) { for (int j = 0; j < static_cast(size[1]); ++j) { // for each vertex, compare the surrounding tiles - std::vector ff_surround{}; - std::vector int_surround{}; + std::vector ff_surround{}; + std::vector int_surround{}; if (j - 1 >= 0 and i - 1 >= 0) { auto ff_cost = flow_field->get_cell((i - 1) / resolution, (j - 1) / resolution); ff_surround.push_back(ff_cost); @@ -769,9 +769,15 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha auto int_flags = int_field->get_cell(i / resolution, (j - 1) / resolution).flags; int_surround.push_back(int_flags); } - // use the cost of the most expensive surrounding tile - auto ff_max_cost = *std::max_element(ff_surround.begin(), ff_surround.end()); - auto int_max_flags = *std::max_element(int_surround.begin(), int_surround.end()); + // combine the flags of the sorrounding tiles + auto ff_max_flags = 0; + for (auto &val : ff_surround) { + ff_max_flags |= val & 0xF0; + } + auto int_max_flags = 0; + for (auto &val : int_surround) { + int_max_flags |= val; + } coord::scene3 v{ static_cast(i * vert_distance), @@ -782,7 +788,7 @@ renderer::resources::MeshData RenderManager0::get_flow_field_mesh(const std::sha verts.push_back(world_v[0]); verts.push_back(world_v[1]); verts.push_back(world_v[2]); - verts.push_back(ff_max_cost); + verts.push_back(ff_max_flags); verts.push_back(int_max_flags); } } diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 3b2dd3e1da..04649bcbe1 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -78,11 +78,6 @@ void FlowField::build(const std::shared_ptr &integration_field flow_cells[idx] |= FLOW_LOS_MASK; } - if (integrate_cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { - // Cell is blocked by a line-of-sight corner - flow_cells[idx] |= FLOW_WAVEFRONT_BLOCKED_MASK; - } - if (integrate_cells[idx].flags & INTEGRATE_TARGET_MASK) { // target cells are pathable flow_cells[idx] |= FLOW_PATHABLE_MASK; @@ -258,7 +253,7 @@ void FlowField::reset() { } void FlowField::reset_dynamic_flags() { - flow_t mask = 0xFF & ~(FLOW_LOS_MASK | FLOW_WAVEFRONT_BLOCKED_MASK); + flow_t mask = 0xFF & ~(FLOW_LOS_MASK); for (flow_t &cell : this->cells) { cell = cell & mask; } @@ -275,11 +270,6 @@ void FlowField::transfer_dynamic_flags(const std::shared_ptr & // Cell is in line of sight flow_cells[idx] |= FLOW_LOS_MASK; } - - if (integrate_cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { - // Cell is blocked by a line-of-sight corner - flow_cells[idx] |= FLOW_WAVEFRONT_BLOCKED_MASK; - } } log::log(DBG << "Flow field dynamic flags have been transferred"); diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index fb62372d67..346b8a1a4e 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -70,8 +70,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[target_idx].flags |= INTEGRATE_FOUND_MASK; - // ASDF: Fix display in demo 0 - // this->cells[target_idx].flags |= INTEGRATE_LOS_MASK; + this->cells[target_idx].flags |= INTEGRATE_LOS_MASK; // Add neighbors to current wave if (target.se > 0) { @@ -267,8 +266,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = wave_cost - 1 + cost_field->get_cost(idx); - // ASDF: Fix display in demo 0 - // this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; } // check each neighbor for a corner diff --git a/libopenage/pathfinding/tests.cpp b/libopenage/pathfinding/tests.cpp index 2d54dd728d..c68ef007f3 100644 --- a/libopenage/pathfinding/tests.cpp +++ b/libopenage/pathfinding/tests.cpp @@ -94,9 +94,9 @@ void flow_field() { // | E | E | N | auto ff_expected = std::vector{ FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), - FLOW_WAVEFRONT_BLOCKED_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), + FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), FLOW_LOS_MASK | FLOW_PATHABLE_MASK, - FLOW_WAVEFRONT_BLOCKED_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), + FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), 0, FLOW_LOS_MASK | FLOW_PATHABLE_MASK, FLOW_LOS_MASK | FLOW_PATHABLE_MASK, diff --git a/libopenage/pathfinding/types.h b/libopenage/pathfinding/types.h index 9a5c3806d6..629f201035 100644 --- a/libopenage/pathfinding/types.h +++ b/libopenage/pathfinding/types.h @@ -55,12 +55,12 @@ struct integrated_t { * * Bit 0-3: Shared flags with the flow field. * - 0: Unused. - * - 1: Wave front blocked flag. + * - 1: Unused. * - 2: Line of sight flag. * - 3: Unused. * Bit 4-7: Integration field specific flags. * - 4: Unused. - * - 5: Unused. + * - 5: Wave front blocked flag. * - 6: LOS found flag. * - 7: Target flag. */ @@ -87,7 +87,7 @@ enum class flow_dir_t : uint8_t { * Flow field cell value. * * Bit 0: Unused. - * Bit 1: Wave front blocked flag. + * Bit 1: Unused. * Bit 2: Line of sight flag. * Bit 3: Pathable flag. * Bits 4-7: flow direction. From 7b7117906eb2f5de99c1c51069097885b666c0cf Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 02:22:23 +0200 Subject: [PATCH 47/68] path: Add FLOW_TARGET flag. --- etc/gdb_pretty/printers.py | 4 ++-- libopenage/pathfinding/definitions.h | 12 +++++++++++- libopenage/pathfinding/flow_field.cpp | 8 +++----- libopenage/pathfinding/types.h | 6 +++--- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index fdab2f12bc..fffce7e63d 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -291,7 +291,7 @@ class PathFlowTypePrinter: FLOW_FLAGS: dict = { 0x10: 'PATHABLE', 0x20: 'LOS', - 0x40: 'UNUSED', + 0x40: 'TARGET', 0x80: 'UNUSED', } @@ -333,10 +333,10 @@ def children(self): # Integrated flags INTEGRATED_FLAGS: dict = { - 0x01: 'TARGET', 0x02: 'FOUND', 0x04: 'WAVEFRONT_BLOCKED', 0x20: 'LOS', + 0x40: 'TARGET', } diff --git a/libopenage/pathfinding/definitions.h b/libopenage/pathfinding/definitions.h index 0dc9594935..0fc8268291 100644 --- a/libopenage/pathfinding/definitions.h +++ b/libopenage/pathfinding/definitions.h @@ -50,7 +50,7 @@ constexpr integrated_flags_t INTEGRATE_LOS_MASK = 0x20; /** * Target flag in an integrated_flags_t value. */ -constexpr integrated_flags_t INTEGRATE_TARGET_MASK = 0x01; +constexpr integrated_flags_t INTEGRATE_TARGET_MASK = 0x40; /** * Found flag in an integrated_flags_t value. @@ -78,6 +78,11 @@ constexpr flow_t FLOW_INIT = 0; */ constexpr flow_t FLOW_DIR_MASK = 0x0F; +/** + * Mask for the flow flag bits in a flow_t value. + */ +constexpr flow_t FLOW_FLAGS_MASK = 0xF0; + /** * Pathable flag in a flow_t value. */ @@ -88,4 +93,9 @@ constexpr flow_t FLOW_PATHABLE_MASK = 0x10; */ constexpr flow_t FLOW_LOS_MASK = 0x20; +/** + * Target flag in a flow_t value. + */ +constexpr flow_t FLOW_TARGET_MASK = 0x40; + } // namespace openage::path diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 04649bcbe1..26b7592da8 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -73,12 +73,10 @@ void FlowField::build(const std::shared_ptr &integration_field continue; } - if (integrate_cells[idx].flags & INTEGRATE_LOS_MASK) { - // Cell is in line of sight - flow_cells[idx] |= FLOW_LOS_MASK; - } + flow_t transfer_flags = integrate_cells[idx].flags & FLOW_FLAGS_MASK; + flow_cells[idx] |= transfer_flags; - if (integrate_cells[idx].flags & INTEGRATE_TARGET_MASK) { + if (flow_cells[idx] & FLOW_TARGET_MASK) { // target cells are pathable flow_cells[idx] |= FLOW_PATHABLE_MASK; diff --git a/libopenage/pathfinding/types.h b/libopenage/pathfinding/types.h index 629f201035..1b48a49654 100644 --- a/libopenage/pathfinding/types.h +++ b/libopenage/pathfinding/types.h @@ -55,14 +55,14 @@ struct integrated_t { * * Bit 0-3: Shared flags with the flow field. * - 0: Unused. - * - 1: Unused. + * - 1: Target flag. * - 2: Line of sight flag. * - 3: Unused. * Bit 4-7: Integration field specific flags. * - 4: Unused. * - 5: Wave front blocked flag. * - 6: LOS found flag. - * - 7: Target flag. + * - 7: Unused. */ integrated_flags_t flags; }; @@ -87,7 +87,7 @@ enum class flow_dir_t : uint8_t { * Flow field cell value. * * Bit 0: Unused. - * Bit 1: Unused. + * Bit 1: Target flag. * Bit 2: Line of sight flag. * Bit 3: Pathable flag. * Bits 4-7: flow direction. From 82ce34ee0ba7ce76726290b47515e0747741e244 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 02:31:22 +0200 Subject: [PATCH 48/68] path: Check ifr target cell has been reached using flow target flag. --- libopenage/pathfinding/pathfinder.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 559a761639..8b4eb45f39 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -312,7 +312,7 @@ const std::vector Pathfinder::get_waypoints(const std::vector Pathfinder::get_waypoints(const std::vectorget_sector(flow_fields.at(i).first); auto flow_field = flow_fields.at(i).second; + auto cell = flow_field->get_cell(current_x, current_y); // navigate the flow field vectors until we reach its edge (or the target) - while (current_x < static_cast(sector_size) - and current_y < static_cast(sector_size) - and current_x >= 0 - and current_y >= 0) { - auto cell = flow_field->get_cell(current_x, current_y); + while (not(cell & FLOW_TARGET_MASK)) { if (cell & FLOW_LOS_MASK) { // check if we reached an LOS cell auto sector_pos = sector->get_position(); auto cell_pos = sector_pos.to_tile(sector_size) + coord::tile_delta(current_x, current_y); waypoints.push_back(cell_pos); - reached_target = true; + los_reached = true; break; } - // ASDF: break if target cell is reached - // idea: target flag for flow field cells - // check if we need to change direction auto cell_direction = flow_field->get_dir(coord::tile_delta(current_x, current_y)); if (cell_direction != current_direction) { @@ -384,9 +378,14 @@ const std::vector Pathfinder::get_waypoints(const std::vector(current_direction)}; } + + // get the next cell + cell = flow_field->get_cell(current_x, current_y); } - if (reached_target) { + if (los_reached or i == flow_fields.size() - 1) { + // exit the loop if we found an LOS cell or reached + // the target cell in the last flow field break; } From c1d5637ddb4e04b89601c05d03ba3da3395809de Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 02:40:08 +0200 Subject: [PATCH 49/68] etc: Add unused placeholders for integration flags. --- etc/gdb_pretty/printers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index fffce7e63d..28bfb5ca22 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -333,10 +333,14 @@ def children(self): # Integrated flags INTEGRATED_FLAGS: dict = { + 0x01: 'UNUSED', 0x02: 'FOUND', 0x04: 'WAVEFRONT_BLOCKED', + 0x08: 'UNUSED', + 0x10: 'UNUSED', 0x20: 'LOS', 0x40: 'TARGET', + 0x80: 'UNUSED', } From 9206916dfc6ac4b23b8833948c7ad11b63182baf Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 03:46:22 +0200 Subject: [PATCH 50/68] path: Optimize performance-critical paths in the code. --- libopenage/pathfinding/integration_field.cpp | 26 ++++++++++++-------- libopenage/pathfinding/pathfinder.cpp | 22 ++++++----------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 346b8a1a4e..4b71f3c586 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -119,6 +119,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_costs(); auto &other_cells = other->get_cells(); // transfer masks for flags from the other side of the portal @@ -147,7 +148,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_cost(target_idx); + auto cell_cost = cost_cells[target_idx]; if (cell_cost > COST_MIN or this->cells[target_idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // cell blocks line of sight @@ -166,8 +167,8 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrbresenhams_line(target, corner.first, corner.second); - for (auto &blocked_idx : blocked_cells) { - if (cost_field->get_cost(blocked_idx) > COST_MIN) { + for (auto blocked_idx : blocked_cells) { + if (cost_cells[blocked_idx] > COST_MIN) { // stop if blocked_idx is not min cost // because this idx may create a new corner break; @@ -229,6 +230,9 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_costs(); + // Add the start wave to the current wave current_wave.insert(current_wave.end(), start_wave.begin(), start_wave.end()); do { @@ -244,7 +248,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // Stop at cells that are blocked by a LOS corner - this->cells[idx].cost = wave_cost - 1 + cost_field->get_cost(idx); + this->cells[idx].cost = wave_cost - 1 + cost_cells[idx]; this->cells[idx].flags |= INTEGRATE_FOUND_MASK; continue; } @@ -257,7 +261,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrsize; // Get the cost of the current cell - auto cell_cost = cost_field->get_cost(idx); + auto cell_cost = cost_cells[idx]; if (cell_cost > COST_MIN) { // cell blocks line of sight @@ -265,7 +269,7 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = wave_cost - 1 + cost_field->get_cost(idx); + this->cells[idx].cost = wave_cost - 1 + cell_cost; this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; } @@ -275,8 +279,8 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrbresenhams_line(target, corner.first, corner.second); - for (auto &blocked_idx : blocked_cells) { - if (cost_field->get_cost(blocked_idx) > COST_MIN) { + for (auto blocked_idx : blocked_cells) { + if (cost_cells[blocked_idx] > COST_MIN) { // stop if blocked_idx is impassable break; } @@ -380,6 +384,8 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie std::vector neighbors; neighbors.reserve(4); + auto &cost_cells = cost_field->get_costs(); + // Move outwards from the wavefront, updating the integration field open_list.insert(open_list.end(), start_cells.begin(), start_cells.end()); while (!open_list.empty()) { @@ -407,9 +413,9 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie } // Update the integration field of the neighboring cells - for (auto &neighbor_idx : neighbors) { + for (auto neighbor_idx : neighbors) { this->update_neighbor(neighbor_idx, - cost_field->get_cost(neighbor_idx), + cost_cells[neighbor_idx], integrated_current, open_list); } diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 8b4eb45f39..ae8a911716 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -309,8 +309,6 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_sector_size(); coord::tile_t start_x = request.start.ne % sector_size; coord::tile_t start_y = request.start.se % sector_size; - coord::tile_t target_x = request.target.ne % sector_size; - coord::tile_t target_y = request.target.se % sector_size; bool los_reached = false; @@ -318,29 +316,26 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(current_x, current_y); for (size_t i = 0; i < flow_fields.size(); ++i) { - auto sector = grid->get_sector(flow_fields.at(i).first); - auto flow_field = flow_fields.at(i).second; + auto sector = grid->get_sector(flow_fields[i].first); + auto sector_pos = sector->get_position().to_tile(sector_size); + auto flow_field = flow_fields[i].second; auto cell = flow_field->get_cell(current_x, current_y); // navigate the flow field vectors until we reach its edge (or the target) while (not(cell & FLOW_TARGET_MASK)) { if (cell & FLOW_LOS_MASK) { // check if we reached an LOS cell - auto sector_pos = sector->get_position(); - auto cell_pos = sector_pos.to_tile(sector_size) - + coord::tile_delta(current_x, current_y); + auto cell_pos = sector_pos + coord::tile_delta(current_x, current_y); waypoints.push_back(cell_pos); los_reached = true; break; } // check if we need to change direction - auto cell_direction = flow_field->get_dir(coord::tile_delta(current_x, current_y)); + auto cell_direction = flow_field->get_dir(current_x, current_y); if (cell_direction != current_direction) { // add the current cell as a waypoint - auto sector_pos = sector->get_position(); - auto cell_pos = sector_pos.to_tile(sector_size) - + coord::tile_delta(current_x, current_y); + auto cell_pos = sector_pos + coord::tile_delta(current_x, current_y); waypoints.push_back(cell_pos); current_direction = cell_direction; } @@ -425,10 +420,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_sector(flow_fields.back().first)->get_position(); - auto target_pos = sector_pos.to_tile(sector_size) - + coord::tile_delta{target_x, target_y}; - waypoints.push_back(target_pos); + waypoints.push_back(request.target); return waypoints; } From 2ac96380b7d4e727553143b029358e71b627c46b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 03:50:49 +0200 Subject: [PATCH 51/68] path: Remopve outdated TODOs. --- libopenage/pathfinding/demo/demo_1.cpp | 1 - libopenage/pathfinding/pathfinder.cpp | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index f9d7cbc92f..cd67e7b79f 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -167,7 +167,6 @@ void path_demo_1(const util::Path &path) { render_manager->create_waypoint_tiles(path_result); // Run the renderer pss to draw the grid and path into a window - // TODO: Make this a while (not window.should_close()) loop render_manager->run(); } diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index ae8a911716..bc1621f217 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -191,8 +191,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req // list of known portals and corresponding node. nodemap_t visited_portals; - // Cost to travel from one portal to another - // TODO: Determine this cost for each portal + // TODO: Compute cost to travel from one portal to another when creating portals // const int distance_cost = 1; // create start nodes From a7415abfa81efed004998422967fbec9c6bfe5e1 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 04:02:53 +0200 Subject: [PATCH 52/68] path: Fix existing unit tests. --- libopenage/pathfinding/tests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libopenage/pathfinding/tests.cpp b/libopenage/pathfinding/tests.cpp index c68ef007f3..5f92496ff8 100644 --- a/libopenage/pathfinding/tests.cpp +++ b/libopenage/pathfinding/tests.cpp @@ -29,7 +29,7 @@ void flow_field() { { auto integration_field = std::make_shared(3); integration_field->integrate_cost(cost_field, coord::tile_delta{2, 2}); - auto int_cells = integration_field->get_cells(); + auto &int_cells = integration_field->get_cells(); // The integration field should look like: // | 4 | 3 | 2 | @@ -60,7 +60,7 @@ void flow_field() { FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH), FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), - FLOW_PATHABLE_MASK | static_cast(flow_dir_t::NORTH), + FLOW_TARGET_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::NORTH), }; // Compare the integration field cells with the expected values @@ -86,7 +86,7 @@ void flow_field() { // Build the flow field auto flow_field = integrator->get(cost_field, coord::tile_delta{2, 2}).second; - auto ff_cells = flow_field->get_cells(); + auto &ff_cells = flow_field->get_cells(); // The flow field for targeting (2, 2) hould look like this: // | E | SE | S | @@ -95,13 +95,13 @@ void flow_field() { auto ff_expected = std::vector{ FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), - FLOW_LOS_MASK | FLOW_PATHABLE_MASK, + FLOW_LOS_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH), FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH_EAST), 0, - FLOW_LOS_MASK | FLOW_PATHABLE_MASK, - FLOW_LOS_MASK | FLOW_PATHABLE_MASK, - FLOW_LOS_MASK | FLOW_PATHABLE_MASK, - FLOW_LOS_MASK | FLOW_PATHABLE_MASK, + FLOW_LOS_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::SOUTH), + FLOW_LOS_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), + FLOW_LOS_MASK | FLOW_PATHABLE_MASK | static_cast(flow_dir_t::EAST), + FLOW_LOS_MASK | FLOW_PATHABLE_MASK | FLOW_TARGET_MASK, }; // Compare the flow field cells with the expected values From 2abbb3db7565e64a1ec0509ad002e451fc9d5a30 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 04:27:26 +0200 Subject: [PATCH 53/68] path: More helpful log messages. --- libopenage/pathfinding/integrator.cpp | 31 +++++++++++++++++++++++++++ libopenage/pathfinding/pathfinder.cpp | 14 ++++++------ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/libopenage/pathfinding/integrator.cpp b/libopenage/pathfinding/integrator.cpp index 990dab37dd..265c5285cc 100644 --- a/libopenage/pathfinding/integrator.cpp +++ b/libopenage/pathfinding/integrator.cpp @@ -2,6 +2,8 @@ #include "integrator.h" +#include "log/log.h" + #include "pathfinding/cost_field.h" #include "pathfinding/flow_field.h" #include "pathfinding/integration_field.h" @@ -15,11 +17,14 @@ std::shared_ptr Integrator::integrate(const std::shared_ptr(cost_field->get_size()); + log::log(DBG << "Integrating cost field for target coord " << target); if (with_los) { + log::log(SPAM << "Performing LOS pass"); auto wavefront_blocked = integration_field->integrate_los(cost_field, target); integration_field->integrate_cost(cost_field, std::move(wavefront_blocked)); } else { + log::log(SPAM << "Skipping LOS pass"); integration_field->integrate_cost(cost_field, target); } @@ -35,7 +40,11 @@ std::shared_ptr Integrator::integrate(const std::shared_ptrget_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + log::log(DBG << "Using cached integration field for portal " << portal->get_id() + << " from sector " << other_sector_id); if (with_los) { + log::log(SPAM << "Performing LOS pass on cached field"); + // Make a copy of the cached field to avoid modifying the cached field auto integration_field = std::make_shared(*cached->second.first); @@ -47,12 +56,16 @@ std::shared_ptr Integrator::integrate(const std::shared_ptrsecond.first; } + log::log(DBG << "Integrating cost field for portal " << portal->get_id() + << " from sector " << other_sector_id); + // Create a new integration field auto integration_field = std::make_shared(cost_field->get_size()); // LOS pass std::vector wavefront_blocked; if (with_los) { + log::log(SPAM << "Performing LOS pass"); wavefront_blocked = integration_field->integrate_los(cost_field, other, other_sector_id, portal, target); } @@ -73,6 +86,8 @@ std::shared_ptr Integrator::integrate(const std::shared_ptr Integrator::build(const std::shared_ptr &integration_field) { auto flow_field = std::make_shared(integration_field->get_size()); + + log::log(DBG << "Building flow field from integration field"); flow_field->build(integration_field); return flow_field; @@ -86,7 +101,11 @@ std::shared_ptr Integrator::build(const std::shared_ptrget_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + log::log(DBG << "Using cached flow field for portal " << portal->get_id() + << " from sector " << other_sector_id); if (with_los) { + log::log(SPAM << "Transferring LOS flags to cached flow field"); + // Make a copy of the cached flow field auto flow_field = std::make_shared(*cached->second.second); @@ -99,6 +118,9 @@ std::shared_ptr Integrator::build(const std::shared_ptrsecond.second; } + log::log(DBG << "Building flow field for portal " << portal->get_id() + << " from sector " << other_sector_id); + auto flow_field = std::make_shared(integration_field->get_size()); flow_field->build(integration_field, other, other_sector_id, portal); @@ -122,13 +144,19 @@ Integrator::get_return_t Integrator::get(const std::shared_ptr &cost_ auto cache_key = std::make_pair(portal->get_id(), other_sector_id); auto cached = this->field_cache.find(cache_key); if (cached != this->field_cache.end()) { + log::log(DBG << "Using cached integration and flow fields for portal " << portal->get_id() + << " from sector " << other_sector_id); if (with_los) { + log::log(SPAM << "Performing LOS pass on cached field"); + // Make a copy of the cached integration field auto integration_field = std::make_shared(*cached->second.first); // Only integrate LOS; leave the rest of the field as is integration_field->integrate_los(cost_field, other, other_sector_id, portal, target); + log::log(SPAM << "Transferring LOS flags to cached flow field"); + // Make a copy of the cached flow field auto flow_field = std::make_shared(*cached->second.second); @@ -144,6 +172,9 @@ Integrator::get_return_t Integrator::get(const std::shared_ptr &cost_ auto integration_field = this->integrate(cost_field, other, other_sector_id, portal, target, with_los); auto flow_field = this->build(integration_field, other, other_sector_id, portal); + log::log(DBG << "Caching integration and flow fields for portal ID: " << portal->get_id() + << ", sector ID: " << other_sector_id); + // Copy the fields to the cache. std::shared_ptr cached_integration_field = std::make_shared(*integration_field); cached_integration_field->reset_dynamic_flags(); diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index bc1621f217..7c063821cb 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -31,7 +31,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { or request.target.se < 0 or request.target.ne >= grid_width or request.target.se >= grid_height) { - log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); log::log(DBG << "Target is out of bounds."); return Path{request.grid_id, PathResult::OUT_OF_BOUNDS, {}}; } @@ -64,7 +64,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { } waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); - log::log(INFO << "Path found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path found (start = " << request.start << "; target = " << request.target << ")"); log::log(DBG << "Path is within the same sector."); return Path{request.grid_id, PathResult::FOUND, waypoints}; } @@ -97,7 +97,7 @@ const Path Pathfinder::get_path(const PathRequest &request) { if (target_portal_ids.empty() or start_portal_ids.empty()) { // Exit early if no portals are reachable from the start or target - log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); log::log(DBG << "No portals are reachable from the start or target."); return Path{request.grid_id, PathResult::NOT_FOUND, {}}; } @@ -157,10 +157,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); if (portal_status == PathResult::NOT_FOUND) { - log::log(INFO << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); } else { - log::log(INFO << "Path found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path found (start = " << request.start << "; target = " << request.target << ")"); } return Path{request.grid_id, portal_status, waypoints}; } @@ -234,7 +234,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req for (auto &node : backtrace) { result.push_back(node->portal); } - log::log(INFO << "Portal path found with " << result.size() << " portal traversals."); + log::log(DBG << "Portal path found with " << result.size() << " portal traversals."); return std::make_pair(PathResult::FOUND, result); } @@ -293,7 +293,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req result.push_back(node->portal); } - log::log(INFO << "Portal path not found."); + log::log(DBG << "Portal path not found."); log::log(DBG << "Closest portal: " << closest_node->portal->get_id()); return std::make_pair(PathResult::NOT_FOUND, result); } From 09e22df6ebe27c21288588fa3f2e853a8f04c150 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 04:49:10 +0200 Subject: [PATCH 54/68] path: Fix waypoint finder exiting one cell too early. --- libopenage/pathfinding/pathfinder.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 7c063821cb..37e049c3e9 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -319,9 +319,11 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_position().to_tile(sector_size); auto flow_field = flow_fields[i].second; - auto cell = flow_field->get_cell(current_x, current_y); // navigate the flow field vectors until we reach its edge (or the target) - while (not(cell & FLOW_TARGET_MASK)) { + flow_t cell; + do { + cell = flow_field->get_cell(current_x, current_y); + if (cell & FLOW_LOS_MASK) { // check if we reached an LOS cell auto cell_pos = sector_pos + coord::tile_delta(current_x, current_y); @@ -372,10 +374,8 @@ const std::vector Pathfinder::get_waypoints(const std::vector(current_direction)}; } - - // get the next cell - cell = flow_field->get_cell(current_x, current_y); } + while (not(cell & FLOW_TARGET_MASK)); if (los_reached or i == flow_fields.size() - 1) { // exit the loop if we found an LOS cell or reached From b651e3697f6bc5bff1f646570508463954c1c45b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 05:10:49 +0200 Subject: [PATCH 55/68] Fix compiler warnings. --- libopenage/gamestate/terrain.cpp | 6 +++--- libopenage/pathfinding/pathfinder.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index 8625c0b727..e7d078f2c5 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -45,7 +45,7 @@ Terrain::Terrain(const util::Vector2s &size, current_offset.ne += chunk_size[0]; // Wrap around to the next row - if (current_offset.ne == size[0]) { + if (current_offset.ne == static_cast(size[0])) { current_offset.ne = 0; current_offset.se += chunk_size[1]; } @@ -61,12 +61,12 @@ Terrain::Terrain(const util::Vector2s &size, } // Check terrain boundaries - if (current_offset.ne > size[0]) { + if (current_offset.ne > static_cast(size[0])) { throw error::Error{ERR << "Width of chunk " << chunk->get_offset() << " exceeds terrain width: " << chunk_size[0] << " (max width: " << size[0] << ")"}; } - else if (current_offset.se > size[1]) { + else if (current_offset.se > static_cast(size[1])) { throw error::Error{ERR << "Height of chunk " << chunk->get_offset() << " exceeds terrain height: " << chunk_size[1] << " (max height: " << size[1] << ")"}; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 37e049c3e9..b734f4a304 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -29,8 +29,8 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto grid_height = grid_size[1] * sector_size; if (request.target.ne < 0 or request.target.se < 0 - or request.target.ne >= grid_width - or request.target.se >= grid_height) { + or request.target.ne >= static_cast(grid_width) + or request.target.se >= static_cast(grid_height)) { log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); log::log(DBG << "Target is out of bounds."); return Path{request.grid_id, PathResult::OUT_OF_BOUNDS, {}}; From 3790ffb5f289d5fc9b7df90a8a11a477b38d66b2 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 15:55:46 +0200 Subject: [PATCH 56/68] path: Vectorize and unroll loops to maximize performance. --- libopenage/pathfinding/integration_field.cpp | 107 +++++++++++-------- libopenage/pathfinding/integration_field.h | 4 +- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 4b71f3c586..6e76542cde 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -222,10 +222,16 @@ std::vector IntegrationField::integrate_los(const std::shared_ptr wavefront_blocked; // Cells that still have to be visited by the current wave - std::deque current_wave; + std::vector current_wave = std::move(start_wave); // Cells that have to be visited in the next wave - std::deque next_wave; + std::vector next_wave; + + // Preallocate ~30% of the field size for the wavefront + // This reduces the number of reallocations on push_back operations + // TODO: Find "optimal" value for reserve + current_wave.reserve(this->size * 3); + next_wave.reserve(this->size * 3); // Cost of the current wave integrated_cost_t wave_cost = start_cost; @@ -233,14 +239,10 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_costs(); - // Add the start wave to the current wave - current_wave.insert(current_wave.end(), start_wave.begin(), start_wave.end()); do { - while (not current_wave.empty()) { + for (size_t i = 0; i < current_wave.size(); ++i) { // inner loop: handle a wave - - auto idx = current_wave.front(); - current_wave.pop_front(); + auto idx = current_wave[i]; if (this->cells[idx].flags & INTEGRATE_FOUND_MASK) { // Skip cells that are already in the line of sight @@ -376,52 +378,65 @@ void IntegrationField::integrate_cost(const std::shared_ptr &cost_fie void IntegrationField::integrate_cost(const std::shared_ptr &cost_field, std::vector &&start_cells) { - // Cells that still have to be visited - // they may be visited multiple times - std::deque open_list; + // Cells that still have to be visited by the current wave + std::vector current_wave = std::move(start_cells); - // Stores neighbors of the current cell - std::vector neighbors; - neighbors.reserve(4); + // Cells that have to be visited in the next wave + std::vector next_wave; + + // Preallocate ~30% of the field size for the wavefront + // This reduces the number of reallocations on push_back operations + // TODO: Find "optimal" value for reserve + current_wave.reserve(this->size * 3); + next_wave.reserve(this->size * 3); + // Get the cost field values auto &cost_cells = cost_field->get_costs(); // Move outwards from the wavefront, updating the integration field - open_list.insert(open_list.end(), start_cells.begin(), start_cells.end()); - while (!open_list.empty()) { - auto idx = open_list.front(); - open_list.pop_front(); + while (not current_wave.empty()) { + for (size_t i = 0; i < current_wave.size(); ++i) { + auto idx = current_wave[i]; - // Get the x and y coordinates of the current cell - auto x = idx % this->size; - auto y = idx / this->size; - - auto integrated_current = this->cells.at(idx).cost; + // Get the x and y coordinates of the current cell + auto x = idx % this->size; + auto y = idx / this->size; - // Get the neighbors of the current cell - if (y > 0) { - neighbors.push_back(idx - this->size); - } - if (x > 0) { - neighbors.push_back(idx - 1); - } - if (y < this->size - 1) { - neighbors.push_back(idx + this->size); - } - if (x < this->size - 1) { - neighbors.push_back(idx + 1); - } + auto integrated_current = this->cells[idx].cost; - // Update the integration field of the neighboring cells - for (auto neighbor_idx : neighbors) { - this->update_neighbor(neighbor_idx, - cost_cells[neighbor_idx], - integrated_current, - open_list); + // Get the neighbors of the current cell + if (y > 0) { + auto neighbor_idx = idx - this->size; + this->update_neighbor(neighbor_idx, + cost_cells[neighbor_idx], + integrated_current, + next_wave); + } + if (x > 0) { + auto neighbor_idx = idx - 1; + this->update_neighbor(neighbor_idx, + cost_cells[neighbor_idx], + integrated_current, + next_wave); + } + if (y < this->size - 1) { + auto neighbor_idx = idx + this->size; + this->update_neighbor(neighbor_idx, + cost_cells[neighbor_idx], + integrated_current, + next_wave); + } + if (x < this->size - 1) { + auto neighbor_idx = idx + 1; + this->update_neighbor(neighbor_idx, + cost_cells[neighbor_idx], + integrated_current, + next_wave); + } } - // Clear the neighbors vector - neighbors.clear(); + current_wave.swap(next_wave); + next_wave.clear(); } } @@ -447,7 +462,7 @@ void IntegrationField::reset_dynamic_flags() { void IntegrationField::update_neighbor(size_t idx, cost_t cell_cost, integrated_cost_t integrated_cost, - std::deque &open_list) { + std::vector &wave) { ENSURE(cell_cost > COST_INIT, "cost field cell value must be non-zero"); // Check if the cell is impassable @@ -462,7 +477,7 @@ void IntegrationField::update_neighbor(size_t idx, // update the cell and add it to the open list this->cells[idx].cost = cost; - open_list.push_back(idx); + wave.push_back(idx); } } diff --git a/libopenage/pathfinding/integration_field.h b/libopenage/pathfinding/integration_field.h index a8564eccd8..c9149cc82f 100644 --- a/libopenage/pathfinding/integration_field.h +++ b/libopenage/pathfinding/integration_field.h @@ -181,14 +181,14 @@ class IntegrationField { * @param idx Index of the neighbor cell that is updated. * @param cell_cost Cost of the neighbor cell from the cost field. * @param integrated_cost Current integrated cost of the updating cell in the integration field. - * @param open_list List of cells to be updated. + * @param wave List of cells that are part of the next wavefront. * * @return New integration value of the cell. */ void update_neighbor(size_t idx, cost_t cell_cost, integrated_cost_t integrated_cost, - std::deque &open_list); + std::vector &wave); /** * Get the LOS corners around a cell. From 91e84a9da004da51f2864bdd4e7b4b56167c2e82 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 17:40:18 +0200 Subject: [PATCH 57/68] path: Speed up flow field access. --- libopenage/pathfinding/flow_field.cpp | 38 +++++++++++++++------------ libopenage/pathfinding/pathfinder.cpp | 6 ++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index 26b7592da8..a3ce56f331 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -68,32 +68,36 @@ void FlowField::build(const std::shared_ptr &integration_field for (size_t x = 0; x < this->size; ++x) { size_t idx = y * this->size + x; - if (integrate_cells[idx].cost == INTEGRATED_COST_UNREACHABLE) { + const auto &integrate_cell = integrate_cells[idx]; + auto &flow_cell = flow_cells[idx]; + + if (integrate_cell.cost == INTEGRATED_COST_UNREACHABLE) { // Cell cannot be used as path continue; } - flow_t transfer_flags = integrate_cells[idx].flags & FLOW_FLAGS_MASK; - flow_cells[idx] |= transfer_flags; + flow_t transfer_flags = integrate_cell.flags & FLOW_FLAGS_MASK; + flow_cell |= transfer_flags; - if (flow_cells[idx] & FLOW_TARGET_MASK) { + if (flow_cell & FLOW_TARGET_MASK) { // target cells are pathable - flow_cells[idx] |= FLOW_PATHABLE_MASK; + flow_cell |= FLOW_PATHABLE_MASK; // they also have a preset flow direction so we can skip here continue; } // Store which of the non-diagonal directions are unreachable. - std::bitset<4> directions_unreachable; + // north == 0x01, east == 0x02, south == 0x04, west == 0x08 + uint8_t directions_unreachable = 0x00; // Find the neighbor with the smallest cost. - flow_dir_t direction = static_cast(flow_cells[idx] & FLOW_DIR_MASK); + flow_dir_t direction = static_cast(flow_cell & FLOW_DIR_MASK); auto smallest_cost = INTEGRATED_COST_UNREACHABLE; if (y > 0) { auto cost = integrate_cells[idx - this->size].cost; if (cost == INTEGRATED_COST_UNREACHABLE) { - directions_unreachable[0] = true; + directions_unreachable |= 0x01; } else if (cost < smallest_cost) { smallest_cost = cost; @@ -103,7 +107,7 @@ void FlowField::build(const std::shared_ptr &integration_field if (x < this->size - 1) { auto cost = integrate_cells[idx + 1].cost; if (cost == INTEGRATED_COST_UNREACHABLE) { - directions_unreachable[1] = true; + directions_unreachable |= 0x02; } else if (cost < smallest_cost) { smallest_cost = cost; @@ -113,7 +117,7 @@ void FlowField::build(const std::shared_ptr &integration_field if (y < this->size - 1) { auto cost = integrate_cells[idx + this->size].cost; if (cost == INTEGRATED_COST_UNREACHABLE) { - directions_unreachable[2] = true; + directions_unreachable |= 0x04; } else if (cost < smallest_cost) { smallest_cost = cost; @@ -123,7 +127,7 @@ void FlowField::build(const std::shared_ptr &integration_field if (x > 0) { auto cost = integrate_cells[idx - 1].cost; if (cost == INTEGRATED_COST_UNREACHABLE) { - directions_unreachable[3] = true; + directions_unreachable |= 0x08; } else if (cost < smallest_cost) { smallest_cost = cost; @@ -132,7 +136,7 @@ void FlowField::build(const std::shared_ptr &integration_field } if (x < this->size - 1 and y > 0 - and not(directions_unreachable[0] and directions_unreachable[1])) { + and not(directions_unreachable & 0x01 and directions_unreachable & 0x02)) { auto cost = integrate_cells[idx - this->size + 1].cost; if (cost < smallest_cost) { smallest_cost = cost; @@ -140,7 +144,7 @@ void FlowField::build(const std::shared_ptr &integration_field } } if (x < this->size - 1 and y < this->size - 1 - and not(directions_unreachable[1] and directions_unreachable[2])) { + and not(directions_unreachable & 0x02 and directions_unreachable & 0x04)) { auto cost = integrate_cells[idx + this->size + 1].cost; if (cost < smallest_cost) { smallest_cost = cost; @@ -148,7 +152,7 @@ void FlowField::build(const std::shared_ptr &integration_field } } if (x > 0 and y < this->size - 1 - and not(directions_unreachable[2] and directions_unreachable[3])) { + and not(directions_unreachable & 0x04 and directions_unreachable & 0x08)) { auto cost = integrate_cells[idx + this->size - 1].cost; if (cost < smallest_cost) { smallest_cost = cost; @@ -156,7 +160,7 @@ void FlowField::build(const std::shared_ptr &integration_field } } if (x > 0 and y > 0 - and not(directions_unreachable[3] and directions_unreachable[0])) { + and not(directions_unreachable & 0x01 and directions_unreachable & 0x08)) { auto cost = integrate_cells[idx - this->size - 1].cost; if (cost < smallest_cost) { smallest_cost = cost; @@ -165,10 +169,10 @@ void FlowField::build(const std::shared_ptr &integration_field } // Set the flow field cell to pathable. - flow_cells[idx] |= FLOW_PATHABLE_MASK; + flow_cell |= FLOW_PATHABLE_MASK; // Set the flow field cell to the direction of the smallest cost. - flow_cells[idx] |= static_cast(direction); + flow_cell |= static_cast(direction); } } } diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index b734f4a304..6f67d254be 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -315,9 +315,9 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(current_x, current_y); for (size_t i = 0; i < flow_fields.size(); ++i) { - auto sector = grid->get_sector(flow_fields[i].first); + auto §or = grid->get_sector(flow_fields[i].first); auto sector_pos = sector->get_position().to_tile(sector_size); - auto flow_field = flow_fields[i].second; + auto &flow_field = flow_fields[i].second; // navigate the flow field vectors until we reach its edge (or the target) flow_t cell; @@ -333,7 +333,7 @@ const std::vector Pathfinder::get_waypoints(const std::vectorget_dir(current_x, current_y); + auto cell_direction = static_cast(cell & FLOW_DIR_MASK); if (cell_direction != current_direction) { // add the current cell as a waypoint auto cell_pos = sector_pos + coord::tile_delta(current_x, current_y); From 156165a4f1f368ce6321423fb54a9893490453c0 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 18:13:36 +0200 Subject: [PATCH 58/68] path: Optimize portal exit node search for speed. --- libopenage/pathfinding/pathfinder.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 6f67d254be..a316a06acd 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -491,22 +491,25 @@ std::vector PortalNode::generate_backtrace() { std::vector PortalNode::get_exits(const nodemap_t &nodes, sector_id_t entry_sector) { - std::vector exits; + auto &exits = this->portal->get_exits(entry_sector); + std::vector exit_nodes; + exit_nodes.reserve(exits.size()); auto exit_sector = this->portal->get_exit_sector(entry_sector); - for (auto &exit : this->portal->get_exits(entry_sector)) { + for (auto &exit : exits) { auto exit_id = exit->get_id(); - if (nodes.contains(exit_id)) { - exits.push_back(nodes.at(exit_id)); + auto exit_node = nodes.find(exit_id); + if (exit_node != nodes.end()) { + exit_nodes.push_back(exit_node->second); } else { - exits.push_back(std::make_shared(exit, - exit_sector, - this->shared_from_this())); + exit_nodes.push_back(std::make_shared(exit, + exit_sector, + this->shared_from_this())); } } - return exits; + return exit_nodes; } From 7eaebae6109ce0d01d71384839427900c676f4d5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jun 2024 18:43:23 +0200 Subject: [PATCH 59/68] path: Optimize access to integration cell in LOS pass. --- libopenage/pathfinding/integration_field.cpp | 36 ++++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/libopenage/pathfinding/integration_field.cpp b/libopenage/pathfinding/integration_field.cpp index 6e76542cde..26134506b8 100644 --- a/libopenage/pathfinding/integration_field.cpp +++ b/libopenage/pathfinding/integration_field.cpp @@ -238,25 +238,27 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrget_costs(); + auto &integrate_cells = this->cells; do { for (size_t i = 0; i < current_wave.size(); ++i) { // inner loop: handle a wave auto idx = current_wave[i]; + auto &cell = integrate_cells[idx]; - if (this->cells[idx].flags & INTEGRATE_FOUND_MASK) { + if (cell.flags & INTEGRATE_FOUND_MASK) { // Skip cells that are already in the line of sight continue; } - else if (this->cells[idx].flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { + else if (cell.flags & INTEGRATE_WAVEFRONT_BLOCKED_MASK) { // Stop at cells that are blocked by a LOS corner - this->cells[idx].cost = wave_cost - 1 + cost_cells[idx]; - this->cells[idx].flags |= INTEGRATE_FOUND_MASK; + cell.cost = wave_cost - 1 + cost_cells[idx]; + cell.flags |= INTEGRATE_FOUND_MASK; continue; } // Add the current cell to the found cells - this->cells[idx].flags |= INTEGRATE_FOUND_MASK; + cell.flags |= INTEGRATE_FOUND_MASK; // Get the x and y coordinates of the current cell auto x = idx % this->size; @@ -271,8 +273,8 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = wave_cost - 1 + cell_cost; - this->cells[idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + cell.cost = wave_cost - 1 + cell_cost; + cell.flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; } // check each neighbor for a corner @@ -287,10 +289,10 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[blocked_idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; + integrate_cells[blocked_idx].flags |= INTEGRATE_WAVEFRONT_BLOCKED_MASK; // clear los flag if it was set - this->cells[blocked_idx].flags &= ~INTEGRATE_LOS_MASK; + integrate_cells[blocked_idx].flags &= ~INTEGRATE_LOS_MASK; wavefront_blocked.push_back(blocked_idx); } @@ -300,21 +302,25 @@ std::vector IntegrationField::integrate_los(const std::shared_ptrcells[idx].cost = wave_cost; - this->cells[idx].flags |= INTEGRATE_LOS_MASK; + cell.cost = wave_cost; + cell.flags |= INTEGRATE_LOS_MASK; // Search the neighbors of the current cell if (y > 0) { - next_wave.push_back(idx - this->size); + auto neighbor_idx = idx - this->size; + next_wave.push_back(neighbor_idx); } if (x > 0) { - next_wave.push_back(idx - 1); + auto neighbor_idx = idx - 1; + next_wave.push_back(neighbor_idx); } if (y < this->size - 1) { - next_wave.push_back(idx + this->size); + auto neighbor_idx = idx + this->size; + next_wave.push_back(neighbor_idx); } if (x < this->size - 1) { - next_wave.push_back(idx + 1); + auto neighbor_idx = idx + 1; + next_wave.push_back(neighbor_idx); } } From 3a63dc69463d4321966da8f46d2bbf80edd4a50d Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 11 Jul 2024 02:03:48 +0200 Subject: [PATCH 60/68] path: Make distinction between cardinal/diagonal checks more clear. --- libopenage/pathfinding/flow_field.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libopenage/pathfinding/flow_field.cpp b/libopenage/pathfinding/flow_field.cpp index a3ce56f331..e582345b48 100644 --- a/libopenage/pathfinding/flow_field.cpp +++ b/libopenage/pathfinding/flow_field.cpp @@ -94,6 +94,8 @@ void FlowField::build(const std::shared_ptr &integration_field // Find the neighbor with the smallest cost. flow_dir_t direction = static_cast(flow_cell & FLOW_DIR_MASK); auto smallest_cost = INTEGRATED_COST_UNREACHABLE; + + // Cardinal directions if (y > 0) { auto cost = integrate_cells[idx - this->size].cost; if (cost == INTEGRATED_COST_UNREACHABLE) { @@ -135,6 +137,7 @@ void FlowField::build(const std::shared_ptr &integration_field } } + // Diagonal directions if (x < this->size - 1 and y > 0 and not(directions_unreachable & 0x01 and directions_unreachable & 0x02)) { auto cost = integrate_cells[idx - this->size + 1].cost; From 498beb32cabaed2e459ffcd868f36b2322529ea2 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 13 Jul 2024 11:57:13 +0200 Subject: [PATCH 61/68] etc: Remove pathfinding TODO from pretty printer. --- etc/gdb_pretty/printers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index 28bfb5ca22..fe6d43e397 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -416,6 +416,5 @@ def children(self): # TODO: curve types -# TODO: pathfinding types # TODO: input event codes # TODO: eigen types https://github.com/dmillard/eigengdb From 966218697f089ba5f5c91b2d8a62248ea492b915 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 27 Jul 2024 22:28:16 +0200 Subject: [PATCH 62/68] path: Check if target cell is impassable. --- libopenage/pathfinding/pathfinder.cpp | 38 +++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index a316a06acd..288f7875df 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -4,6 +4,7 @@ #include "coord/chunk.h" #include "coord/phys.h" +#include "pathfinding/cost_field.h" #include "pathfinding/flow_field.h" #include "pathfinding/grid.h" #include "pathfinding/integration_field.h" @@ -31,8 +32,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { or request.target.se < 0 or request.target.ne >= static_cast(grid_width) or request.target.se >= static_cast(grid_height)) { - log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); - log::log(DBG << "Target is out of bounds."); + log::log(DBG << "Path not found (start = " + << request.start << "; target = " + << request.target << "): " + << "Target is out of bounds."); return Path{request.grid_id, PathResult::OUT_OF_BOUNDS, {}}; } @@ -44,6 +47,16 @@ const Path Pathfinder::get_path(const PathRequest &request) { auto target_sector_y = request.target.se / sector_size; auto target_sector = grid->get_sector(target_sector_x, target_sector_y); + auto target = request.target - target_sector->get_position().to_tile(sector_size); + if (target_sector->get_cost_field()->get_cost(target) == COST_IMPASSABLE) { + // TODO: This may be okay if the target is a building or unit + log::log(DBG << "Path not found (start = " + << request.start << "; target = " + << request.target << "): " + << "Target is impassable."); + return Path{request.grid_id, PathResult::NOT_FOUND, {}}; + } + // Integrate the target field coord::tile_delta target_delta = request.target - target_sector->get_position().to_tile(sector_size); auto target_integration_field = this->integrator->integrate(target_sector->get_cost_field(), @@ -64,8 +77,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { } waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); - log::log(DBG << "Path found (start = " << request.start << "; target = " << request.target << ")"); - log::log(DBG << "Path is within the same sector."); + log::log(DBG << "Path found (start = " + << request.start << "; target = " + << request.target << "): " + << "Path is within the same sector."); return Path{request.grid_id, PathResult::FOUND, waypoints}; } } @@ -97,8 +112,10 @@ const Path Pathfinder::get_path(const PathRequest &request) { if (target_portal_ids.empty() or start_portal_ids.empty()) { // Exit early if no portals are reachable from the start or target - log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); - log::log(DBG << "No portals are reachable from the start or target."); + log::log(DBG << "Path not found (start = " + << request.start << "; target = " + << request.target << "): " + << "No portals are reachable from the start or target."); return Path{request.grid_id, PathResult::NOT_FOUND, {}}; } @@ -157,11 +174,16 @@ const Path Pathfinder::get_path(const PathRequest &request) { waypoints.insert(waypoints.end(), flow_field_waypoints.begin(), flow_field_waypoints.end()); if (portal_status == PathResult::NOT_FOUND) { - log::log(DBG << "Path not found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path not found (start = " + << request.start << "; target = " + << request.target << ")"); } else { - log::log(DBG << "Path found (start = " << request.start << "; target = " << request.target << ")"); + log::log(DBG << "Path found (start = " + << request.start << "; target = " + << request.target << ")"); } + return Path{request.grid_id, portal_status, waypoints}; } From 38d05e6d3ef9b7f283c068c5d3db50e570618327 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 27 Jul 2024 23:00:41 +0200 Subject: [PATCH 63/68] renderer: Update animation frame based on keyframe insertion time. --- libopenage/renderer/stages/world/object.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index d4629f866b..0a707807ca 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -98,7 +98,7 @@ void WorldObject::update_uniforms(const time::time_t &time) { auto angle_degrees = this->angle.get(time).to_float(); // Animation information - auto animation_info = this->animation_info.get(time); + auto [last_update, animation_info] = this->animation_info.frame(time); for (size_t layer_idx = 0; layer_idx < this->layer_uniforms.size(); ++layer_idx) { auto &layer_unifs = this->layer_uniforms.at(layer_idx); @@ -123,7 +123,7 @@ void WorldObject::update_uniforms(const time::time_t &time) { case renderer::resources::display_mode::LOOP: { // ONCE and LOOP are animated based on time auto &timing = layer.get_frame_timing(); - frame_idx = timing->get_frame(time, this->render_entity->get_update_time()); + frame_idx = timing->get_frame(time, last_update); } break; case renderer::resources::display_mode::OFF: default: From 703250569b3d644ad8370b018e18e00afaa2a703 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 27 Jul 2024 23:11:45 +0200 Subject: [PATCH 64/68] gamestate: Do not spawn entities outside of map area. --- libopenage/gamestate/event/spawn_entity.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/libopenage/gamestate/event/spawn_entity.cpp b/libopenage/gamestate/event/spawn_entity.cpp index 9a728a71ef..66f6df9be7 100644 --- a/libopenage/gamestate/event/spawn_entity.cpp +++ b/libopenage/gamestate/event/spawn_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "spawn_entity.h" @@ -19,6 +19,7 @@ #include "gamestate/game_entity.h" #include "gamestate/game_state.h" #include "gamestate/manager.h" +#include "gamestate/map.h" #include "gamestate/types.h" // TODO: Testing @@ -155,6 +156,21 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, const param_map ¶ms) { auto gstate = std::dynamic_pointer_cast(state); + // Check if spawn position is on the map + auto pos = params.get("position", gamestate::WORLD_ORIGIN); + auto map_size = gstate->get_map()->get_size(); + if (not(pos.ne >= 0 + and pos.ne < map_size[0] + and pos.se >= 0 + and pos.se < map_size[1])) { + // Do nothing if the spawn position is not on the map + log::log(DBG << "Entity spawn failed: " + << "Spawn position " << pos + << " is not inside the map area " + << "(map size: " << map_size << ")"); + return; + } + auto nyan_db = gstate->get_db_view(); auto game_entities = nyan_db->get_obj_children_all("engine.util.game_entity.GameEntity"); @@ -183,7 +199,6 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, auto entity_pos = std::dynamic_pointer_cast( entity->get_component(component::component_t::POSITION)); - auto pos = params.get("position", gamestate::WORLD_ORIGIN); entity_pos->set_position(time, pos); entity_pos->set_angle(time, coord::phys_angle_t::from_int(315)); From e999fe4ae8c43695a6376f31b4df63f487be6e26 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 28 Jul 2024 02:17:11 +0200 Subject: [PATCH 65/68] doc: Document field types in flow field pathfinder. --- doc/code/pathfinding/README.md | 5 +- doc/code/pathfinding/field_types.md | 78 ++++++++++++++++++ doc/code/pathfinding/images/cost_field.png | Bin 0 -> 118593 bytes doc/code/pathfinding/images/flow_field.png | Bin 0 -> 173423 bytes .../pathfinding/images/integration_field.png | Bin 0 -> 219179 bytes libopenage/pathfinding/definitions.h | 2 +- 6 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 doc/code/pathfinding/field_types.md create mode 100644 doc/code/pathfinding/images/cost_field.png create mode 100644 doc/code/pathfinding/images/flow_field.png create mode 100644 doc/code/pathfinding/images/integration_field.png diff --git a/doc/code/pathfinding/README.md b/doc/code/pathfinding/README.md index a7ec70d1bd..19d1dc8d19 100644 --- a/doc/code/pathfinding/README.md +++ b/doc/code/pathfinding/README.md @@ -109,13 +109,12 @@ a path request is made, the main influence on performance is the A\* algorithm. a limited number of portals, the A\* search should overall be very cheap. The resulting list of sectors and portals is subsequently used in the low-level flow -field calculations. As a first step, the pathfinder uses its integrator to generate +field calculations. More details can be found in the [field types](field_types.md) document. +As a first step, the pathfinder uses its integrator to generate a flow field for each identified sector. Generation starts with the target sector and ends with the start sector. Flow field results are passed through at the cells of the identified portals to make the flow between sectors seamless. - - In a second step, the pathfinder follows the movement vectors in the flow fields from the start cell to the target cell. Waypoints are created for every direction change, so that game entities can travel in straight lines between them. The list of waypoints diff --git a/doc/code/pathfinding/field_types.md b/doc/code/pathfinding/field_types.md new file mode 100644 index 0000000000..d7251da686 --- /dev/null +++ b/doc/code/pathfinding/field_types.md @@ -0,0 +1,78 @@ +# Field Types + +This document describes the field types used in the flow field pathfinding system. + +Most of the descriptions are based on the [*Crowd Pathfinding and Steering Using Flow Field Tiles*](http://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf) article by Elijah Emerson. + +## Cost Field + +A cost field is a square grid of cells that record the cost of movement on the location +of each cell. Higher cost values indicate that it is less desirable to move through that cell. +The field is usually initialized at the start of the game and persists for the lifetime of +the entire pathfinding grid. During gameplay, individual cell costs may be altered to reflect +changes in the environment. + +Cost values are represented as `uint8_t` (0-255) values. The range of usable cost values +is `1` to `254`. `255` is a special value that represents an impassable cell. `0` is reserved +for initialization and should not be used for flow field calculations. + +![Cost Field](images/cost_field.png) + +- **green**: minimum cost +- **red**: maximum cost +- **black**: impassable cell + +## Integration Field + +The integration field is created from a cost field when a path is requested. For a specific +target cell, the integration field stores the accumulated cost of reaching that cell from +every other cell in the field. + +Integration values are calculated using a wavefront algorithm. The algorithm starts at the +target cell(s) and propagates outward, updating the integration value of each cell it visits. +The integration value is calculated by adding the cost value of the current cell to the lowest +integration value of the 4 cardinal neighbors. The integration value of the target cell(s) is `0`. + +Integration values are represented as `uint16_t` (0-65535) values. The range of usable integration +values is `1` to `65534`. `65535` is a special value that represents an unreachable cell. During +initialization, all cells are set to `65535`. + +An additional refinement step in the form of line-of-sight testing may be performed before the +integration values are calculated. This step flags every cell that is in line of sight of the +target cell. This allows for smoother pathing, as game entities can move in a straight line to +the target cell. The algorithm for this step is described in more detail in section 23.6.2 +of the [*Crowd Pathfinding and Steering Using Flow Field Tiles*](http://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf) article. + +In addition to the integration values, the integration field also stores flags for each cell: + +- `FOUND`: cell has been visited +- `TARGET`: cell is a target cell +- `LOS`: cell is in line of sight of target cell +- `WAVEFRONT_BLOCKED`: cell is blocking line of sight to target cell + +![Integration Field](images/integration_field.png) + +- **green**: lower integration values +- **purple**: higher integration values +- **black**: unreachable cell + +## Flow Field + +Creating the flow field is the final step in the flow field calculation. The field +is created from the integration field. Cells in the flow field store the direction to +the neighbor cell with the lowest *integrated* cost. Thus, directions create a "flow" +towards the target cell. Following the directions from anywhere on the field will lead +to the shortest path to the target cell. + +Flow field values are represented as `uint8_t` values. The 4 least significant bits are used +to store the direction to the neighbor cell with the lowest integrated cost. Therefore, 8 +directions can be represented. The 4 most significant bits are used for flags: +- `PATHABLE`: cell is passable +- `LOS`: cell is in line of sight of target cell +- `TARGET`: cell is a target cell + +![Flow Field](images/flow_field.png) + +- **white**: line of sight +- **bright/dark grey**: passable cells (not in line of sight) +- **black**: impassable cell diff --git a/doc/code/pathfinding/images/cost_field.png b/doc/code/pathfinding/images/cost_field.png new file mode 100644 index 0000000000000000000000000000000000000000..8d820b93faa52071ed4e6b1a71b3bd8d553dae87 GIT binary patch literal 118593 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 %ki6DfoBZi5QYrob*b}o1Rs1u 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�K<#c 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

%Rdmx&F65d5>q8)F0A2L3xyPn19y}E& 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

U9Kc)Cs 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

EEL~#BuQV?; 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�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 Date: Sun, 28 Jul 2024 03:34:49 +0200 Subject: [PATCH 66/68] gamestate: Use nyan terrain definition for modpacks without terrain graphics. --- libopenage/gamestate/map.cpp | 32 ++++++-------- libopenage/gamestate/terrain_factory.cpp | 56 +++++++++++++++++------- libopenage/gamestate/terrain_tile.h | 4 +- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index beb02b5e8a..3d9af584cb 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -27,17 +27,15 @@ Map::Map(const std::shared_ptr &terrain) : util::Vector2s grid_size{this->terrain->get_row_size(), this->terrain->get_column_size()}; for (const auto &chunk : this->terrain->get_chunks()) { for (const auto &tile : chunk->get_tiles()) { - if (tile.terrain != std::nullopt) { - auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); + auto path_costs = api::APITerrain::get_path_costs(tile.terrain); - for (const auto &path_cost : path_costs) { - if (not this->grid_lookup.contains(path_cost.first)) { - auto grid = std::make_shared(grid_idx, grid_size, side_length); - this->pathfinder->add_grid(grid); + for (const auto &path_cost : path_costs) { + if (not this->grid_lookup.contains(path_cost.first)) { + auto grid = std::make_shared(grid_idx, grid_size, side_length); + this->pathfinder->add_grid(grid); - this->grid_lookup.emplace(path_cost.first, grid_idx); - grid_idx += 1; - } + this->grid_lookup.emplace(path_cost.first, grid_idx); + grid_idx += 1; } } } @@ -48,17 +46,15 @@ Map::Map(const std::shared_ptr &terrain) : auto chunk_terrain = this->terrain->get_chunk(chunk_idx); for (size_t tile_idx = 0; tile_idx < chunk_terrain->get_tiles().size(); ++tile_idx) { auto tile = chunk_terrain->get_tile(tile_idx); - if (tile.terrain != std::nullopt) { - auto path_costs = api::APITerrain::get_path_costs(*tile.terrain); + auto path_costs = api::APITerrain::get_path_costs(tile.terrain); - for (const auto &path_cost : path_costs) { - auto grid_id = this->grid_lookup.at(path_cost.first); - auto grid = this->pathfinder->get_grid(grid_id); + for (const auto &path_cost : path_costs) { + auto grid_id = this->grid_lookup.at(path_cost.first); + auto grid = this->pathfinder->get_grid(grid_id); - auto sector = grid->get_sector(chunk_idx); - auto cost_field = sector->get_cost_field(); - cost_field->set_cost(tile_idx, path_cost.second); - } + auto sector = grid->get_sector(chunk_idx); + auto cost_field = sector->get_cost_field(); + cost_field->set_cost(tile_idx, path_cost.second); } } } diff --git a/libopenage/gamestate/terrain_factory.cpp b/libopenage/gamestate/terrain_factory.cpp index 55c70b34cf..3f16a6acf1 100644 --- a/libopenage/gamestate/terrain_factory.cpp +++ b/libopenage/gamestate/terrain_factory.cpp @@ -26,15 +26,30 @@ static const std::vector test_terrain_paths = { "../test/textures/test_terrain2.terrain", }; -static const std::vector aoe1_test_terrain = {}; -static const std::vector de1_test_terrain = {}; +static const std::vector aoe1_test_terrain = { + "aoe1_base.data.terrain.forest.forest.Forest", + "aoe1_base.data.terrain.grass.grass.Grass", + "aoe1_base.data.terrain.dirt.dirt.Dirt", + "aoe1_base.data.terrain.water.water.Water", +}; +static const std::vector de1_test_terrain = { + "aoe1_base.data.terrain.desert.desert.Desert", + "aoe1_base.data.terrain.grass.grass.Grass", + "aoe1_base.data.terrain.dirt.dirt.Dirt", + "aoe1_base.data.terrain.water.water.Water", +}; static const std::vector aoe2_test_terrain = { "aoe2_base.data.terrain.foundation.foundation.Foundation", "aoe2_base.data.terrain.grass.grass.Grass", "aoe2_base.data.terrain.dirt.dirt.Dirt", "aoe2_base.data.terrain.water.water.Water", }; -static const std::vector de2_test_terrain = {}; +static const std::vector de2_test_terrain = { + "de2_base.data.terrain.foundation.foundation.Foundation", + "de2_base.data.terrain.grass.grass.Grass", + "de2_base.data.terrain.dirt.dirt.Dirt", + "de2_base.data.terrain.water.water.Water", +}; static const std::vector hd_test_terrain = { "hd_base.data.terrain.foundation.foundation.Foundation", "hd_base.data.terrain.grass.grass.Grass", @@ -47,10 +62,16 @@ static const std::vector swgb_test_terrain = { "swgb_base.data.terrain.desert0.desert0.Desert0", "swgb_base.data.terrain.water1.water1.Water1", }; -static const std::vector trial_test_terrain = {}; +static const std::vector trial_test_terrain = { + "trial_base.data.terrain.foundation.foundation.Foundation", + "trial_base.data.terrain.grass.grass.Grass", + "trial_base.data.terrain.dirt.dirt.Dirt", + "trial_base.data.terrain.water.water.Water", +}; // TODO: Remove hardcoded test texture references static std::vector test_terrains; // declare static so we only have to do this once +static bool has_graphics = false; void build_test_terrains(const std::shared_ptr &gstate) { auto modpack_ids = gstate->get_mod_manager()->get_load_order(); @@ -59,36 +80,43 @@ void build_test_terrains(const std::shared_ptr &gstate) { test_terrains.insert(test_terrains.end(), aoe1_test_terrain.begin(), aoe1_test_terrain.end()); + has_graphics = false; } else if (modpack_id == "de1_base") { test_terrains.insert(test_terrains.end(), de1_test_terrain.begin(), de1_test_terrain.end()); + has_graphics = false; } else if (modpack_id == "aoe2_base") { test_terrains.insert(test_terrains.end(), aoe2_test_terrain.begin(), aoe2_test_terrain.end()); + has_graphics = true; } else if (modpack_id == "de2_base") { test_terrains.insert(test_terrains.end(), de2_test_terrain.begin(), de2_test_terrain.end()); + has_graphics = false; } else if (modpack_id == "hd_base") { test_terrains.insert(test_terrains.end(), hd_test_terrain.begin(), hd_test_terrain.end()); + has_graphics = true; } else if (modpack_id == "swgb_base") { test_terrains.insert(test_terrains.end(), swgb_test_terrain.begin(), swgb_test_terrain.end()); + has_graphics = true; } else if (modpack_id == "trial_base") { test_terrains.insert(test_terrains.end(), trial_test_terrain.begin(), trial_test_terrain.end()); + has_graphics = true; } } } @@ -185,7 +213,7 @@ std::shared_ptr TerrainFactory::add_chunk(const std::shared_ptr terrain_obj; + nyan::Object terrain_obj; if (test_terrains.empty()) { build_test_terrains(gstate); } @@ -204,20 +232,14 @@ std::shared_ptr TerrainFactory::add_chunk(const std::shared_ptrget_db_view()->get_object(test_terrains.at(terrain_index)); - terrain_info_path = api::APITerrain::get_terrain_path(terrain_obj.value()); - tiles.push_back({terrain_obj, terrain_info_path, terrain_elevation_t::zero()}); - } - test_chunk_index += 1; - } - else { - // use a test texture - if (test_chunk_index >= test_terrain_paths.size()) { - test_chunk_index = 0; - } - terrain_info_path = test_terrain_paths.at(test_chunk_index); + if (has_graphics) { + terrain_info_path = api::APITerrain::get_terrain_path(terrain_obj); + } + else { + terrain_info_path = test_terrain_paths.at(test_chunk_index % test_terrain_paths.size()); + } - for (size_t i = 0; i < size[0] * size[1]; ++i) { tiles.push_back({terrain_obj, terrain_info_path, terrain_elevation_t::zero()}); } diff --git a/libopenage/gamestate/terrain_tile.h b/libopenage/gamestate/terrain_tile.h index 36ae11ddf3..6d6dd99b55 100644 --- a/libopenage/gamestate/terrain_tile.h +++ b/libopenage/gamestate/terrain_tile.h @@ -20,10 +20,8 @@ using terrain_elevation_t = util::FixedPoint; struct TerrainTile { /** * Terrain definition used by this tile. - * - * TODO: Make this non-optional once all modpacks support terrain graphics. */ - std::optional terrain; + nyan::Object terrain; /** * Path to the terrain asset used by this tile. From 347d586e41107d2f22e55c5f0501b4a7d0cafe4a Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 28 Jul 2024 14:46:53 +0200 Subject: [PATCH 67/68] presenter: Let camera look at map center on startup. --- libopenage/presenter/presenter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libopenage/presenter/presenter.cpp b/libopenage/presenter/presenter.cpp index 62435074d4..33c798bcef 100644 --- a/libopenage/presenter/presenter.cpp +++ b/libopenage/presenter/presenter.cpp @@ -116,6 +116,7 @@ void Presenter::init_graphics(bool debug) { // Camera this->camera = std::make_shared(this->renderer, this->window->get_size()); + this->camera->look_at_coord(coord::scene3{10.0, 10.0, 0}); // Center camera on the map this->window->add_resize_callback([this](size_t w, size_t h, double /*scale*/) { this->camera->resize(w, h); }); From eb62b8ce432c3209cabdb4b52c8e2d24bd184c5b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 8 Sep 2024 22:32:05 +0200 Subject: [PATCH 68/68] gamestate: Create grids for all existing path types. --- libopenage/gamestate/game.cpp | 2 +- libopenage/gamestate/map.cpp | 35 ++++++++++++++------------------ libopenage/gamestate/map.h | 5 ++++- libopenage/gamestate/terrain.cpp | 9 ++------ libopenage/gamestate/terrain.h | 13 +++--------- libopenage/pathfinding/grid.h | 4 ++-- 6 files changed, 27 insertions(+), 41 deletions(-) diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp index db50f8cc72..e63499d769 100644 --- a/libopenage/gamestate/game.cpp +++ b/libopenage/gamestate/game.cpp @@ -143,7 +143,7 @@ void Game::generate_terrain(const std::shared_ptr &terrain_facto auto terrain = terrain_factory->add_terrain({20, 20}, {chunk0, chunk1, chunk2, chunk3}); - auto map = std::make_shared(terrain); + auto map = std::make_shared(this->state, terrain); this->state->set_map(map); } diff --git a/libopenage/gamestate/map.cpp b/libopenage/gamestate/map.cpp index 3d9af584cb..1de5b0fe27 100644 --- a/libopenage/gamestate/map.cpp +++ b/libopenage/gamestate/map.cpp @@ -5,6 +5,7 @@ #include #include "gamestate/api/terrain.h" +#include "gamestate/game_state.h" #include "gamestate/terrain.h" #include "gamestate/terrain_chunk.h" #include "pathfinding/cost_field.h" @@ -14,31 +15,25 @@ namespace openage::gamestate { -Map::Map(const std::shared_ptr &terrain) : +Map::Map(const std::shared_ptr &state, + const std::shared_ptr &terrain) : terrain{terrain}, pathfinder{std::make_shared()}, grid_lookup{} { - // Get grid types - // TODO: This should probably not be dependent on the existing tiles, but - // on all defined nyan PathType objects + // Create a grid for each path type + // TODO: This is non-deterministic because of the unordered set. Is this a problem? + auto nyan_db = state->get_db_view(); + std::unordered_set path_types = nyan_db->get_obj_children_all("engine.util.path_type.PathType"); size_t grid_idx = 0; auto chunk_size = this->terrain->get_chunk(0)->get_size(); - auto side_length = std::max(chunk_size[0], chunk_size[0]); - util::Vector2s grid_size{this->terrain->get_row_size(), this->terrain->get_column_size()}; - for (const auto &chunk : this->terrain->get_chunks()) { - for (const auto &tile : chunk->get_tiles()) { - auto path_costs = api::APITerrain::get_path_costs(tile.terrain); - - for (const auto &path_cost : path_costs) { - if (not this->grid_lookup.contains(path_cost.first)) { - auto grid = std::make_shared(grid_idx, grid_size, side_length); - this->pathfinder->add_grid(grid); - - this->grid_lookup.emplace(path_cost.first, grid_idx); - grid_idx += 1; - } - } - } + auto side_length = std::max(chunk_size[0], chunk_size[1]); + auto grid_size = this->terrain->get_chunks_size(); + for (const auto &path_type : path_types) { + auto grid = std::make_shared(grid_idx, grid_size, side_length); + this->pathfinder->add_grid(grid); + + this->grid_lookup.emplace(path_type, grid_idx); + grid_idx += 1; } // Set path costs diff --git a/libopenage/gamestate/map.h b/libopenage/gamestate/map.h index 4033373023..2817f74457 100644 --- a/libopenage/gamestate/map.h +++ b/libopenage/gamestate/map.h @@ -17,6 +17,7 @@ class Pathfinder; } // namespace path namespace gamestate { +class GameState; class Terrain; class Map { @@ -26,9 +27,11 @@ class Map { * * Initializes the pathfinder with the terrain path costs. * + * @param state Game state. * @param terrain Terrain object. */ - Map(const std::shared_ptr &terrain); + Map(const std::shared_ptr &state, + const std::shared_ptr &terrain); ~Map() = default; diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index e7d078f2c5..97c5ef5b51 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -78,14 +78,9 @@ const util::Vector2s &Terrain::get_size() const { return this->size; } -size_t Terrain::get_row_size() const { +const util::Vector2s Terrain::get_chunks_size() const { auto chunk_size = this->chunks[0]->get_size(); - return this->size[0] / chunk_size[0]; -} - -size_t Terrain::get_column_size() const { - auto chunk_size = this->chunks[0]->get_size(); - return this->size[1] / chunk_size[1]; + return {this->size[0] / chunk_size[0], this->size[1] / chunk_size[1]}; } void Terrain::add_chunk(const std::shared_ptr &chunk) { diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h index 1a5db1bf95..d77eaf939c 100644 --- a/libopenage/gamestate/terrain.h +++ b/libopenage/gamestate/terrain.h @@ -52,18 +52,11 @@ class Terrain { const util::Vector2s &get_size() const; /** - * Get the size of a row in the terrain. + * Get the size of the terrain (in chunks). * - * @return Row size (width). + * @return Terrain chunk size (width x height). */ - size_t get_row_size() const; - - /** - * Get the size of a column in the terrain. - * - * @return Column size (height). - */ - size_t get_column_size() const; + const util::Vector2s get_chunks_size() const; /** * Add a chunk to the terrain. diff --git a/libopenage/pathfinding/grid.h b/libopenage/pathfinding/grid.h index 8f34a7643c..8f24743433 100644 --- a/libopenage/pathfinding/grid.h +++ b/libopenage/pathfinding/grid.h @@ -23,7 +23,7 @@ class Grid { * Create a new empty grid of width x height sectors with a specified size. * * @param id ID of the grid. - * @param size Size of the grid (width x height). + * @param size Size of the grid in sectors (width x height). * @param sector_size Side length of each sector. */ Grid(grid_id_t id, @@ -34,7 +34,7 @@ class Grid { * Create a grid of width x height sectors from a list of existing sectors. * * @param id ID of the grid. - * @param size Size of the grid (width x height). + * @param size Size of the grid in sectors (width x height). * @param sectors Existing sectors. */ Grid(grid_id_t id,