minerender 2.1.0-alpha.87 → 2.1.0-alpha.88

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.
@@ -191,6 +191,23 @@ async function sleep(timeout) {
191
191
  setTimeout(() => resolve(), timeout);
192
192
  });
193
193
  }
194
+ function yieldToEventLoop() {
195
+ const scheduler = globalThis.scheduler;
196
+ if (scheduler?.yield) return scheduler.yield();
197
+ return new Promise((resolve) => {
198
+ if (typeof MessageChannel !== "undefined") {
199
+ const channel = new MessageChannel();
200
+ channel.port1.onmessage = () => {
201
+ channel.port1.close();
202
+ channel.port2.close();
203
+ resolve();
204
+ };
205
+ channel.port2.postMessage(void 0);
206
+ } else {
207
+ setTimeout(resolve, 0);
208
+ }
209
+ });
210
+ }
194
211
  function clampRotationDegrees(deg) {
195
212
  return deg % 360;
196
213
  }
@@ -18798,6 +18815,14 @@ var BrowserEnv = class {
18798
18815
  version: version2
18799
18816
  }));
18800
18817
  }
18818
+ createWorker(name) {
18819
+ if (name !== "section" || typeof Worker === "undefined") return void 0;
18820
+ try {
18821
+ return new Worker(new URL("./section.worker.mjs", import.meta.url), { type: "module" });
18822
+ } catch {
18823
+ return void 0;
18824
+ }
18825
+ }
18801
18826
  };
