terrain_service.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "terrain_service.h"
  2. #include "../systems/building_collision_registry.h"
  3. #include "map_definition.h"
  4. #include "terrain.h"
  5. #include <QVector3D>
  6. #include <algorithm>
  7. #include <cmath>
  8. #include <memory>
  9. #include <optional>
  10. #include <vector>
  11. namespace Game::Map {
  12. auto TerrainService::instance() -> TerrainService & {
  13. static TerrainService s_instance;
  14. return s_instance;
  15. }
  16. void TerrainService::initialize(const MapDefinition &mapDef) {
  17. m_height_map = std::make_unique<TerrainHeightMap>(
  18. mapDef.grid.width, mapDef.grid.height, mapDef.grid.tile_size);
  19. m_height_map->buildFromFeatures(mapDef.terrain);
  20. m_height_map->addRiverSegments(mapDef.rivers);
  21. m_height_map->addBridges(mapDef.bridges);
  22. m_biome_settings = mapDef.biome;
  23. m_height_map->applyBiomeVariation(m_biome_settings);
  24. m_fire_camps = mapDef.firecamps;
  25. m_road_segments = mapDef.roads;
  26. }
  27. void TerrainService::clear() {
  28. m_height_map.reset();
  29. m_biome_settings = BiomeSettings();
  30. m_fire_camps.clear();
  31. m_road_segments.clear();
  32. }
  33. auto TerrainService::get_terrain_height(float world_x,
  34. float world_z) const -> float {
  35. if (!m_height_map) {
  36. return 0.0F;
  37. }
  38. float height = m_height_map->getHeightAt(world_x, world_z);
  39. if (!is_on_bridge(world_x, world_z) && is_point_on_road(world_x, world_z)) {
  40. constexpr float k_road_y_offset = 0.02F;
  41. height += k_road_y_offset;
  42. }
  43. return height;
  44. }
  45. auto TerrainService::get_terrain_height_grid(int grid_x,
  46. int grid_z) const -> float {
  47. if (!m_height_map) {
  48. return 0.0F;
  49. }
  50. return m_height_map->getHeightAtGrid(grid_x, grid_z);
  51. }
  52. auto TerrainService::is_walkable(int grid_x, int grid_z) const -> bool {
  53. if (!m_height_map) {
  54. return true;
  55. }
  56. return m_height_map->is_walkable(grid_x, grid_z);
  57. }
  58. auto TerrainService::is_forbidden(int grid_x, int grid_z) const -> bool {
  59. if (!m_height_map) {
  60. return false;
  61. }
  62. if (!m_height_map->is_walkable(grid_x, grid_z)) {
  63. return true;
  64. }
  65. constexpr float k_half_cell_offset = 0.5F;
  66. const float half_width =
  67. static_cast<float>(m_height_map->getWidth()) * k_half_cell_offset -
  68. k_half_cell_offset;
  69. const float half_height =
  70. static_cast<float>(m_height_map->getHeight()) * k_half_cell_offset -
  71. k_half_cell_offset;
  72. const float tile_size = m_height_map->getTileSize();
  73. const float world_x = (static_cast<float>(grid_x) - half_width) * tile_size;
  74. const float world_z = (static_cast<float>(grid_z) - half_height) * tile_size;
  75. auto &registry = Game::Systems::BuildingCollisionRegistry::instance();
  76. return registry.is_point_in_building(world_x, world_z);
  77. }
  78. auto TerrainService::is_forbidden_world(float world_x,
  79. float world_z) const -> bool {
  80. if (!m_height_map) {
  81. return false;
  82. }
  83. constexpr float k_half_cell_offset = 0.5F;
  84. const float grid_half_width =
  85. static_cast<float>(m_height_map->getWidth()) * k_half_cell_offset -
  86. k_half_cell_offset;
  87. const float grid_half_height =
  88. static_cast<float>(m_height_map->getHeight()) * k_half_cell_offset -
  89. k_half_cell_offset;
  90. const float grid_x = world_x / m_height_map->getTileSize() + grid_half_width;
  91. const float grid_z = world_z / m_height_map->getTileSize() + grid_half_height;
  92. const int grid_x_int = static_cast<int>(std::round(grid_x));
  93. const int grid_z_int = static_cast<int>(std::round(grid_z));
  94. return is_forbidden(grid_x_int, grid_z_int);
  95. }
  96. auto TerrainService::is_hill_entrance(int grid_x, int grid_z) const -> bool {
  97. if (!m_height_map) {
  98. return false;
  99. }
  100. return m_height_map->isHillEntrance(grid_x, grid_z);
  101. }
  102. auto TerrainService::get_terrain_type(int grid_x,
  103. int grid_z) const -> TerrainType {
  104. if (!m_height_map) {
  105. return TerrainType::Flat;
  106. }
  107. return m_height_map->getTerrainType(grid_x, grid_z);
  108. }
  109. void TerrainService::restore_from_serialized(
  110. int width, int height, float tile_size, const std::vector<float> &heights,
  111. const std::vector<TerrainType> &terrain_types,
  112. const std::vector<RiverSegment> &rivers,
  113. const std::vector<RoadSegment> &roads, const std::vector<Bridge> &bridges,
  114. const BiomeSettings &biome) {
  115. m_height_map = std::make_unique<TerrainHeightMap>(width, height, tile_size);
  116. m_height_map->restoreFromData(heights, terrain_types, rivers, bridges);
  117. m_biome_settings = biome;
  118. m_road_segments = roads;
  119. }
  120. auto TerrainService::is_point_on_road(float world_x,
  121. float world_z) const -> bool {
  122. for (const auto &segment : m_road_segments) {
  123. const float dx = segment.end.x() - segment.start.x();
  124. const float dz = segment.end.z() - segment.start.z();
  125. const float segment_length_sq = dx * dx + dz * dz;
  126. if (segment_length_sq < 0.0001F) {
  127. const float dist_x = world_x - segment.start.x();
  128. const float dist_z = world_z - segment.start.z();
  129. const float dist_sq = dist_x * dist_x + dist_z * dist_z;
  130. const float half_width = segment.width * 0.5F;
  131. if (dist_sq <= half_width * half_width) {
  132. return true;
  133. }
  134. continue;
  135. }
  136. const float px = world_x - segment.start.x();
  137. const float pz = world_z - segment.start.z();
  138. float t = (px * dx + pz * dz) / segment_length_sq;
  139. t = std::clamp(t, 0.0F, 1.0F);
  140. const float closest_x = segment.start.x() + t * dx;
  141. const float closest_z = segment.start.z() + t * dz;
  142. const float dist_x = world_x - closest_x;
  143. const float dist_z = world_z - closest_z;
  144. const float dist_sq = dist_x * dist_x + dist_z * dist_z;
  145. const float half_width = segment.width * 0.5F;
  146. if (dist_sq <= half_width * half_width) {
  147. return true;
  148. }
  149. }
  150. return false;
  151. }
  152. auto TerrainService::is_on_bridge(float world_x, float world_z) const -> bool {
  153. if (!m_height_map) {
  154. return false;
  155. }
  156. return m_height_map->isOnBridge(world_x, world_z);
  157. }
  158. auto TerrainService::get_bridge_center_position(
  159. float world_x, float world_z) const -> std::optional<QVector3D> {
  160. if (!m_height_map) {
  161. return std::nullopt;
  162. }
  163. return m_height_map->getBridgeCenterPosition(world_x, world_z);
  164. }
  165. } // namespace Game::Map