minerender 2.1.0-beta.6 → 2.1.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +10 -1
  2. package/dist/browser/esm/Materials.mjs +3 -1
  3. package/dist/browser/esm/Materials.mjs.map +1 -1
  4. package/dist/browser/esm/Ticker.mjs +3 -1
  5. package/dist/browser/esm/Ticker.mjs.map +1 -1
  6. package/dist/browser/esm/UVMapper.mjs +9 -9
  7. package/dist/browser/esm/UVMapper.mjs.map +1 -1
  8. package/dist/browser/esm/armor/Armor.mjs +84 -0
  9. package/dist/browser/esm/armor/Armor.mjs.map +1 -0
  10. package/dist/browser/esm/armor/index.mjs +3 -0
  11. package/dist/browser/esm/armor/index.mjs.map +1 -0
  12. package/dist/browser/esm/assets/AssetDefaults.mjs +8 -0
  13. package/dist/browser/esm/assets/AssetDefaults.mjs.map +1 -0
  14. package/dist/browser/esm/assets/AssetKey.mjs +1 -1
  15. package/dist/browser/esm/assets/AssetKey.mjs.map +1 -1
  16. package/dist/browser/esm/assets/AssetLoader.mjs +5 -5
  17. package/dist/browser/esm/assets/AssetLoader.mjs.map +1 -1
  18. package/dist/browser/esm/assets/Assets.mjs +1 -5
  19. package/dist/browser/esm/assets/Assets.mjs.map +1 -1
  20. package/dist/browser/esm/assets/BlockEntities.mjs +5 -1
  21. package/dist/browser/esm/assets/BlockEntities.mjs.map +1 -1
  22. package/dist/browser/esm/assets/BlockStates.mjs +1 -1
  23. package/dist/browser/esm/assets/BlockStates.mjs.map +1 -1
  24. package/dist/browser/esm/assets/Entities.mjs +40 -12
  25. package/dist/browser/esm/assets/Entities.mjs.map +1 -1
  26. package/dist/browser/esm/assets/MineRenderData.mjs +8 -17
  27. package/dist/browser/esm/assets/MineRenderData.mjs.map +1 -1
  28. package/dist/browser/esm/assets/ModelTextures.mjs +13 -7
  29. package/dist/browser/esm/assets/ModelTextures.mjs.map +1 -1
  30. package/dist/browser/esm/assets/Models.mjs +1 -1
  31. package/dist/browser/esm/assets/Models.mjs.map +1 -1
  32. package/dist/browser/esm/assets/_fonts/Unihex.mjs +5 -3
  33. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -1
  34. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs +22 -0
  35. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs.map +1 -0
  36. package/dist/browser/esm/assets/index.mjs +1 -0
  37. package/dist/browser/esm/assets/index.mjs.map +1 -1
  38. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +7 -8
  39. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  40. package/dist/browser/esm/cache/Caching.mjs +8 -0
  41. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  42. package/dist/browser/esm/entity/scene/EntityObject.mjs +31 -5
  43. package/dist/browser/esm/entity/scene/EntityObject.mjs.map +1 -1
  44. package/dist/browser/esm/env/browser/BrowserCache.mjs +3 -3
  45. package/dist/browser/esm/env/browser/BrowserCache.mjs.map +1 -1
  46. package/dist/browser/esm/env/browser/BrowserEnv.mjs +11 -4
  47. package/dist/browser/esm/env/browser/BrowserEnv.mjs.map +1 -1
  48. package/dist/browser/esm/image/ImageLoader.mjs +3 -3
  49. package/dist/browser/esm/image/ImageLoader.mjs.map +1 -1
  50. package/dist/browser/esm/index.browser.d.mts +191 -22
  51. package/dist/browser/esm/index.browser.mjs.map +1 -1
  52. package/dist/browser/esm/index.mjs +1 -0
  53. package/dist/browser/esm/index.mjs.map +1 -1
  54. package/dist/browser/esm/inspector/SceneInspector.mjs +3 -3
  55. package/dist/browser/esm/inspector/SceneInspector.mjs.map +1 -1
  56. package/dist/browser/esm/instance/InstanceManager.mjs +4 -4
  57. package/dist/browser/esm/instance/InstanceManager.mjs.map +1 -1
  58. package/dist/browser/esm/model/ItemGlint.mjs +7 -6
  59. package/dist/browser/esm/model/ItemGlint.mjs.map +1 -1
  60. package/dist/browser/esm/model/block/scene/BlockObject.mjs +19 -6
  61. package/dist/browser/esm/model/block/scene/BlockObject.mjs.map +1 -1
  62. package/dist/browser/esm/model/multiblock/StructureParser.mjs +7 -4
  63. package/dist/browser/esm/model/multiblock/StructureParser.mjs.map +1 -1
  64. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -4
  65. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  66. package/dist/browser/esm/nbt/NBTHelper.mjs +5 -2
  67. package/dist/browser/esm/nbt/NBTHelper.mjs.map +1 -1
  68. package/dist/browser/esm/renderer/Renderer.mjs +4 -2
  69. package/dist/browser/esm/renderer/Renderer.mjs.map +1 -1
  70. package/dist/browser/esm/renderer/SceneObject.mjs +0 -6
  71. package/dist/browser/esm/renderer/SceneObject.mjs.map +1 -1
  72. package/dist/browser/esm/request/Requests.mjs +3 -3
  73. package/dist/browser/esm/request/Requests.mjs.map +1 -1
  74. package/dist/browser/esm/scene/SceneDocument.mjs +35 -5
  75. package/dist/browser/esm/scene/SceneDocument.mjs.map +1 -1
  76. package/dist/browser/esm/scene/SceneDocumentLoader.mjs +14 -3
  77. package/dist/browser/esm/scene/SceneDocumentLoader.mjs.map +1 -1
  78. package/dist/browser/esm/skin/SkinGeometries.mjs +4 -1
  79. package/dist/browser/esm/skin/SkinGeometries.mjs.map +1 -1
  80. package/dist/browser/esm/skin/SkinPart.mjs +2 -0
  81. package/dist/browser/esm/skin/SkinPart.mjs.map +1 -1
  82. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs +11 -1
  83. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs.map +1 -1
  84. package/dist/browser/esm/skin/SkinTextures.mjs +1 -1
  85. package/dist/browser/esm/skin/SkinTextures.mjs.map +1 -1
  86. package/dist/browser/esm/skin/Skins.mjs +6 -4
  87. package/dist/browser/esm/skin/Skins.mjs.map +1 -1
  88. package/dist/browser/esm/skin/scene/SkinObject.mjs +119 -7
  89. package/dist/browser/esm/skin/scene/SkinObject.mjs.map +1 -1
  90. package/dist/browser/esm/texture/PalettedTextures.mjs +46 -0
  91. package/dist/browser/esm/texture/PalettedTextures.mjs.map +1 -0
  92. package/dist/browser/esm/texture/TextureLoader.mjs +4 -2
  93. package/dist/browser/esm/texture/TextureLoader.mjs.map +1 -1
  94. package/dist/browser/esm/texture/index.mjs +1 -0
  95. package/dist/browser/esm/texture/index.mjs.map +1 -1
  96. package/dist/browser/esm/util/log.mjs +75 -0
  97. package/dist/browser/esm/util/log.mjs.map +1 -1
  98. package/dist/browser/esm/util/performance.mjs.map +1 -1
  99. package/dist/browser/esm/util/util.mjs +0 -2
  100. package/dist/browser/esm/util/util.mjs.map +1 -1
  101. package/dist/browser/esm/world/Chunk.mjs +70 -38
  102. package/dist/browser/esm/world/Chunk.mjs.map +1 -1
  103. package/dist/browser/esm/world/MineRenderWorld.mjs +187 -60
  104. package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
  105. package/dist/browser/esm/world/SectionMesh.mjs +65 -9
  106. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  107. package/dist/browser/esm/world/SectionModels.mjs +21 -15
  108. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  109. package/dist/browser/esm/world/SectionWorker.mjs +8 -2
  110. package/dist/browser/esm/world/SectionWorker.mjs.map +1 -1
  111. package/dist/browser/esm/world/WorldStreamer.mjs +129 -34
  112. package/dist/browser/esm/world/WorldStreamer.mjs.map +1 -1
  113. package/dist/browser/esm/world/_culling/BlockCulling.mjs +1 -1
  114. package/dist/browser/esm/world/_culling/BlockCulling.mjs.map +1 -1
  115. package/dist/browser/esm/world/_entities/WorldEntities.mjs +3 -1
  116. package/dist/browser/esm/world/_entities/WorldEntities.mjs.map +1 -1
  117. package/dist/browser/index.d.ts +191 -22
  118. package/dist/browser/index.js +4461 -3648
  119. package/dist/browser/index.js.map +1 -1
  120. package/dist/bundle.js +13482 -12651
  121. package/dist/bundle.js.map +1 -1
  122. package/dist/node/index.d.mts +179 -21
  123. package/dist/node/index.d.ts +179 -21
  124. package/dist/node/index.js +4495 -3666
  125. package/dist/node/index.js.map +1 -1
  126. package/dist/node/index.mjs +4412 -3586
  127. package/dist/node/index.mjs.map +1 -1
  128. package/package.json +2 -1