18802
18827
  function registerBrowserEnv() {
18803
18828
  const env = new BrowserEnv();
@@ -28500,6 +28525,16 @@ init_NBTHelper();
28500
28525
 
28501
28526
  // src/world/ChunkData.ts
28502
28527
  init_AssetKey();
28528
+ var blockKeys = /* @__PURE__ */ new Map();
28529
+ function blockKey(type) {
28530
+ let key = blockKeys.get(type);
28531
+ if (!key) {
28532
+ key = AssetKey.parse("blockstates", type);
28533
+ if (blockKeys.size >= 4096) blockKeys.clear();
28534
+ blockKeys.set(type, key);
28535
+ }
28536
+ return key;
28537
+ }
28503
28538
  var ChunkData = class _ChunkData {
28504
28539
  constructor() {
28505
28540
  this.indices = new Uint16Array(4096);
@@ -28527,10 +28562,10 @@ var ChunkData = class _ChunkData {
28527
28562
  let key;
28528
28563
  let nbt;
28529
28564
  if (block && !_ChunkData.isAir(block)) {
28530
- const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
28565
+ const type = blockKey(block.type).toNamespacedString();
28531
28566
  const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
28532
28567
  state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
28533
- key = JSON.stringify([type, properties]);
28568
+ key = _ChunkData.paletteKey(block);
28534
28569
  if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
28535
28570
  }
28536
28571
  const previousId = this.indices[index];
@@ -28567,9 +28602,14 @@ var ChunkData = class _ChunkData {
28567
28602
  }
28568
28603
  static isAir(block) {
28569
28604
  if (typeof block === "undefined" || typeof block.type === "undefined") return true;
28570
- const key = AssetKey.parse("blockstates", block.type);
28605
+ const key = blockKey(block.type);
28571
28606
  return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
28572
28607
  }
28608
+ /** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
28609
+ static paletteKey(block) {
28610
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
28611
+ return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
28612
+ }
28573
28613
  static copyBlock(state, nbt) {
28574
28614
  const block = { type: state.type };
28575
28615
  if (state.properties) block.properties = { ...state.properties };
@@ -28749,14 +28789,17 @@ var AnvilParser = class {
28749
28789
  const perLong = Math.floor(64 / bits);
28750
28790
  const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
28751
28791
  if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
28752
- const words = tag.value.map(([high, low]) => BigInt(high >>> 0) << 32n | BigInt(low >>> 0));
28753
- const mask = (1n << BigInt(bits)) - 1n;
28792
+ const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
28793
+ const mask = (1 << bits) - 1;
28754
28794
  for (let index = 0; index < 4096; index++) {
28755
- const word = padded ? Math.floor(index / perLong) : Math.floor(index * bits / 64);
28756
- const shift = padded ? index % perLong * bits : index * bits % 64;
28757
- let value = words[word] >> BigInt(shift);
28758
- if (!padded && shift + bits > 64) value |= words[word + 1] << BigInt(64 - shift);
28759
- indices[index] = Number(value & mask);
28795
+ const bitPos = index * bits;
28796
+ const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
28797
+ const shift = padded ? index % perLong * bits : bitPos & 63;
28798
+ const [high, low] = words[word];
28799
+ let value = shift < 32 ? low >>> shift : high >>> shift - 32;
28800
+ if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
28801
+ if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
28802
+ indices[index] = value & mask;
28760
28803
  }
28761
28804
  return indices;
28762
28805
  }
@@ -28897,8 +28940,8 @@ init_AssetKey();
28897
28940
 
28898
28941
  // src/world/MineRenderWorld.ts
28899
28942
  init_Model();
28943
+ init_util();
28900
28944
  import { Vector3 as Vector316 } from "three";
28901
- init_BatchedExecutor();
28902
28945
  init_AssetKey();
28903
28946
  init_BlockStates();
28904
28947
  init_CubeFace();
@@ -29043,7 +29086,7 @@ var MineRenderWorld = class {
29043
29086
  * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
29044
29087
  * Entity NBT is retained by the structure but is not rendered.
29045
29088
  *
29046
- * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
29089
+ * @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
29047
29090
  * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
29048
29091
  */
29049
29092
  async placeMultiBlock(multiblock, useBatches = true, executor) {
@@ -29060,8 +29103,10 @@ var MineRenderWorld = class {
29060
29103
  };
29061
29104
  const place = (block) => this.placeBlock(new Vector316(...block.position), block, collect);
29062
29105
  const keys = /* @__PURE__ */ new Map();
29106
+ const types = /* @__PURE__ */ new Set();
29063
29107
  for (const block of multiblock.blocks) {
29064
- if (Chunk.isAir(block)) continue;
29108
+ if (Chunk.isAir(block) || types.has(block.type)) continue;
29109
+ types.add(block.type);
29065
29110
  const key = AssetKey.parse("blockstates", block.type);
29066
29111
  keys.set(key.serialize(), key);
29067
29112
  }
@@ -29070,6 +29115,23 @@ var MineRenderWorld = class {
29070
29115
  for (const block of multiblock.blocks) await place(block);
29071
29116
  return;
29072
29117
  }
29118
+ if (!executor) {
29119
+ const groups2 = /* @__PURE__ */ new Map();
29120
+ const position = new Vector316();
29121
+ for (const block of multiblock.blocks) {
29122
+ const [x, y, z] = block.position;
29123
+ this.validatePosBounds(position.set(x, y, z));
29124
+ const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
29125
+ const key = `${cx}_${cy}_${cz}`;
29126
+ const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
29127
+ if (!chunk) continue;
29128
+ let group = groups2.get(chunk);
29129
+ if (!group) groups2.set(chunk, group = []);
29130
+ group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
29131
+ }
29132
+ await this.placeChunkGroups(groups2, changes);
29133
+ return;
29134
+ }
29073
29135
  const positions = /* @__PURE__ */ new Map();
29074
29136
  for (const block of multiblock.blocks) {
29075
29137
  const key = block.position.join(",");
@@ -29078,19 +29140,34 @@ var MineRenderWorld = class {
29078
29140
  else positions.set(key, [block]);
29079
29141
  }
29080
29142
  const groups = [...positions.values()];
29081
- const queue = executor ?? new BatchedExecutor();
29082
- try {
29083
- for (let i = 0; i < groups.length; i += queue.batch) {
29084
- const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
29085
- (blocks) => queue.submit(async () => {
29086
- for (const block of blocks) await place(block);
29087
- })
29088
- ));
29089
- const failure = results.find((result) => result.status === "rejected");
29090
- if (failure?.status === "rejected") throw failure.reason;
29143
+ for (let i = 0; i < groups.length; i += executor.batch) {
29144
+ const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
29145
+ (blocks) => executor.submit(async () => {
29146
+ for (const block of blocks) await place(block);
29147
+ })
29148
+ ));
29149
+ const failure = results.find((result) => result.status === "rejected");
29150
+ if (failure?.status === "rejected") throw failure.reason;
29151
+ }
29152
+ }
29153
+ async placeChunkGroups(groups, changes) {
29154
+ let sliceStart = performance.now();
29155
+ for (const [chunk, blocks] of groups) {
29156
+ let positions;
29157
+ try {
29158
+ positions = await chunk.placeBlocks(blocks);
29159
+ } finally {
29160
+ positions ?? (positions = blocks.map(({ index }) => new Vector316(
29161
+ chunk.x * 16 + index % 16,
29162
+ chunk.y * 16 + Math.floor(index / 256),
29163
+ chunk.z * 16 + Math.floor(index / 16) % 16
29164
+ )));
29165
+ for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
29166
+ }
29167
+ if (performance.now() - sliceStart > 8) {
29168
+ await yieldToEventLoop();
29169
+ sliceStart = performance.now();
29091
29170
  }
29092
- } finally {
29093
- if (!executor) queue.stop();
29094
29171
  }
29095
29172
  }
29096
29173
  /** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
@@ -29098,7 +29175,24 @@ var MineRenderWorld = class {
29098
29175
  const changes = /* @__PURE__ */ new Map();
29099
29176
  try {
29100
29177
  await this.clearChunkColumn(chunk.x, chunk.z, changes);
29178
+ const groups = /* @__PURE__ */ new Map();
29101
29179
  for (const section of chunk.sections) {
29180
+ if (!executor) {
29181
+ const blocks2 = [];
29182
+ for (let index = 0; index < 4096; index++) {
29183
+ const block = section.data.get(index);
29184
+ if (block) blocks2.push({ index, block });
29185
+ }
29186
+ if (blocks2.length) {
29187
+ const position = new Vector316(chunk.x * 16, section.y * 16, chunk.z * 16);
29188
+ this.validatePosBounds(position);
29189
+ const target = this.getOrCreateChunkAt(position);
29190
+ const previous = groups.get(target);
29191
+ if (previous) previous.push(...blocks2);
29192
+ else groups.set(target, blocks2);
29193
+ }
29194
+ continue;
29195
+ }
29102
29196
  const blocks = [];
29103
29197
  for (let index = 0; index < 4096; index++) {
29104
29198
  const block = section.data.get(index);
@@ -29113,6 +29207,7 @@ var MineRenderWorld = class {
29113
29207
  }
29114
29208
  await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
29115
29209
  }
29210
+ if (!executor) await this.placeChunkGroups(groups, changes);
29116
29211
  } finally {
29117
29212
  await this.updateCulling([...changes.values()]);
29118
29213
  }
@@ -29153,19 +29248,23 @@ var MineRenderWorld = class {
29153
29248
  this.sectionModels?.clear();
29154
29249
  }
29155
29250
  updateCulling(positions) {
29251
+ const neighbor = new Vector316();
29156
29252
  for (const pos of positions) {
29157
- this.pendingCulling.set(pos.toArray().join(","), pos.clone());
29158
- for (const offset of CUBE_FACE_OFFSETS) {
29159
- const neighbor = pos.clone().add(new Vector316(...offset));
29160
- this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
29161
- }
29253
+ const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
29254
+ const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
29255
+ const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
29162
29256
  for (let y = -1; y <= 1; y++) {
29163
29257
  for (let z = -1; z <= 1; z++) {
29164
29258
  for (let x = -1; x <= 1; x++) {
29165
- const neighbor = pos.clone().add(new Vector316(x, y, z));
29166
- if (this.getBlockAt(neighbor)?.object?.fluidKind) {
29167
- this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
29168
- }
29259
+ const nx = lx + x, ny = ly + y, nz = lz + z;
29260
+ const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${cx + Math.floor(nx / 16)}_${cy + Math.floor(ny / 16)}_${cz + Math.floor(nz / 16)}`);
29261
+ if (!section) continue;
29262
+ const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
29263
+ let pending = this.pendingCulling.get(section);
29264
+ if (pending?.has(index)) continue;
29265
+ if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.getBlockAt(neighbor.set(pos.x + x, pos.y + y, pos.z + z))?.object?.fluidKind) continue;
29266
+ if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
29267
+ pending.add(index);
29169
29268
  }
29170
29269
  }
29171
29270
  }
@@ -29173,39 +29272,38 @@ var MineRenderWorld = class {
29173
29272
  return this.culling ?? (this.culling = Promise.resolve().then(async () => {
29174
29273
  try {
29175
29274
  while (this.pendingCulling.size) {
29176
- const batch = [...this.pendingCulling.values()];
29275
+ const batch = [...this.pendingCulling];
29177
29276
  this.pendingCulling.clear();
29178
- const changed = /* @__PURE__ */ new Set();
29179
- for (const pos of batch) {
29180
- const chunk = this.getChunkAt(pos);
29181
- if (!chunk) continue;
29182
- changed.add(chunk);
29183
- const block = chunk.getBlockAt(pos);
29184
- if (!block || !chunk.isBlockVisibleAt(pos)) {
29185
- continue;
29186
- }
29187
- if (block.object?.fluidKind) {
29188
- await block.object.updateFluid((x, y, z) => {
29189
- const neighbor = pos.clone().add(new Vector316(x, y, z));
29190
- const section = this.getChunkAt(neighbor);
29191
- const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
29192
- return {
29193
- fluid: object3?.fluidKind,
29194
- level: object3?.fluidLevel,
29195
- solid: section?.isOccludingAt(neighbor) ?? false
29196
- };
29197
- });
29198
- }
29199
- let mask = 0;
29200
- for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
29201
- const neighbor = pos.clone().add(new Vector316(...offset));
29202
- if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
29203
- mask |= 1 << face;
29277
+ for (const [chunk, indices] of batch) {
29278
+ for (const index of indices) {
29279
+ if (!chunk.isBlockVisibleIndex(index)) continue;
29280
+ const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
29281
+ const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
29282
+ const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
29283
+ if (block.object?.fluidKind) {
29284
+ await block.object.updateFluid((x, y, z) => {
29285
+ const nx = lx + x, ny = ly + y, nz = lz + z;
29286
+ 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)}`);
29287
+ const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
29288
+ const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
29289
+ return {
29290
+ fluid: object3?.fluidKind,
29291
+ level: object3?.fluidLevel,
29292
+ solid: section?.isOccludingIndex(neighborIndex) ?? false
29293
+ };
29294
+ });
29204
29295
  }
29296
+ let mask = 0;
29297
+ for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
29298
+ const offset = CUBE_FACE_OFFSETS[face];
29299
+ const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
29300
+ 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)}`);
29301
+ if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
29302
+ }
29303
+ await chunk.setCullMaskIndex(index, mask);
29205
29304
  }
29206
- await chunk.setCullMaskAt(pos, mask);
29207
29305
  }
29208
- for (const chunk of changed) chunk.rebuildSectionMesh();
29306
+ await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
29209
29307
  }
29210
29308
  } finally {
29211
29309
  this.culling = void 0;
@@ -29266,8 +29364,229 @@ init_Model();
29266
29364
 
29267
29365
  // src/world/SectionMesh.ts
29268
29366
  init_CanvasCompat();
29367
+ init_CubeFace();
29269
29368
  init_Materials();
29270
- import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
29369
+ import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9, Uint32BufferAttribute } from "three";
29370
+
29371
+ // src/world/SectionGeometry.ts
29372
+ init_CubeFace();
29373
+ function buildSectionGeometry(input) {
29374
+ const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
29375
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29376
+ if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
29377
+ const visible = [];
29378
+ const visibleAtlases = /* @__PURE__ */ new Set();
29379
+ for (let entry = 0; entry < count; entry++) {
29380
+ const template = templateData[templates[entry]];
29381
+ if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
29382
+ visible.push(entry);
29383
+ visibleAtlases.add(template.atlas);
29384
+ }
29385
+ const pages = [];
29386
+ const locations = /* @__PURE__ */ new Map();
29387
+ for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
29388
+ const { width, height } = atlases[atlas];
29389
+ if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
29390
+ let selected = -1;
29391
+ for (let i = 0; i < pages.length; i++) {
29392
+ const page2 = pages[i];
29393
+ const nextRow = page2.x + width > maxAtlasSize;
29394
+ if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
29395
+ if (nextRow) {
29396
+ page2.y += page2.rowHeight;
29397
+ page2.x = 0;
29398
+ page2.rowHeight = 0;
29399
+ }
29400
+ selected = i;
29401
+ break;
29402
+ }
29403
+ if (selected < 0) {
29404
+ selected = pages.length;
29405
+ pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
29406
+ }
29407
+ const page = pages[selected];
29408
+ page.placements.push({ atlas, x: page.x, y: page.y });
29409
+ locations.set(atlas, { page: selected, x: page.x, y: page.y });
29410
+ page.x += width;
29411
+ page.rowHeight = Math.max(page.rowHeight, height);
29412
+ page.width = Math.max(page.width, page.x);
29413
+ page.height = Math.max(page.height, page.y + height);
29414
+ }
29415
+ for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
29416
+ return pages.map((page) => {
29417
+ let faces = 0;
29418
+ for (const entry of page.entries) {
29419
+ const template = templateData[templates[entry]];
29420
+ for (let face = 0; face < CUBE_FACES.length; face++) {
29421
+ if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
29422
+ }
29423
+ }
29424
+ const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
29425
+ const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
29426
+ const outputIndices = new Uint32Array(faces * 6);
29427
+ let vertex = 0, indexOffset = 0;
29428
+ for (const entry of page.entries) {
29429
+ const template = templateData[templates[entry]];
29430
+ const atlas = atlases[template.atlas];
29431
+ const placement = locations.get(template.atlas);
29432
+ const mapU = (u) => (placement.x + u * atlas.width) / page.width;
29433
+ const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
29434
+ const offsetX = indices[entry] % 16 * 16;
29435
+ const offsetY = Math.floor(indices[entry] / 256) * 16;
29436
+ const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
29437
+ for (let face = 0; face < CUBE_FACES.length; face++) {
29438
+ if (cullMasks[entry] & template.cullFaces[face]) continue;
29439
+ const start = vertex;
29440
+ for (let corner = 0; corner < 4; corner++, vertex++) {
29441
+ const source = face * 4 + corner;
29442
+ positions[vertex * 3] = template.positions[source * 3] + offsetX;
29443
+ positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
29444
+ positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
29445
+ normals[vertex * 3] = template.normals[source * 3];
29446
+ normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
29447
+ normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
29448
+ uvs[vertex * 2] = mapU(template.uvs[source * 2]);
29449
+ uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
29450
+ uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
29451
+ uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
29452
+ uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
29453
+ uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
29454
+ colors[vertex * 3] = template.colors[source * 3];
29455
+ colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
29456
+ colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
29457
+ }
29458
+ for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
29459
+ }
29460
+ }
29461
+ return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
29462
+ });
29463
+ }
29464
+ function sectionGeometryTransferables(pages) {
29465
+ return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
29466
+ }
29467
+
29468
+ // src/world/SectionWorker.ts
29469
+ init_Env();
29470
+ var _SectionWorker = class _SectionWorker {
29471
+ constructor(worker) {
29472
+ this.worker = worker;
29473
+ this.nextId = 0;
29474
+ this.pending = /* @__PURE__ */ new Map();
29475
+ worker.addEventListener("message", (event) => {
29476
+ const reply = event.data;
29477
+ const pending = this.pending.get(reply.id);
29478
+ if (!pending) return;
29479
+ this.pending.delete(reply.id);
29480
+ if (reply.type === "pages") pending.resolve(reply.pages);
29481
+ else pending.reject(new Error(reply.message));
29482
+ });
29483
+ worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
29484
+ worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
29485
+ }
29486
+ /** The shared worker, or undefined when the environment has none or a previous worker failed. */
29487
+ static shared() {
29488
+ if (!this.initialized) {
29489
+ this.initialized = true;
29490
+ const worker = Env.provider.createWorker?.("section");
29491
+ if (worker) this.instance = new _SectionWorker(worker);
29492
+ }
29493
+ return this.instance;
29494
+ }
29495
+ /** Builds geometry without transferring the input buffers away from the caller. */
29496
+ build(input) {
29497
+ if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
29498
+ return new Promise((resolve, reject) => {
29499
+ const id = this.nextId++;
29500
+ this.pending.set(id, { resolve, reject });
29501
+ this.worker.postMessage({ id, type: "build", input });
29502
+ });
29503
+ }
29504
+ /** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
29505
+ terminate() {
29506
+ this.fail("Section worker terminated");
29507
+ }
29508
+ fail(message) {
29509
+ const error = new Error(message);
29510
+ for (const pending of this.pending.values()) pending.reject(error);
29511
+ this.pending.clear();
29512
+ this.worker?.terminate();
29513
+ this.worker = void 0;
29514
+ _SectionWorker.instance = void 0;
29515
+ }
29516
+ };
29517
+ _SectionWorker.initialized = false;
29518
+ var SectionWorker = _SectionWorker;
29519
+
29520
+ // src/world/SectionMesh.ts
29521
+ var templateCache = /* @__PURE__ */ new WeakMap();
29522
+ function toInput(entries, maxAtlasSize) {
29523
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29524
+ const atlases = [];
29525
+ const atlasIds = /* @__PURE__ */ new Map();
29526
+ const templateIds = /* @__PURE__ */ new Map();
29527
+ const input = {
29528
+ count: entries.length,
29529
+ indices: new Uint16Array(entries.length),
29530
+ templates: new Uint16Array(entries.length),
29531
+ cullMasks: new Uint8Array(entries.length),
29532
+ templateData: [],
29533
+ atlases: [],
29534
+ maxAtlasSize
29535
+ };
29536
+ for (let i = 0; i < entries.length; i++) {
29537
+ const { index, template, cullMask } = entries[i];
29538
+ let id = templateIds.get(template);
29539
+ if (id === void 0) {
29540
+ let atlas = atlasIds.get(template.atlas);
29541
+ if (atlas === void 0) {
29542
+ atlas = atlases.length;
29543
+ atlasIds.set(template.atlas, atlas);
29544
+ atlases.push(template.atlas);
29545
+ input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
29546
+ }
29547
+ let data = templateCache.get(template);
29548
+ if (!data) {
29549
+ data = {
29550
+ positions: new Float32Array(72),
29551
+ normals: new Float32Array(72),
29552
+ uvs: new Float32Array(48),
29553
+ uvBounds: new Float32Array(96),
29554
+ colors: new Float32Array(72),
29555
+ indices: new Uint16Array(36),
29556
+ cullFaces: new Uint8Array(template.cullFaces),
29557
+ atlas: 0
29558
+ };
29559
+ const position = template.geometry.getAttribute("position");
29560
+ const normal = template.geometry.getAttribute("normal");
29561
+ const uv = template.geometry.getAttribute("uv");
29562
+ const bounds = template.geometry.getAttribute("uvBounds");
29563
+ const color2 = template.geometry.getAttribute("color");
29564
+ for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
29565
+ data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
29566
+ data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
29567
+ data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
29568
+ data.uvBounds.set([
29569
+ bounds?.getX(vertex) ?? 0,
29570
+ bounds?.getY(vertex) ?? 0,
29571
+ bounds?.getZ(vertex) ?? 1,
29572
+ bounds?.getW(vertex) ?? 1
29573
+ ], vertex * 4);
29574
+ data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
29575
+ }
29576
+ const indices = template.geometry.getIndex();
29577
+ for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
29578
+ templateCache.set(template, data);
29579
+ }
29580
+ id = input.templateData.length;
29581
+ templateIds.set(template, id);
29582
+ input.templateData.push({ ...data, atlas });
29583
+ }
29584
+ input.indices[i] = index;
29585
+ input.templates[i] = id;
29586
+ input.cullMasks[i] = cullMask;
29587
+ }
29588
+ return { input, atlases };
29589
+ }
29271
29590
  var SectionMesh = class _SectionMesh extends Group3 {
29272
29591
  constructor() {
29273
29592
  super(...arguments);
@@ -29275,86 +29594,31 @@ var SectionMesh = class _SectionMesh extends Group3 {
29275
29594
  }
29276
29595
  /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
29277
29596
  static build(entries, maxAtlasSize = 2048) {
29278
- if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29279
- const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
29280
- const atlases = [...new Set(visible.map((entry) => entry.template.atlas))].sort((a, b) => b.image.height - a.image.height);
29281
- const pages = [];
29282
- const locations = /* @__PURE__ */ new Map();
29283
- for (const atlas of atlases) {
29284
- const { width, height } = atlas.image;
29285
- if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
29286
- let selected;
29287
- for (const page of pages) {
29288
- const nextRow = page.x + width > maxAtlasSize;
29289
- if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
29290
- if (nextRow) {
29291
- page.y += page.rowHeight;
29292
- page.x = 0;
29293
- page.rowHeight = 0;
29294
- }
29295
- selected = page;
29296
- break;
29297
- }
29298
- if (!selected) {
29299
- selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
29300
- pages.push(selected);
29301
- }
29302
- const placement = { atlas, x: selected.x, y: selected.y };
29303
- selected.placements.push(placement);
29304
- selected.x += width;
29305
- selected.rowHeight = Math.max(selected.rowHeight, height);
29306
- selected.width = Math.max(selected.width, selected.x);
29307
- selected.height = Math.max(selected.height, selected.y + height);
29308
- locations.set(atlas, { page: selected, placement });
29309
- }
29310
- for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
29597
+ const { input, atlases } = toInput(entries, maxAtlasSize);
29598
+ return this.fromPages(buildSectionGeometry(input), atlases);
29599
+ }
29600
+ /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
29601
+ static async buildAsync(entries, maxAtlasSize = 2048) {
29602
+ const { input, atlases } = toInput(entries, maxAtlasSize);
29603
+ const worker = SectionWorker.shared();
29604
+ const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
29605
+ return this.fromPages(pages, atlases);
29606
+ }
29607
+ static fromPages(pages, atlases) {
29311
29608
  const section = new _SectionMesh();
29312
29609
  for (const page of pages) {
29313
29610
  const canvas = createCanvas(page.width, page.height);
29314
29611
  const context = canvas.getContext("2d");
29315
29612
  for (const { atlas, x, y } of page.placements) {
29316
- context.drawImage(atlas.image.canvas, x, y);
29317
- }
29318
- const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
29319
- for (const { index, template, cullMask } of page.entries) {
29320
- const position = template.geometry.getAttribute("position");
29321
- const normal = template.geometry.getAttribute("normal");
29322
- const uv = template.geometry.getAttribute("uv");
29323
- const bounds = template.geometry.getAttribute("uvBounds");
29324
- const color2 = template.geometry.getAttribute("color");
29325
- const sourceIndices = template.geometry.getIndex();
29326
- const placement = locations.get(template.atlas).placement;
29327
- const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
29328
- const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
29329
- const offsetX = index % 16 * 16;
29330
- const offsetY = Math.floor(index / 256) * 16;
29331
- const offsetZ = Math.floor(index / 16) % 16 * 16;
29332
- for (let face = 0; face < 6; face++) {
29333
- if (cullMask & template.cullFaces[face]) continue;
29334
- const start = positions.length / 3;
29335
- for (let corner = 0; corner < 4; corner++) {
29336
- const vertex = face * 4 + corner;
29337
- positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
29338
- normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
29339
- uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
29340
- uvBounds.push(
29341
- mapU(bounds?.getX(vertex) ?? 0),
29342
- mapV(bounds?.getY(vertex) ?? 0),
29343
- mapU(bounds?.getZ(vertex) ?? 1),
29344
- mapV(bounds?.getW(vertex) ?? 1)
29345
- );
29346
- colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
29347
- }
29348
- for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
29349
- }
29613
+ context.drawImage(atlases[atlas].image.canvas, x, y);
29350
29614
  }
29351
29615
  const geometry = new BufferGeometry8();
29352
- geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
29353
- geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
29354
- geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
29355
- geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
29356
- geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
29357
- geometry.setIndex(indices);
29616
+ geometry.setAttribute("position", new Float32BufferAttribute8(page.positions.buffer, 3));
29617
+ geometry.setAttribute("normal", new Float32BufferAttribute8(page.normals.buffer, 3));
29618
+ geometry.setAttribute("uv", new Float32BufferAttribute8(page.uvs.buffer, 2));
29619
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute8(page.uvBounds.buffer, 4));
29620
+ geometry.setAttribute("color", new Float32BufferAttribute8(page.colors.buffer, 3));
29621
+ geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
29358
29622
  geometry.computeBoundingBox();
29359
29623
  geometry.computeBoundingSphere();
29360
29624
  const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
@@ -29388,7 +29652,9 @@ var Chunk = class _Chunk {
29388
29652
  this.renderedBlocks = /* @__PURE__ */ new Map();
29389
29653
  this.sectionBlocks = /* @__PURE__ */ new Map();
29390
29654
  this.hiddenBlocks = /* @__PURE__ */ new Set();
29655
+ this.blockPosition = new Vector317();
29391
29656
  this.meshDirty = false;
29657
+ this.meshGeneration = 0;
29392
29658
  this.scene = scene;
29393
29659
  this.x = x;
29394
29660
  this.y = y;
@@ -29401,7 +29667,7 @@ var Chunk = class _Chunk {
29401
29667
  if (isTripleArray(posOrX)) {
29402
29668
  return this.getBlockAt(new Vector317(posOrX[0], posOrX[1], posOrX[2]));
29403
29669
  }
29404
- const pos = this.worldPosToChunkPos(posOrX);
29670
+ const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
29405
29671
  const index = _Chunk.chunkPosToBlockIndex(pos);
29406
29672
  return this.renderedBlocks.get(index);
29407
29673
  }
@@ -29425,48 +29691,81 @@ var Chunk = class _Chunk {
29425
29691
  worldPos = this.chunkPosToWorldPos(pos);
29426
29692
  }
29427
29693
  const index = _Chunk.chunkPosToBlockIndex(pos);
29428
- this.hiddenBlocks.delete(index);
29429
- this.data.set(index, block);
29430
- const readBlock = this.data.snapshot(index);
29431
- this.renderedBlocks.get(index)?.object?.removeFromScene();
29432
- if (this.sectionBlocks.delete(index)) this.meshDirty = true;
29433
- this.renderedBlocks.delete(index);
29434
- let object3;
29435
29694
  try {
29436
- if (!readBlock) return void 0;
29437
- const stored = readBlock();
29438
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
29439
- if (!blockState) {
29440
- this.data.set(index, void 0);
29441
- return void 0;
29442
- }
29443
- const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
29444
- const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
29445
- if (template) {
29446
- this.sectionBlocks.set(index, { index, template, cullMask: 0 });
29447
- this.meshDirty = true;
29448
- } else {
29449
- object3 = await this.scene.addBlock(blockState, {
29450
- mergeMeshes: true,
29451
- instanceMeshes: true,
29452
- maxInstanceCount: 2e3,
29453
- initialState: stored.properties
29454
- });
29455
- object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
29456
- }
29457
- const info = { get block() {
29458
- return readBlock();
29459
- }, object: object3 };
29460
- this.renderedBlocks.set(index, info);
29461
- return info;
29462
- } catch (error) {
29463
- this.data.set(index, void 0);
29464
- object3?.removeFromScene();
29465
- throw error;
29695
+ await this.placeBlocks([{ index, block }]);
29696
+ return this.renderedBlocks.get(index);
29466
29697
  } finally {
29467
29698
  await onBlocksChanged?.([worldPos]);
29468
29699
  }
29469
29700
  }
29701
+ /** Places cells in input order and returns changed world positions without refreshing neighbors. */
29702
+ async placeBlocks(blocks) {
29703
+ const resolutions = /* @__PURE__ */ new Map();
29704
+ for (const { block } of blocks) {
29705
+ if (!block || ChunkData.isAir(block)) continue;
29706
+ const key = ChunkData.paletteKey(block);
29707
+ if (resolutions.has(key)) continue;
29708
+ const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
29709
+ const resolved = (async () => {
29710
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
29711
+ const perBlock = !blockState || !this.sectionModels || !!getFluidKind(blockState.key, stored.properties) || !!(blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString()));
29712
+ if (!perBlock) await this.sectionModels.get(blockState, stored.properties);
29713
+ return { blockState, perBlock };
29714
+ })();
29715
+ resolutions.set(key, resolved);
29716
+ void resolved.catch(() => void 0);
29717
+ }
29718
+ const positions = [];
29719
+ let failed = false, failure;
29720
+ for (const { index, block } of blocks) {
29721
+ this.hiddenBlocks.delete(index);
29722
+ this.data.set(index, block);
29723
+ const readBlock = this.data.snapshot(index);
29724
+ this.renderedBlocks.get(index)?.object?.removeFromScene();
29725
+ if (this.sectionBlocks.delete(index)) this.meshDirty = true;
29726
+ this.renderedBlocks.delete(index);
29727
+ const worldPos = new Vector317(
29728
+ this.x * 16 + index % 16,
29729
+ this.y * 16 + Math.floor(index / 256),
29730
+ this.z * 16 + Math.floor(index / 16) % 16
29731
+ );
29732
+ positions.push(worldPos);
29733
+ let object3;
29734
+ try {
29735
+ if (!readBlock) continue;
29736
+ const stored = readBlock();
29737
+ const key = ChunkData.paletteKey(stored);
29738
+ const { blockState, perBlock } = await resolutions.get(key);
29739
+ if (!blockState) {
29740
+ this.data.set(index, void 0);
29741
+ continue;
29742
+ }
29743
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
29744
+ if (template) {
29745
+ this.sectionBlocks.set(index, { index, template, cullMask: 0 });
29746
+ this.meshDirty = true;
29747
+ } else {
29748
+ object3 = await this.scene.addBlock(blockState, {
29749
+ mergeMeshes: true,
29750
+ instanceMeshes: true,
29751
+ maxInstanceCount: 2e3,
29752
+ initialState: stored.properties
29753
+ });
29754
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
29755
+ }
29756
+ this.renderedBlocks.set(index, { get block() {
29757
+ return readBlock();
29758
+ }, object: object3 });
29759
+ } catch (error) {
29760
+ this.data.set(index, void 0);
29761
+ object3?.removeFromScene();
29762
+ if (!failed) failure = error;
29763
+ failed = true;
29764
+ }
29765
+ }
29766
+ if (failed) throw failure;
29767
+ return positions;
29768
+ }
29470
29769
  /** Changes visibility at world block coordinates while preserving the block's data. */
29471
29770
  async setBlockVisibleAt(pos, visible) {
29472
29771
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
@@ -29487,15 +29786,24 @@ var Chunk = class _Chunk {
29487
29786
  await this.onBlocksChanged?.([pos]);
29488
29787
  }
29489
29788
  isBlockVisibleAt(pos) {
29490
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29789
+ return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
29790
+ }
29791
+ /** Reports whether a cell has a visible render object or section entry. */
29792
+ isBlockVisibleIndex(index) {
29491
29793
  return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
29492
29794
  }
29493
29795
  isOccludingAt(pos) {
29494
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29796
+ return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
29797
+ }
29798
+ /** Reports whether a visible cell hides neighboring cube faces. */
29799
+ isOccludingIndex(index) {
29495
29800
  return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
29496
29801
  }
29497
29802
  async setCullMaskAt(pos, mask) {
29498
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29803
+ await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
29804
+ }
29805
+ /** Updates hidden faces for a cell's section entry or individual render object. */
29806
+ async setCullMaskIndex(index, mask) {
29499
29807
  const entry = this.sectionBlocks.get(index);
29500
29808
  if (entry) {
29501
29809
  if (entry.cullMask !== mask) {
@@ -29506,12 +29814,17 @@ var Chunk = class _Chunk {
29506
29814
  await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
29507
29815
  }
29508
29816
  }
29509
- rebuildSectionMesh() {
29817
+ /** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
29818
+ async rebuildSectionMesh() {
29510
29819
  if (!this.meshDirty) return;
29511
- const next = this.sectionBlocks.size ? SectionMesh.build(
29512
- [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index)),
29513
- this.sectionModels.maxAtlasSize
29514
- ) : void 0;
29820
+ this.meshDirty = false;
29821
+ const generation = ++this.meshGeneration;
29822
+ const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
29823
+ const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
29824
+ if (generation !== this.meshGeneration) {
29825
+ next?.dispose();
29826
+ return;
29827
+ }
29515
29828
  this.sectionMesh?.dispose();
29516
29829
  this.sectionMesh = next;
29517
29830
  if (next) {
@@ -29519,7 +29832,6 @@ var Chunk = class _Chunk {
29519
29832
  this.scene.add(next);
29520
29833
  }
29521
29834
  this.scene.dirty = true;
29522
- this.meshDirty = false;
29523
29835
  }
29524
29836
  /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
29525
29837
  async clear(onBlocksChanged = this.onBlocksChanged) {
@@ -29528,6 +29840,7 @@ var Chunk = class _Chunk {
29528
29840
  ));
29529
29841
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
29530
29842
  this.sectionBlocks.clear();
29843
+ this.meshGeneration++;
29531
29844
  this.sectionMesh?.dispose();
29532
29845
  this.sectionMesh = void 0;
29533
29846
  this.meshDirty = false;
@@ -29831,6 +30144,7 @@ export {
29831
30144
  SchematicParser,
29832
30145
  SectionMesh,
29833
30146
  SectionModels,
30147
+ SectionWorker,
29834
30148
  SkinImage,
29835
30149
  SkinObject,
29836
30150
  SkinPart,
@@ -29855,6 +30169,7 @@ export {
29855
30169
  axisToVec3,
29856
30170
  base64decode,
29857
30171
  base64encode,
30172
+ buildSectionGeometry,
29858
30173
  capeTextureSizes,
29859
30174
  captureException,
29860
30175
  changeEvent,
@@ -29907,6 +30222,7 @@ export {
29907
30222
  sampleEntityAnimation,
29908
30223
  sampleEntityKeyframes,
29909
30224
  sampleFluid,
30225
+ sectionGeometryTransferables,
29910
30226
  serializeBoxGeometryKey,
29911
30227
  serializeImageKey,
29912
30228
  serializeMaterialKey,
@@ -29924,6 +30240,7 @@ export {
29924
30240
  toDegrees,
29925
30241
  toRadians,
29926
30242
  toTrianglesDrawMode,
29927
- trimCanvas
30243
+ trimCanvas,
30244
+ yieldToEventLoop
29928
30245
  };
29929
30246
  //# sourceMappingURL=index.mjs.map