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.
@@ -192,6 +192,23 @@ async function sleep(timeout) {
192
192
  setTimeout(() => resolve(), timeout);
193
193
  });
194
194
  }
195
+ function yieldToEventLoop() {
196
+ const scheduler = globalThis.scheduler;
197
+ if (scheduler?.yield) return scheduler.yield();
198
+ return new Promise((resolve) => {
199
+ if (typeof MessageChannel !== "undefined") {
200
+ const channel = new MessageChannel();
201
+ channel.port1.onmessage = () => {
202
+ channel.port1.close();
203
+ channel.port2.close();
204
+ resolve();
205
+ };
206
+ channel.port2.postMessage(void 0);
207
+ } else {
208
+ setTimeout(resolve, 0);
209
+ }
210
+ });
211
+ }
195
212
  function clampRotationDegrees(deg) {
196
213
  return deg % 360;
197
214
  }
@@ -28557,6 +28574,16 @@ init_NBTHelper();
28557
28574
 
28558
28575
  // src/world/ChunkData.ts
28559
28576
  init_AssetKey();
28577
+ var blockKeys = /* @__PURE__ */ new Map();
28578
+ function blockKey(type) {
28579
+ let key = blockKeys.get(type);
28580
+ if (!key) {
28581
+ key = AssetKey.parse("blockstates", type);
28582
+ if (blockKeys.size >= 4096) blockKeys.clear();
28583
+ blockKeys.set(type, key);
28584
+ }
28585
+ return key;
28586
+ }
28560
28587
  var ChunkData = class _ChunkData {
28561
28588
  constructor() {
28562
28589
  this.indices = new Uint16Array(4096);
@@ -28584,10 +28611,10 @@ var ChunkData = class _ChunkData {
28584
28611
  let key;
28585
28612
  let nbt;
28586
28613
  if (block && !_ChunkData.isAir(block)) {
28587
- const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
28614
+ const type = blockKey(block.type).toNamespacedString();
28588
28615
  const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
28589
28616
  state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
28590
- key = JSON.stringify([type, properties]);
28617
+ key = _ChunkData.paletteKey(block);
28591
28618
  if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
28592
28619
  }
28593
28620
  const previousId = this.indices[index];
@@ -28624,9 +28651,14 @@ var ChunkData = class _ChunkData {
28624
28651
  }
28625
28652
  static isAir(block) {
28626
28653
  if (typeof block === "undefined" || typeof block.type === "undefined") return true;
28627
- const key = AssetKey.parse("blockstates", block.type);
28654
+ const key = blockKey(block.type);
28628
28655
  return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
28629
28656
  }
28657
+ /** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
28658
+ static paletteKey(block) {
28659
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
28660
+ return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
28661
+ }
28630
28662
  static copyBlock(state, nbt) {
28631
28663
  const block = { type: state.type };
28632
28664
  if (state.properties) block.properties = { ...state.properties };
@@ -28806,14 +28838,17 @@ var AnvilParser = class {
28806
28838
  const perLong = Math.floor(64 / bits);
28807
28839
  const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
28808
28840
  if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
28809
- const words = tag.value.map(([high, low]) => BigInt(high >>> 0) << 32n | BigInt(low >>> 0));
28810
- const mask = (1n << BigInt(bits)) - 1n;
28841
+ const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
28842
+ const mask = (1 << bits) - 1;
28811
28843
  for (let index = 0; index < 4096; index++) {
28812
- const word = padded ? Math.floor(index / perLong) : Math.floor(index * bits / 64);
28813
- const shift = padded ? index % perLong * bits : index * bits % 64;
28814
- let value = words[word] >> BigInt(shift);
28815
- if (!padded && shift + bits > 64) value |= words[word + 1] << BigInt(64 - shift);
28816
- indices[index] = Number(value & mask);
28844
+ const bitPos = index * bits;
28845
+ const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
28846
+ const shift = padded ? index % perLong * bits : bitPos & 63;
28847
+ const [high, low] = words[word];
28848
+ let value = shift < 32 ? low >>> shift : high >>> shift - 32;
28849
+ if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
28850
+ if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
28851
+ indices[index] = value & mask;
28817
28852
  }
28818
28853
  return indices;
28819
28854
  }
@@ -28954,8 +28989,8 @@ init_AssetKey();
28954
28989
 
28955
28990
  // src/world/MineRenderWorld.ts
28956
28991
  init_Model();
28992
+ init_util();
28957
28993
  import { Vector3 as Vector316 } from "three";
28958
- init_BatchedExecutor();
28959
28994
  init_AssetKey();
28960
28995
  init_BlockStates();
28961
28996
  init_CubeFace();
@@ -29100,7 +29135,7 @@ var MineRenderWorld = class {
29100
29135
  * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
29101
29136
  * Entity NBT is retained by the structure but is not rendered.
29102
29137
  *
29103
- * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
29138
+ * @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
29104
29139
  * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
29105
29140
  */
29106
29141
  async placeMultiBlock(multiblock, useBatches = true, executor) {
@@ -29117,8 +29152,10 @@ var MineRenderWorld = class {
29117
29152
  };
29118
29153
  const place = (block) => this.placeBlock(new Vector316(...block.position), block, collect);
29119
29154
  const keys = /* @__PURE__ */ new Map();
29155
+ const types = /* @__PURE__ */ new Set();
29120
29156
  for (const block of multiblock.blocks) {
29121
- if (Chunk.isAir(block)) continue;
29157
+ if (Chunk.isAir(block) || types.has(block.type)) continue;
29158
+ types.add(block.type);
29122
29159
  const key = AssetKey.parse("blockstates", block.type);
29123
29160
  keys.set(key.serialize(), key);
29124
29161
  }
@@ -29127,6 +29164,23 @@ var MineRenderWorld = class {
29127
29164
  for (const block of multiblock.blocks) await place(block);
29128
29165
  return;
29129
29166
  }
29167
+ if (!executor) {
29168
+ const groups2 = /* @__PURE__ */ new Map();
29169
+ const position = new Vector316();
29170
+ for (const block of multiblock.blocks) {
29171
+ const [x, y, z] = block.position;
29172
+ this.validatePosBounds(position.set(x, y, z));
29173
+ const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
29174
+ const key = `${cx}_${cy}_${cz}`;
29175
+ const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
29176
+ if (!chunk) continue;
29177
+ let group = groups2.get(chunk);
29178
+ if (!group) groups2.set(chunk, group = []);
29179
+ group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
29180
+ }
29181
+ await this.placeChunkGroups(groups2, changes);
29182
+ return;
29183
+ }
29130
29184
  const positions = /* @__PURE__ */ new Map();
29131
29185
  for (const block of multiblock.blocks) {
29132
29186
  const key = block.position.join(",");
@@ -29135,19 +29189,34 @@ var MineRenderWorld = class {
29135
29189
  else positions.set(key, [block]);
29136
29190
  }
29137
29191
  const groups = [...positions.values()];
29138
- const queue = executor ?? new BatchedExecutor();
29139
- try {
29140
- for (let i = 0; i < groups.length; i += queue.batch) {
29141
- const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
29142
- (blocks) => queue.submit(async () => {
29143
- for (const block of blocks) await place(block);
29144
- })
29192
+ for (let i = 0; i < groups.length; i += executor.batch) {
29193
+ const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
29194
+ (blocks) => executor.submit(async () => {
29195
+ for (const block of blocks) await place(block);
29196
+ })
29197
+ ));
29198
+ const failure = results.find((result) => result.status === "rejected");
29199
+ if (failure?.status === "rejected") throw failure.reason;
29200
+ }
29201
+ }
29202
+ async placeChunkGroups(groups, changes) {
29203
+ let sliceStart = performance.now();
29204
+ for (const [chunk, blocks] of groups) {
29205
+ let positions;
29206
+ try {
29207
+ positions = await chunk.placeBlocks(blocks);
29208
+ } finally {
29209
+ positions ??= blocks.map(({ index }) => new Vector316(
29210
+ chunk.x * 16 + index % 16,
29211
+ chunk.y * 16 + Math.floor(index / 256),
29212
+ chunk.z * 16 + Math.floor(index / 16) % 16
29145
29213
  ));
29146
- const failure = results.find((result) => result.status === "rejected");
29147
- if (failure?.status === "rejected") throw failure.reason;
29214
+ for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
29215
+ }
29216
+ if (performance.now() - sliceStart > 8) {
29217
+ await yieldToEventLoop();
29218
+ sliceStart = performance.now();
29148
29219
  }
29149
- } finally {
29150
- if (!executor) queue.stop();
29151
29220
  }
29152
29221
  }
29153
29222
  /** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
@@ -29155,7 +29224,24 @@ var MineRenderWorld = class {
29155
29224
  const changes = /* @__PURE__ */ new Map();
29156
29225
  try {
29157
29226
  await this.clearChunkColumn(chunk.x, chunk.z, changes);
29227
+ const groups = /* @__PURE__ */ new Map();
29158
29228
  for (const section of chunk.sections) {
29229
+ if (!executor) {
29230
+ const blocks2 = [];
29231
+ for (let index = 0; index < 4096; index++) {
29232
+ const block = section.data.get(index);
29233
+ if (block) blocks2.push({ index, block });
29234
+ }
29235
+ if (blocks2.length) {
29236
+ const position = new Vector316(chunk.x * 16, section.y * 16, chunk.z * 16);
29237
+ this.validatePosBounds(position);
29238
+ const target = this.getOrCreateChunkAt(position);
29239
+ const previous = groups.get(target);
29240
+ if (previous) previous.push(...blocks2);
29241
+ else groups.set(target, blocks2);
29242
+ }
29243
+ continue;
29244
+ }
29159
29245
  const blocks = [];
29160
29246
  for (let index = 0; index < 4096; index++) {
29161
29247
  const block = section.data.get(index);
@@ -29170,6 +29256,7 @@ var MineRenderWorld = class {
29170
29256
  }
29171
29257
  await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
29172
29258
  }
29259
+ if (!executor) await this.placeChunkGroups(groups, changes);
29173
29260
  } finally {
29174
29261
  await this.updateCulling([...changes.values()]);
29175
29262
  }
@@ -29210,19 +29297,23 @@ var MineRenderWorld = class {
29210
29297
  this.sectionModels?.clear();
29211
29298
  }
29212
29299
  updateCulling(positions) {
29300
+ const neighbor = new Vector316();
29213
29301
  for (const pos of positions) {
29214
- this.pendingCulling.set(pos.toArray().join(","), pos.clone());
29215
- for (const offset of CUBE_FACE_OFFSETS) {
29216
- const neighbor = pos.clone().add(new Vector316(...offset));
29217
- this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
29218
- }
29302
+ const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
29303
+ const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
29304
+ const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
29219
29305
  for (let y = -1; y <= 1; y++) {
29220
29306
  for (let z = -1; z <= 1; z++) {
29221
29307
  for (let x = -1; x <= 1; x++) {
29222
- const neighbor = pos.clone().add(new Vector316(x, y, z));
29223
- if (this.getBlockAt(neighbor)?.object?.fluidKind) {
29224
- this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
29225
- }
29308
+ const nx = lx + x, ny = ly + y, nz = lz + z;
29309
+ 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)}`);
29310
+ if (!section) continue;
29311
+ const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
29312
+ let pending = this.pendingCulling.get(section);
29313
+ if (pending?.has(index)) continue;
29314
+ 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;
29315
+ if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
29316
+ pending.add(index);
29226
29317
  }
29227
29318
  }
29228
29319
  }
@@ -29230,39 +29321,38 @@ var MineRenderWorld = class {
29230
29321
  return this.culling ??= Promise.resolve().then(async () => {
29231
29322
  try {
29232
29323
  while (this.pendingCulling.size) {
29233
- const batch = [...this.pendingCulling.values()];
29324
+ const batch = [...this.pendingCulling];
29234
29325
  this.pendingCulling.clear();
29235
- const changed = /* @__PURE__ */ new Set();
29236
- for (const pos of batch) {
29237
- const chunk = this.getChunkAt(pos);
29238
- if (!chunk) continue;
29239
- changed.add(chunk);
29240
- const block = chunk.getBlockAt(pos);
29241
- if (!block || !chunk.isBlockVisibleAt(pos)) {
29242
- continue;
29243
- }
29244
- if (block.object?.fluidKind) {
29245
- await block.object.updateFluid((x, y, z) => {
29246
- const neighbor = pos.clone().add(new Vector316(x, y, z));
29247
- const section = this.getChunkAt(neighbor);
29248
- const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
29249
- return {
29250
- fluid: object3?.fluidKind,
29251
- level: object3?.fluidLevel,
29252
- solid: section?.isOccludingAt(neighbor) ?? false
29253
- };
29254
- });
29255
- }
29256
- let mask = 0;
29257
- for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
29258
- const neighbor = pos.clone().add(new Vector316(...offset));
29259
- if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
29260
- mask |= 1 << face;
29326
+ for (const [chunk, indices] of batch) {
29327
+ for (const index of indices) {
29328
+ if (!chunk.isBlockVisibleIndex(index)) continue;
29329
+ const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
29330
+ const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
29331
+ const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
29332
+ if (block.object?.fluidKind) {
29333
+ await block.object.updateFluid((x, y, z) => {
29334
+ const nx = lx + x, ny = ly + y, nz = lz + z;
29335
+ 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)}`);
29336
+ const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
29337
+ const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
29338
+ return {
29339
+ fluid: object3?.fluidKind,
29340
+ level: object3?.fluidLevel,
29341
+ solid: section?.isOccludingIndex(neighborIndex) ?? false
29342
+ };
29343
+ });
29344
+ }
29345
+ let mask = 0;
29346
+ for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
29347
+ const offset = CUBE_FACE_OFFSETS[face];
29348
+ const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
29349
+ 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)}`);
29350
+ if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
29261
29351
  }
29352
+ await chunk.setCullMaskIndex(index, mask);
29262
29353
  }
29263
- await chunk.setCullMaskAt(pos, mask);
29264
29354
  }
29265
- for (const chunk of changed) chunk.rebuildSectionMesh();
29355
+ await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
29266
29356
  }
29267
29357
  } finally {
29268
29358
  this.culling = void 0;
@@ -29323,8 +29413,230 @@ init_Model();
29323
29413
 
29324
29414
  // src/world/SectionMesh.ts
29325
29415
  init_CanvasCompat();
29416
+ init_CubeFace();
29326
29417
  init_Materials();
29327
- import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
29418
+ import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9, Uint32BufferAttribute } from "three";
29419
+
29420
+ // src/world/SectionGeometry.ts
29421
+ init_CubeFace();
29422
+ function buildSectionGeometry(input) {
29423
+ const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
29424
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29425
+ if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
29426
+ const visible = [];
29427
+ const visibleAtlases = /* @__PURE__ */ new Set();
29428
+ for (let entry = 0; entry < count; entry++) {
29429
+ const template = templateData[templates[entry]];
29430
+ if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
29431
+ visible.push(entry);
29432
+ visibleAtlases.add(template.atlas);
29433
+ }
29434
+ const pages = [];
29435
+ const locations = /* @__PURE__ */ new Map();
29436
+ for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
29437
+ const { width, height } = atlases[atlas];
29438
+ if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
29439
+ let selected = -1;
29440
+ for (let i = 0; i < pages.length; i++) {
29441
+ const page2 = pages[i];
29442
+ const nextRow = page2.x + width > maxAtlasSize;
29443
+ if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
29444
+ if (nextRow) {
29445
+ page2.y += page2.rowHeight;
29446
+ page2.x = 0;
29447
+ page2.rowHeight = 0;
29448
+ }
29449
+ selected = i;
29450
+ break;
29451
+ }
29452
+ if (selected < 0) {
29453
+ selected = pages.length;
29454
+ pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
29455
+ }
29456
+ const page = pages[selected];
29457
+ page.placements.push({ atlas, x: page.x, y: page.y });
29458
+ locations.set(atlas, { page: selected, x: page.x, y: page.y });
29459
+ page.x += width;
29460
+ page.rowHeight = Math.max(page.rowHeight, height);
29461
+ page.width = Math.max(page.width, page.x);
29462
+ page.height = Math.max(page.height, page.y + height);
29463
+ }
29464
+ for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
29465
+ return pages.map((page) => {
29466
+ let faces = 0;
29467
+ for (const entry of page.entries) {
29468
+ const template = templateData[templates[entry]];
29469
+ for (let face = 0; face < CUBE_FACES.length; face++) {
29470
+ if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
29471
+ }
29472
+ }
29473
+ const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
29474
+ const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
29475
+ const outputIndices = new Uint32Array(faces * 6);
29476
+ let vertex = 0, indexOffset = 0;
29477
+ for (const entry of page.entries) {
29478
+ const template = templateData[templates[entry]];
29479
+ const atlas = atlases[template.atlas];
29480
+ const placement = locations.get(template.atlas);
29481
+ const mapU = (u) => (placement.x + u * atlas.width) / page.width;
29482
+ const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
29483
+ const offsetX = indices[entry] % 16 * 16;
29484
+ const offsetY = Math.floor(indices[entry] / 256) * 16;
29485
+ const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
29486
+ for (let face = 0; face < CUBE_FACES.length; face++) {
29487
+ if (cullMasks[entry] & template.cullFaces[face]) continue;
29488
+ const start = vertex;
29489
+ for (let corner = 0; corner < 4; corner++, vertex++) {
29490
+ const source = face * 4 + corner;
29491
+ positions[vertex * 3] = template.positions[source * 3] + offsetX;
29492
+ positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
29493
+ positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
29494
+ normals[vertex * 3] = template.normals[source * 3];
29495
+ normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
29496
+ normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
29497
+ uvs[vertex * 2] = mapU(template.uvs[source * 2]);
29498
+ uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
29499
+ uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
29500
+ uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
29501
+ uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
29502
+ uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
29503
+ colors[vertex * 3] = template.colors[source * 3];
29504
+ colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
29505
+ colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
29506
+ }
29507
+ for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
29508
+ }
29509
+ }
29510
+ return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
29511
+ });
29512
+ }
29513
+ function sectionGeometryTransferables(pages) {
29514
+ return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
29515
+ }
29516
+
29517
+ // src/world/SectionWorker.ts
29518
+ init_Env();
29519
+ var SectionWorker = class _SectionWorker {
29520
+ constructor(worker) {
29521
+ this.worker = worker;
29522
+ this.nextId = 0;
29523
+ this.pending = /* @__PURE__ */ new Map();
29524
+ worker.addEventListener("message", (event) => {
29525
+ const reply = event.data;
29526
+ const pending = this.pending.get(reply.id);
29527
+ if (!pending) return;
29528
+ this.pending.delete(reply.id);
29529
+ if (reply.type === "pages") pending.resolve(reply.pages);
29530
+ else pending.reject(new Error(reply.message));
29531
+ });
29532
+ worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
29533
+ worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
29534
+ }
29535
+ static {
29536
+ this.initialized = false;
29537
+ }
29538
+ /** The shared worker, or undefined when the environment has none or a previous worker failed. */
29539
+ static shared() {
29540
+ if (!this.initialized) {
29541
+ this.initialized = true;
29542
+ const worker = Env.provider.createWorker?.("section");
29543
+ if (worker) this.instance = new _SectionWorker(worker);
29544
+ }
29545
+ return this.instance;
29546
+ }
29547
+ /** Builds geometry without transferring the input buffers away from the caller. */
29548
+ build(input) {
29549
+ if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
29550
+ return new Promise((resolve, reject) => {
29551
+ const id = this.nextId++;
29552
+ this.pending.set(id, { resolve, reject });
29553
+ this.worker.postMessage({ id, type: "build", input });
29554
+ });
29555
+ }
29556
+ /** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
29557
+ terminate() {
29558
+ this.fail("Section worker terminated");
29559
+ }
29560
+ fail(message) {
29561
+ const error = new Error(message);
29562
+ for (const pending of this.pending.values()) pending.reject(error);
29563
+ this.pending.clear();
29564
+ this.worker?.terminate();
29565
+ this.worker = void 0;
29566
+ _SectionWorker.instance = void 0;
29567
+ }
29568
+ };
29569
+
29570
+ // src/world/SectionMesh.ts
29571
+ var templateCache = /* @__PURE__ */ new WeakMap();
29572
+ function toInput(entries, maxAtlasSize) {
29573
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29574
+ const atlases = [];
29575
+ const atlasIds = /* @__PURE__ */ new Map();
29576
+ const templateIds = /* @__PURE__ */ new Map();
29577
+ const input = {
29578
+ count: entries.length,
29579
+ indices: new Uint16Array(entries.length),
29580
+ templates: new Uint16Array(entries.length),
29581
+ cullMasks: new Uint8Array(entries.length),
29582
+ templateData: [],
29583
+ atlases: [],
29584
+ maxAtlasSize
29585
+ };
29586
+ for (let i = 0; i < entries.length; i++) {
29587
+ const { index, template, cullMask } = entries[i];
29588
+ let id = templateIds.get(template);
29589
+ if (id === void 0) {
29590
+ let atlas = atlasIds.get(template.atlas);
29591
+ if (atlas === void 0) {
29592
+ atlas = atlases.length;
29593
+ atlasIds.set(template.atlas, atlas);
29594
+ atlases.push(template.atlas);
29595
+ input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
29596
+ }
29597
+ let data = templateCache.get(template);
29598
+ if (!data) {
29599
+ data = {
29600
+ positions: new Float32Array(72),
29601
+ normals: new Float32Array(72),
29602
+ uvs: new Float32Array(48),
29603
+ uvBounds: new Float32Array(96),
29604
+ colors: new Float32Array(72),
29605
+ indices: new Uint16Array(36),
29606
+ cullFaces: new Uint8Array(template.cullFaces),
29607
+ atlas: 0
29608
+ };
29609
+ const position = template.geometry.getAttribute("position");
29610
+ const normal = template.geometry.getAttribute("normal");
29611
+ const uv = template.geometry.getAttribute("uv");
29612
+ const bounds = template.geometry.getAttribute("uvBounds");
29613
+ const color2 = template.geometry.getAttribute("color");
29614
+ for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
29615
+ data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
29616
+ data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
29617
+ data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
29618
+ data.uvBounds.set([
29619
+ bounds?.getX(vertex) ?? 0,
29620
+ bounds?.getY(vertex) ?? 0,
29621
+ bounds?.getZ(vertex) ?? 1,
29622
+ bounds?.getW(vertex) ?? 1
29623
+ ], vertex * 4);
29624
+ data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
29625
+ }
29626
+ const indices = template.geometry.getIndex();
29627
+ for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
29628
+ templateCache.set(template, data);
29629
+ }
29630
+ id = input.templateData.length;
29631
+ templateIds.set(template, id);
29632
+ input.templateData.push({ ...data, atlas });
29633
+ }
29634
+ input.indices[i] = index;
29635
+ input.templates[i] = id;
29636
+ input.cullMasks[i] = cullMask;
29637
+ }
29638
+ return { input, atlases };
29639
+ }
29328
29640
  var SectionMesh = class _SectionMesh extends Group3 {
29329
29641
  constructor() {
29330
29642
  super(...arguments);
@@ -29332,86 +29644,31 @@ var SectionMesh = class _SectionMesh extends Group3 {
29332
29644
  }
29333
29645
  /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
29334
29646
  static build(entries, maxAtlasSize = 2048) {
29335
- if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
29336
- const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
29337
- const atlases = [...new Set(visible.map((entry) => entry.template.atlas))].sort((a, b) => b.image.height - a.image.height);
29338
- const pages = [];
29339
- const locations = /* @__PURE__ */ new Map();
29340
- for (const atlas of atlases) {
29341
- const { width, height } = atlas.image;
29342
- if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
29343
- let selected;
29344
- for (const page of pages) {
29345
- const nextRow = page.x + width > maxAtlasSize;
29346
- if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
29347
- if (nextRow) {
29348
- page.y += page.rowHeight;
29349
- page.x = 0;
29350
- page.rowHeight = 0;
29351
- }
29352
- selected = page;
29353
- break;
29354
- }
29355
- if (!selected) {
29356
- selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
29357
- pages.push(selected);
29358
- }
29359
- const placement = { atlas, x: selected.x, y: selected.y };
29360
- selected.placements.push(placement);
29361
- selected.x += width;
29362
- selected.rowHeight = Math.max(selected.rowHeight, height);
29363
- selected.width = Math.max(selected.width, selected.x);
29364
- selected.height = Math.max(selected.height, selected.y + height);
29365
- locations.set(atlas, { page: selected, placement });
29366
- }
29367
- for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
29647
+ const { input, atlases } = toInput(entries, maxAtlasSize);
29648
+ return this.fromPages(buildSectionGeometry(input), atlases);
29649
+ }
29650
+ /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
29651
+ static async buildAsync(entries, maxAtlasSize = 2048) {
29652
+ const { input, atlases } = toInput(entries, maxAtlasSize);
29653
+ const worker = SectionWorker.shared();
29654
+ const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
29655
+ return this.fromPages(pages, atlases);
29656
+ }
29657
+ static fromPages(pages, atlases) {
29368
29658
  const section = new _SectionMesh();
29369
29659
  for (const page of pages) {
29370
29660
  const canvas = createCanvas2(page.width, page.height);
29371
29661
  const context = canvas.getContext("2d");
29372
29662
  for (const { atlas, x, y } of page.placements) {
29373
- context.drawImage(atlas.image.canvas, x, y);
29374
- }
29375
- const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
29376
- for (const { index, template, cullMask } of page.entries) {
29377
- const position = template.geometry.getAttribute("position");
29378
- const normal = template.geometry.getAttribute("normal");
29379
- const uv = template.geometry.getAttribute("uv");
29380
- const bounds = template.geometry.getAttribute("uvBounds");
29381
- const color2 = template.geometry.getAttribute("color");
29382
- const sourceIndices = template.geometry.getIndex();
29383
- const placement = locations.get(template.atlas).placement;
29384
- const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
29385
- const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
29386
- const offsetX = index % 16 * 16;
29387
- const offsetY = Math.floor(index / 256) * 16;
29388
- const offsetZ = Math.floor(index / 16) % 16 * 16;
29389
- for (let face = 0; face < 6; face++) {
29390
- if (cullMask & template.cullFaces[face]) continue;
29391
- const start = positions.length / 3;
29392
- for (let corner = 0; corner < 4; corner++) {
29393
- const vertex = face * 4 + corner;
29394
- positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
29395
- normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
29396
- uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
29397
- uvBounds.push(
29398
- mapU(bounds?.getX(vertex) ?? 0),
29399
- mapV(bounds?.getY(vertex) ?? 0),
29400
- mapU(bounds?.getZ(vertex) ?? 1),
29401
- mapV(bounds?.getW(vertex) ?? 1)
29402
- );
29403
- colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
29404
- }
29405
- for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
29406
- }
29663
+ context.drawImage(atlases[atlas].image.canvas, x, y);
29407
29664
  }
29408
29665
  const geometry = new BufferGeometry8();
29409
- geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
29410
- geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
29411
- geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
29412
- geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
29413
- geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
29414
- geometry.setIndex(indices);
29666
+ geometry.setAttribute("position", new Float32BufferAttribute8(page.positions.buffer, 3));
29667
+ geometry.setAttribute("normal", new Float32BufferAttribute8(page.normals.buffer, 3));
29668
+ geometry.setAttribute("uv", new Float32BufferAttribute8(page.uvs.buffer, 2));
29669
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute8(page.uvBounds.buffer, 4));
29670
+ geometry.setAttribute("color", new Float32BufferAttribute8(page.colors.buffer, 3));
29671
+ geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
29415
29672
  geometry.computeBoundingBox();
29416
29673
  geometry.computeBoundingSphere();
29417
29674
  const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
@@ -29445,7 +29702,9 @@ var Chunk = class _Chunk {
29445
29702
  this.renderedBlocks = /* @__PURE__ */ new Map();
29446
29703
  this.sectionBlocks = /* @__PURE__ */ new Map();
29447
29704
  this.hiddenBlocks = /* @__PURE__ */ new Set();
29705
+ this.blockPosition = new Vector317();
29448
29706
  this.meshDirty = false;
29707
+ this.meshGeneration = 0;
29449
29708
  this.scene = scene;
29450
29709
  this.x = x;
29451
29710
  this.y = y;
@@ -29458,7 +29717,7 @@ var Chunk = class _Chunk {
29458
29717
  if (isTripleArray(posOrX)) {
29459
29718
  return this.getBlockAt(new Vector317(posOrX[0], posOrX[1], posOrX[2]));
29460
29719
  }
29461
- const pos = this.worldPosToChunkPos(posOrX);
29720
+ const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
29462
29721
  const index = _Chunk.chunkPosToBlockIndex(pos);
29463
29722
  return this.renderedBlocks.get(index);
29464
29723
  }
@@ -29482,48 +29741,81 @@ var Chunk = class _Chunk {
29482
29741
  worldPos = this.chunkPosToWorldPos(pos);
29483
29742
  }
29484
29743
  const index = _Chunk.chunkPosToBlockIndex(pos);
29485
- this.hiddenBlocks.delete(index);
29486
- this.data.set(index, block);
29487
- const readBlock = this.data.snapshot(index);
29488
- this.renderedBlocks.get(index)?.object?.removeFromScene();
29489
- if (this.sectionBlocks.delete(index)) this.meshDirty = true;
29490
- this.renderedBlocks.delete(index);
29491
- let object3;
29492
29744
  try {
29493
- if (!readBlock) return void 0;
29494
- const stored = readBlock();
29495
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
29496
- if (!blockState) {
29497
- this.data.set(index, void 0);
29498
- return void 0;
29499
- }
29500
- const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
29501
- const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
29502
- if (template) {
29503
- this.sectionBlocks.set(index, { index, template, cullMask: 0 });
29504
- this.meshDirty = true;
29505
- } else {
29506
- object3 = await this.scene.addBlock(blockState, {
29507
- mergeMeshes: true,
29508
- instanceMeshes: true,
29509
- maxInstanceCount: 2e3,
29510
- initialState: stored.properties
29511
- });
29512
- object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
29513
- }
29514
- const info = { get block() {
29515
- return readBlock();
29516
- }, object: object3 };
29517
- this.renderedBlocks.set(index, info);
29518
- return info;
29519
- } catch (error) {
29520
- this.data.set(index, void 0);
29521
- object3?.removeFromScene();
29522
- throw error;
29745
+ await this.placeBlocks([{ index, block }]);
29746
+ return this.renderedBlocks.get(index);
29523
29747
  } finally {
29524
29748
  await onBlocksChanged?.([worldPos]);
29525
29749
  }
29526
29750
  }
29751
+ /** Places cells in input order and returns changed world positions without refreshing neighbors. */
29752
+ async placeBlocks(blocks) {
29753
+ const resolutions = /* @__PURE__ */ new Map();
29754
+ for (const { block } of blocks) {
29755
+ if (!block || ChunkData.isAir(block)) continue;
29756
+ const key = ChunkData.paletteKey(block);
29757
+ if (resolutions.has(key)) continue;
29758
+ const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
29759
+ const resolved = (async () => {
29760
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
29761
+ const perBlock = !blockState || !this.sectionModels || !!getFluidKind(blockState.key, stored.properties) || !!(blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString()));
29762
+ if (!perBlock) await this.sectionModels.get(blockState, stored.properties);
29763
+ return { blockState, perBlock };
29764
+ })();
29765
+ resolutions.set(key, resolved);
29766
+ void resolved.catch(() => void 0);
29767
+ }
29768
+ const positions = [];
29769
+ let failed = false, failure;
29770
+ for (const { index, block } of blocks) {
29771
+ this.hiddenBlocks.delete(index);
29772
+ this.data.set(index, block);
29773
+ const readBlock = this.data.snapshot(index);
29774
+ this.renderedBlocks.get(index)?.object?.removeFromScene();
29775
+ if (this.sectionBlocks.delete(index)) this.meshDirty = true;
29776
+ this.renderedBlocks.delete(index);
29777
+ const worldPos = new Vector317(
29778
+ this.x * 16 + index % 16,
29779
+ this.y * 16 + Math.floor(index / 256),
29780
+ this.z * 16 + Math.floor(index / 16) % 16
29781
+ );
29782
+ positions.push(worldPos);
29783
+ let object3;
29784
+ try {
29785
+ if (!readBlock) continue;
29786
+ const stored = readBlock();
29787
+ const key = ChunkData.paletteKey(stored);
29788
+ const { blockState, perBlock } = await resolutions.get(key);
29789
+ if (!blockState) {
29790
+ this.data.set(index, void 0);
29791
+ continue;
29792
+ }
29793
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
29794
+ if (template) {
29795
+ this.sectionBlocks.set(index, { index, template, cullMask: 0 });
29796
+ this.meshDirty = true;
29797
+ } else {
29798
+ object3 = await this.scene.addBlock(blockState, {
29799
+ mergeMeshes: true,
29800
+ instanceMeshes: true,
29801
+ maxInstanceCount: 2e3,
29802
+ initialState: stored.properties
29803
+ });
29804
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
29805
+ }
29806
+ this.renderedBlocks.set(index, { get block() {
29807
+ return readBlock();
29808
+ }, object: object3 });
29809
+ } catch (error) {
29810
+ this.data.set(index, void 0);
29811
+ object3?.removeFromScene();
29812
+ if (!failed) failure = error;
29813
+ failed = true;
29814
+ }
29815
+ }
29816
+ if (failed) throw failure;
29817
+ return positions;
29818
+ }
29527
29819
  /** Changes visibility at world block coordinates while preserving the block's data. */
29528
29820
  async setBlockVisibleAt(pos, visible) {
29529
29821
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
@@ -29544,15 +29836,24 @@ var Chunk = class _Chunk {
29544
29836
  await this.onBlocksChanged?.([pos]);
29545
29837
  }
29546
29838
  isBlockVisibleAt(pos) {
29547
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29839
+ return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
29840
+ }
29841
+ /** Reports whether a cell has a visible render object or section entry. */
29842
+ isBlockVisibleIndex(index) {
29548
29843
  return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
29549
29844
  }
29550
29845
  isOccludingAt(pos) {
29551
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29846
+ return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
29847
+ }
29848
+ /** Reports whether a visible cell hides neighboring cube faces. */
29849
+ isOccludingIndex(index) {
29552
29850
  return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
29553
29851
  }
29554
29852
  async setCullMaskAt(pos, mask) {
29555
- const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
29853
+ await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
29854
+ }
29855
+ /** Updates hidden faces for a cell's section entry or individual render object. */
29856
+ async setCullMaskIndex(index, mask) {
29556
29857
  const entry = this.sectionBlocks.get(index);
29557
29858
  if (entry) {
29558
29859
  if (entry.cullMask !== mask) {
@@ -29563,12 +29864,17 @@ var Chunk = class _Chunk {
29563
29864
  await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
29564
29865
  }
29565
29866
  }
29566
- rebuildSectionMesh() {
29867
+ /** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
29868
+ async rebuildSectionMesh() {
29567
29869
  if (!this.meshDirty) return;
29568
- const next = this.sectionBlocks.size ? SectionMesh.build(
29569
- [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index)),
29570
- this.sectionModels.maxAtlasSize
29571
- ) : void 0;
29870
+ this.meshDirty = false;
29871
+ const generation = ++this.meshGeneration;
29872
+ const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
29873
+ const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
29874
+ if (generation !== this.meshGeneration) {
29875
+ next?.dispose();
29876
+ return;
29877
+ }
29572
29878
  this.sectionMesh?.dispose();
29573
29879
  this.sectionMesh = next;
29574
29880
  if (next) {
@@ -29576,7 +29882,6 @@ var Chunk = class _Chunk {
29576
29882
  this.scene.add(next);
29577
29883
  }
29578
29884
  this.scene.dirty = true;
29579
- this.meshDirty = false;
29580
29885
  }
29581
29886
  /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
29582
29887
  async clear(onBlocksChanged = this.onBlocksChanged) {
@@ -29585,6 +29890,7 @@ var Chunk = class _Chunk {
29585
29890
  ));
29586
29891
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
29587
29892
  this.sectionBlocks.clear();
29893
+ this.meshGeneration++;
29588
29894
  this.sectionMesh?.dispose();
29589
29895
  this.sectionMesh = void 0;
29590
29896
  this.meshDirty = false;
@@ -29888,6 +30194,7 @@ export {
29888
30194
  SchematicParser,
29889
30195
  SectionMesh,
29890
30196
  SectionModels,
30197
+ SectionWorker,
29891
30198
  SkinImage,
29892
30199
  SkinObject,
29893
30200
  SkinPart,
@@ -29912,6 +30219,7 @@ export {
29912
30219
  axisToVec3,
29913
30220
  base64decode,
29914
30221
  base64encode,
30222
+ buildSectionGeometry,
29915
30223
  capeTextureSizes,
29916
30224
  captureException,
29917
30225
  changeEvent,
@@ -29963,6 +30271,7 @@ export {
29963
30271
  sampleEntityAnimation,
29964
30272
  sampleEntityKeyframes,
29965
30273
  sampleFluid,
30274
+ sectionGeometryTransferables,
29966
30275
  serializeBoxGeometryKey,
29967
30276
  serializeImageKey,
29968
30277
  serializeMaterialKey,
@@ -29980,6 +30289,7 @@ export {
29980
30289
  toDegrees,
29981
30290
  toRadians,
29982
30291
  toTrianglesDrawMode,
29983
- trimCanvas
30292
+ trimCanvas,
30293
+ yieldToEventLoop
29984
30294
  };
29985
30295
  //# sourceMappingURL=index.mjs.map