@@ -3,8 +3,10 @@ import { Vector3 } from "three";
3
3
  import { isTripleArray } from "../model/Model.mjs";
4
4
  import { yieldToEventLoop } from "../util/util.mjs";
5
5
  import { Chunk } from "./Chunk.mjs";
6
+ import { ChunkData } from "./ChunkData.mjs";
6
7
  import { AssetKey } from "../assets/AssetKey.mjs";
7
8
  import { BlockStates } from "../assets/BlockStates.mjs";
9
+ import { BlockEntities } from "../assets/BlockEntities.mjs";
8
10
  import { CUBE_FACE_OFFSETS } from "../CubeFace.mjs";
9
11
  import { SectionModels } from "./SectionModels.mjs";
10
12
  import { WorldEntities } from "./_entities/WorldEntities.mjs";
@@ -12,6 +14,9 @@ import { fluidKindOf } from "../model/fluid/FluidQuads.mjs";
12
14
  import { BiomeBlend, validateBiomeBlendRadius } from "./_biomes/BiomeBlend.mjs";
13
15
  import { copySectionLight, sampleSectionLight } from "./WorldLight.mjs";
14
16
  import { loadBlockCulling } from "./_culling/BlockCulling.mjs";
17
+ import { logger } from "../util/log.mjs";
18
+ var log = logger("MineRenderWorld");
19
+ var YIELD_INTERVAL_MS = 8;
15
20
  var MineRenderWorld = class {
16
21
  constructor(scene, options = {}) {
17
22
  this._chunks = /* @__PURE__ */ new Map();
@@ -101,8 +106,14 @@ var MineRenderWorld = class {
101
106
  }
102
107
  return queued;
103
108
  }
104
- async refreshBiomes() {
105
- if (this.biomeRefreshing) await this.biomeRefreshing;
109
+ async refreshBiomes(deferCulling = false) {
110
+ if (this.biomeRefreshing) {
111
+ await this.biomeRefreshing;
112
+ if (!deferCulling) {
113
+ if (this.pendingCulling.size) await this.updateCulling([]);
114
+ else await this.culling;
115
+ }
116
+ }
106
117
  if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
107
118
  return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
108
119
  const failures = /* @__PURE__ */ new Map();
@@ -114,12 +125,12 @@ var MineRenderWorld = class {
114
125
  this.pendingBiomes.delete(chunk);
115
126
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
116
127
  try {
117
- await chunk.refreshBiomes(columns);
128
+ await chunk.refreshBiomes(columns, deferCulling);
118
129
  } catch (error) {
119
130
  failures.set(chunk, error);
120
131
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
121
132
  }
122
- if (performance.now() - sliceStart > 8) {
133
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
123
134
  await yieldToEventLoop();
124
135
  sliceStart = performance.now();
125
136
  }
@@ -220,15 +231,18 @@ var MineRenderWorld = class {
220
231
  const key = AssetKey.parse("blockstates", block.type);
221
232
  keys.set(key.serialize(), key);
222
233
  }
223
- await BlockStates.getAll(keys.values());
234
+ const loaded = await BlockStates.getAll(keys.values());
235
+ const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
224
236
  if (!useBatches) {
225
237
  for (const block of multiblock.blocks) await place(block);
226
238
  return;
227
239
  }
228
240
  if (!executor) {
229
241
  const groups2 = /* @__PURE__ */ new Map();
242
+ const distinct = /* @__PURE__ */ new Map();
230
243
  const position = new Vector3();
231
244
  for (const block of multiblock.blocks) {
245
+ if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
232
246
  const [x, y, z] = block.position;
233
247
  this.validatePosBounds(position.set(x, y, z));
234
248
  const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
@@ -239,6 +253,16 @@ var MineRenderWorld = class {
239
253
  if (!group) groups2.set(chunk, group = []);
240
254
  group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
241
255
  }
256
+ if (this.sectionModels) {
257
+ for (const block of distinct.values()) {
258
+ const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
259
+ if (!state) continue;
260
+ void (async () => {
261
+ if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
262
+ await this.sectionModels.preload(state, block.properties);
263
+ })().catch(() => void 0);
264
+ }
265
+ }
242
266
  await this.placeChunkGroups(groups2, changes);
243
267
  return;
244
268
  }
@@ -274,7 +298,7 @@ var MineRenderWorld = class {
274
298
  )));
275
299
  for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
276
300
  }
277
- if (performance.now() - sliceStart > 8) {
301
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
278
302
  await yieldToEventLoop();
279
303
  sliceStart = performance.now();
280
304
  }
@@ -282,6 +306,62 @@ var MineRenderWorld = class {
282
306
  }
283
307
  /** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
284
308
  async placeChunk(chunk, executor) {
309
+ return this.placeColumn(chunk, executor);
310
+ }
311
+ /**
312
+ * Replaces parsed columns in input order, then refreshes neighboring geometry once.
313
+ * Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
314
+ */
315
+ async placeChunks(chunks) {
316
+ const columns = [];
317
+ for (const chunk of chunks) {
318
+ const { x, z } = chunk ?? {};
319
+ if (![x, z].every(Number.isSafeInteger)) {
320
+ throw new RangeError("Chunk column coordinates must be safe integers");
321
+ }
322
+ const sections = Array.from(chunk.sections, (section) => {
323
+ const { y } = section ?? {};
324
+ if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
325
+ return { ...section, y };
326
+ });
327
+ columns.push({ ...chunk, x, z, sections });
328
+ }
329
+ if (!columns.length) return;
330
+ await this.culling;
331
+ let failed = false, failure, sliceStart = performance.now();
332
+ try {
333
+ for (const column of columns) {
334
+ await this.placeColumn(column, void 0, true);
335
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
336
+ await yieldToEventLoop();
337
+ sliceStart = performance.now();
338
+ }
339
+ }
340
+ } catch (error) {
341
+ failed = true;
342
+ failure = error;
343
+ } finally {
344
+ try {
345
+ const failures = await this.refreshBiomes(true);
346
+ if (failures.size) log.warn("Failed to refresh biome colors", failures.values().next().value);
347
+ } catch (error) {
348
+ if (!failed) {
349
+ failed = true;
350
+ failure = error;
351
+ }
352
+ }
353
+ try {
354
+ await this.updateCulling([]);
355
+ } catch (error) {
356
+ if (!failed) {
357
+ failed = true;
358
+ failure = error;
359
+ }
360
+ }
361
+ }
362
+ if (failed) throw failure;
363
+ }
364
+ async placeColumn(chunk, executor, deferCulling = false) {
285
365
  if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
286
366
  throw new RangeError("Chunk column coordinates must be safe integers");
287
367
  }
@@ -343,7 +423,7 @@ var MineRenderWorld = class {
343
423
  } finally {
344
424
  this.markSectionChanged(target);
345
425
  }
346
- if (performance.now() - sliceStart > 8) {
426
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
347
427
  await yieldToEventLoop();
348
428
  sliceStart = performance.now();
349
429
  }
@@ -366,9 +446,12 @@ var MineRenderWorld = class {
366
446
  await placeEntities?.();
367
447
  } finally {
368
448
  this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
369
- const failures = await this.refreshBiomes();
370
- if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
371
- await this.updateCulling([...changes.values()]);
449
+ if (deferCulling) await this.updateCulling([...changes.values()], true);
450
+ else {
451
+ const failures = await this.refreshBiomes();
452
+ if (failures.size) log.warn("Failed to refresh biome colors", failures.values().next().value);
453
+ await this.updateCulling([...changes.values()]);
454
+ }
372
455
  }
373
456
  }
