minerender 2.1.0-alpha.137 → 2.1.0-alpha.138

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.
package/dist/bundle.js CHANGED
@@ -103880,8 +103880,14 @@ gl_FragDepth=readDepth(vUv);
103880
103880
  }
103881
103881
  return queued;
103882
103882
  }
103883
- async refreshBiomes() {
103884
- if (this.biomeRefreshing) await this.biomeRefreshing;
103883
+ async refreshBiomes(deferCulling = false) {
103884
+ if (this.biomeRefreshing) {
103885
+ await this.biomeRefreshing;
103886
+ if (!deferCulling) {
103887
+ if (this.pendingCulling.size) await this.updateCulling([]);
103888
+ else await this.culling;
103889
+ }
103890
+ }
103885
103891
  if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
103886
103892
  return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
103887
103893
  const failures = /* @__PURE__ */ new Map();
@@ -103893,7 +103899,7 @@ gl_FragDepth=readDepth(vUv);
103893
103899
  this.pendingBiomes.delete(chunk);
103894
103900
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
103895
103901
  try {
103896
- await chunk.refreshBiomes(columns);
103902
+ await chunk.refreshBiomes(columns, deferCulling);
103897
103903
  } catch (error2) {
103898
103904
  failures.set(chunk, error2);
103899
103905
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
@@ -104155,15 +104161,35 @@ gl_FragDepth=readDepth(vUv);
104155
104161
  * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
104156
104162
  */
104157
104163
  async unloadChunkColumn(x4, z4) {
104158
- if (!Number.isInteger(x4) || !Number.isInteger(z4)) {
104159
- throw new RangeError("Chunk column coordinates must be integers");
104164
+ return this.unloadChunkColumns([{ x: x4, z: z4 }]);
104165
+ }
104166
+ /**
104167
+ * Removes chunk columns together and refreshes surviving neighbors once.
104168
+ * Coordinates are absolute chunk columns. Duplicate and missing columns are ignored.
104169
+ */
104170
+ async unloadChunkColumns(positions) {
104171
+ const columns = /* @__PURE__ */ new Map();
104172
+ for (const position of positions) {
104173
+ const { x: x4, z: z4 } = position ?? {};
104174
+ if (!Number.isInteger(x4) || !Number.isInteger(z4)) {
104175
+ throw new RangeError("Chunk column coordinates must be integers");
104176
+ }
104177
+ columns.set(`${x4}_${z4}`, { x: x4, z: z4 });
104160
104178
  }
104161
- this.entities?.clearColumn(x4, z4);
104179
+ if (!columns.size) return;
104162
104180
  await this.culling;
104181
+ let sliceStart = performance.now();
104163
104182
  try {
104164
- await this.clearChunkColumn(x4, z4);
104183
+ for (const { x: x4, z: z4 } of columns.values()) {
104184
+ this.entities?.clearColumn(x4, z4);
104185
+ await this.clearChunkColumn(x4, z4);
104186
+ if (performance.now() - sliceStart > 8) {
104187
+ await yieldToEventLoop();
104188
+ sliceStart = performance.now();
104189
+ }
104190
+ }
104165
104191
  } finally {
104166
- const failures = await this.refreshBiomes();
104192
+ const failures = await this.refreshBiomes(true);
104167
104193
  if (failures.size) log21.warn("Failed to refresh biome colors", failures.values().next().value);
104168
104194
  await this.updateCulling([]);
104169
104195
  }
@@ -104226,7 +104252,7 @@ gl_FragDepth=readDepth(vUv);
104226
104252
  }
104227
104253
  this.sectionModels?.clear();
104228
104254
  }
104229
- updateCulling(positions) {
104255
+ updateCulling(positions, defer = false) {
104230
104256
  const neighbor = new Vector3();
104231
104257
  for (const pos of positions) {
104232
104258
  const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
@@ -104248,58 +104274,72 @@ gl_FragDepth=readDepth(vUv);
104248
104274
  }
104249
104275
  }
104250
104276
  }
104277
+ if (defer) return Promise.resolve();
104251
104278
  return this.culling ?? (this.culling = Promise.resolve().then(async () => {
104252
104279
  let sliceStart = performance.now();
104253
104280
  try {
104254
104281
  const culling = await loadBlockCulling();
104255
104282
  while (this.pendingCulling.size) {
104256
- const batch = [...this.pendingCulling];
104283
+ const batch = new Map(this.pendingCulling);
104257
104284
  this.pendingCulling.clear();
104258
- for (const [chunk, indices] of batch) {
104259
- const neighbors = CUBE_FACE_OFFSETS.map(([x4, y5, z4]) => this._chunks.get(`${chunk.x + x4}_${chunk.y + y5}_${chunk.z + z4}`));
104260
- const neighborAt = (x4, y5, z4) => {
104261
- const lx = x4 - chunk.x * 16, ly = y5 - chunk.y * 16, lz = z4 - chunk.z * 16;
104262
- const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
104263
- return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
104264
- };
104265
- const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x4, y5, z4) => {
104266
- const [section, i4] = neighborAt(x4, y5, z4);
104267
- return section?.isOccludingIndex(i4) ?? false;
104268
- }, culling, (x4, y5, z4) => {
104269
- const [section, i4] = neighborAt(x4, y5, z4);
104270
- return section?.visibleBlockIndex(i4);
104271
- });
104272
- const fluidIndices = indices === "all" ? function* () {
104273
- for (let index = 0; index < 4096; index++) yield index;
104274
- }() : indices;
104275
- for (const index of fluidIndices) {
104276
- if (!chunk.fluidByteIndex(index) || !chunk.isBlockVisibleIndex(index)) continue;
104277
- const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
104278
- const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
104279
- const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
104280
- if (block.object?.fluidKind) {
104281
- await block.object.updateFluid((x4, y5, z4) => {
104282
- const nx = lx + x4, ny = ly + y5, nz = lz + z4;
104283
- 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)}`);
104284
- const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
104285
- const byte = section?.fluidByteIndex(neighborIndex) ?? 0;
104286
- return {
104287
- fluid: fluidKindOf(byte),
104288
- level: byte & 15,
104289
- solid: section?.isOccludingIndex(neighborIndex) ?? false
104290
- };
104291
- });
104285
+ try {
104286
+ for (const [chunk, indices] of batch) {
104287
+ const neighbors = CUBE_FACE_OFFSETS.map(([x4, y5, z4]) => this._chunks.get(`${chunk.x + x4}_${chunk.y + y5}_${chunk.z + z4}`));
104288
+ const neighborAt = (x4, y5, z4) => {
104289
+ const lx = x4 - chunk.x * 16, ly = y5 - chunk.y * 16, lz = z4 - chunk.z * 16;
104290
+ const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
104291
+ return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
104292
+ };
104293
+ const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x4, y5, z4) => {
104294
+ const [section, i4] = neighborAt(x4, y5, z4);
104295
+ return section?.isOccludingIndex(i4) ?? false;
104296
+ }, culling, (x4, y5, z4) => {
104297
+ const [section, i4] = neighborAt(x4, y5, z4);
104298
+ return section?.visibleBlockIndex(i4);
104299
+ });
104300
+ const fluidIndices = indices === "all" ? function* () {
104301
+ for (let index = 0; index < 4096; index++) yield index;
104302
+ }() : indices;
104303
+ for (const index of fluidIndices) {
104304
+ if (!chunk.fluidByteIndex(index) || !chunk.isBlockVisibleIndex(index)) continue;
104305
+ const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
104306
+ const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
104307
+ const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
104308
+ if (block.object?.fluidKind) {
104309
+ await block.object.updateFluid((x4, y5, z4) => {
104310
+ const nx = lx + x4, ny = ly + y5, nz = lz + z4;
104311
+ 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)}`);
104312
+ const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
104313
+ const byte = section?.fluidByteIndex(neighborIndex) ?? 0;
104314
+ return {
104315
+ fluid: fluidKindOf(byte),
104316
+ level: byte & 15,
104317
+ solid: section?.isOccludingIndex(neighborIndex) ?? false
104318
+ };
104319
+ });
104320
+ }
104321
+ }
104322
+ for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
104323
+ await chunk.refreshLight(indices === "all" ? void 0 : indices);
104324
+ chunk.refreshAmbientOcclusion(this._ambientOcclusion);
104325
+ if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
104326
+ await yieldToEventLoop();
104327
+ sliceStart = performance.now();
104292
104328
  }
104293
104329
  }
104294
- for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
104295
- await chunk.refreshLight(indices === "all" ? void 0 : indices);
104296
- chunk.refreshAmbientOcclusion(this._ambientOcclusion);
104297
- if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
104298
- await yieldToEventLoop();
104299
- sliceStart = performance.now();
104330
+ const builds = await Promise.allSettled([...batch.keys()].map(async (chunk) => {
104331
+ await chunk.rebuildSectionMesh();
104332
+ batch.delete(chunk);
104333
+ }));
104334
+ const failure = builds.find((result) => result.status === "rejected");
104335
+ if (failure?.status === "rejected") throw failure.reason;
104336
+ } catch (error2) {
104337
+ for (const [chunk, indices] of batch) {
104338
+ const pending = this.pendingCulling.get(chunk);
104339
+ this.pendingCulling.set(chunk, indices === "all" || pending === "all" ? "all" : /* @__PURE__ */ new Set([...indices, ...pending ?? []]));
104300
104340
  }
104341
+ throw error2;
104301
104342
  }
104302
- await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
104303
104343
  }
