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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. #include "LightServer.h"
  2. #include <algorithm>
  3. #include "log.h"
  4. namespace Swan {
  5. static ChunkPos lightChunkPos(TilePos pos) {
  6. // Same logic as in WorldPlane.cc
  7. return Vec2i(
  8. ((size_t)pos.x + (LLONG_MAX / 2) + 1) / CHUNK_WIDTH -
  9. ((LLONG_MAX / 2) / CHUNK_WIDTH) - 1,
  10. ((size_t)pos.y + (LLONG_MAX / 2) + 1) / CHUNK_HEIGHT -
  11. ((LLONG_MAX / 2) / CHUNK_HEIGHT) - 1);
  12. }
  13. static Vec2i lightRelPos(TilePos pos) {
  14. // Same logic as in WorldPlane.cc
  15. return Vec2i(
  16. (pos.x + (size_t)CHUNK_WIDTH * ((LLONG_MAX / 2) /
  17. CHUNK_WIDTH)) % CHUNK_WIDTH,
  18. (pos.y + (size_t)CHUNK_HEIGHT * ((LLONG_MAX / 2) /
  19. CHUNK_HEIGHT)) % CHUNK_HEIGHT);
  20. }
  21. static uint8_t linToSRGB(float lin) {
  22. float s;
  23. if (lin <= 0.0031308) {
  24. s = lin / 12.92;
  25. } else {
  26. s = 1.055 * std::pow(lin, 1/2.4) - 0.055;
  27. }
  28. return std::clamp((int)round(s * 255), 0, 255);
  29. }
  30. static float attenuate(float dist, float squareDist) {
  31. return 1 / (1 + 1 * dist + 0.02 * squareDist);
  32. }
  33. static float attenuate(float dist) {
  34. return attenuate(dist, dist * dist);
  35. }
  36. static float attenuateSquared(float squareDist) {
  37. return attenuate(sqrt(squareDist), squareDist);
  38. }
  39. LightChunk::LightChunk(NewLightChunk &&ch):
  40. blocks(std::move(ch.blocks)), lightSources(std::move(ch.lightSources)) {
  41. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  42. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  43. if (blocks[y * CHUNK_WIDTH + x]) {
  44. blocksLine[x] += 1;
  45. }
  46. }
  47. }
  48. }
  49. LightServer::LightServer(LightCallback &cb):
  50. cb_(cb), thread_(&LightServer::run, this) {}
  51. LightServer::~LightServer() {
  52. running_ = false;
  53. cond_.notify_one();
  54. thread_.join();
  55. }
  56. bool LightServer::tileIsSolid(TilePos pos) {
  57. ChunkPos cpos = lightChunkPos(pos);
  58. LightChunk *chunk = getChunk(cpos);
  59. if (chunk == nullptr) {
  60. return true;
  61. }
  62. Vec2i rpos = lightRelPos(pos);
  63. return chunk->blocks[rpos.y * CHUNK_WIDTH + rpos.x];
  64. }
  65. LightChunk *LightServer::getChunk(ChunkPos cpos) {
  66. if (cachedChunk_ && cachedChunkPos_ == cpos) {
  67. return cachedChunk_;
  68. }
  69. auto it = chunks_.find(cpos);
  70. if (it != chunks_.end()) {
  71. cachedChunk_ = &it->second;
  72. cachedChunkPos_ = cpos;
  73. return &it->second;
  74. }
  75. return nullptr;
  76. }
  77. void LightServer::processEvent(const Event &evt, std::vector<NewLightChunk> &newChunks) {
  78. auto markAdjacentChunksModified = [&](ChunkPos cpos) {
  79. for (int y = -1; y <= 1; ++y) {
  80. for (int x = -1; x <= 1; ++x) {
  81. updatedChunks_.insert(cpos + Vec2i(x, y));
  82. }
  83. }
  84. };
  85. auto markChunks = [&](ChunkPos cpos, Vec2i rpos, bool l, bool r, bool t, bool b) {
  86. updatedChunks_.insert(cpos);
  87. if (l) updatedChunks_.insert(cpos + Vec2i(-1, 0));
  88. if (r) updatedChunks_.insert(cpos + Vec2i(1, 0));
  89. if (t) updatedChunks_.insert(cpos + Vec2i(0, -1));
  90. if (b) updatedChunks_.insert(cpos + Vec2i(0, 1));
  91. if (l && t) updatedChunks_.insert(cpos + Vec2i(-1, -1));
  92. if (r && t) updatedChunks_.insert(cpos + Vec2i(1, -1));
  93. if (l && b) updatedChunks_.insert(cpos + Vec2i(-1, 1));
  94. if (r && b) updatedChunks_.insert(cpos + Vec2i(1, 1));
  95. };
  96. auto markChunksModified = [&](ChunkPos cpos, Vec2i rpos, float light) {
  97. markChunks(cpos, rpos,
  98. light * attenuate(std::max(rpos.x - 5, 0)) >= LIGHT_CUTOFF,
  99. light * attenuate(std::max(CHUNK_WIDTH - rpos.x - 5, 0)) >= LIGHT_CUTOFF,
  100. light * attenuate(std::max(rpos.y - 5, 0)) >= LIGHT_CUTOFF,
  101. light * attenuate(std::max(CHUNK_HEIGHT - rpos.y - 5, 0)) >= LIGHT_CUTOFF);
  102. };
  103. auto markChunksModifiedRange = [&](ChunkPos cpos, Vec2i rpos, int range) {
  104. markChunks(cpos, rpos,
  105. rpos.x <= range,
  106. CHUNK_WIDTH - rpos.x <= range,
  107. rpos.y <= range,
  108. CHUNK_WIDTH - rpos.y <= range);
  109. };
  110. if (evt.tag == Event::Tag::CHUNK_ADDED) {
  111. chunks_.emplace(std::piecewise_construct,
  112. std::forward_as_tuple(evt.pos),
  113. std::forward_as_tuple(std::move(newChunks[evt.i])));
  114. markAdjacentChunksModified(evt.pos);
  115. return;
  116. } else if (evt.tag == Event::Tag::CHUNK_REMOVED) {
  117. chunks_.erase(evt.pos);
  118. markAdjacentChunksModified(evt.pos);
  119. return;
  120. }
  121. ChunkPos cpos = lightChunkPos(evt.pos);
  122. LightChunk *ch = getChunk(cpos);
  123. if (!ch) return;
  124. Vec2i rpos = lightRelPos(evt.pos);
  125. switch (evt.tag) {
  126. case Event::Tag::BLOCK_ADDED:
  127. ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, true);
  128. ch->blocksLine[rpos.x] += 1;
  129. markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST);
  130. break;
  131. case Event::Tag::BLOCK_REMOVED:
  132. ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, false);
  133. ch->blocksLine[rpos.x] -= 1;
  134. markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST);
  135. break;
  136. case Event::Tag::LIGHT_ADDED:
  137. info << cpos << ": Add " << evt.f << " light to " << rpos;
  138. ch->lightSources[rpos] += evt.f;
  139. markChunksModified(cpos, rpos, ch->lightSources[rpos]);
  140. break;
  141. case Event::Tag::LIGHT_REMOVED:
  142. info << cpos << ": Remove " << evt.f << " light to " << rpos;
  143. markChunksModified(cpos, rpos, ch->lightSources[rpos]);
  144. ch->lightSources[rpos] -= evt.f;
  145. if (ch->lightSources[rpos] < LIGHT_CUTOFF) {
  146. ch->lightSources.erase(rpos);
  147. }
  148. break;
  149. // These were handled earlier
  150. case Event::Tag::CHUNK_ADDED:
  151. case Event::Tag::CHUNK_REMOVED:
  152. break;
  153. }
  154. }
  155. float LightServer::recalcTile(
  156. LightChunk &chunk, ChunkPos cpos, Vec2i rpos, TilePos base,
  157. std::vector<std::pair<TilePos, float>> &lights) {
  158. TilePos pos = rpos + base;
  159. constexpr int accuracy = 4;
  160. auto raycast = [&](Vec2 from, Vec2 to) {
  161. auto diff = to - from;
  162. float dist = ((Vec2)diff).length();
  163. Vec2 step = (Vec2)diff / (dist * accuracy);
  164. Vec2 currpos = from;
  165. TilePos currtile = TilePos(floor(currpos.x), floor(currpos.y));
  166. auto proceed = [&]() {
  167. TilePos t;
  168. while ((t = TilePos(floor(currpos.x), floor(currpos.y))) == currtile) {
  169. currpos += step;
  170. }
  171. currtile = t;
  172. };
  173. bool hit = false;
  174. Vec2i target = TilePos(floor(to.x), floor(to.y));
  175. while (currtile != target && (currpos - from).squareLength() <= diff.squareLength()) {
  176. if (tileIsSolid(currtile)) {
  177. hit = true;
  178. break;
  179. }
  180. proceed();
  181. }
  182. return hit;
  183. };
  184. auto diffusedRaycast = [&](Vec2 from, Vec2 to, Vec2 norm) {
  185. if (raycast(from, to)) {
  186. return 0.0f;
  187. }
  188. float dot = (to - from).norm().dot(norm);
  189. if (dot > 1) dot = 1;
  190. else if (dot < 0) dot = 0;
  191. return dot;
  192. };
  193. bool isSolid = tileIsSolid(pos);
  194. bool culled =
  195. tileIsSolid(pos + Vec2i(-1, 0)) &&
  196. tileIsSolid(pos + Vec2i(1, 0)) &&
  197. tileIsSolid(pos + Vec2i(0, -1)) &&
  198. tileIsSolid(pos + Vec2i(0, 1));
  199. float acc = 0;
  200. for (auto &[lightpos, level]: lights) {
  201. if (lightpos == pos) {
  202. acc += level;
  203. continue;
  204. }
  205. if (culled) {
  206. continue;
  207. }
  208. int squareDist = (lightpos - pos).squareLength();
  209. if (squareDist > LIGHT_CUTOFF_DIST * LIGHT_CUTOFF_DIST) {
  210. continue;
  211. }
  212. float light = level * attenuateSquared(squareDist);
  213. if (light < LIGHT_CUTOFF) {
  214. continue;
  215. }
  216. if (!isSolid) {
  217. bool hit = raycast(
  218. Vec2(pos.x + 0.5, pos.y + 0.5),
  219. Vec2(lightpos.x + 0.5, lightpos.y + 0.5));
  220. if (!hit) {
  221. acc += light;
  222. }
  223. continue;
  224. }
  225. float frac = 0;
  226. float f;
  227. if ((f = diffusedRaycast(
  228. Vec2(pos.x + 0.5, pos.y - 0.1),
  229. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  230. Vec2(0, -1))) > frac) {
  231. frac = f;
  232. }
  233. if ((f = diffusedRaycast(
  234. Vec2(pos.x + 0.5, pos.y + 1.1),
  235. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  236. Vec2(0, 1))) > frac) {
  237. frac = f;
  238. }
  239. if ((f = diffusedRaycast(
  240. Vec2(pos.x - 0.1, pos.y + 0.5),
  241. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  242. Vec2(-1, 0))) > frac) {
  243. frac = f;
  244. }
  245. if ((f = diffusedRaycast(
  246. Vec2(pos.x + 1.1, pos.y + 0.5),
  247. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  248. Vec2(1, 0))) > frac) {
  249. frac = f;
  250. }
  251. acc += light * frac;
  252. }
  253. return acc;
  254. }
  255. void LightServer::processChunkSun(LightChunk &chunk, ChunkPos cpos) {
  256. LightChunk *tc = getChunk(cpos + Vec2i(0, -1));
  257. int base = cpos.y * CHUNK_HEIGHT;
  258. std::bitset<CHUNK_WIDTH> line;
  259. for (int ry = 0; ry < CHUNK_HEIGHT; ++ry) {
  260. int y = base + ry;
  261. float light;
  262. if (y <= 20) {
  263. light = 1;
  264. } else {
  265. light = attenuate(y - 20);
  266. if (light < LIGHT_CUTOFF) {
  267. light = 0;
  268. }
  269. }
  270. for (int rx = 0; rx < CHUNK_WIDTH; ++rx) {
  271. bool lit = light > 0 && tc && tc->blocksLine[rx] == 0 && !line[rx];
  272. if (lit) {
  273. chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = light;
  274. if (chunk.blocks[ry * CHUNK_WIDTH + rx]) {
  275. line[rx] = true;
  276. }
  277. } else {
  278. chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = 0;
  279. }
  280. }
  281. }
  282. }
  283. void LightServer::processChunkLights(LightChunk &chunk, ChunkPos cpos) {
  284. TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
  285. std::vector<std::pair<TilePos, float>> lights;
  286. for (auto &[pos, level]: chunk.lightSources) {
  287. lights.emplace_back(Vec2i(pos) + base, level);
  288. }
  289. auto addLightFromChunk = [&](LightChunk *chunk, int dx, int dy) {
  290. if (chunk == nullptr) {
  291. return;
  292. }
  293. TilePos b = base + Vec2i(dx * CHUNK_WIDTH, dy * CHUNK_HEIGHT);
  294. for (auto &[pos, level]: chunk->lightSources) {
  295. lights.emplace_back(TilePos(pos) + b, level);
  296. }
  297. };
  298. for (int y = -1; y <= 1; ++y) {
  299. for (int x = -1; x <= 1; ++x) {
  300. if (y == 0 && x == 0) continue;
  301. addLightFromChunk(getChunk(cpos + Vec2i(x, y)), x, y);
  302. }
  303. }
  304. chunk.bounces.clear();
  305. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  306. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  307. float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
  308. chunk.lightBuffer()[y * CHUNK_WIDTH + x] += light;
  309. if (light > 0 && chunk.blocks[y * CHUNK_WIDTH + x]) {
  310. chunk.bounces.emplace_back(base + Vec2i(x, y), light * 0.1);
  311. }
  312. }
  313. }
  314. }
  315. void LightServer::processChunkBounces(LightChunk &chunk, ChunkPos cpos) {
  316. TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
  317. std::vector<std::pair<TilePos, float>> lights;
  318. for (auto &light: chunk.bounces) {
  319. lights.emplace_back(light);
  320. }
  321. auto addLightFromChunk = [&](LightChunk *chunk) {
  322. if (chunk == nullptr) {
  323. return;
  324. }
  325. for (auto &light: chunk->bounces) {
  326. lights.emplace_back(light);
  327. }
  328. };
  329. for (int y = -1; y <= 1; ++y) {
  330. for (int x = -1; x <= 1; ++x) {
  331. if (y == 0 && x == 0) continue;
  332. addLightFromChunk(getChunk(cpos + Vec2i(x, y)));
  333. }
  334. }
  335. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  336. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  337. float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
  338. float sum = chunk.lightBuffer()[y * CHUNK_WIDTH + x] + light;
  339. chunk.lightBuffer()[y * CHUNK_WIDTH + x] = sum;
  340. }
  341. }
  342. }
  343. void LightServer::processChunkSmoothing(LightChunk &chunk, ChunkPos cpos) {
  344. LightChunk *tc = getChunk(cpos + Vec2i(0, -1));
  345. LightChunk *bc = getChunk(cpos + Vec2i(0, 1));
  346. LightChunk *lc = getChunk(cpos + Vec2i(-1, 0));
  347. LightChunk *rc = getChunk(cpos + Vec2i(1, 0));
  348. auto getLight = [&](LightChunk &chunk, int x, int y) {
  349. return chunk.lightBuffer()[y * CHUNK_WIDTH + x];
  350. };
  351. auto calc = [&](int x1, int x2, int y1, int y2, auto tf, auto bf, auto lf, auto rf) {
  352. float *dest = chunk.lightBuffers + CHUNK_WIDTH * CHUNK_HEIGHT * ((chunk.buffer + 1) % 2);
  353. for (int y = y1; y < y2; ++y) {
  354. for (int x = x1; x < x2; ++x) {
  355. float t = tf(x, y);
  356. float b = bf(x, y);
  357. float l = lf(x, y);
  358. float r = rf(x, y);
  359. float light = chunk.lightBuffer()[y * CHUNK_WIDTH + x];
  360. int count = 1;
  361. if (t > light) { light += t; count += 1; }
  362. if (b > light) { light += b; count += 1; }
  363. if (l > light) { light += l; count += 1; }
  364. if (r > light) { light += r; count += 1; }
  365. light /= count;
  366. dest[y * CHUNK_WIDTH + x] = light;
  367. }
  368. }
  369. };
  370. calc(1, CHUNK_WIDTH - 1, 1, CHUNK_HEIGHT - 1,
  371. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  372. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  373. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  374. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  375. if (tc) {
  376. calc(1, CHUNK_WIDTH - 1, 0, 1,
  377. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  378. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  379. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  380. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  381. }
  382. if (bc) {
  383. calc(1, CHUNK_WIDTH - 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  384. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  385. [&](int x, int y) { return bc->lightBuffer()[x]; },
  386. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  387. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  388. }
  389. if (lc) {
  390. calc(0, 1, 1, CHUNK_HEIGHT - 1,
  391. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  392. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  393. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  394. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  395. }
  396. if (rc) {
  397. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 1, CHUNK_HEIGHT - 1,
  398. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  399. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  400. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  401. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  402. }
  403. if (tc && lc) {
  404. calc(0, 1, 0, 1,
  405. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  406. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  407. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  408. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  409. }
  410. if (tc && rc) {
  411. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 0, 1,
  412. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  413. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  414. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  415. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  416. }
  417. if (bc && lc) {
  418. calc(0, 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  419. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  420. [&](int x, int y) { return bc->lightBuffer()[x]; },
  421. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  422. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  423. }
  424. if (bc && rc) {
  425. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  426. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  427. [&](int x, int y) { return bc->lightBuffer()[x]; },
  428. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  429. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  430. }
  431. }
  432. void LightServer::finalizeChunk(LightChunk &chunk) {
  433. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  434. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  435. chunk.lightLevels[y * CHUNK_WIDTH + x] =
  436. linToSRGB(chunk.lightBuffer()[y * CHUNK_HEIGHT + x]);
  437. }
  438. }
  439. }
  440. void LightServer::run() {
  441. std::unique_lock<std::mutex> lock(mut_, std::defer_lock);
  442. while (running_) {
  443. lock.lock();
  444. cond_.wait(lock, [&] { return buffers_[buffer_].size() > 0 || !running_; });
  445. std::vector<Event> &buf = buffers_[buffer_];
  446. std::vector<NewLightChunk> &newChunks = newChunkBuffers_[buffer_];
  447. buffer_ = (buffer_ + 1) % 2;
  448. lock.unlock();
  449. updatedChunks_.clear();
  450. for (auto &evt: buf) {
  451. processEvent(evt, newChunks);
  452. }
  453. buf.clear();
  454. newChunks.clear();
  455. auto start = std::chrono::steady_clock::now();
  456. for (auto &pos: updatedChunks_) {
  457. auto ch = chunks_.find(pos);
  458. if (ch != chunks_.end()) {
  459. processChunkSun(ch->second, ChunkPos(pos.first, pos.second));
  460. }
  461. }
  462. for (auto &pos: updatedChunks_) {
  463. auto ch = chunks_.find(pos);
  464. if (ch != chunks_.end()) {
  465. processChunkLights(ch->second, ChunkPos(pos.first, pos.second));
  466. }
  467. }
  468. for (auto &pos: updatedChunks_) {
  469. auto ch = chunks_.find(pos);
  470. if (ch != chunks_.end()) {
  471. processChunkBounces(ch->second, ChunkPos(pos.first, pos.second));
  472. }
  473. }
  474. for (int i = 0; i < 4; ++i) {
  475. for (auto &pos: updatedChunks_) {
  476. auto ch = chunks_.find(pos);
  477. if (ch != chunks_.end()) {
  478. processChunkSmoothing(ch->second, ChunkPos(pos.first, pos.second));
  479. ch->second.buffer = (ch->second.buffer + 1) % 2;
  480. }
  481. }
  482. }
  483. for (auto &pos: updatedChunks_) {
  484. auto ch = chunks_.find(pos);
  485. if (ch != chunks_.end()) {
  486. finalizeChunk(ch->second);
  487. }
  488. }
  489. auto end = std::chrono::steady_clock::now();
  490. auto dur = std::chrono::duration<double, std::milli>(end - start);
  491. info << "Generating light for " << updatedChunks_.size()
  492. << " chunks took " << dur.count() << "ms";
  493. for (auto &pos: updatedChunks_) {
  494. auto ch = chunks_.find(pos);
  495. if (ch != chunks_.end()) {
  496. cb_.onLightChunkUpdated(ch->second, pos);
  497. }
  498. }
  499. }
  500. }
  501. }