374
457
  /**
@@ -376,16 +459,36 @@ var MineRenderWorld = class {
376
459
  * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
377
460
  */
378
461
  async unloadChunkColumn(x, z) {
379
- if (!Number.isInteger(x) || !Number.isInteger(z)) {
380
- throw new RangeError("Chunk column coordinates must be integers");
462
+ return this.unloadChunkColumns([{ x, z }]);
463
+ }
464
+ /**
465
+ * Removes chunk columns together and refreshes surviving neighbors once.
466
+ * Coordinates are absolute chunk columns. Duplicate and missing columns are ignored.
467
+ */
468
+ async unloadChunkColumns(positions) {
469
+ const columns = /* @__PURE__ */ new Map();
470
+ for (const position of positions) {
471
+ const { x, z } = position ?? {};
472
+ if (!Number.isInteger(x) || !Number.isInteger(z)) {
473
+ throw new RangeError("Chunk column coordinates must be integers");
474
+ }
475
+ columns.set(`${x}_${z}`, { x, z });
381
476
  }
382
- this.entities?.clearColumn(x, z);
477
+ if (!columns.size) return;
383
478
  await this.culling;
479
+ let sliceStart = performance.now();
384
480
  try {
385
- await this.clearChunkColumn(x, z);
481
+ for (const { x, z } of columns.values()) {
482
+ this.entities?.clearColumn(x, z);
483
+ await this.clearChunkColumn(x, z);
484
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
485
+ await yieldToEventLoop();
486
+ sliceStart = performance.now();
487
+ }
488
+ }
386
489
  } finally {
387
- const failures = await this.refreshBiomes();
388
- if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
490
+ const failures = await this.refreshBiomes(true);
491
+ if (failures.size) log.warn("Failed to refresh biome colors", failures.values().next().value);
389
492
  await this.updateCulling([]);
390
493
  }
391
494
  }