104304
104344
  } finally {
104305
104345
  this.culling = void 0;
@@ -104316,7 +104356,7 @@ gl_FragDepth=readDepth(vUv);
104316
104356
  x4,
104317
104357
  y5,
104318
104358
  z4,
104319
- (positions) => this.updateCulling(positions),
104359
+ (positions, defer) => this.updateCulling(positions, defer),
104320
104360
  this.sectionModels,
104321
104361
  (x5, y6, z5) => {
104322
104362
  const cx = Math.floor(x5 / 16), cy = Math.floor(y6 / 16), cz = Math.floor(z5 / 16);
@@ -105065,11 +105105,14 @@ gl_FragDepth=readDepth(vUv);
105065
105105
  this.biomes = copy;
105066
105106
  return this.refreshBiomes();
105067
105107
  }
105068
- /** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
105069
- refreshBiomes(columns) {
105070
- return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
105108
+ /**
105109
+ * Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects.
105110
+ * The owning world can defer neighbor culling until all affected chunks are refreshed.
105111
+ */
105112
+ refreshBiomes(columns, deferCulling = false) {
105113
+ return this.changeBlocks(() => this.refreshBiomeBlocks(columns, deferCulling), true);
105071
105114
  }
105072
- async refreshBiomeBlocks(columns) {
105115
+ async refreshBiomeBlocks(columns, deferCulling = false) {
105073
105116
  const blend = columns && this.biomeBlend?.();
105074
105117
  const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
105075
105118
  const affected = /* @__PURE__ */ new Map();
@@ -105126,7 +105169,7 @@ gl_FragDepth=readDepth(vUv);
105126
105169
  this.renderedBlocks.get(index)?.object?.setVisible(false);
105127
105170
  }
105128
105171
  const positions = blocks.map(({ index }) => new Vector3(...this.positionAt(index)));
105129
- if (this.onBlocksChanged) await this.onBlocksChanged(positions);
105172
+ if (this.onBlocksChanged) await this.onBlocksChanged(positions, deferCulling);
105130
105173
  else await this.rebuildSectionMesh();
105131
105174
  }
105132
105175
  }
