You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

world.cpp 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #include "world.h"
  2. #include "chunk.h"
  3. #include "threadpool.h"
  4. #include <cmath>
  5. #include <iostream>
  6. #include <thread>
  7. #include <GL/glew.h>
  8. #include <GL/gl.h>
  9. #define GLM_FORCE_RADIANS
  10. #include <glm/glm.hpp>
  11. #include <glm/gtc/type_ptr.hpp>
  12. #include <glm/gtc/matrix_transform.hpp>
  13. namespace vtk {
  14. World::World() {
  15. chunkSize = 16;
  16. voxelSize = 1.0f;
  17. voxelInfo.linkedWorld = this;
  18. voxelMath.linkedWorld = this;
  19. rebuildThreadActive = false;
  20. mLoadThreadActive = false;
  21. }
  22. unsigned World::breakVoxel(const glm::ivec3& pos) {
  23. auto chunkPos = worldPosToChunkPos(pos);
  24. auto chunk = getChunk(chunkPos);
  25. if (!chunk) return 0;
  26. return chunk->breakVoxel(glm::ivec3(pos.x - (chunkPos.x * 16),
  27. pos.y - (chunkPos.y * 16),
  28. pos.z - (chunkPos.z * 16)));
  29. }
  30. bool World::placeVoxel(const glm::ivec3 &pos, const unsigned int &id) {
  31. auto chunkPos = worldPosToChunkPos(pos);
  32. auto chunk = getChunk(chunkPos);
  33. if (!chunk) return 0;
  34. return chunk->placeVoxel(glm::ivec3(pos.x - (chunkPos.x * 16),
  35. pos.y - (chunkPos.y * 16),
  36. pos.z - (chunkPos.z * 16)),
  37. id);
  38. }
  39. bool World::isVoxelSolid(const int& x, const int& y, const int& z) {
  40. auto chunkPos = glm::ivec3(floor((float)x / (float)chunkSize),
  41. floor((float)y / (float)chunkSize),
  42. floor((float)z / (float)chunkSize));
  43. auto chunk = getChunk(chunkPos);
  44. if (!chunk) //block is in nonexistant chunk
  45. return true;
  46. int relPosX = x - chunkPos.x * chunkSize;
  47. int relPosY = y - chunkPos.y * chunkSize;
  48. int relPosZ = z - chunkPos.z * chunkSize;
  49. if (!chunk->isLoaded()) return true;
  50. return chunk->isVoxelSolid(relPosX, relPosY, relPosZ);
  51. }
  52. bool World::setVoxelType(const int& x, const int& y, const int& z, const unsigned& type, const bool& updateChunk) {
  53. auto chunkPos = worldPosToChunkPos(glm::ivec3(x,y,z));
  54. auto chunk = getChunk(chunkPos);
  55. if (!chunk) return false;
  56. int relPosX = x - chunkPos.x * chunkSize;
  57. int relPosY = y - chunkPos.y * chunkSize;
  58. int relPosZ = z - chunkPos.z * chunkSize;
  59. chunk->setVoxelType(relPosX, relPosY, relPosZ, type, updateChunk);
  60. if (type != 0)
  61. getHeightMap(glm::ivec2(chunkPos.x, chunkPos.z))->blockHeight(glm::ivec3(relPosX, y, relPosZ));
  62. else
  63. getHeightMap(glm::ivec2(chunkPos.x, chunkPos.z))->unblockHeight(glm::ivec3(relPosX, y, relPosZ));
  64. return true;
  65. }
  66. unsigned World::getVoxelType(const glm::ivec3& pos) {
  67. auto chunkPos = glm::ivec3(floor((float)pos.x / (float)chunkSize),
  68. floor((float)pos.y / (float)chunkSize),
  69. floor((float)pos.z / (float)chunkSize));
  70. Chunk* chunk = getChunk(chunkPos);
  71. if (!chunk) return 0;
  72. int relPosX = pos.x - chunkPos.x * chunkSize;
  73. int relPosY = pos.y - chunkPos.y * chunkSize;
  74. int relPosZ = pos.z - chunkPos.z * chunkSize;
  75. return chunk->getVoxelType((unsigned)relPosX, (unsigned)relPosY, (unsigned)relPosZ);
  76. }
  77. Chunk* World::makeChunk(const int& x, const int& y, const int& z, bool insertAfter) {
  78. auto pos = glm::ivec3(x, y, z);
  79. Chunk* chunk;
  80. if (mChunks.find(pos, chunk)) { //chunk already exists
  81. return chunk;
  82. }
  83. chunk = new Chunk(*this);
  84. chunk->setPos(pos);
  85. if (insertAfter) insertChunk(chunk);
  86. return chunk;
  87. }
  88. bool World::generateChunk(const int& x, const int& y, const int& z) {
  89. auto chunkMade = makeChunk(x,y,z);
  90. if (chunkMade != nullptr) {
  91. terrain.generateChunk(chunkMade);
  92. chunkMade->rebuildLighting();
  93. //queue this chunk for geometry update
  94. queueChunkUpdate(x,y,z);
  95. //queue the neighboring 6 chunks. the queue functions throws them out
  96. // if they don't exist
  97. queueChunkUpdate(x+1,y,z);
  98. queueChunkUpdate(x-1,y,z);
  99. queueChunkUpdate(x,y+1,z);
  100. queueChunkUpdate(x,y-1,z);
  101. queueChunkUpdate(x,y,z+1);
  102. queueChunkUpdate(x,y,z-1);
  103. }
  104. insertChunk(chunkMade);
  105. return chunkMade;
  106. }
  107. bool World::insertChunk(Chunk* chunk) {
  108. mChunks.insert(chunk->getPos(), chunk);
  109. mChunkMeshes.insert(chunk->getPos(), new ChunkMesh(*this, chunk->getPos()));
  110. return true;
  111. }
  112. Chunk* World::getChunk(const glm::ivec3& pos) {
  113. Chunk* chunk;
  114. if (mChunks.find(pos, chunk) && chunk->isLoaded()) return chunk;
  115. else return nullptr;
  116. }
  117. HeightMap* World::getHeightMap(const glm::ivec2& pos) {
  118. HeightMap* heightMap;
  119. //return heightmap if it already exists
  120. if (mHeightMaps.find(pos, heightMap)) {
  121. return heightMap;
  122. }
  123. //else make a new one, add to map, and return
  124. heightMap = new HeightMap(pos, *this);
  125. mHeightMaps.insert(pos, heightMap);
  126. return heightMap;
  127. }
  128. int World::getHeight(const glm::ivec2& pos) {
  129. glm::ivec2 cPos = worldPosToChunkPos(pos);
  130. return getHeightMap(cPos)->getHeight(pos - (cPos * 16));
  131. std::cout << cPos.x << ", " << cPos.y << std::endl;
  132. }
  133. void World::queueChunkUpdate(const int& x, const int& y, const int& z, const bool& highpriority) {
  134. queueChunkUpdate(glm::ivec3(x,y,z));
  135. }
  136. void World::queueChunkUpdate(const glm::ivec3& pos, const bool& highpriority) {
  137. auto chunk = getChunk(pos);
  138. if (!chunk || chunk->isQueuedForMeshRebuild()) return;
  139. chunk->setQueuedForMeshRebuild(true);
  140. mHeightMapUpdateQueue.enqueue(glm::ivec2(pos.x, pos.z));
  141. if (highpriority) { // queue for higher rebuild priority
  142. mMeshUpdateQueueSoon.enqueue(pos);
  143. return;
  144. }
  145. mMeshUpdateQueue.enqueue(pos);
  146. }
  147. void World::queueChunkLoad(const glm::ivec3 &pos) {
  148. if (getChunk(pos)) return; //chunk already exists
  149. for (auto& i : mChunkLoadQueue) {
  150. if (i == pos) return;
  151. }
  152. makeChunk(pos.x, pos.y, pos.z);
  153. mChunkLoadQueue.push_back(pos);
  154. }
  155. void World::draw() {
  156. auto lt = mChunkMeshes.lock_table();
  157. //naively iterate through all chunks and draw them
  158. for (const auto& i : lt) {
  159. glm::mat4 modelMat = glm::translate(glm::mat4(), glm::vec3((float)i.first.x * 16,
  160. (float)i.first.y * 16,
  161. (float)i.first.z * 16
  162. ));
  163. glUniformMatrix4fv(modelMatUni, 1, GL_FALSE, glm::value_ptr(modelMat));
  164. i.second->draw();
  165. }
  166. }
  167. void World::update() {
  168. //if the rebuild thread is not active and there are meshes to be updated
  169. if (!rebuildThreadActive && (mMeshUpdateQueueSoon.size_approx() > 0 ||
  170. mMeshUpdateQueue.size_approx() > 0)) {
  171. auto updatefunc = [&]() {
  172. glm::ivec3 pos;
  173. glm::ivec2 hm_pos;
  174. while (mMeshUpdateQueueSoon.try_dequeue(pos)) {
  175. ChunkMesh* mesh;
  176. mChunkMeshes.find(pos, mesh);
  177. if (mesh) {
  178. mesh->rebuildChunkGeometry();
  179. }
  180. getChunk(pos)->setQueuedForMeshRebuild(false);
  181. }
  182. while (mMeshUpdateQueue.try_dequeue(pos)) {
  183. //getChunk(pos)->rebuildLighting();
  184. ChunkMesh* mesh;
  185. mChunkMeshes.find(pos, mesh);
  186. if (mesh) {
  187. mesh->rebuildChunkGeometry();
  188. }
  189. getChunk(pos)->setQueuedForMeshRebuild(false);
  190. }
  191. rebuildThreadActive = false;
  192. };
  193. rebuildThreadActive = true;
  194. ThreadPool::getInstance().addJob(updatefunc);
  195. }
  196. if (!mLoadThreadActive && !mChunkLoadQueue.empty()) {
  197. auto loadFunc = [&]() {
  198. while (!mChunkLoadQueue.empty()) {
  199. auto& pos = mChunkLoadQueue.back();
  200. generateChunk(pos.x, pos.y, pos.z);
  201. mChunkLoadQueue.pop_back();
  202. }
  203. mLoadThreadActive = false;
  204. };
  205. mLoadThreadActive = true;
  206. ThreadPool::getInstance().addJob(loadFunc);
  207. }
  208. }
  209. void World::queueChunkLoadsAroundPoint(const glm::vec3 &point, const int &chunkRadius) {
  210. glm::ivec3 chunkPoint = point / 16.0f;
  211. for (int z = -chunkRadius; z <= chunkRadius; ++z) {
  212. for (int x = -chunkRadius; x <= chunkRadius; ++x) {
  213. if (z*z + x*x <= chunkRadius * chunkRadius) {
  214. for (int y = 0; y < 8; ++y) {
  215. queueChunkLoad(glm::ivec3(x + chunkPoint.x, y, z + chunkPoint.z));
  216. }
  217. }
  218. }
  219. }
  220. }
  221. }