@@ -447,7 +550,7 @@ var MineRenderWorld = class {
447
550
  }
448
551
  this.sectionModels?.clear();
449
552
  }
450
- updateCulling(positions) {
553
+ updateCulling(positions, defer = false) {
451
554
  const neighbor = new Vector3();
452
555
  for (const pos of positions) {
453
556
  const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
@@ -469,58 +572,82 @@ var MineRenderWorld = class {
469
572
  }
470
573
  }
471
574
  }
575
+ if (defer) return Promise.resolve();
472
576
  return this.culling ?? (this.culling = Promise.resolve().then(async () => {
473
577
  let sliceStart = performance.now();
474
578
  try {
475
579
  const culling = await loadBlockCulling();
476
580
  while (this.pendingCulling.size) {
477
- const batch = [...this.pendingCulling];
581
+ const batch = new Map(this.pendingCulling);
478
582
  this.pendingCulling.clear();
479
- for (const [chunk, indices] of batch) {
480
- const neighbors = CUBE_FACE_OFFSETS.map(([x, y, z]) => this._chunks.get(`${chunk.x + x}_${chunk.y + y}_${chunk.z + z}`));
481
- const neighborAt = (x, y, z) => {
482
- const lx = x - chunk.x * 16, ly = y - chunk.y * 16, lz = z - chunk.z * 16;
483
- const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
484
- return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
485
- };
486
- const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
487
- const [section, i] = neighborAt(x, y, z);
488
- return section?.isOccludingIndex(i) ?? false;
489
- }, culling, (x, y, z) => {
490
- const [section, i] = neighborAt(x, y, z);
491
- return section?.visibleBlockIndex(i);
492
- });
493
- const fluidIndices = indices === "all" ? function* () {
494
- for (let index = 0; index < 4096; index++) yield index;
495
- }() : indices;
496
- for (const index of fluidIndices) {
497
- if (!chunk.fluidByteIndex(index) || !chunk.isBlockVisibleIndex(index)) continue;
498
- const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
499
- const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
500
- const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
501
- if (block.object?.fluidKind) {
502
- await block.object.updateFluid((x, y, z) => {
503
- const nx = lx + x, ny = ly + y, nz = lz + z;
504
- const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${chunk.x + Math.floor(nx / 16)}_${chunk.y + Math.floor(ny / 16)}_${chunk.z + Math.floor(nz / 16)}`);
505
- const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
506
- const byte = section?.fluidByteIndex(neighborIndex) ?? 0;
507
- return {
508
- fluid: fluidKindOf(byte),
509
- level: byte & 15,
510
- solid: section?.isOccludingIndex(neighborIndex) ?? false
511
- };
512
- });
583
+ try {
584
+ for (const [chunk, indices] of batch) {
585
+ const neighbors = CUBE_FACE_OFFSETS.map(([x, y, z]) => this._chunks.get(`${chunk.x + x}_${chunk.y + y}_${chunk.z + z}`));
586
+ const neighborAt = (x, y, z) => {
587
+ const lx = x - chunk.x * 16, ly = y - chunk.y * 16, lz = z - chunk.z * 16;
588
+ const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
589
+ return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
590
+ };
591
+ const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
592
+ const [section, i] = neighborAt(x, y, z);
593
+ return section?.isOccludingIndex(i) ?? false;
594
+ }, culling, (x, y, z) => {
595
+ const [section, i] = neighborAt(x, y, z);
596
+ return section?.visibleBlockIndex(i);
597
+ });
598
+ const fluidIndices = indices === "all" ? function* () {
599
+ for (let index = 0; index < 4096; index++) yield index;
600
+ }() : indices;
601
+ for (const index of fluidIndices) {
602
+ if (!chunk.fluidByteIndex(index) || !chunk.isBlockVisibleIndex(index)) continue;
603
+ const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
604
+ const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
605
+ const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
606
+ if (block.object?.fluidKind) {
607
+ await block.object.updateFluid((x, y, z) => {
608
+ const nx = lx + x, ny = ly + y, nz = lz + z;
609
+ const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${chunk.x + Math.floor(nx / 16)}_${chunk.y + Math.floor(ny / 16)}_${chunk.z + Math.floor(nz / 16)}`);
610
+ const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
611
+ const byte = section?.fluidByteIndex(neighborIndex) ?? 0;
612
+ return {
613
+ fluid: fluidKindOf(byte),
614
+ level: byte & 15,
615
+ solid: section?.isOccludingIndex(neighborIndex) ?? false
616
+ };
617
+ });
618
+ }
619
+ }
620
+ for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
621
+ await chunk.refreshLight(indices === "all" ? void 0 : indices);
622
+ chunk.refreshAmbientOcclusion(this._ambientOcclusion);
623
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
624
+ await yieldToEventLoop();
625
+ sliceStart = performance.now();
626
+ }
627
+ }
628
+ const pending = [];
629
+ for (const chunk of [...batch.keys()]) {
630
+ const build = (async () => {
631
+ await chunk.rebuildSectionMesh();
632
+ batch.delete(chunk);
633
+ })();
634
+ void build.catch(() => void 0);
635
+ pending.push(build);
636
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
637
+ await yieldToEventLoop();
638
+ sliceStart = performance.now();
513
639
  }
514
640
  }
515
- for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
516
- await chunk.refreshLight(indices === "all" ? void 0 : indices);
517
- chunk.refreshAmbientOcclusion(this._ambientOcclusion);
518
- if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
519
- await yieldToEventLoop();
520
- sliceStart = performance.now();
641
+ const builds = await Promise.allSettled(pending);
642
+ const failure = builds.find((result) => result.status === "rejected");
643
+ if (failure?.status === "rejected") throw failure.reason;
644
+ } catch (error) {
645
+ for (const [chunk, indices] of batch) {
646
+ const pending = this.pendingCulling.get(chunk);
647
+ this.pendingCulling.set(chunk, indices === "all" || pending === "all" ? "all" : /* @__PURE__ */ new Set([...indices, ...pending ?? []]));
521
648
  }
649
+ throw error;
522
650
  }
523
- await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
524
651
  }
525
652
  } finally {
526
653
  this.culling = void 0;
@@ -537,7 +664,7 @@ var MineRenderWorld = class {
537
664
  x,
538
665
  y,
539
666
  z,
540
- (positions) => this.updateCulling(positions),
667
+ (positions, defer) => this.updateCulling(positions, defer),
541
668
  this.sectionModels,
542
669
  (x2, y2, z2) => {
543
670
  const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);