A 2D tile-based sandbox game.
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.

LightServer.h 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #pragma once
  2. #include <thread>
  3. #include <vector>
  4. #include <unordered_map>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <utility>
  8. #include <bitset>
  9. #include "common.h"
  10. namespace Swan {
  11. struct NewLightChunk {
  12. std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks;
  13. std::map<std::pair<int, int>, float> lightSources;
  14. };
  15. struct LightChunk {
  16. LightChunk() = default;
  17. LightChunk(NewLightChunk &&ch);
  18. std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks;
  19. uint8_t lightLevels[CHUNK_WIDTH * CHUNK_HEIGHT] = { 0 };
  20. float lightBuffers[CHUNK_WIDTH * CHUNK_HEIGHT * 2] = { 0 };
  21. int buffer = 0;
  22. uint8_t blocksLine[CHUNK_WIDTH] = { 0 };
  23. std::map<std::pair<int, int>, float> lightSources;
  24. std::vector<std::pair<TilePos, float>> bounces;
  25. float *lightBuffer() { return lightBuffers + CHUNK_WIDTH * CHUNK_HEIGHT * buffer; }
  26. bool wasUpdated = false;
  27. };
  28. class LightCallback {
  29. public:
  30. virtual ~LightCallback() = default;
  31. virtual void onLightChunkUpdated(const LightChunk &chunk, ChunkPos pos) = 0;
  32. };
  33. class LightServer {
  34. public:
  35. LightServer(LightCallback &cb);
  36. ~LightServer();
  37. void onSolidBlockAdded(TilePos pos);
  38. void onSolidBlockRemoved(TilePos pos);
  39. void onLightAdded(TilePos pos, float level);
  40. void onLightRemoved(TilePos pos, float level);
  41. void onChunkAdded(ChunkPos pos, NewLightChunk &&chunk);
  42. void onChunkRemoved(ChunkPos pos);
  43. void flip();
  44. private:
  45. static constexpr int LIGHT_CUTOFF_DIST = 64;
  46. static constexpr float LIGHT_CUTOFF = 0.001;
  47. struct Event {
  48. enum class Tag {
  49. BLOCK_ADDED, BLOCK_REMOVED, LIGHT_ADDED, LIGHT_REMOVED,
  50. CHUNK_ADDED, CHUNK_REMOVED,
  51. } tag;
  52. TilePos pos;
  53. union {
  54. float f;
  55. int i;
  56. };
  57. };
  58. bool tileIsSolid(TilePos pos);
  59. LightChunk *getChunk(ChunkPos cpos);
  60. float recalcTile(
  61. LightChunk &chunk, ChunkPos cpos, Vec2i rpos, TilePos base,
  62. std::vector<std::pair<TilePos, float>> &lights);
  63. void processChunkSun(LightChunk &chunk, ChunkPos cpos);
  64. void processChunkLights(LightChunk &chunk, ChunkPos cpos);
  65. void processChunkBounces(LightChunk &chunk, ChunkPos cpos);
  66. void processChunkSmoothing(LightChunk &chunk, ChunkPos cpos);
  67. void finalizeChunk(LightChunk &chunk);
  68. void processEvent(const Event &event, std::vector<NewLightChunk> &newChunks);
  69. void run();
  70. bool running_ = true;
  71. std::map<std::pair<int, int>, LightChunk> chunks_;
  72. std::set<std::pair<int, int>> updatedChunks_;
  73. LightChunk *cachedChunk_ = nullptr;
  74. Vec2i cachedChunkPos_;
  75. int buffer_ = 0;
  76. std::vector<Event> buffers_[2] = { {}, {} };
  77. std::vector<NewLightChunk> newChunkBuffers_[2] = { {}, {} };
  78. std::condition_variable cond_;
  79. std::mutex mut_;
  80. LightCallback &cb_;
  81. std::thread thread_;
  82. };
  83. inline void LightServer::onSolidBlockAdded(TilePos pos) {
  84. std::lock_guard<std::mutex> lock(mut_);
  85. buffers_[buffer_].push_back({ Event::Tag::BLOCK_ADDED, pos, { .i = 0 } });
  86. }
  87. inline void LightServer::onSolidBlockRemoved(TilePos pos) {
  88. std::lock_guard<std::mutex> lock(mut_);
  89. buffers_[buffer_].push_back({ Event::Tag::BLOCK_REMOVED, pos, { .i = 0 } });
  90. }
  91. inline void LightServer::onLightAdded(TilePos pos, float level) {
  92. std::lock_guard<std::mutex> lock(mut_);
  93. buffers_[buffer_].push_back({ Event::Tag::LIGHT_ADDED, pos, { .f = level } });
  94. }
  95. inline void LightServer::onLightRemoved(TilePos pos, float level) {
  96. std::lock_guard<std::mutex> lock(mut_);
  97. buffers_[buffer_].push_back({ Event::Tag::LIGHT_REMOVED, pos, { .f = level } });
  98. }
  99. inline void LightServer::onChunkAdded(Vec2i pos, NewLightChunk &&chunk) {
  100. std::lock_guard<std::mutex> lock(mut_);
  101. buffers_[buffer_].push_back({ Event::Tag::CHUNK_ADDED, pos,
  102. { .i = (int)newChunkBuffers_[buffer_].size() } });
  103. newChunkBuffers_[buffer_].push_back(std::move(chunk));
  104. }
  105. inline void LightServer::onChunkRemoved(Vec2i pos) {
  106. std::lock_guard<std::mutex> lock(mut_);
  107. buffers_[buffer_].push_back({ Event::Tag::CHUNK_REMOVED, pos, { .i = 0 } });
  108. }
  109. inline void LightServer::flip() {
  110. cond_.notify_one();
  111. }
  112. }