world_generator.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include "world_generator.h"
  2. // STD
  3. #include <iostream>
  4. #include <algorithm>
  5. #include <chrono>
  6. // Coral
  7. #include "coral_device.h"
  8. #include "atlas_generator.h"
  9. world_generator::world_generator(coral_3d::coral_device& device)
  10. : device_{ device }
  11. {
  12. start_worker_threads();
  13. }
  14. world_generator::~world_generator()
  15. {
  16. threads_finished_ = true;
  17. for (size_t i = 0; i < thread_count_; i++)
  18. worker_threads_[i].join();
  19. }
  20. /*======================== World Generation ========================*/
  21. void world_generator::generate_world()
  22. {
  23. auto start = std::chrono::high_resolution_clock::now();
  24. // First generate all the chunks
  25. for (int x = -render_distance_; x <= render_distance_; x++)
  26. for (int z = -render_distance_; z <= render_distance_; z++)
  27. chunks_.emplace_back(generate_chunk({ x,z }));
  28. // Then build all the chunks
  29. for (auto& chunk : chunks_)
  30. build_chunk(chunk);
  31. for (auto chunk : chunks_to_build_)
  32. build_chunk_mesh(*chunk);
  33. chunks_to_build_.clear();
  34. auto end = std::chrono::high_resolution_clock::now();
  35. auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  36. std::cout << "\nGenerated world with:\n\t"
  37. << chunks_.size() << " chunks\n\t"
  38. << "Took: " << duration << "ms\n\n";
  39. }
  40. void world_generator::update_world(const glm::vec3& position)
  41. {
  42. // Get the chunk the player is in
  43. glm::ivec2 player_chunk_coord = get_coord(position);
  44. if(old_player_chunk_coord == player_chunk_coord) return;
  45. old_player_chunk_coord = player_chunk_coord;
  46. for (auto& chunk : chunks_)
  47. {
  48. chunk.is_active = false;
  49. }
  50. // Get the chunks around the player
  51. uint64_t num_chunks = static_cast<uint64_t>(chunks_.size());
  52. for (int x = -render_distance_; x <= render_distance_; x++)
  53. {
  54. for (int z = -render_distance_; z <= render_distance_; z++)
  55. {
  56. glm::ivec2 chunk_coord{ old_player_chunk_coord.x + x, old_player_chunk_coord.y + z };
  57. Chunk* chunk = get_chunk_at_coord(chunk_coord);
  58. if (chunk && chunk->is_initialized)
  59. chunk->is_active = true;
  60. else
  61. {
  62. chunks_.emplace_back(generate_chunk(chunk_coord));
  63. build_chunk(chunks_.back());
  64. build_chunk_mesh(chunks_.back());
  65. }
  66. }
  67. }
  68. }
  69. Chunk& world_generator::add_chunk(const glm::ivec2& chunk_coord)
  70. {
  71. chunks_.emplace_back(generate_chunk(chunk_coord));
  72. return chunks_.back();
  73. }
  74. Chunk world_generator::generate_chunk(const glm::ivec2& coord)
  75. {
  76. Chunk chunk{};
  77. chunk.coord = coord;
  78. chunk.world_position = { coord.x * chunk_size_, coord.y * chunk_size_ };
  79. for (int x = 0; x < chunk_size_; x++)
  80. for (int y = 0; y < world_height_; y++)
  81. for (int z = 0; z < chunk_size_; z++)
  82. chunk.block_map[{ x,y,z }] = generate_block({ x,y,z });
  83. return chunk;
  84. }
  85. BlockType world_generator::generate_block(const glm::vec3& position)
  86. {
  87. if (position.y == 0)
  88. return BlockType::GRASS_BLOCK;
  89. if (position.y == world_height_ - 1)
  90. return BlockType::BEDROCK;
  91. if (position.y < 4)
  92. return BlockType::DIRT;
  93. else
  94. return BlockType::STONE;
  95. }
  96. void world_generator::build_chunk(Chunk& chunk)
  97. {
  98. for (int x = 0; x < chunk_size_; x++)
  99. for (int y = 0; y < world_height_; y++)
  100. for (int z = 0; z < chunk_size_; z++)
  101. add_block_vertices_to_chunk({ x,y,z }, chunk);
  102. chunks_to_build_.emplace_back(&chunk);
  103. }
  104. /// <summary>
  105. /// Rebuilds the chunks north, east, south, and west of the given chunk.
  106. /// </summary>
  107. /// <param name="coord">Coord of the center chunk</param>
  108. void world_generator::rebuild_surrounding_chunks(const Chunk& chunk)
  109. {
  110. // Recalculate bordering faces of chunk north
  111. Chunk* north_chunk = get_chunk_at_coord({ chunk.coord.x, chunk.coord.y + 1 });
  112. if (north_chunk && north_chunk->is_active)
  113. build_chunk(*north_chunk);
  114. // Recalculate bordering faces of chunk east
  115. Chunk* east_chunk = get_chunk_at_coord({ chunk.coord.x + 1, chunk.coord.y });
  116. if (east_chunk && east_chunk->is_active)
  117. build_chunk(*east_chunk);
  118. // Recalculate bordering faces of chunk south
  119. Chunk* south_chunk = get_chunk_at_coord({ chunk.coord.x, chunk.coord.y - 1 });
  120. if (south_chunk && south_chunk->is_active)
  121. build_chunk(*south_chunk);
  122. // Recalculate bordering faces of chunk west
  123. Chunk* west_chunk = get_chunk_at_coord({ chunk.coord.x - 1, chunk.coord.y });
  124. if (west_chunk && west_chunk->is_active)
  125. build_chunk(*west_chunk);
  126. }
  127. /// <summary>
  128. /// Calculates the visible faces of a block in the chunk and adds them to the chunk.
  129. /// </summary>
  130. /// <param name="position">Relative chunk position of the block</param>
  131. /// <param name="chunk">Chunk the block is in</param>
  132. /// <returns>Returns false if the block is not in the chunk, returns true otherwise</returns>
  133. bool world_generator::add_block_vertices_to_chunk(const glm::vec3& position, Chunk& chunk)
  134. {
  135. // If we are trying to add a block outside of the chunk, return false
  136. if( position.x < 0 || position.x >= chunk_size_ ||
  137. position.y < 0 || position.y >= world_height_ ||
  138. position.z < 0 || position.z >= chunk_size_)
  139. return false;
  140. Block block{ voxel_data::get_block(chunk.block_map[position]) };
  141. int num_deleted_faces{ 0 };
  142. auto it = block.faces.begin();
  143. while (it != block.faces.end())
  144. {
  145. BlockType adjecent_block{};
  146. // Get block at world position
  147. if(position.x == chunk_size_ - 1 || position.x == 0 || position.z == chunk_size_ - 1 || position.z == 0)
  148. adjecent_block = get_block_at_position((position + it->vertices[0].normal) +
  149. glm::vec3{chunk.world_position.x, 0.f, chunk.world_position.y});
  150. else
  151. adjecent_block = chunk.block_map[position + it->vertices[0].normal];
  152. // Check if there is a block and if it is solid, if so, don't add this face
  153. if (voxel_data::is_block_solid(adjecent_block))
  154. {
  155. // If there is, delete this face
  156. it = block.faces.erase(it);
  157. num_deleted_faces++;
  158. continue;
  159. }
  160. // Else, add this face to the chunk
  161. for (auto& vertex : it->vertices)
  162. {
  163. vertex.position += position;
  164. }
  165. for (auto& index : it->indices)
  166. {
  167. index += chunk.vertices.size() - (num_deleted_faces * 4);
  168. }
  169. ++it;
  170. }
  171. // Calculate the UVs for each face
  172. atlas_generator::calculate_uvs(block);
  173. for (auto& face : block.faces)
  174. {
  175. chunk.vertices.insert(chunk.vertices.end(), face.vertices.begin(), face.vertices.end());
  176. chunk.indices.insert(chunk.indices.end(), face.indices.begin(), face.indices.end());
  177. }
  178. return true;
  179. }
  180. void world_generator::build_chunk_mesh(Chunk& chunk)
  181. {
  182. chunk.mesh = coral_3d::coral_mesh::create_mesh_from_vertices(device_, chunk.vertices, chunk.indices);
  183. chunk.vertices.clear();
  184. chunk.indices.clear();
  185. // After the chunk is built, we can set it active for rendering
  186. chunk.is_active = true;
  187. chunk.is_initialized = true;
  188. }
  189. /*======================== Chunk Getters ========================*/
  190. #pragma region Chunk Getters
  191. glm::ivec2 world_generator::get_coord(const glm::vec3& position)
  192. {
  193. return
  194. {
  195. position.x < 0 ? std::floor(position.x / chunk_size_) : position.x / chunk_size_,
  196. position.z < 0 ? std::floor(position.z / chunk_size_) : position.z / chunk_size_
  197. };
  198. }
  199. Chunk* world_generator::get_chunk_at_coord(const glm::ivec2& coord)
  200. {
  201. auto it = std::find_if(chunks_.begin(), chunks_.end(), [&](const Chunk& chunk) {
  202. return chunk.coord == coord;
  203. });
  204. return it != chunks_.end() ? &*it : nullptr;
  205. }
  206. Chunk* world_generator::get_chunk_at_position(const glm::vec3& position)
  207. {
  208. glm::ivec2 coord { get_coord(position) };
  209. return get_chunk_at_coord(coord);
  210. }
  211. BlockType world_generator::get_block_at_position(const glm::vec3& position)
  212. {
  213. Chunk* chunk{ get_chunk_at_position(position) };
  214. // If there is no chunk, return air
  215. if (!chunk) return BlockType::AIR;
  216. // Get chunk relative position
  217. glm::vec3 chunk_relative_position
  218. {
  219. position.x - chunk->world_position.x,
  220. position.y,
  221. position.z - chunk->world_position.y
  222. };
  223. // Position lies outside of the world if chunk is null
  224. // or if the chunk does not contain the position
  225. if (!chunk->block_map.contains(chunk_relative_position)) return BlockType::AIR;
  226. return chunk->block_map[chunk_relative_position];
  227. }
  228. #pragma endregion
  229. /* ======================================================= Jobs ======================================================= */
  230. #pragma region Jobs
  231. void world_generator::start_worker_threads()
  232. {
  233. thread_count_ = 1;
  234. worker_threads_.reserve(thread_count_);
  235. command_pools_.reserve(thread_count_);
  236. for (size_t i = 0; i < thread_count_; i++)
  237. {
  238. command_pools_.emplace_back(device_.create_command_pool(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
  239. command_buffers_.emplace_back(device_.create_command_buffer(command_pools_[i], VK_COMMAND_BUFFER_LEVEL_SECONDARY));
  240. worker_threads_.emplace_back(std::jthread(
  241. worker_thread(threads_finished_, work_queue_, i, command_buffers_.back())
  242. ));
  243. }
  244. }
  245. GenerateChunk::GenerateChunk(coral_3d::coral_device& device, world_generator& generator, const glm::ivec2& chunk_coord)
  246. : device_{ device }, generator_{ generator }, chunk_coord_{ chunk_coord } {}
  247. void GenerateChunk::execute(VkCommandBuffer command_buffer)
  248. {
  249. auto& chunk = generator_.add_chunk(chunk_coord_);
  250. generator_.rebuild_surrounding_chunks(chunk);
  251. generator_.build_chunk(chunk);
  252. }
  253. #pragma endregion