minerender 2.1.0-alpha.143 → 2.1.0-alpha.144

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
  }
@@ -20087,8 +20108,10 @@ var Chunk = class _Chunk {
20087
20108
  if (rejected?.status === "rejected") throw rejected.reason;
20088
20109
  }
20089
20110
  const positions = [];
20090
- let failed = false, failure;
20091
- for (const { index, key, block } of placements) {
20111
+ const jobs = /* @__PURE__ */ new Map();
20112
+ let failureOrder = Infinity, failure;
20113
+ for (const [order, { index, key, block }] of placements.entries()) {
20114
+ jobs.delete(index);
20092
20115
  this.hiddenBlocks.delete(index);
20093
20116
  this.data.assign(index, this.data.intern(block), block?.nbt);
20094
20117
  const readBlock = this.data.snapshot(index);
@@ -20104,7 +20127,6 @@ var Chunk = class _Chunk {
20104
20127
  this.z * 16 + Math.floor(index / 16) % 16
20105
20128
  );
20106
20129
  positions.push(worldPos);
20107
- let object4;
20108
20130
  try {
20109
20131
  if (!readBlock) continue;
20110
20132
  const resolved = resolutions.get(key);
@@ -20126,7 +20148,7 @@ var Chunk = class _Chunk {
20126
20148
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
20127
20149
  this.meshDirty = true;
20128
20150
  } else {
20129
- object4 = await this.scene.addBlock(blockState3, {
20151
+ jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
20130
20152
  mergeMeshes: true,
20131
20153
  instanceMeshes: true,
20132
20154
  maxInstanceCount: 2e3,
@@ -20134,13 +20156,12 @@ var Chunk = class _Chunk {
20134
20156
  variantPosition,
20135
20157
  biome: context?.biome,
20136
20158
  biomePosition: this.biomePosition(index, blockState3, stored)
20137
- });
20138
- object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
20159
+ } });
20160
+ continue;
20139
20161
  }
20140
20162
  this.renderedBlocks.set(index, { get block() {
20141
20163
  return readBlock();
20142
- }, object: object4 });
20143
- if (object4) this.objectBlocks.add(index);
20164
+ }, object: void 0 });
20144
20165
  } catch (error) {
20145
20166
  if (preserveDataOnFailure && readBlock) {
20146
20167
  this.renderedBlocks.set(index, { get block() {
@@ -20148,12 +20169,35 @@ var Chunk = class _Chunk {
20148
20169
  }, object: void 0 });
20149
20170
  } else this.data.assign(index, 0);
20150
20171
  this.fluidCells[index] = 0;
20151
- object4?.removeFromScene();
20152
- if (!failed) failure = error;
20153
- failed = true;
20172
+ if (order < failureOrder) {
20173
+ failureOrder = order;
20174
+ failure = error;
20175
+ }
20154
20176
  }
20155
20177
  }
20156
- if (failed) throw failure;
20178
+ const pending = [...jobs];
20179
+ const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
20180
+ for (const [i, [index, { order, readBlock }]] of pending.entries()) {
20181
+ const result = objects[i];
20182
+ if (result.status === "fulfilled") {
20183
+ this.renderedBlocks.set(index, { get block() {
20184
+ return readBlock();
20185
+ }, object: result.value });
20186
+ this.objectBlocks.add(index);
20187
+ } else {
20188
+ if (preserveDataOnFailure) {
20189
+ this.renderedBlocks.set(index, { get block() {
20190
+ return readBlock();
20191
+ }, object: void 0 });
20192
+ } else this.data.assign(index, 0);
20193
+ this.fluidCells[index] = 0;
20194
+ if (order < failureOrder) {
20195
+ failureOrder = order;
20196
+ failure = result.reason;
20197
+ }
20198
+ }
20199
+ }
20200
+ if (failureOrder < Infinity) throw failure;
20157
20201
  return positions;
20158
20202
  }
20159
20203
  /** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
@@ -20224,37 +20268,42 @@ var Chunk = class _Chunk {
20224
20268
  failed = true;
20225
20269
  }
20226
20270
  }
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;
20271
+ const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new Vector321(
20272
+ this.x * 256 + index % 16 * 16,
20273
+ this.y * 256 + Math.floor(index / 256) * 16,
20274
+ this.z * 256 + Math.floor(index / 16) % 16 * 16
20275
+ ), {
20276
+ mergeMeshes: true,
20277
+ instanceMeshes: true,
20278
+ maxInstanceCount: 2e3,
20279
+ initialState: this.data.stateAt(this.data.idAt(index)).properties,
20280
+ variantPosition: this.positionAt(index),
20281
+ biome,
20282
+ biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20283
+ })));
20284
+ for (const [i, { index }] of perBlocks.entries()) {
20285
+ const result = objects[i];
20286
+ if (result.status === "fulfilled") {
20287
+ this.renderedBlocks.get(index).object = result.value;
20245
20288
  this.objectBlocks.add(index);
20246
- } catch (error) {
20289
+ } else {
20247
20290
  this.data.assign(index, 0);
20248
20291
  this.fluidCells[index] = 0;
20249
20292
  this.renderedBlocks.delete(index);
20250
20293
  this.objectBlocks.delete(index);
20251
- object4?.removeFromScene();
20252
- if (!failed) failure = error;
20294
+ if (!failed) failure = result.reason;
20253
20295
  failed = true;
20254
20296
  }
20255
20297
  }
20256
20298
  if (failed) throw failure;
20257
20299
  }
20300
+ createBlockObject(blockState3, position, options) {
20301
+ return this.scene.addSceneObject(blockState3, () => {
20302
+ const object4 = new BlockObject(blockState3, options);
20303
+ object4.position.copy(position);
20304
+ return object4;
20305
+ }, options);
20306
+ }
20258
20307
  positionAt(index) {
20259
20308
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
20260
20309
  }