@@ -105798,19 +105841,12 @@ gl_FragDepth=readDepth(vUv);
105798
105841
  this.loading?.controller.abort();
105799
105842
  return this.disposal ?? (this.disposal = Promise.resolve().then(async () => {
105800
105843
  await this.running?.catch(() => void 0);
105801
- let failure;
105802
- let failed = false;
105803
- for (const position of this.owned.values()) {
105804
- try {
105805
- await this.unload(position);
105806
- } catch (error2) {
105807
- failed = true;
105808
- failure ?? (failure = error2);
105809
- }
105844
+ try {
105845
+ await this.unloadMany([...this.owned.values()]);
105846
+ } finally {
105847
+ this.missing.clear();
105848
+ this.failed.clear();
105810
105849
  }
105811
- this.missing.clear();
105812
- this.failed.clear();
105813
- if (failed) throw failure;
105814
105850
  }).catch((error2) => {
105815
105851
  this.disposal = void 0;
105816
105852
  throw error2;
@@ -105825,9 +105861,9 @@ gl_FragDepth=readDepth(vUv);
105825
105861
  for (const [key, position2] of this.failed) {
105826
105862
  if (!this.within(position2, this.unloadRadius)) this.failed.delete(key);
105827
105863
  }
105828
- const obsolete = [...this.owned.values()].find((position2) => !this.loaded.has(this.key(position2)) || !this.within(position2, this.unloadRadius));
105829
- if (obsolete) {
105830
- await this.unload(obsolete);
105864
+ const obsolete = [...this.owned.values()].filter((position2) => !this.loaded.has(this.key(position2)) || !this.within(position2, this.unloadRadius));
105865
+ if (obsolete.length) {
105866
+ await this.unloadMany(obsolete);
105831
105867
  continue;
105832
105868
  }
105833
105869
  const position = this.desired.find((position2) => this.needsLoad(position2));
@@ -105857,7 +105893,7 @@ gl_FragDepth=readDepth(vUv);
105857
105893
  this.loaded.set(this.key(position), position);
105858
105894
  } catch (error2) {
105859
105895
  try {
105860
- await this.unload(position);
105896
+ await this.unloadMany([position]);
105861
105897
  } catch (cleanupError) {
105862
105898
  const failure = error2 instanceof Error ? error2 : new Error(String(error2));
105863
105899
  try {
@@ -105878,10 +105914,18 @@ gl_FragDepth=readDepth(vUv);
105878
105914
  const key = this.key(position);
105879
105915
  return !this.loaded.has(key) && !this.missing.has(key) && !this.failed.has(key);
105880
105916
  }
105881
- async unload(position) {
105882
- await this.world.unloadChunkColumn(position.x, position.z);
105883
- this.owned.delete(this.key(position));
105884
- this.loaded.delete(this.key(position));
105917
+ async unloadMany(positions) {
105918
+ if (!positions.length) return;
105919
+ try {
105920
+ await this.world.unloadChunkColumns(positions.map(({ x: x4, z: z4 }) => ({ x: x4, z: z4 })));
105921
+ } catch (error2) {
105922
+ for (const position of positions) this.loaded.delete(this.key(position));
105923
+ throw error2;
105924
+ }
105925
+ for (const position of positions) {
105926
+ this.owned.delete(this.key(position));
105927
+ this.loaded.delete(this.key(position));
105928
+ }
105885
105929
  }
105886
105930
  validateChunk(chunk, position) {
105887
105931
  if (chunk.x !== position.x || chunk.z !== position.z) {