terrain.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. #pragma once
  2. #include <QString>
  3. #include <QVector3D>
  4. #include <cstdint>
  5. #include <memory>
  6. #include <optional>
  7. #include <string>
  8. #include <vector>
  9. namespace Game::Map {
  10. enum class TerrainType { Flat, Hill, Mountain, River, Forest };
  11. enum class GroundType {
  12. ForestMud,
  13. GrassDry,
  14. SoilRocky,
  15. AlpineMix,
  16. SoilFertile
  17. };
  18. inline auto ground_type_to_qstring(GroundType type) -> QString {
  19. switch (type) {
  20. case GroundType::ForestMud:
  21. return QStringLiteral("forest_mud");
  22. case GroundType::GrassDry:
  23. return QStringLiteral("grass_dry");
  24. case GroundType::SoilRocky:
  25. return QStringLiteral("soil_rocky");
  26. case GroundType::AlpineMix:
  27. return QStringLiteral("alpine_mix");
  28. case GroundType::SoilFertile:
  29. return QStringLiteral("soil_fertile");
  30. }
  31. return QStringLiteral("forest_mud");
  32. }
  33. inline auto ground_type_to_string(GroundType type) -> std::string {
  34. return ground_type_to_qstring(type).toStdString();
  35. }
  36. inline auto try_parse_ground_type(const QString &value,
  37. GroundType &out) -> bool {
  38. const QString lowered = value.trimmed().toLower();
  39. if (lowered == QStringLiteral("forest_mud")) {
  40. out = GroundType::ForestMud;
  41. return true;
  42. }
  43. if (lowered == QStringLiteral("grass_dry")) {
  44. out = GroundType::GrassDry;
  45. return true;
  46. }
  47. if (lowered == QStringLiteral("soil_rocky")) {
  48. out = GroundType::SoilRocky;
  49. return true;
  50. }
  51. if (lowered == QStringLiteral("alpine_mix")) {
  52. out = GroundType::AlpineMix;
  53. return true;
  54. }
  55. if (lowered == QStringLiteral("soil_fertile")) {
  56. out = GroundType::SoilFertile;
  57. return true;
  58. }
  59. return false;
  60. }
  61. inline auto
  62. ground_type_from_string(const std::string &str) -> std::optional<GroundType> {
  63. GroundType result;
  64. if (try_parse_ground_type(QString::fromStdString(str), result)) {
  65. return result;
  66. }
  67. return std::nullopt;
  68. }
  69. inline auto terrainTypeToQString(TerrainType type) -> QString {
  70. switch (type) {
  71. case TerrainType::Flat:
  72. return QStringLiteral("flat");
  73. case TerrainType::Hill:
  74. return QStringLiteral("hill");
  75. case TerrainType::Mountain:
  76. return QStringLiteral("mountain");
  77. case TerrainType::River:
  78. return QStringLiteral("river");
  79. case TerrainType::Forest:
  80. return QStringLiteral("forest");
  81. }
  82. return QStringLiteral("flat");
  83. }
  84. inline auto terrainTypeToString(TerrainType type) -> std::string {
  85. return terrainTypeToQString(type).toStdString();
  86. }
  87. inline auto tryParseTerrainType(const QString &value,
  88. TerrainType &out) -> bool {
  89. const QString lowered = value.trimmed().toLower();
  90. if (lowered == QStringLiteral("flat")) {
  91. out = TerrainType::Flat;
  92. return true;
  93. }
  94. if (lowered == QStringLiteral("hill")) {
  95. out = TerrainType::Hill;
  96. return true;
  97. }
  98. if (lowered == QStringLiteral("mountain")) {
  99. out = TerrainType::Mountain;
  100. return true;
  101. }
  102. if (lowered == QStringLiteral("river")) {
  103. out = TerrainType::River;
  104. return true;
  105. }
  106. if (lowered == QStringLiteral("forest")) {
  107. out = TerrainType::Forest;
  108. return true;
  109. }
  110. return false;
  111. }
  112. inline auto
  113. terrainTypeFromString(const std::string &str) -> std::optional<TerrainType> {
  114. TerrainType result;
  115. if (tryParseTerrainType(QString::fromStdString(str), result)) {
  116. return result;
  117. }
  118. return std::nullopt;
  119. }
  120. struct BiomeSettings {
  121. GroundType ground_type = GroundType::ForestMud;
  122. QVector3D grass_primary{0.30F, 0.60F, 0.28F};
  123. QVector3D grass_secondary{0.44F, 0.70F, 0.32F};
  124. QVector3D grass_dry{0.72F, 0.66F, 0.48F};
  125. QVector3D soil_color{0.28F, 0.24F, 0.18F};
  126. QVector3D rock_low{0.48F, 0.46F, 0.44F};
  127. QVector3D rock_high{0.68F, 0.69F, 0.73F};
  128. float patch_density = 4.5F;
  129. float patch_jitter = 0.95F;
  130. float background_blade_density = 0.65F;
  131. float blade_height_min = 0.55F;
  132. float blade_height_max = 1.35F;
  133. float blade_width_min = 0.025F;
  134. float blade_width_max = 0.055F;
  135. float sway_strength = 0.25F;
  136. float sway_speed = 1.4F;
  137. float height_noise_amplitude = 0.16F;
  138. float height_noise_frequency = 0.05F;
  139. float terrain_macro_noise_scale = 0.035F;
  140. float terrain_detail_noise_scale = 0.14F;
  141. float terrain_soil_height = 0.6F;
  142. float terrain_soil_sharpness = 3.8F;
  143. float terrain_rock_threshold = 0.42F;
  144. float terrain_rock_sharpness = 3.1F;
  145. float terrain_ambient_boost = 1.08F;
  146. float terrain_rock_detail_strength = 0.35F;
  147. float background_sway_variance = 0.2F;
  148. float background_scatter_radius = 0.35F;
  149. float plant_density = 0.5F;
  150. float spawn_edge_padding = 0.08F;
  151. std::uint32_t seed = 1337U;
  152. bool ground_irregularity_enabled = true;
  153. float irregularity_scale = 0.15F;
  154. float irregularity_amplitude = 0.08F;
  155. float snow_coverage = 0.0F;
  156. float moisture_level = 0.5F;
  157. float crack_intensity = 0.0F;
  158. float rock_exposure = 0.3F;
  159. float grass_saturation = 1.0F;
  160. float soil_roughness = 0.5F;
  161. QVector3D snow_color{0.92F, 0.94F, 0.98F};
  162. };
  163. inline void apply_ground_type_defaults(BiomeSettings &settings,
  164. GroundType ground_type) {
  165. settings.ground_type = ground_type;
  166. switch (ground_type) {
  167. case GroundType::ForestMud:
  168. settings.grass_primary = QVector3D(0.30F, 0.60F, 0.28F);
  169. settings.grass_secondary = QVector3D(0.44F, 0.70F, 0.32F);
  170. settings.grass_dry = QVector3D(0.72F, 0.66F, 0.48F);
  171. settings.soil_color = QVector3D(0.28F, 0.24F, 0.18F);
  172. settings.rock_low = QVector3D(0.48F, 0.46F, 0.44F);
  173. settings.rock_high = QVector3D(0.68F, 0.69F, 0.73F);
  174. settings.terrain_ambient_boost = 1.08F;
  175. settings.terrain_rock_detail_strength = 0.35F;
  176. settings.patch_density = 4.5F;
  177. settings.patch_jitter = 0.95F;
  178. settings.background_blade_density = 0.70F;
  179. settings.blade_height_min = 0.60F;
  180. settings.blade_height_max = 1.40F;
  181. settings.blade_width_min = 0.028F;
  182. settings.blade_width_max = 0.058F;
  183. settings.sway_strength = 0.28F;
  184. settings.sway_speed = 1.3F;
  185. settings.terrain_macro_noise_scale = 0.035F;
  186. settings.terrain_detail_noise_scale = 0.14F;
  187. settings.terrain_soil_height = 0.65F;
  188. settings.terrain_soil_sharpness = 3.5F;
  189. settings.terrain_rock_threshold = 0.48F;
  190. settings.terrain_rock_sharpness = 3.2F;
  191. settings.ground_irregularity_enabled = true;
  192. settings.irregularity_scale = 0.15F;
  193. settings.irregularity_amplitude = 0.09F;
  194. settings.plant_density = 0.60F;
  195. settings.snow_coverage = 0.0F;
  196. settings.moisture_level = 0.70F;
  197. settings.crack_intensity = 0.0F;
  198. settings.rock_exposure = 0.25F;
  199. settings.grass_saturation = 1.05F;
  200. settings.soil_roughness = 0.55F;
  201. settings.snow_color = QVector3D(0.92F, 0.94F, 0.98F);
  202. break;
  203. case GroundType::GrassDry:
  204. settings.grass_primary = QVector3D(0.58F, 0.54F, 0.32F);
  205. settings.grass_secondary = QVector3D(0.65F, 0.60F, 0.38F);
  206. settings.grass_dry = QVector3D(0.78F, 0.72F, 0.45F);
  207. settings.soil_color = QVector3D(0.52F, 0.44F, 0.32F);
  208. settings.rock_low = QVector3D(0.62F, 0.58F, 0.52F);
  209. settings.rock_high = QVector3D(0.78F, 0.75F, 0.70F);
  210. settings.terrain_ambient_boost = 1.18F;
  211. settings.terrain_rock_detail_strength = 0.28F;
  212. settings.patch_density = 2.8F;
  213. settings.patch_jitter = 0.75F;
  214. settings.background_blade_density = 0.35F;
  215. settings.blade_height_min = 0.35F;
  216. settings.blade_height_max = 0.80F;
  217. settings.blade_width_min = 0.018F;
  218. settings.blade_width_max = 0.038F;
  219. settings.sway_strength = 0.15F;
  220. settings.sway_speed = 1.8F;
  221. settings.terrain_macro_noise_scale = 0.028F;
  222. settings.terrain_detail_noise_scale = 0.22F;
  223. settings.terrain_soil_height = 0.50F;
  224. settings.terrain_soil_sharpness = 4.5F;
  225. settings.terrain_rock_threshold = 0.38F;
  226. settings.terrain_rock_sharpness = 2.8F;
  227. settings.ground_irregularity_enabled = true;
  228. settings.irregularity_scale = 0.10F;
  229. settings.irregularity_amplitude = 0.04F;
  230. settings.plant_density = 0.25F;
  231. settings.snow_coverage = 0.0F;
  232. settings.moisture_level = 0.15F;
  233. settings.crack_intensity = 0.65F;
  234. settings.rock_exposure = 0.35F;
  235. settings.grass_saturation = 0.75F;
  236. settings.soil_roughness = 0.72F;
  237. settings.snow_color = QVector3D(0.92F, 0.94F, 0.98F);
  238. break;
  239. case GroundType::SoilRocky:
  240. settings.grass_primary = QVector3D(0.40F, 0.45F, 0.28F);
  241. settings.grass_secondary = QVector3D(0.48F, 0.52F, 0.32F);
  242. settings.grass_dry = QVector3D(0.58F, 0.52F, 0.38F);
  243. settings.soil_color = QVector3D(0.55F, 0.48F, 0.38F);
  244. settings.rock_low = QVector3D(0.52F, 0.50F, 0.46F);
  245. settings.rock_high = QVector3D(0.72F, 0.70F, 0.66F);
  246. settings.terrain_ambient_boost = 1.05F;
  247. settings.terrain_rock_detail_strength = 0.65F;
  248. settings.patch_density = 2.2F;
  249. settings.patch_jitter = 0.60F;
  250. settings.background_blade_density = 0.28F;
  251. settings.blade_height_min = 0.30F;
  252. settings.blade_height_max = 0.70F;
  253. settings.blade_width_min = 0.020F;
  254. settings.blade_width_max = 0.040F;
  255. settings.sway_strength = 0.18F;
  256. settings.sway_speed = 1.5F;
  257. settings.terrain_macro_noise_scale = 0.055F;
  258. settings.terrain_detail_noise_scale = 0.28F;
  259. settings.terrain_soil_height = 0.40F;
  260. settings.terrain_soil_sharpness = 5.0F;
  261. settings.terrain_rock_threshold = 0.28F;
  262. settings.terrain_rock_sharpness = 4.0F;
  263. settings.ground_irregularity_enabled = true;
  264. settings.irregularity_scale = 0.22F;
  265. settings.irregularity_amplitude = 0.14F;
  266. settings.plant_density = 0.18F;
  267. settings.snow_coverage = 0.0F;
  268. settings.moisture_level = 0.35F;
  269. settings.crack_intensity = 0.25F;
  270. settings.rock_exposure = 0.75F;
  271. settings.grass_saturation = 0.85F;
  272. settings.soil_roughness = 0.85F;
  273. settings.snow_color = QVector3D(0.92F, 0.94F, 0.98F);
  274. break;
  275. case GroundType::AlpineMix:
  276. settings.grass_primary = QVector3D(0.32F, 0.40F, 0.30F);
  277. settings.grass_secondary = QVector3D(0.38F, 0.46F, 0.36F);
  278. settings.grass_dry = QVector3D(0.50F, 0.48F, 0.42F);
  279. settings.soil_color = QVector3D(0.42F, 0.40F, 0.38F);
  280. settings.rock_low = QVector3D(0.58F, 0.60F, 0.64F);
  281. settings.rock_high = QVector3D(0.88F, 0.90F, 0.94F);
  282. settings.terrain_ambient_boost = 1.25F;
  283. settings.terrain_rock_detail_strength = 0.52F;
  284. settings.patch_density = 1.8F;
  285. settings.patch_jitter = 0.50F;
  286. settings.background_blade_density = 0.22F;
  287. settings.blade_height_min = 0.20F;
  288. settings.blade_height_max = 0.50F;
  289. settings.blade_width_min = 0.015F;
  290. settings.blade_width_max = 0.032F;
  291. settings.sway_strength = 0.22F;
  292. settings.sway_speed = 2.0F;
  293. settings.terrain_macro_noise_scale = 0.042F;
  294. settings.terrain_detail_noise_scale = 0.18F;
  295. settings.terrain_soil_height = 0.55F;
  296. settings.terrain_soil_sharpness = 3.0F;
  297. settings.terrain_rock_threshold = 0.32F;
  298. settings.terrain_rock_sharpness = 2.5F;
  299. settings.ground_irregularity_enabled = true;
  300. settings.irregularity_scale = 0.18F;
  301. settings.irregularity_amplitude = 0.12F;
  302. settings.plant_density = 0.12F;
  303. settings.snow_coverage = 0.55F;
  304. settings.moisture_level = 0.45F;
  305. settings.crack_intensity = 0.10F;
  306. settings.rock_exposure = 0.60F;
  307. settings.grass_saturation = 0.80F;
  308. settings.soil_roughness = 0.62F;
  309. settings.snow_color = QVector3D(0.94F, 0.96F, 1.0F);
  310. break;
  311. case GroundType::SoilFertile:
  312. settings.grass_primary = QVector3D(0.25F, 0.55F, 0.22F);
  313. settings.grass_secondary = QVector3D(0.35F, 0.65F, 0.30F);
  314. settings.grass_dry = QVector3D(0.52F, 0.48F, 0.32F);
  315. settings.soil_color = QVector3D(0.20F, 0.16F, 0.12F);
  316. settings.rock_low = QVector3D(0.38F, 0.36F, 0.34F);
  317. settings.rock_high = QVector3D(0.52F, 0.50F, 0.48F);
  318. settings.terrain_ambient_boost = 1.02F;
  319. settings.terrain_rock_detail_strength = 0.22F;
  320. settings.patch_density = 5.2F;
  321. settings.patch_jitter = 0.90F;
  322. settings.background_blade_density = 0.80F;
  323. settings.blade_height_min = 0.55F;
  324. settings.blade_height_max = 1.25F;
  325. settings.blade_width_min = 0.030F;
  326. settings.blade_width_max = 0.062F;
  327. settings.sway_strength = 0.32F;
  328. settings.sway_speed = 1.2F;
  329. settings.terrain_macro_noise_scale = 0.025F;
  330. settings.terrain_detail_noise_scale = 0.10F;
  331. settings.terrain_soil_height = 0.75F;
  332. settings.terrain_soil_sharpness = 2.8F;
  333. settings.terrain_rock_threshold = 0.58F;
  334. settings.terrain_rock_sharpness = 2.2F;
  335. settings.ground_irregularity_enabled = true;
  336. settings.irregularity_scale = 0.08F;
  337. settings.irregularity_amplitude = 0.05F;
  338. settings.plant_density = 0.45F;
  339. settings.snow_coverage = 0.0F;
  340. settings.moisture_level = 0.80F;
  341. settings.crack_intensity = 0.0F;
  342. settings.rock_exposure = 0.12F;
  343. settings.grass_saturation = 1.15F;
  344. settings.soil_roughness = 0.42F;
  345. settings.snow_color = QVector3D(0.92F, 0.94F, 0.98F);
  346. break;
  347. }
  348. }
  349. struct TerrainFeature {
  350. TerrainType type;
  351. float center_x{};
  352. float center_z{};
  353. float radius{};
  354. float width{};
  355. float depth{};
  356. float height{};
  357. std::vector<QVector3D> entrances;
  358. float rotationDeg = 0.0F;
  359. };
  360. struct RiverSegment {
  361. QVector3D start;
  362. QVector3D end;
  363. float width = 2.0F;
  364. };
  365. struct RoadSegment {
  366. QVector3D start;
  367. QVector3D end;
  368. float width = 3.0F;
  369. QString style = QStringLiteral("default");
  370. };
  371. struct Bridge {
  372. QVector3D start;
  373. QVector3D end;
  374. float width = 3.0F;
  375. float height = 0.5F;
  376. };
  377. class TerrainHeightMap {
  378. public:
  379. TerrainHeightMap(int width, int height, float tile_size);
  380. void buildFromFeatures(const std::vector<TerrainFeature> &features);
  381. void addRiverSegments(const std::vector<RiverSegment> &riverSegments);
  382. [[nodiscard]] auto getHeightAt(float world_x, float world_z) const -> float;
  383. [[nodiscard]] auto getHeightAtGrid(int grid_x, int grid_z) const -> float;
  384. [[nodiscard]] auto is_walkable(int grid_x, int grid_z) const -> bool;
  385. [[nodiscard]] auto isHillEntrance(int grid_x, int grid_z) const -> bool;
  386. [[nodiscard]] auto getTerrainType(int grid_x,
  387. int grid_z) const -> TerrainType;
  388. [[nodiscard]] auto isRiverOrNearby(int grid_x, int grid_z,
  389. int margin = 1) const -> bool;
  390. [[nodiscard]] auto getWidth() const -> int { return m_width; }
  391. [[nodiscard]] auto getHeight() const -> int { return m_height; }
  392. [[nodiscard]] auto getTileSize() const -> float { return m_tile_size; }
  393. [[nodiscard]] auto getHeightData() const -> const std::vector<float> & {
  394. return m_heights;
  395. }
  396. [[nodiscard]] auto
  397. getTerrainTypes() const -> const std::vector<TerrainType> & {
  398. return m_terrain_types;
  399. }
  400. [[nodiscard]] auto getHillEntrances() const -> const std::vector<bool> & {
  401. return m_hillEntrances;
  402. }
  403. [[nodiscard]] auto
  404. getRiverSegments() const -> const std::vector<RiverSegment> & {
  405. return m_riverSegments;
  406. }
  407. void addBridges(const std::vector<Bridge> &bridges);
  408. [[nodiscard]] auto getBridges() const -> const std::vector<Bridge> & {
  409. return m_bridges;
  410. }
  411. [[nodiscard]] auto isOnBridge(float world_x, float world_z) const -> bool;
  412. [[nodiscard]] auto getBridgeCenterPosition(float world_x, float world_z) const
  413. -> std::optional<QVector3D>;
  414. [[nodiscard]] auto getBridgeDeckHeight(float world_x, float world_z) const
  415. -> std::optional<float>;
  416. void applyBiomeVariation(const BiomeSettings &settings);
  417. void restoreFromData(const std::vector<float> &heights,
  418. const std::vector<TerrainType> &terrain_types,
  419. const std::vector<RiverSegment> &rivers,
  420. const std::vector<Bridge> &bridges);
  421. private:
  422. int m_width;
  423. int m_height;
  424. float m_tile_size;
  425. std::vector<float> m_heights;
  426. std::vector<TerrainType> m_terrain_types;
  427. std::vector<bool> m_hillEntrances;
  428. std::vector<bool> m_hillWalkable;
  429. std::vector<RiverSegment> m_riverSegments;
  430. std::vector<Bridge> m_bridges;
  431. std::vector<bool> m_onBridge;
  432. std::vector<QVector3D> m_bridgeCenters;
  433. [[nodiscard]] auto indexAt(int x, int z) const -> int;
  434. [[nodiscard]] auto inBounds(int x, int z) const -> bool;
  435. void precomputeBridgeData();
  436. [[nodiscard]] static auto
  437. calculateFeatureHeight(const TerrainFeature &feature, float world_x,
  438. float world_z) -> float;
  439. };
  440. } // namespace Game::Map