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 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  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. removeLight(pos);
  32. mLinkedWorld.queueChunkUpdate(mPos);
  33. //update the neightboring chunks if voxel lies along border
  34. glm::ivec3 neighborPos(0,0,0);
  35. if (pos.x == 0) neighborPos.x--;
  36. else if (pos.x == 15) neighborPos.x++;
  37. if (pos.y == 0) neighborPos.y--;
  38. else if (pos.y == 15) neighborPos.y++;
  39. if (pos.z == 0) neighborPos.z--;
  40. else if (pos.z == 15) neighborPos.z++;
  41. if (neighborPos.x != 0)
  42. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x + neighborPos.x,
  43. mPos.y,
  44. mPos.z));
  45. if (neighborPos.y != 0)
  46. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  47. mPos.y + neighborPos.y,
  48. mPos.z));
  49. if (neighborPos.z != 0)
  50. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  51. mPos.y,
  52. mPos.z + neighborPos.z));
  53. return voxelType;
  54. }
  55. bool Chunk::placeVoxel(const glm::ivec3& pos, const unsigned& type) {
  56. unsigned curType = getVoxelType(pos);
  57. if (curType != 0) // return false if the voxel is NOT air
  58. return false;
  59. setVoxelType(pos, type);
  60. //update heightmap
  61. mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
  62. ->blockHeight(glm::ivec3(pos.x, pos.y + mPos.y * 16, pos.z));
  63. unsigned short emission = mLinkedWorld.voxelInfo.getEmission(type);
  64. removeLight(pos);
  65. if (emission != 0) {
  66. addLight(pos, emission);
  67. }
  68. mLinkedWorld.queueChunkUpdate(mPos, true);
  69. //update the neightboring chunks if voxel lies along border
  70. // TODO: make this a function in voxelutils(voxelmath?)
  71. glm::ivec3 neighborPos(0,0,0);
  72. if (pos.x == 0) neighborPos.x--;
  73. else if (pos.x == 15) neighborPos.x++;
  74. if (pos.y == 0) neighborPos.y--;
  75. else if (pos.y == 15) neighborPos.y++;
  76. if (pos.z == 0) neighborPos.z--;
  77. else if (pos.z == 15) neighborPos.z++;
  78. if (neighborPos.x != 0)
  79. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x + neighborPos.x,
  80. mPos.y,
  81. mPos.z));
  82. if (neighborPos.y != 0)
  83. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  84. mPos.y + neighborPos.y,
  85. mPos.z));
  86. if (neighborPos.z != 0)
  87. mLinkedWorld.queueChunkUpdate(glm::ivec3(mPos.x,
  88. mPos.y,
  89. mPos.z + neighborPos.z));
  90. return true;
  91. }
  92. bool Chunk::isVoxelSolid(const int& x, const int& y, const int& z) {
  93. if (x < 0 || x > 15 ||
  94. y < 0 || y > 15 ||
  95. z < 0 || z > 15 )
  96. { //position is outside of the chunk
  97. return mLinkedWorld.isVoxelSolid(mPos.x * 16 + x,
  98. mPos.y * 16 + y,
  99. mPos.z * 16 + z);
  100. }
  101. return !mLinkedWorld.voxelInfo.isTransparent(getVoxelType((unsigned)x, (unsigned)y, (unsigned)z));
  102. }
  103. bool Chunk::isVoxelSolid(const glm::ivec3& pos) {
  104. return isVoxelSolid(pos.x, pos.y, pos.z);
  105. }
  106. void Chunk::setVoxelType(const glm::ivec3 &pos, const unsigned& type) {
  107. setVoxelType(pos.x, pos.y, pos.z, type);
  108. }
  109. void Chunk::setVoxelType(const int& x, const int& y, const int& z, const unsigned& type, const bool& update) {
  110. auto index = x + 16 * (y + 16 * z);
  111. if (index > 4095) {
  112. std::cout << "CHUNK ACCESS ERROR (set voxel): Out of range, doing nothing\n";
  113. return;
  114. }
  115. mData[index].store(type, std::memory_order_release);
  116. if (!update) return;
  117. mLinkedWorld.queueChunkUpdate(mPos);
  118. }
  119. unsigned Chunk::getVoxelType(const glm::ivec3& pos) {
  120. auto index = pos.x + 16 * (pos.y + 16 * pos.z);
  121. if (index > 4095) {
  122. std::cout << "CHUNK ACCESS ERROR (get voxel): Out of range, returning type 0\n";
  123. return 0;
  124. }
  125. return mData[index].load(std::memory_order_consume);
  126. }
  127. unsigned Chunk::getVoxelType(const unsigned& x, const unsigned& y, const unsigned& z) {
  128. return getVoxelType(glm::ivec3(x,y,z));
  129. }
  130. void Chunk::rebuildLighting() {
  131. std::queue<LightIndexTup> sunBFSQueue;
  132. unsigned short mask2 = 0x000F;
  133. //FIRST PASS, block out all solid blocks
  134. for (short i = 0; i < 4096; ++i) {
  135. glm::ivec3 pos(i % 16, (i / 16) % 16,(i / 256)); // get current coord
  136. auto lightVal = mLinkedWorld.voxelInfo.getEmission(getVoxelType(pos));
  137. //set sunlighting if at the top of chunk and above the ground
  138. if(pos.y == 15 &&
  139. mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
  140. ->getHeight(glm::ivec2(pos.x, pos.z)) <
  141. pos.y + mPos.y * 16)
  142. {
  143. lightVal = lightVal | 0xF; //max out the sun lighting
  144. }
  145. mLighting[i].store(lightVal, std::memory_order_release);
  146. if ((lightVal & 0xF) != 0) {
  147. sunBFSQueue.push(std::make_tuple(i,mask2,this));
  148. }
  149. }
  150. //iterate through sunlight
  151. while (!sunBFSQueue.empty()) {
  152. short index;
  153. unsigned short mask;
  154. Chunk* chunk;
  155. std::tie(index, mask, chunk) = sunBFSQueue.front();
  156. glm::ivec3 pos( index % 16,
  157. (index / 16) % 16,
  158. index / 256);
  159. unsigned short light;
  160. light = chunk->getLightPacked(pos) & 0xF;
  161. auto propogateSun =
  162. [&](glm::ivec3 nPos, Chunk* nChunk)
  163. {
  164. bool straightDown = (pos.y > nPos.y && light == 0xF);
  165. auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
  166. nChunk = mLinkedWorld.getChunk(checkPos.first);
  167. nPos = checkPos.second;
  168. if (nChunk == nullptr) {
  169. return;
  170. }
  171. auto newLight = nChunk->getLightPacked(nPos) & 0xF;
  172. if (!nChunk->isVoxelSolid(nPos)) {
  173. if (light >= newLight + 2) {
  174. if (straightDown) {
  175. nChunk->setLightPacked(nPos, (newLight & 0xFFF0) | 0xF);
  176. } else {
  177. nChunk->setLightPacked(nPos, ((newLight & 0xFFF0) | light) - 1);
  178. }
  179. if (light - 1 > 1) {
  180. sunBFSQueue.push(std::make_tuple((short)(nPos.x + 16 * (nPos.y + 16 * nPos.z)), mask, nChunk));
  181. }
  182. }
  183. }
  184. };
  185. //visit neighbors
  186. propogateSun(glm::ivec3(pos.x-1, pos.y, pos.z), this);
  187. propogateSun(glm::ivec3(pos.x+1, pos.y, pos.z), this);
  188. propogateSun(glm::ivec3(pos.x, pos.y-1, pos.z), this);
  189. propogateSun(glm::ivec3(pos.x, pos.y+1, pos.z), this);
  190. propogateSun(glm::ivec3(pos.x, pos.y, pos.z-1), this);
  191. propogateSun(glm::ivec3(pos.x, pos.y, pos.z+1), this);
  192. sunBFSQueue.pop();
  193. }
  194. }
  195. void Chunk::addLight(const glm::ivec3 &pos, const unsigned short &light) {
  196. setLightPacked(pos, light);
  197. //check channels
  198. if (light >> 12 > 0) {
  199. mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this));
  200. }
  201. if (((light >> 8) & 0xF) > 0) {
  202. mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this));
  203. }
  204. if (((light >> 4) & 0xF) > 0) {
  205. mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this));
  206. }
  207. if ((light & 0xF) > 0) {
  208. mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x000F, this));
  209. }
  210. propogateLight();
  211. }
  212. void Chunk::removeLight(const glm::ivec3 &pos) {
  213. //propogate all 4 channels
  214. /*mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this));
  215. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this));
  216. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this));
  217. */
  218. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x000F, this)); //sunlight
  219. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this)); //R
  220. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this)); //G
  221. mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this)); //B
  222. propogateLight();
  223. }
  224. glm::ivec3 Chunk::getWorldCoords(const int& x, const int& y, const int& z) {
  225. return glm::ivec3(mPos.x * 16 + x,
  226. mPos.y * 16 + y,
  227. mPos.z * 16 + z);
  228. }
  229. unsigned Chunk::getLightLevel(const glm::ivec3 &pos) {
  230. if (isVoxelSolid(pos.x, pos.y, pos.z)) {
  231. return 0;
  232. }
  233. return 15;
  234. }
  235. unsigned short Chunk::getLightPacked(const glm::ivec3 &pos) {
  236. //checking if pos is inside current chunk
  237. auto lPos = localPosToLocalPos(mPos, pos); //returns a chunk/local pos pair
  238. if (lPos.first != mPos) { //if it's not THIS chunk
  239. auto chunk = mLinkedWorld.getChunk(lPos.first);
  240. if (chunk)
  241. return chunk->fastGetLightPacked(lPos.second);
  242. else
  243. return 0xFFFF;
  244. }
  245. int index = pos.x + 16 * (pos.y + 16 * pos.z);
  246. return mLighting[index].load(std::memory_order_consume);
  247. }
  248. void Chunk::setLightPacked(const glm::ivec3& pos, const unsigned short& light) {
  249. //checking if pos is inside current chunk
  250. auto lPos = localPosToLocalPos(mPos, pos); //returns a chunk/local pos pair
  251. if (lPos.first != mPos) { //if it's not THIS chunk
  252. auto chunk = mLinkedWorld.getChunk(lPos.first);
  253. if (chunk)
  254. return chunk->setLightPacked(lPos.second, light);
  255. else
  256. return;
  257. }
  258. int index = pos.x + 16 * (pos.y + 16 * pos.z);
  259. mLighting[index].store(light, std::memory_order_release);
  260. }
  261. HeightMap* Chunk::getHeightMap() {
  262. return mLinkedWorld.getHeightMap(glm::ivec2(mPos.x,mPos.z));
  263. }
  264. void Chunk::setPos(const glm::ivec3& pos) {
  265. mPos = pos;
  266. }
  267. glm::ivec3 Chunk::getPos() {
  268. return mPos;
  269. }
  270. World& Chunk::getWorld() {
  271. return mLinkedWorld;
  272. }
  273. unsigned short Chunk::fastGetLightPacked(const glm::ivec3 &pos) {
  274. int index = vecToIndex(pos);
  275. //std::cout << mPos.x << "," << mPos.y << "," << mPos.z << std::endl;
  276. return mLighting[index].load(std::memory_order_acquire);
  277. }
  278. void Chunk::setQueuedForMeshRebuild(const bool& rebuild) {
  279. mQueuedForMeshRebuild = rebuild;
  280. }
  281. bool Chunk::isQueuedForMeshRebuild() {
  282. return mQueuedForMeshRebuild.load();
  283. }
  284. void Chunk::propogateLight() {
  285. if (mPropogating.load(std::memory_order_acq_rel)) return;
  286. mPropogating.store(true, std::memory_order_acq_rel);
  287. propogateLightTask();
  288. }
  289. void Chunk::propogateLightTask() {
  290. while(!mDarkBFSQueue.empty()) {
  291. //TODO Mask channels properly
  292. //load front of queue
  293. short index;
  294. unsigned short mask;
  295. Chunk* chunk;
  296. std::tie(index, mask, chunk) = mDarkBFSQueue.front();
  297. mDarkBFSQueue.pop();
  298. glm::ivec3 pos = indexToVec(index);
  299. unsigned short light = chunk->getLightPacked(pos);
  300. chunk->setLightPacked(pos, light & (~ mask)); //mask out correct channel
  301. auto propogateDark =
  302. [&](glm::ivec3 nPos, Chunk* nChunk)
  303. {
  304. auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
  305. nChunk = mLinkedWorld.getChunk(checkPos.first);
  306. nPos = checkPos.second;
  307. //return early if chunk doesn't exist or voxel is solid
  308. if (nChunk == nullptr || nChunk->isVoxelSolid(nPos)) return;
  309. auto newLight = nChunk->fastGetLightPacked(nPos);
  310. if ((newLight & mask) == 0) return;
  311. //sunglight (to handle special case with straight down
  312. if (mask == 0x000F && ((newLight & 0xF) < (light & 0xF) || (pos.y + chunk->getPos().y * 16 > nPos.y + nChunk->getPos().y * 16 && (light & 0xF) == 0xF))) {
  313. mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  314. } else if (mask == 0x000F) {
  315. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  316. }
  317. // Red
  318. if (mask == 0xF000 && ((newLight >> 12) < (light >> 12))) {
  319. mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  320. } else if (mask == 0xF000) {
  321. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  322. }
  323. // Green
  324. if (mask == 0x0F00 && ((newLight & mask) >> 8 < (light & mask) >> 8)) {
  325. mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  326. } else if (mask == 0x0F00) {
  327. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  328. }
  329. // Blue
  330. if (mask == 0x00F0 && ((newLight & mask) >> 4 < (light & mask) >> 4)) {
  331. mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  332. } else if (mask == 0x00F0) {
  333. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  334. }
  335. };
  336. //visit neighbors
  337. propogateDark(glm::ivec3(pos.x-1, pos.y, pos.z), chunk);
  338. propogateDark(glm::ivec3(pos.x+1, pos.y, pos.z), chunk);
  339. propogateDark(glm::ivec3(pos.x, pos.y-1, pos.z), chunk);
  340. propogateDark(glm::ivec3(pos.x, pos.y+1, pos.z), chunk);
  341. propogateDark(glm::ivec3(pos.x, pos.y, pos.z-1), chunk);
  342. propogateDark(glm::ivec3(pos.x, pos.y, pos.z+1), chunk);
  343. }
  344. while(!mLightBFSQueue.empty()) {
  345. //TODO Mask channels properly
  346. //load front of queue
  347. short index;
  348. unsigned short mask;
  349. Chunk* chunk;
  350. std::tie(index, mask, chunk) = mLightBFSQueue.front();
  351. mLightBFSQueue.pop();
  352. glm::ivec3 pos = indexToVec(index);
  353. unsigned short light = chunk->getLightPacked(pos);
  354. //std::cout << light << std::endl;
  355. //std::cout << pos.x << "," << pos.y << "," << pos.z << std::endl;
  356. auto propogateLight =
  357. [&](glm::ivec3 nPos, Chunk* nChunk)
  358. {
  359. auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
  360. nChunk = mLinkedWorld.getChunk(checkPos.first);
  361. nPos = checkPos.second;
  362. //return early if chunk doesn't exist or voxel is solid
  363. if (nChunk == nullptr || nChunk->isVoxelSolid(nPos)) return;
  364. auto newLight = nChunk->fastGetLightPacked(nPos);
  365. //red channel
  366. if (mask == 0xF000 && (light >> 12) > (newLight >> 12) + 1) {
  367. nChunk->setLightPacked(nPos, ((newLight & (~ mask)) | (((light >> 12) - 1)) << 12)); //set new light level with proper masking
  368. if (((light & mask) >> 12) - 1 > 1) {
  369. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  370. }
  371. }
  372. //green channel
  373. if (mask == 0x0F00 && ((light >> 8) & 0xF) > ((newLight >> 8) & 0xF) + 1) {
  374. nChunk->setLightPacked(nPos, ((newLight & (~ mask)) | ((((light >> 8) & 0xF) - 1)) << 8)); //set new light level with proper masking
  375. if (((light & mask) >> 8) - 1 > 1) {
  376. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  377. }
  378. }
  379. //blue channel
  380. if (mask == 0x00F0 && ((light >> 4) & 0xF) > ((newLight >> 4) & 0xF) + 1) {
  381. nChunk->setLightPacked(nPos, ((newLight & (~ mask)) | ((((light >> 4) & 0xF) - 1)) << 4)); //set new light level with proper masking
  382. if (((light & mask) >> 4) - 1 > 1) {
  383. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  384. }
  385. }
  386. //sunglight channel
  387. if (mask == 0x000F && (light & 0xF) > (newLight & 0xF) + 1) {
  388. //handle straight down propogation
  389. if (pos.y + chunk->getPos().y * 16 > nPos.y + nChunk->getPos().y * 16 && (light & 0xF) == 0xF) {
  390. nChunk->setLightPacked(nPos, newLight | 0xF);
  391. } else {
  392. nChunk->setLightPacked(nPos, (((newLight & (~ mask)) | (light & 0xF)) - 1));
  393. }
  394. if ((light & mask) - 1 > 1) {
  395. mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
  396. }
  397. }
  398. };
  399. //visit neighbors
  400. propogateLight(glm::ivec3(pos.x-1, pos.y, pos.z), chunk);
  401. propogateLight(glm::ivec3(pos.x+1, pos.y, pos.z), chunk);
  402. propogateLight(glm::ivec3(pos.x, pos.y-1, pos.z), chunk);
  403. propogateLight(glm::ivec3(pos.x, pos.y+1, pos.z), chunk);
  404. propogateLight(glm::ivec3(pos.x, pos.y, pos.z-1), chunk);
  405. propogateLight(glm::ivec3(pos.x, pos.y, pos.z+1), chunk);
  406. }
  407. //release the the task
  408. mPropogating.store(false);
  409. //restart it if any light updates slipped through the cracks
  410. if (!mDarkBFSQueue.empty() ) {//|| !mLightBFSQueue.empty()) {
  411. propogateLight();
  412. }
  413. }
  414. short Chunk::vecToIndex(const glm::ivec3& pos) {
  415. return pos.x + 16 * (pos.y + 16 * pos.z);
  416. }
  417. glm::ivec3 Chunk::indexToVec(const short& index) {
  418. return glm::ivec3(index % 16,
  419. (index / 16) % 16,
  420. index / 256);
  421. }
  422. }