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.

chunk.cpp 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #include "chunk.h"
  2. #include "world.h"
  3. #include <iostream>
  4. #include <queue>
  5. namespace vtk {
  6. Chunk::Chunk(World& world) :
  7. mLinkedWorld(world),
  8. mLoaded(false),
  9. mQueuedForMeshRebuild(false)
  10. {
  11. mPos = glm::ivec3(0,0,0);
  12. //fill voxels with 0
  13. for (unsigned i = 0; i < mData.size(); ++i) {
  14. mData[i].store(0);
  15. }
  16. for (unsigned i = 0; i < mLighting.size(); ++i) {
  17. mLighting[i].store(0xFFFF);
  18. }
  19. mLoaded.store(true);
  20. }
  21. bool Chunk::isLoaded() {
  22. return mLoaded.load();
  23. }
  24. unsigned Chunk::breakVoxel(const glm::ivec3& pos) {
  25. unsigned voxelType = getVoxelType(pos);
  26. if (voxelType == 0) return voxelType; //return early if nothing broken
  27. setVoxelType(pos, 0);
  28. //update heightmap
  29. mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
  30. ->unblockHeight(glm::ivec3(pos.x, pos.y + mPos.y * 16, pos.z));
  31. mLinkedWorld.queueChunkUpdate(mPos);
  32. //update the neightboring chunks if voxel lies along border
  33. glm::ivec3 neighborPos(0,0,0);
  34. if (pos.x == 0) neighborPos.x--;
  35. else if (pos.x == 15) neighborPos.x++;
  36. if (pos.y == 0) neighborPos.y--;
  37. else if (pos.y == 15) neighborPos.y++;
  38. if (pos.z == 0) neighborPos.z--;
  39. else if (pos.z == 15) neighborPos.z++;
  40. if (neighborPos.x != 0)
  41. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x + neighborPos.x,
  42. mPos.y,
  43. mPos.z));
  44. if (neighborPos.y != 0)
  45. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  46. mPos.y + neighborPos.y,
  47. mPos.z));
  48. if (neighborPos.z != 0)
  49. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  50. mPos.y,
  51. mPos.z + neighborPos.z));
  52. return voxelType;
  53. }
  54. bool Chunk::placeVoxel(const glm::ivec3& pos, const unsigned& type) {
  55. unsigned curType = getVoxelType(pos);
  56. if (curType != 0) // return false if the voxel is NOT air
  57. return false;
  58. setVoxelType(pos, type);
  59. //update heightmap
  60. mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
  61. ->blockHeight(glm::ivec3(pos.x, pos.y + mPos.y * 16, pos.z));
  62. mLinkedWorld.queueChunkUpdate(mPos, true);
  63. //update the neightboring chunks if voxel lies along border
  64. // TODO: make this a function in voxelutils(voxelmath?)
  65. glm::ivec3 neighborPos(0,0,0);
  66. if (pos.x == 0) neighborPos.x--;
  67. else if (pos.x == 15) neighborPos.x++;
  68. if (pos.y == 0) neighborPos.y--;
  69. else if (pos.y == 15) neighborPos.y++;
  70. if (pos.z == 0) neighborPos.z--;
  71. else if (pos.z == 15) neighborPos.z++;
  72. if (neighborPos.x != 0)
  73. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x + neighborPos.x,
  74. mPos.y,
  75. mPos.z));
  76. if (neighborPos.y != 0)
  77. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  78. mPos.y + neighborPos.y,
  79. mPos.z));
  80. if (neighborPos.z != 0)
  81. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  82. mPos.y,
  83. mPos.z + neighborPos.z));
  84. return true;
  85. }
  86. bool Chunk::isVoxelSolid(const int& x, const int& y, const int& z) {
  87. if (x < 0 || x > 15 ||
  88. y < 0 || y > 15 ||
  89. z < 0 || z > 15 )
  90. { //position is outside of the chunk
  91. return mLinkedWorld.isVoxelSolid(mPos.x * 16 + x,
  92. mPos.y * 16 + y,
  93. mPos.z * 16 + z);
  94. }
  95. return !mLinkedWorld.voxelInfo.isTransparent(getVoxelType((unsigned)x, (unsigned)y, (unsigned)z));
  96. }
  97. bool Chunk::isVoxelSolid(const glm::ivec3& pos) {
  98. return isVoxelSolid(pos.x, pos.y, pos.z);
  99. }
  100. void Chunk::setVoxelType(const glm::ivec3 &pos, const unsigned& type) {
  101. setVoxelType(pos.x, pos.y, pos.z, type);
  102. }
  103. void Chunk::setVoxelType(const int& x, const int& y, const int& z, const unsigned& type, const bool& update) {
  104. auto index = x + 16 * (y + 16 * z);
  105. if (index > 4095) {
  106. std::cout << "CHUNK ACCESS ERROR (set voxel): Out of range, doing nothing\n";
  107. return;
  108. }
  109. mData[index].store(type, std::memory_order_release);
  110. if (!update) return;
  111. mLinkedWorld.queueChunkUpdate(mPos);
  112. }
  113. unsigned Chunk::getVoxelType(const glm::ivec3& pos) {
  114. auto index = pos.x + 16 * (pos.y + 16 * pos.z);
  115. if (index > 4095) {
  116. std::cout << "CHUNK ACCESS ERROR (get voxel): Out of range, returning type 0\n";
  117. return 0;
  118. }
  119. return mData[index].load(std::memory_order_consume);
  120. }
  121. unsigned Chunk::getVoxelType(const unsigned& x, const unsigned& y, const unsigned& z) {
  122. return getVoxelType(glm::ivec3(x,y,z));
  123. }
  124. void Chunk::rebuildLighting() {
  125. std::queue<LightIndexTup> sunBFSQueue;
  126. //FIRST PASS, block out all solid blocks
  127. for (short i = 0; i < 4096; ++i) {
  128. glm::ivec3 pos(i % 16, (i / 16) % 16,(i / 256)); // get current coord
  129. mLighting[i].store(0);
  130. auto lightVal = mLinkedWorld.voxelInfo.getEmission(getVoxelType(pos));
  131. //set sunlighting if at the top of chunk and above the ground
  132. if(pos.y == 15 &&
  133. mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
  134. ->getHeight(glm::ivec2(pos.x, pos.z)) <=
  135. pos.y + mPos.y * 16)
  136. {
  137. lightVal = lightVal | 0xF; //max out the sun lighting
  138. }
  139. mLighting[i].store(lightVal);
  140. if ((lightVal & 0xF) != 0) {
  141. sunBFSQueue.push(std::make_tuple(i,this));
  142. }
  143. }
  144. //iterate through sunlight
  145. while (!sunBFSQueue.empty()) {
  146. short index;
  147. Chunk* chunk;
  148. unsigned short light;
  149. std::tie(index, chunk) = sunBFSQueue.front();
  150. light = light & 0xF; // so I don't have to remask every time
  151. glm::ivec3 pos( index % 16,
  152. (index / 16) % 16,
  153. index / 256);
  154. light = chunk->getLightPacked(pos) & 0xF;
  155. auto propogateSun =
  156. [&](glm::ivec3 nPos, Chunk* nChunk)
  157. {
  158. bool straightDown = (pos.y > nPos.y && light == 0xF);
  159. auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
  160. nChunk = mLinkedWorld.getChunk(checkPos.first);
  161. nPos = checkPos.second;
  162. auto newLight = chunk->getLightPacked(nPos) & 0xF;
  163. if (!chunk->isVoxelSolid(nPos)) {
  164. if (light >= newLight + 2) {
  165. if (straightDown) {
  166. chunk->setLightPacked(nPos, (newLight & 0xFFF0) | 0xF);
  167. }
  168. chunk->setLightPacked(nPos, (newLight & 0xFFF0) | light - 1);
  169. if (light - 1 > 1) {
  170. sunBFSQueue.push(std::make_tuple((short)(nPos.x + 16 * (nPos.y + 16 * nPos.z)), chunk));
  171. }
  172. }
  173. }
  174. };
  175. //visit neighbors
  176. propogateSun(glm::ivec3(pos.x-1, pos.y, pos.z), this);
  177. propogateSun(glm::ivec3(pos.x+1, pos.y, pos.z), this);
  178. propogateSun(glm::ivec3(pos.x, pos.y-1, pos.z), this);
  179. propogateSun(glm::ivec3(pos.x, pos.y+1, pos.z), this);
  180. propogateSun(glm::ivec3(pos.x, pos.y, pos.z-1), this);
  181. propogateSun(glm::ivec3(pos.x, pos.y, pos.z+1), this);
  182. sunBFSQueue.pop();
  183. }
  184. }
  185. glm::ivec3 Chunk::getWorldCoords(const int& x, const int& y, const int& z) {
  186. return glm::ivec3(mPos.x * 16 + x,
  187. mPos.y * 16 + y,
  188. mPos.z * 16 + z);
  189. }
  190. unsigned Chunk::getLightLevel(const glm::ivec3 &pos) {
  191. if (isVoxelSolid(pos.x, pos.y, pos.z)) {
  192. return 0;
  193. }
  194. return 15;
  195. }
  196. unsigned short Chunk::getLightPacked(const glm::ivec3 &pos) {
  197. //checking if pos is inside current chunk
  198. auto lPos = localPosToLocalPos(mPos, pos); //returns a chunk/local pos pair
  199. if (lPos.first != mPos) { //if it's not THIS chunk
  200. auto chunk = mLinkedWorld.getChunk(lPos.first);
  201. if (chunk)
  202. return chunk->getLightPacked(lPos.second);
  203. else
  204. return 0xFFFF;
  205. }
  206. int index = pos.x + 16 * (pos.y + 16 * pos.z);
  207. return mLighting[index].load();
  208. }
  209. void Chunk::setLightPacked(const glm::ivec3& pos, const unsigned short& light) {
  210. int index = pos.x + 16 * (pos.y + 16 * pos.z);
  211. mLighting[index].store(light);
  212. }
  213. HeightMap* Chunk::getHeightMap() {
  214. return mLinkedWorld.getHeightMap(glm::ivec2(mPos.x,mPos.z));
  215. }
  216. void Chunk::setPos(const glm::ivec3& pos) {
  217. mPos = pos;
  218. }
  219. glm::ivec3 Chunk::getPos() {
  220. return mPos;
  221. }
  222. World& Chunk::getWorld() {
  223. return mLinkedWorld;
  224. }
  225. void Chunk::setQueuedForMeshRebuild(const bool& rebuild) {
  226. mQueuedForMeshRebuild = rebuild;
  227. }
  228. bool Chunk::isQueuedForMeshRebuild() {
  229. return mQueuedForMeshRebuild.load();
  230. }
  231. }