minerender 2.1.0-alpha.143 → 2.1.0-alpha.145

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.
@@ -17922,6 +17922,7 @@ var AnvilWorldSource = class {
17922
17922
  };
17923
17923
 
17924
17924
  // src/world/Chunk.ts
17925
+ init_BlockObject();
17925
17926
  import { Color as Color13, Vector3 as Vector321 } from "three";
17926
17927
  init_BlockStates();
17927
17928
  init_AssetKey();
@@ -17932,6 +17933,7 @@ init_util();
17932
17933
  import { Vector3 as Vector319 } from "three";
17933
17934
  init_AssetKey();
17934
17935
  init_BlockStates();
17936
+ init_BlockEntities();
17935
17937
  init_CubeFace();
17936
17938
 
17937
17939
  // src/world/SectionModels.ts
@@ -18029,6 +18031,10 @@ var SectionModels = class {
18029
18031
  biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
18030
18032
  };
18031
18033
  }
18034
+ /** Starts loading the state's models and atlases so a later `prepareState` with the same properties finds them loaded. */
18035
+ async preload(blockState3, properties = {}) {
18036
+ await this.state(blockState3, properties);
18037
+ }
18032
18038
  async state(blockState3, properties) {
18033
18039
  let states = this.states.get(blockState3);
18034
18040
  if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
@@ -18051,22 +18057,24 @@ var SectionModels = class {
18051
18057
  async prepare(blockState3, properties) {
18052
18058
  const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
18053
18059
  const groups = BlockStateResolver.matching(blockState3, state);
18054
- if (!groups.length) return void 0;
18060
+ if (!groups.length || groups.some((group) => Array.isArray(group) && !group.length)) return void 0;
18061
+ const variants = groups.flatMap((group) => Array.isArray(group) ? group : [group]);
18062
+ if (variants.some((variant) => !variant.model)) return void 0;
18063
+ const settled = await Promise.allSettled(variants.map(async (variant) => {
18064
+ const modelKey = AssetKey.parse("models", variant.model);
18065
+ modelKey.root = blockState3.key?.root;
18066
+ const model = await Models.getMerged(modelKey);
18067
+ if (!model?.elements?.length) return void 0;
18068
+ const atlas = await UVMapper.getAtlas(model);
18069
+ const rotation = BlockStateResolver.rotation(variant);
18070
+ if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18071
+ return { model, atlas, rotation };
18072
+ }));
18055
18073
  const prepared = /* @__PURE__ */ new Map();
18056
- for (const group of groups) {
18057
- const choices = Array.isArray(group) ? group : [group];
18058
- if (!choices.length) return void 0;
18059
- for (const variant of choices) {
18060
- if (!variant.model) return void 0;
18061
- const modelKey = AssetKey.parse("models", variant.model);
18062
- modelKey.root = blockState3.key?.root;
18063
- const model = await Models.getMerged(modelKey);
18064
- if (!model?.elements?.length) return void 0;
18065
- const atlas = await UVMapper.getAtlas(model);
18066
- const rotation = BlockStateResolver.rotation(variant);
18067
- if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18068
- prepared.set(variant, { model, atlas, rotation });
18069
- }
18074
+ for (const [i, result] of settled.entries()) {
18075
+ if (result.status === "rejected") throw result.reason;
18076
+ if (!result.value) return void 0;
18077
+ prepared.set(variants[i], result.value);
18070
18078
  }
18071
18079
  return { state, groups, choices: prepared };
18072
18080
  }
@@ -18741,15 +18749,18 @@ var MineRenderWorld = class {
18741
18749
  const key = AssetKey.parse("blockstates", block.type);
18742
18750
  keys.set(key.serialize(), key);
18743
18751
  }
18744
- await BlockStates.getAll(keys.values());
18752
+ const loaded = await BlockStates.getAll(keys.values());
18753
+ const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
18745
18754
  if (!useBatches) {
18746
18755
  for (const block of multiblock.blocks) await place(block);
18747
18756
  return;
18748
18757
  }
18749
18758
  if (!executor) {
18750
18759
  const groups2 = /* @__PURE__ */ new Map();
18760
+ const distinct = /* @__PURE__ */ new Map();
18751
18761
  const position = new Vector319();
18752
18762
  for (const block of multiblock.blocks) {
18763
+ if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
18753
18764
  const [x, y, z] = block.position;
18754
18765
  this.validatePosBounds(position.set(x, y, z));
18755
18766
  const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
@@ -18760,6 +18771,16 @@ var MineRenderWorld = class {
18760
18771
  if (!group) groups2.set(chunk, group = []);
18761
18772
  group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
18762
18773
  }
18774
+ if (this.sectionModels) {
18775
+ for (const block of distinct.values()) {
18776
+ const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
18777
+ if (!state) continue;
18778
+ void (async () => {
18779
+ if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
18780
+ await this.sectionModels.preload(state, block.properties);
18781
+ })().catch(() => void 0);
18782
+ }
18783
+ }
18763
18784
  await this.placeChunkGroups(groups2, changes);
18764
18785
  return;
18765
18786
  }
@@ -19527,6 +19548,8 @@ function sectionGeometryTransferables(pages) {
19527
19548
 
19528
19549
  // src/world/SectionWorker.ts
19529
19550
  init_Env();
19551
+ init_log();
19552
+ var log19 = logger("SectionWorker");
19530
19553
  var SectionWorker = class _SectionWorker {
19531
19554
  constructor(worker) {
19532
19555
  this.worker = worker;
@@ -19540,8 +19563,12 @@ var SectionWorker = class _SectionWorker {
19540
19563
  if (reply.type === "pages") pending.resolve(reply.pages);
19541
19564
  else pending.reject(new Error(reply.message));
19542
19565
  });
19543
- worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
19544
- worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
19566
+ const fallBack = (message) => {
19567
+ log19.warn(`${message}, so sections build on the main thread`);
19568
+ this.fail(message);
19569
+ };
19570
+ worker.addEventListener("error", (event) => fallBack(event.message || "Section worker failed"));
19571
+ worker.addEventListener("messageerror", () => fallBack("Section worker message could not be deserialized"));
19545
19572
  }
19546
19573
  static {
19547
19574
  this.initialized = false;
@@ -20087,8 +20114,10 @@ var Chunk = class _Chunk {
20087
20114
  if (rejected?.status === "rejected") throw rejected.reason;
20088
20115
  }
20089
20116
  const positions = [];
20090
- let failed = false, failure;
20091
- for (const { index, key, block } of placements) {
20117
+ const jobs = /* @__PURE__ */ new Map();
20118
+ let failureOrder = Infinity, failure;
20119
+ for (const [order, { index, key, block }] of placements.entries()) {
20120
+ jobs.delete(index);
20092
20121
  this.hiddenBlocks.delete(index);
20093
20122
  this.data.assign(index, this.data.intern(block), block?.nbt);
20094
20123
  const readBlock = this.data.snapshot(index);
@@ -20104,7 +20133,6 @@ var Chunk = class _Chunk {
20104
20133
  this.z * 16 + Math.floor(index / 16) % 16
20105
20134
  );
20106
20135
  positions.push(worldPos);
20107
- let object4;
20108
20136
  try {
20109
20137
  if (!readBlock) continue;
20110
20138
  const resolved = resolutions.get(key);
@@ -20126,7 +20154,7 @@ var Chunk = class _Chunk {
20126
20154
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
20127
20155
  this.meshDirty = true;
20128
20156
  } else {
20129
- object4 = await this.scene.addBlock(blockState3, {
20157
+ jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
20130
20158
  mergeMeshes: true,
20131
20159
  instanceMeshes: true,
20132
20160
  maxInstanceCount: 2e3,
@@ -20134,13 +20162,12 @@ var Chunk = class _Chunk {
20134
20162
  variantPosition,
20135
20163
  biome: context?.biome,
20136
20164
  biomePosition: this.biomePosition(index, blockState3, stored)
20137
- });
20138
- object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
20165
+ } });
20166
+ continue;
20139
20167
  }
20140
20168
  this.renderedBlocks.set(index, { get block() {
20141
20169
  return readBlock();
20142
- }, object: object4 });
20143
- if (object4) this.objectBlocks.add(index);
20170
+ }, object: void 0 });
20144
20171
  } catch (error) {
20145
20172
  if (preserveDataOnFailure && readBlock) {
20146
20173
  this.renderedBlocks.set(index, { get block() {
@@ -20148,12 +20175,35 @@ var Chunk = class _Chunk {
20148
20175
  }, object: void 0 });
20149
20176
  } else this.data.assign(index, 0);
20150
20177
  this.fluidCells[index] = 0;
20151
- object4?.removeFromScene();
20152
- if (!failed) failure = error;
20153
- failed = true;
20178
+ if (order < failureOrder) {
20179
+ failureOrder = order;
20180
+ failure = error;
20181
+ }
20154
20182
  }
20155
20183
  }
20156
- if (failed) throw failure;
20184
+ const pending = [...jobs];
20185
+ const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
20186
+ for (const [i, [index, { order, readBlock }]] of pending.entries()) {
20187
+ const result = objects[i];
20188
+ if (result.status === "fulfilled") {
20189
+ this.renderedBlocks.set(index, { get block() {
20190
+ return readBlock();
20191
+ }, object: result.value });
20192
+ this.objectBlocks.add(index);
20193
+ } else {
20194
+ if (preserveDataOnFailure) {
20195
+ this.renderedBlocks.set(index, { get block() {
20196
+ return readBlock();
20197
+ }, object: void 0 });
20198
+ } else this.data.assign(index, 0);
20199
+ this.fluidCells[index] = 0;
20200
+ if (order < failureOrder) {
20201
+ failureOrder = order;
20202
+ failure = result.reason;
20203
+ }
20204
+ }
20205
+ }
20206
+ if (failureOrder < Infinity) throw failure;
20157
20207
  return positions;
20158
20208
  }
20159
20209
  /** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
@@ -20224,37 +20274,42 @@ var Chunk = class _Chunk {
20224
20274
  failed = true;
20225
20275
  }
20226
20276
  }
20227
- for (const { index, blockState: blockState3, biome } of perBlocks) {
20228
- let object4;
20229
- try {
20230
- object4 = await this.scene.addBlock(blockState3, {
20231
- mergeMeshes: true,
20232
- instanceMeshes: true,
20233
- maxInstanceCount: 2e3,
20234
- initialState: this.data.stateAt(this.data.idAt(index)).properties,
20235
- variantPosition: this.positionAt(index),
20236
- biome,
20237
- biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20238
- });
20239
- object4.setPosition(new Vector321(
20240
- this.x * 256 + index % 16 * 16,
20241
- this.y * 256 + Math.floor(index / 256) * 16,
20242
- this.z * 256 + Math.floor(index / 16) % 16 * 16
20243
- ));
20244
- this.renderedBlocks.get(index).object = object4;
20277
+ const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new Vector321(
20278
+ this.x * 256 + index % 16 * 16,
20279
+ this.y * 256 + Math.floor(index / 256) * 16,
20280
+ this.z * 256 + Math.floor(index / 16) % 16 * 16
20281
+ ), {
20282
+ mergeMeshes: true,
20283
+ instanceMeshes: true,
20284
+ maxInstanceCount: 2e3,
20285
+ initialState: this.data.stateAt(this.data.idAt(index)).properties,
20286
+ variantPosition: this.positionAt(index),
20287
+ biome,
20288
+ biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20289
+ })));
20290
+ for (const [i, { index }] of perBlocks.entries()) {
20291
+ const result = objects[i];
20292
+ if (result.status === "fulfilled") {
20293
+ this.renderedBlocks.get(index).object = result.value;
20245
20294
  this.objectBlocks.add(index);
20246
- } catch (error) {
20295
+ } else {
20247
20296
  this.data.assign(index, 0);
20248
20297
  this.fluidCells[index] = 0;
20249
20298
  this.renderedBlocks.delete(index);
20250
20299
  this.objectBlocks.delete(index);
20251
- object4?.removeFromScene();
20252
- if (!failed) failure = error;
20300
+ if (!failed) failure = result.reason;
20253
20301
  failed = true;
20254
20302
  }
20255
20303
  }
20256
20304
  if (failed) throw failure;
20257
20305
  }
20306
+ createBlockObject(blockState3, position, options) {
20307
+ return this.scene.addSceneObject(blockState3, () => {
20308
+ const object4 = new BlockObject(blockState3, options);
20309
+ object4.position.copy(position);
20310
+ return object4;
20311
+ }, options);
20312
+ }
20258
20313
  positionAt(index) {
20259
20314
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
20260
20315
  }