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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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. ch->lightSources[rpos] += evt.f;
  138. markChunksModified(cpos, rpos, ch->lightSources[rpos]);
  139. break;
  140. case Event::Tag::LIGHT_REMOVED:
  141. markChunksModified(cpos, rpos, ch->lightSources[rpos]);
  142. ch->lightSources[rpos] -= evt.f;
  143. if (ch->lightSources[rpos] < LIGHT_CUTOFF) {
  144. ch->lightSources.erase(rpos);
  145. }
  146. break;
  147. // These were handled earlier
  148. case Event::Tag::CHUNK_ADDED:
  149. case Event::Tag::CHUNK_REMOVED:
  150. break;
  151. }
  152. }
  153. float LightServer::recalcTile(
  154. LightChunk &chunk, ChunkPos cpos, Vec2i rpos, TilePos base,
  155. std::vector<std::pair<TilePos, float>> &lights) {
  156. TilePos pos = rpos + base;
  157. constexpr int accuracy = 4;
  158. auto raycast = [&](Vec2 from, Vec2 to) {
  159. auto diff = to - from;
  160. float dist = ((Vec2)diff).length();
  161. Vec2 step = (Vec2)diff / (dist * accuracy);
  162. Vec2 currpos = from;
  163. TilePos currtile = TilePos(floor(currpos.x), floor(currpos.y));
  164. auto proceed = [&]() {
  165. TilePos t;
  166. while ((t = TilePos(floor(currpos.x), floor(currpos.y))) == currtile) {
  167. currpos += step;
  168. }
  169. currtile = t;
  170. };
  171. bool hit = false;
  172. Vec2i target = TilePos(floor(to.x), floor(to.y));
  173. while (currtile != target && (currpos - from).squareLength() <= diff.squareLength()) {
  174. if (tileIsSolid(currtile)) {
  175. hit = true;
  176. break;
  177. }
  178. proceed();
  179. }
  180. return hit;
  181. };
  182. auto diffusedRaycast = [&](Vec2 from, Vec2 to, Vec2 norm) {
  183. if (raycast(from, to)) {
  184. return 0.0f;
  185. }
  186. float dot = (to - from).norm().dot(norm);
  187. if (dot > 1) dot = 1;
  188. else if (dot < 0) dot = 0;
  189. return dot;
  190. };
  191. bool isSolid = tileIsSolid(pos);
  192. bool culled =
  193. tileIsSolid(pos + Vec2i(-1, 0)) &&
  194. tileIsSolid(pos + Vec2i(1, 0)) &&
  195. tileIsSolid(pos + Vec2i(0, -1)) &&
  196. tileIsSolid(pos + Vec2i(0, 1));
  197. float acc = 0;
  198. for (auto &[lightpos, level]: lights) {
  199. if (lightpos == pos) {
  200. acc += level;
  201. continue;
  202. }
  203. if (culled) {
  204. continue;
  205. }
  206. int squareDist = (lightpos - pos).squareLength();
  207. if (squareDist > LIGHT_CUTOFF_DIST * LIGHT_CUTOFF_DIST) {
  208. continue;
  209. }
  210. float light = level * attenuateSquared(squareDist);
  211. if (light < LIGHT_CUTOFF) {
  212. continue;
  213. }
  214. if (!isSolid) {
  215. bool hit = raycast(
  216. Vec2(pos.x + 0.5, pos.y + 0.5),
  217. Vec2(lightpos.x + 0.5, lightpos.y + 0.5));
  218. if (!hit) {
  219. acc += light;
  220. }
  221. continue;
  222. }
  223. float frac = 0;
  224. float f;
  225. if ((f = diffusedRaycast(
  226. Vec2(pos.x + 0.5, pos.y - 0.1),
  227. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  228. Vec2(0, -1))) > frac) {
  229. frac = f;
  230. }
  231. if ((f = diffusedRaycast(
  232. Vec2(pos.x + 0.5, pos.y + 1.1),
  233. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  234. Vec2(0, 1))) > frac) {
  235. frac = f;
  236. }
  237. if ((f = diffusedRaycast(
  238. Vec2(pos.x - 0.1, pos.y + 0.5),
  239. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  240. Vec2(-1, 0))) > frac) {
  241. frac = f;
  242. }
  243. if ((f = diffusedRaycast(
  244. Vec2(pos.x + 1.1, pos.y + 0.5),
  245. Vec2(lightpos.x + 0.5, lightpos.y + 0.5),
  246. Vec2(1, 0))) > frac) {
  247. frac = f;
  248. }
  249. acc += light * frac;
  250. }
  251. return acc;
  252. }
  253. void LightServer::processChunkSun(LightChunk &chunk, ChunkPos cpos) {
  254. LightChunk *tc = getChunk(cpos + Vec2i(0, -1));
  255. int base = cpos.y * CHUNK_HEIGHT;
  256. std::bitset<CHUNK_WIDTH> line;
  257. for (int ry = 0; ry < CHUNK_HEIGHT; ++ry) {
  258. int y = base + ry;
  259. float light;
  260. if (y <= 20) {
  261. light = 1;
  262. } else {
  263. light = attenuate(y - 20);
  264. if (light < LIGHT_CUTOFF) {
  265. light = 0;
  266. }
  267. }
  268. for (int rx = 0; rx < CHUNK_WIDTH; ++rx) {
  269. bool lit = light > 0 && tc && tc->blocksLine[rx] == 0 && !line[rx];
  270. if (lit) {
  271. chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = light;
  272. if (chunk.blocks[ry * CHUNK_WIDTH + rx]) {
  273. line[rx] = true;
  274. }
  275. } else {
  276. chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = 0;
  277. }
  278. }
  279. }
  280. }
  281. void LightServer::processChunkLights(LightChunk &chunk, ChunkPos cpos) {
  282. TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
  283. std::vector<std::pair<TilePos, float>> lights;
  284. for (auto &[pos, level]: chunk.lightSources) {
  285. lights.emplace_back(Vec2i(pos) + base, level);
  286. }
  287. auto addLightFromChunk = [&](LightChunk *chunk, int dx, int dy) {
  288. if (chunk == nullptr) {
  289. return;
  290. }
  291. TilePos b = base + Vec2i(dx * CHUNK_WIDTH, dy * CHUNK_HEIGHT);
  292. for (auto &[pos, level]: chunk->lightSources) {
  293. lights.emplace_back(TilePos(pos) + b, level);
  294. }
  295. };
  296. for (int y = -1; y <= 1; ++y) {
  297. for (int x = -1; x <= 1; ++x) {
  298. if (y == 0 && x == 0) continue;
  299. addLightFromChunk(getChunk(cpos + Vec2i(x, y)), x, y);
  300. }
  301. }
  302. chunk.bounces.clear();
  303. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  304. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  305. float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
  306. chunk.lightBuffer()[y * CHUNK_WIDTH + x] += light;
  307. if (light > 0 && chunk.blocks[y * CHUNK_WIDTH + x]) {
  308. chunk.bounces.emplace_back(base + Vec2i(x, y), light * 0.1);
  309. }
  310. }
  311. }
  312. }
  313. void LightServer::processChunkBounces(LightChunk &chunk, ChunkPos cpos) {
  314. TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
  315. std::vector<std::pair<TilePos, float>> lights;
  316. for (auto &light: chunk.bounces) {
  317. lights.emplace_back(light);
  318. }
  319. auto addLightFromChunk = [&](LightChunk *chunk) {
  320. if (chunk == nullptr) {
  321. return;
  322. }
  323. for (auto &light: chunk->bounces) {
  324. lights.emplace_back(light);
  325. }
  326. };
  327. for (int y = -1; y <= 1; ++y) {
  328. for (int x = -1; x <= 1; ++x) {
  329. if (y == 0 && x == 0) continue;
  330. addLightFromChunk(getChunk(cpos + Vec2i(x, y)));
  331. }
  332. }
  333. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  334. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  335. float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
  336. float sum = chunk.lightBuffer()[y * CHUNK_WIDTH + x] + light;
  337. chunk.lightBuffer()[y * CHUNK_WIDTH + x] = sum;
  338. }
  339. }
  340. }
  341. void LightServer::processChunkSmoothing(LightChunk &chunk, ChunkPos cpos) {
  342. LightChunk *tc = getChunk(cpos + Vec2i(0, -1));
  343. LightChunk *bc = getChunk(cpos + Vec2i(0, 1));
  344. LightChunk *lc = getChunk(cpos + Vec2i(-1, 0));
  345. LightChunk *rc = getChunk(cpos + Vec2i(1, 0));
  346. auto getLight = [&](LightChunk &chunk, int x, int y) {
  347. return chunk.lightBuffer()[y * CHUNK_WIDTH + x];
  348. };
  349. auto calc = [&](int x1, int x2, int y1, int y2, auto tf, auto bf, auto lf, auto rf) {
  350. float *dest = chunk.lightBuffers + CHUNK_WIDTH * CHUNK_HEIGHT * ((chunk.buffer + 1) % 2);
  351. for (int y = y1; y < y2; ++y) {
  352. for (int x = x1; x < x2; ++x) {
  353. float t = tf(x, y);
  354. float b = bf(x, y);
  355. float l = lf(x, y);
  356. float r = rf(x, y);
  357. float light = chunk.lightBuffer()[y * CHUNK_WIDTH + x];
  358. int count = 1;
  359. if (t > light) { light += t; count += 1; }
  360. if (b > light) { light += b; count += 1; }
  361. if (l > light) { light += l; count += 1; }
  362. if (r > light) { light += r; count += 1; }
  363. light /= count;
  364. dest[y * CHUNK_WIDTH + x] = light;
  365. }
  366. }
  367. };
  368. calc(1, CHUNK_WIDTH - 1, 1, CHUNK_HEIGHT - 1,
  369. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  370. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  371. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  372. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  373. if (tc) {
  374. calc(1, CHUNK_WIDTH - 1, 0, 1,
  375. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  376. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  377. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  378. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  379. }
  380. if (bc) {
  381. calc(1, CHUNK_WIDTH - 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  382. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  383. [&](int x, int y) { return bc->lightBuffer()[x]; },
  384. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  385. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  386. }
  387. if (lc) {
  388. calc(0, 1, 1, CHUNK_HEIGHT - 1,
  389. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  390. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  391. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  392. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  393. }
  394. if (rc) {
  395. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 1, CHUNK_HEIGHT - 1,
  396. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  397. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  398. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  399. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  400. }
  401. if (tc && lc) {
  402. calc(0, 1, 0, 1,
  403. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  404. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  405. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  406. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  407. }
  408. if (tc && rc) {
  409. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 0, 1,
  410. [&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
  411. [&](int x, int y) { return getLight(chunk, x, y + 1); },
  412. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  413. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  414. }
  415. if (bc && lc) {
  416. calc(0, 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  417. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  418. [&](int x, int y) { return bc->lightBuffer()[x]; },
  419. [&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
  420. [&](int x, int y) { return getLight(chunk, x + 1, y); });
  421. }
  422. if (bc && rc) {
  423. calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
  424. [&](int x, int y) { return getLight(chunk, x, y - 1); },
  425. [&](int x, int y) { return bc->lightBuffer()[x]; },
  426. [&](int x, int y) { return getLight(chunk, x - 1, y); },
  427. [&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
  428. }
  429. }
  430. void LightServer::finalizeChunk(LightChunk &chunk) {
  431. for (int y = 0; y < CHUNK_HEIGHT; ++y) {
  432. for (int x = 0; x < CHUNK_WIDTH; ++x) {
  433. chunk.lightLevels[y * CHUNK_WIDTH + x] =
  434. linToSRGB(chunk.lightBuffer()[y * CHUNK_HEIGHT + x]);
  435. }
  436. }
  437. }
  438. void LightServer::run() {
  439. std::unique_lock<std::mutex> lock(mut_, std::defer_lock);
  440. while (running_) {
  441. lock.lock();
  442. cond_.wait(lock, [&] { return buffers_[buffer_].size() > 0 || !running_; });
  443. std::vector<Event> &buf = buffers_[buffer_];
  444. std::vector<NewLightChunk> &newChunks = newChunkBuffers_[buffer_];
  445. buffer_ = (buffer_ + 1) % 2;
  446. lock.unlock();
  447. updatedChunks_.clear();
  448. for (auto &evt: buf) {
  449. processEvent(evt, newChunks);
  450. }
  451. buf.clear();
  452. newChunks.clear();
  453. for (auto &pos: updatedChunks_) {
  454. auto ch = chunks_.find(pos);
  455. if (ch != chunks_.end()) {
  456. processChunkSun(ch->second, ChunkPos(pos.first, pos.second));
  457. }
  458. }
  459. for (auto &pos: updatedChunks_) {
  460. auto ch = chunks_.find(pos);
  461. if (ch != chunks_.end()) {
  462. processChunkLights(ch->second, ChunkPos(pos.first, pos.second));
  463. }
  464. }
  465. for (auto &pos: updatedChunks_) {
  466. auto ch = chunks_.find(pos);
  467. if (ch != chunks_.end()) {
  468. processChunkBounces(ch->second, ChunkPos(pos.first, pos.second));
  469. }
  470. }
  471. for (int i = 0; i < 4; ++i) {
  472. for (auto &pos: updatedChunks_) {
  473. auto ch = chunks_.find(pos);
  474. if (ch != chunks_.end()) {
  475. processChunkSmoothing(ch->second, ChunkPos(pos.first, pos.second));
  476. ch->second.buffer = (ch->second.buffer + 1) % 2;
  477. }
  478. }
  479. }
  480. for (auto &pos: updatedChunks_) {
  481. auto ch = chunks_.find(pos);
  482. if (ch != chunks_.end()) {
  483. finalizeChunk(ch->second);
  484. }
  485. }
  486. for (auto &pos: updatedChunks_) {
  487. auto ch = chunks_.find(pos);
  488. if (ch != chunks_.end()) {
  489. cb_.onLightChunkUpdated(ch->second, pos);
  490. }
  491. }
  492. }
  493. }
  494. }