plant_renderer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. #include "plant_renderer.h"
  2. #include "../../game/map/visibility_service.h"
  3. #include "../../game/systems/building_collision_registry.h"
  4. #include "../gl/buffer.h"
  5. #include "../scene_renderer.h"
  6. #include <QVector2D>
  7. #include <algorithm>
  8. #include <array>
  9. #include <cmath>
  10. #include <optional>
  11. namespace {
  12. using std::uint32_t;
  13. inline uint32_t hashCoords(int x, int z, uint32_t salt = 0u) {
  14. uint32_t ux = static_cast<uint32_t>(x * 73856093);
  15. uint32_t uz = static_cast<uint32_t>(z * 19349663);
  16. return ux ^ uz ^ (salt * 83492791u);
  17. }
  18. inline float rand01(uint32_t &state) {
  19. state = state * 1664525u + 1013904223u;
  20. return static_cast<float>((state >> 8) & 0xFFFFFF) /
  21. static_cast<float>(0xFFFFFF);
  22. }
  23. inline float remap(float value, float minOut, float maxOut) {
  24. return minOut + (maxOut - minOut) * value;
  25. }
  26. inline float hashTo01(uint32_t h) {
  27. h ^= h >> 17;
  28. h *= 0xed5ad4bbu;
  29. h ^= h >> 11;
  30. h *= 0xac4c1b51u;
  31. h ^= h >> 15;
  32. h *= 0x31848babu;
  33. h ^= h >> 14;
  34. return (h & 0x00FFFFFFu) / float(0x01000000);
  35. }
  36. inline float valueNoise(float x, float z, uint32_t salt = 0u) {
  37. int x0 = int(std::floor(x)), z0 = int(std::floor(z));
  38. int x1 = x0 + 1, z1 = z0 + 1;
  39. float tx = x - float(x0), tz = z - float(z0);
  40. float n00 = hashTo01(hashCoords(x0, z0, salt));
  41. float n10 = hashTo01(hashCoords(x1, z0, salt));
  42. float n01 = hashTo01(hashCoords(x0, z1, salt));
  43. float n11 = hashTo01(hashCoords(x1, z1, salt));
  44. float nx0 = n00 * (1 - tx) + n10 * tx;
  45. float nx1 = n01 * (1 - tx) + n11 * tx;
  46. return nx0 * (1 - tz) + nx1 * tz;
  47. }
  48. } // namespace
  49. namespace Render::GL {
  50. PlantRenderer::PlantRenderer() = default;
  51. PlantRenderer::~PlantRenderer() = default;
  52. void PlantRenderer::configure(const Game::Map::TerrainHeightMap &heightMap,
  53. const Game::Map::BiomeSettings &biomeSettings) {
  54. m_width = heightMap.getWidth();
  55. m_height = heightMap.getHeight();
  56. m_tileSize = heightMap.getTileSize();
  57. m_heightData = heightMap.getHeightData();
  58. m_terrainTypes = heightMap.getTerrainTypes();
  59. m_biomeSettings = biomeSettings;
  60. m_noiseSeed = biomeSettings.seed;
  61. m_plantInstances.clear();
  62. m_plantInstanceBuffer.reset();
  63. m_plantInstanceCount = 0;
  64. m_plantInstancesDirty = false;
  65. m_plantParams.lightDirection = QVector3D(0.35f, 0.8f, 0.45f);
  66. m_plantParams.time = 0.0f;
  67. m_plantParams.windStrength = m_biomeSettings.swayStrength;
  68. m_plantParams.windSpeed = m_biomeSettings.swaySpeed;
  69. generatePlantInstances();
  70. }
  71. void PlantRenderer::submit(Renderer &renderer, ResourceManager *resources) {
  72. (void)resources;
  73. m_plantInstanceCount = static_cast<uint32_t>(m_plantInstances.size());
  74. if (m_plantInstanceCount > 0) {
  75. if (!m_plantInstanceBuffer) {
  76. m_plantInstanceBuffer = std::make_unique<Buffer>(Buffer::Type::Vertex);
  77. }
  78. if (m_plantInstancesDirty && m_plantInstanceBuffer) {
  79. m_plantInstanceBuffer->setData(m_plantInstances, Buffer::Usage::Static);
  80. m_plantInstancesDirty = false;
  81. }
  82. } else {
  83. m_plantInstanceBuffer.reset();
  84. return;
  85. }
  86. auto &visibility = Game::Map::VisibilityService::instance();
  87. const bool useVisibility = visibility.isInitialized();
  88. if (useVisibility) {
  89. std::vector<PlantInstanceGpu> visibleInstances;
  90. visibleInstances.reserve(m_plantInstances.size());
  91. for (const auto &instance : m_plantInstances) {
  92. float worldX = instance.posScale.x();
  93. float worldZ = instance.posScale.z();
  94. if (visibility.isVisibleWorld(worldX, worldZ)) {
  95. visibleInstances.push_back(instance);
  96. }
  97. }
  98. if (visibleInstances.empty()) {
  99. return;
  100. }
  101. if (!m_visibleInstanceBuffer) {
  102. m_visibleInstanceBuffer = std::make_unique<Buffer>(Buffer::Type::Vertex);
  103. }
  104. m_visibleInstanceBuffer->setData(visibleInstances, Buffer::Usage::Stream);
  105. PlantBatchParams params = m_plantParams;
  106. params.time = renderer.getAnimationTime();
  107. renderer.plantBatch(m_visibleInstanceBuffer.get(),
  108. static_cast<uint32_t>(visibleInstances.size()), params);
  109. } else {
  110. if (m_plantInstanceBuffer && m_plantInstanceCount > 0) {
  111. PlantBatchParams params = m_plantParams;
  112. params.time = renderer.getAnimationTime();
  113. renderer.plantBatch(m_plantInstanceBuffer.get(), m_plantInstanceCount,
  114. params);
  115. }
  116. }
  117. }
  118. void PlantRenderer::clear() {
  119. m_plantInstances.clear();
  120. m_plantInstanceBuffer.reset();
  121. m_visibleInstanceBuffer.reset();
  122. m_plantInstanceCount = 0;
  123. m_plantInstancesDirty = false;
  124. }
  125. void PlantRenderer::generatePlantInstances() {
  126. m_plantInstances.clear();
  127. if (m_width < 2 || m_height < 2 || m_heightData.empty()) {
  128. m_plantInstanceCount = 0;
  129. m_plantInstancesDirty = false;
  130. return;
  131. }
  132. const float plantDensity =
  133. std::clamp(m_biomeSettings.plantDensity, 0.0f, 2.0f);
  134. if (plantDensity < 0.01f) {
  135. m_plantInstanceCount = 0;
  136. m_plantInstancesDirty = false;
  137. return;
  138. }
  139. const float halfWidth = m_width * 0.5f - 0.5f;
  140. const float halfHeight = m_height * 0.5f - 0.5f;
  141. const float tileSafe = std::max(0.001f, m_tileSize);
  142. const float edgePadding =
  143. std::clamp(m_biomeSettings.spawnEdgePadding, 0.0f, 0.5f);
  144. const float edgeMarginX = static_cast<float>(m_width) * edgePadding;
  145. const float edgeMarginZ = static_cast<float>(m_height) * edgePadding;
  146. std::vector<QVector3D> normals(m_width * m_height,
  147. QVector3D(0.0f, 1.0f, 0.0f));
  148. auto sampleHeightAt = [&](float gx, float gz) -> float {
  149. gx = std::clamp(gx, 0.0f, float(m_width - 1));
  150. gz = std::clamp(gz, 0.0f, float(m_height - 1));
  151. int x0 = int(std::floor(gx));
  152. int z0 = int(std::floor(gz));
  153. int x1 = std::min(x0 + 1, m_width - 1);
  154. int z1 = std::min(z0 + 1, m_height - 1);
  155. float tx = gx - float(x0);
  156. float tz = gz - float(z0);
  157. float h00 = m_heightData[z0 * m_width + x0];
  158. float h10 = m_heightData[z0 * m_width + x1];
  159. float h01 = m_heightData[z1 * m_width + x0];
  160. float h11 = m_heightData[z1 * m_width + x1];
  161. float h0 = h00 * (1.0f - tx) + h10 * tx;
  162. float h1 = h01 * (1.0f - tx) + h11 * tx;
  163. return h0 * (1.0f - tz) + h1 * tz;
  164. };
  165. for (int z = 0; z < m_height; ++z) {
  166. for (int x = 0; x < m_width; ++x) {
  167. int idx = z * m_width + x;
  168. float gx0 = std::clamp(float(x) - 1.0f, 0.0f, float(m_width - 1));
  169. float gx1 = std::clamp(float(x) + 1.0f, 0.0f, float(m_width - 1));
  170. float gz0 = std::clamp(float(z) - 1.0f, 0.0f, float(m_height - 1));
  171. float gz1 = std::clamp(float(z) + 1.0f, 0.0f, float(m_height - 1));
  172. float hL = sampleHeightAt(gx0, float(z));
  173. float hR = sampleHeightAt(gx1, float(z));
  174. float hD = sampleHeightAt(float(x), gz0);
  175. float hU = sampleHeightAt(float(x), gz1);
  176. QVector3D dx(2.0f * m_tileSize, hR - hL, 0.0f);
  177. QVector3D dz(0.0f, hU - hD, 2.0f * m_tileSize);
  178. QVector3D n = QVector3D::crossProduct(dz, dx);
  179. if (n.lengthSquared() > 0.0f) {
  180. n.normalize();
  181. } else {
  182. n = QVector3D(0, 1, 0);
  183. }
  184. normals[idx] = n;
  185. }
  186. }
  187. auto addPlant = [&](float gx, float gz, uint32_t &state) -> bool {
  188. if (gx < edgeMarginX || gx > m_width - 1 - edgeMarginX ||
  189. gz < edgeMarginZ || gz > m_height - 1 - edgeMarginZ) {
  190. return false;
  191. }
  192. float sgx = std::clamp(gx, 0.0f, float(m_width - 1));
  193. float sgz = std::clamp(gz, 0.0f, float(m_height - 1));
  194. int ix = std::clamp(int(std::floor(sgx + 0.5f)), 0, m_width - 1);
  195. int iz = std::clamp(int(std::floor(sgz + 0.5f)), 0, m_height - 1);
  196. int normalIdx = iz * m_width + ix;
  197. if (m_terrainTypes[normalIdx] == Game::Map::TerrainType::Mountain) {
  198. return false;
  199. }
  200. if (m_terrainTypes[normalIdx] == Game::Map::TerrainType::River) {
  201. return false;
  202. }
  203. constexpr int kRiverMargin = 1;
  204. for (int dz = -kRiverMargin; dz <= kRiverMargin; ++dz) {
  205. for (int dx = -kRiverMargin; dx <= kRiverMargin; ++dx) {
  206. if (dx == 0 && dz == 0) {
  207. continue;
  208. }
  209. int nx = ix + dx;
  210. int nz = iz + dz;
  211. if (nx >= 0 && nx < m_width && nz >= 0 && nz < m_height) {
  212. int nIdx = nz * m_width + nx;
  213. if (m_terrainTypes[nIdx] == Game::Map::TerrainType::River) {
  214. return false;
  215. }
  216. }
  217. }
  218. }
  219. QVector3D normal = normals[normalIdx];
  220. float slope = 1.0f - std::clamp(normal.y(), 0.0f, 1.0f);
  221. if (slope > 0.65f) {
  222. return false;
  223. }
  224. float worldX = (gx - halfWidth) * m_tileSize;
  225. float worldZ = (gz - halfHeight) * m_tileSize;
  226. float worldY = sampleHeightAt(sgx, sgz);
  227. auto &buildingRegistry =
  228. Game::Systems::BuildingCollisionRegistry::instance();
  229. if (buildingRegistry.isPointInBuilding(worldX, worldZ)) {
  230. return false;
  231. }
  232. float scale = remap(rand01(state), 0.30f, 0.80f) * tileSafe;
  233. float plantType = std::floor(rand01(state) * 4.0f);
  234. float colorVar = remap(rand01(state), 0.0f, 1.0f);
  235. QVector3D baseColor = m_biomeSettings.grassPrimary * 0.7f;
  236. QVector3D varColor = m_biomeSettings.grassSecondary * 0.8f;
  237. QVector3D tintColor = baseColor * (1.0f - colorVar) + varColor * colorVar;
  238. float brownMix = remap(rand01(state), 0.15f, 0.35f);
  239. QVector3D brownTint(0.55f, 0.50f, 0.35f);
  240. tintColor = tintColor * (1.0f - brownMix) + brownTint * brownMix;
  241. float swayPhase = rand01(state) * 6.2831853f;
  242. float swayStrength = remap(rand01(state), 0.6f, 1.2f);
  243. float swaySpeed = remap(rand01(state), 0.8f, 1.3f);
  244. float rotation = rand01(state) * 6.2831853f;
  245. PlantInstanceGpu instance;
  246. instance.posScale = QVector4D(worldX, worldY + 0.05f, worldZ, scale);
  247. instance.colorSway =
  248. QVector4D(tintColor.x(), tintColor.y(), tintColor.z(), swayPhase);
  249. instance.typeParams =
  250. QVector4D(plantType, rotation, swayStrength, swaySpeed);
  251. m_plantInstances.push_back(instance);
  252. return true;
  253. };
  254. int cellsChecked = 0;
  255. int cellsPassed = 0;
  256. int plantsAdded = 0;
  257. for (int z = 0; z < m_height; z += 3) {
  258. for (int x = 0; x < m_width; x += 3) {
  259. cellsChecked++;
  260. int idx = z * m_width + x;
  261. if (m_terrainTypes[idx] == Game::Map::TerrainType::Mountain ||
  262. m_terrainTypes[idx] == Game::Map::TerrainType::River) {
  263. continue;
  264. }
  265. QVector3D normal = normals[idx];
  266. float slope = 1.0f - std::clamp(normal.y(), 0.0f, 1.0f);
  267. if (slope > 0.65f) {
  268. continue;
  269. }
  270. uint32_t state = hashCoords(
  271. x, z, m_noiseSeed ^ 0x8F3C5A7Eu ^ static_cast<uint32_t>(idx));
  272. float worldX = (x - halfWidth) * m_tileSize;
  273. float worldZ = (z - halfHeight) * m_tileSize;
  274. float clusterNoise =
  275. valueNoise(worldX * 0.05f, worldZ * 0.05f, m_noiseSeed ^ 0x4B9D2F1Au);
  276. if (clusterNoise < 0.45f) {
  277. continue;
  278. }
  279. cellsPassed++;
  280. float densityMult = 1.0f;
  281. if (m_terrainTypes[idx] == Game::Map::TerrainType::Hill) {
  282. densityMult = 0.6f;
  283. }
  284. float effectiveDensity = plantDensity * densityMult * 2.0f;
  285. int plantCount = static_cast<int>(std::floor(effectiveDensity));
  286. float frac = effectiveDensity - float(plantCount);
  287. if (rand01(state) < frac) {
  288. plantCount += 1;
  289. }
  290. for (int i = 0; i < plantCount; ++i) {
  291. float gx = float(x) + rand01(state) * 3.0f;
  292. float gz = float(z) + rand01(state) * 3.0f;
  293. if (addPlant(gx, gz, state)) {
  294. plantsAdded++;
  295. }
  296. }
  297. }
  298. }
  299. m_plantInstanceCount = m_plantInstances.size();
  300. m_plantInstancesDirty = m_plantInstanceCount > 0;
  301. }
  302. } // namespace Render::GL