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.
@@ -18063,6 +18063,7 @@ var AnvilWorldSource = class {
18063
18063
  };
18064
18064
 
18065
18065
  // src/world/Chunk.ts
18066
+ init_BlockObject();
18066
18067
  var import_three47 = require("three");
18067
18068
  init_BlockStates();
18068
18069
  init_AssetKey();
@@ -18073,6 +18074,7 @@ init_Model();
18073
18074
  init_util();
18074
18075
  init_AssetKey();
18075
18076
  init_BlockStates();
18077
+ init_BlockEntities();
18076
18078
  init_CubeFace();
18077
18079
 
18078
18080
  // src/world/SectionModels.ts
@@ -18170,6 +18172,10 @@ var SectionModels = class {
18170
18172
  biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
18171
18173
  };
18172
18174
  }
18175
+ /** Starts loading the state's models and atlases so a later `prepareState` with the same properties finds them loaded. */
18176
+ async preload(blockState3, properties = {}) {
18177
+ await this.state(blockState3, properties);
18178
+ }
18173
18179
  async state(blockState3, properties) {
18174
18180
  let states = this.states.get(blockState3);
18175
18181
  if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
@@ -18192,22 +18198,24 @@ var SectionModels = class {
18192
18198
  async prepare(blockState3, properties) {
18193
18199
  const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
18194
18200
  const groups = BlockStateResolver.matching(blockState3, state);
18195
- if (!groups.length) return void 0;
18201
+ if (!groups.length || groups.some((group) => Array.isArray(group) && !group.length)) return void 0;
18202
+ const variants = groups.flatMap((group) => Array.isArray(group) ? group : [group]);
18203
+ if (variants.some((variant) => !variant.model)) return void 0;
18204
+ const settled = await Promise.allSettled(variants.map(async (variant) => {
18205
+ const modelKey = AssetKey.parse("models", variant.model);
18206
+ modelKey.root = blockState3.key?.root;
18207
+ const model = await Models.getMerged(modelKey);
18208
+ if (!model?.elements?.length) return void 0;
18209
+ const atlas = await UVMapper.getAtlas(model);
18210
+ const rotation = BlockStateResolver.rotation(variant);
18211
+ if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18212
+ return { model, atlas, rotation };
18213
+ }));
18196
18214
  const prepared = /* @__PURE__ */ new Map();
18197
- for (const group of groups) {
18198
- const choices = Array.isArray(group) ? group : [group];
18199
- if (!choices.length) return void 0;
18200
- for (const variant of choices) {
18201
- if (!variant.model) return void 0;
18202
- const modelKey = AssetKey.parse("models", variant.model);
18203
- modelKey.root = blockState3.key?.root;
18204
- const model = await Models.getMerged(modelKey);
18205
- if (!model?.elements?.length) return void 0;
18206
- const atlas = await UVMapper.getAtlas(model);
18207
- const rotation = BlockStateResolver.rotation(variant);
18208
- if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18209
- prepared.set(variant, { model, atlas, rotation });
18210
- }
18215
+ for (const [i, result] of settled.entries()) {
18216
+ if (result.status === "rejected") throw result.reason;
18217
+ if (!result.value) return void 0;
18218
+ prepared.set(variants[i], result.value);
18211
18219
  }
18212
18220
  return { state, groups, choices: prepared };
18213
18221
  }
@@ -18882,15 +18890,18 @@ var MineRenderWorld = class {
18882
18890
  const key = AssetKey.parse("blockstates", block.type);
18883
18891
  keys.set(key.serialize(), key);
18884
18892
  }
18885
- await BlockStates.getAll(keys.values());
18893
+ const loaded = await BlockStates.getAll(keys.values());
18894
+ const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
18886
18895
  if (!useBatches) {
18887
18896
  for (const block of multiblock.blocks) await place(block);
18888
18897
  return;
18889
18898
  }
18890
18899
  if (!executor) {
18891
18900
  const groups2 = /* @__PURE__ */ new Map();
18901
+ const distinct = /* @__PURE__ */ new Map();
18892
18902
  const position = new import_three45.Vector3();
18893
18903
  for (const block of multiblock.blocks) {
18904
+ if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
18894
18905
  const [x, y, z] = block.position;
18895
18906
  this.validatePosBounds(position.set(x, y, z));
18896
18907
  const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
@@ -18901,6 +18912,16 @@ var MineRenderWorld = class {
18901
18912
  if (!group) groups2.set(chunk, group = []);
18902
18913
  group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
18903
18914
  }
18915
+ if (this.sectionModels) {
18916
+ for (const block of distinct.values()) {
18917
+ const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
18918
+ if (!state) continue;
18919
+ void (async () => {
18920
+ if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
18921
+ await this.sectionModels.preload(state, block.properties);
18922
+ })().catch(() => void 0);
18923
+ }
18924
+ }
18904
18925
  await this.placeChunkGroups(groups2, changes);
18905
18926
  return;
18906
18927
  }
@@ -20227,8 +20248,10 @@ var Chunk = class _Chunk {
20227
20248
  if (rejected?.status === "rejected") throw rejected.reason;
20228
20249
  }
20229
20250
  const positions = [];
20230
- let failed = false, failure;
20231
- for (const { index, key, block } of placements) {
20251
+ const jobs = /* @__PURE__ */ new Map();
20252
+ let failureOrder = Infinity, failure;
20253
+ for (const [order, { index, key, block }] of placements.entries()) {
20254
+ jobs.delete(index);
20232
20255
  this.hiddenBlocks.delete(index);
20233
20256
  this.data.assign(index, this.data.intern(block), block?.nbt);
20234
20257
  const readBlock = this.data.snapshot(index);
@@ -20244,7 +20267,6 @@ var Chunk = class _Chunk {
20244
20267
  this.z * 16 + Math.floor(index / 16) % 16
20245
20268
  );
20246
20269
  positions.push(worldPos);
20247
- let object4;
20248
20270
  try {
20249
20271
  if (!readBlock) continue;
20250
20272
  const resolved = resolutions.get(key);
@@ -20266,7 +20288,7 @@ var Chunk = class _Chunk {
20266
20288
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
20267
20289
  this.meshDirty = true;
20268
20290
  } else {
20269
- object4 = await this.scene.addBlock(blockState3, {
20291
+ jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
20270
20292
  mergeMeshes: true,
20271
20293
  instanceMeshes: true,
20272
20294
  maxInstanceCount: 2e3,
@@ -20274,13 +20296,12 @@ var Chunk = class _Chunk {
20274
20296
  variantPosition,
20275
20297
  biome: context?.biome,
20276
20298
  biomePosition: this.biomePosition(index, blockState3, stored)
20277
- });
20278
- object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
20299
+ } });
20300
+ continue;
20279
20301
  }
20280
20302
  this.renderedBlocks.set(index, { get block() {
20281
20303
  return readBlock();
20282
- }, object: object4 });
20283
- if (object4) this.objectBlocks.add(index);
20304
+ }, object: void 0 });
20284
20305
  } catch (error) {
20285
20306
  if (preserveDataOnFailure && readBlock) {
20286
20307
  this.renderedBlocks.set(index, { get block() {
@@ -20288,12 +20309,35 @@ var Chunk = class _Chunk {
20288
20309
  }, object: void 0 });
20289
20310
  } else this.data.assign(index, 0);
20290
20311
  this.fluidCells[index] = 0;
20291
- object4?.removeFromScene();
20292
- if (!failed) failure = error;
20293
- failed = true;
20312
+ if (order < failureOrder) {
20313
+ failureOrder = order;
20314
+ failure = error;
20315
+ }
20294
20316
  }
20295
20317
  }
20296
- if (failed) throw failure;
20318
+ const pending = [...jobs];
20319
+ const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
20320
+ for (const [i, [index, { order, readBlock }]] of pending.entries()) {
20321
+ const result = objects[i];
20322
+ if (result.status === "fulfilled") {
20323
+ this.renderedBlocks.set(index, { get block() {
20324
+ return readBlock();
20325
+ }, object: result.value });
20326
+ this.objectBlocks.add(index);
20327
+ } else {
20328
+ if (preserveDataOnFailure) {
20329
+ this.renderedBlocks.set(index, { get block() {
20330
+ return readBlock();
20331
+ }, object: void 0 });
20332
+ } else this.data.assign(index, 0);
20333
+ this.fluidCells[index] = 0;
20334
+ if (order < failureOrder) {
20335
+ failureOrder = order;
20336
+ failure = result.reason;
20337
+ }
20338
+ }
20339
+ }
20340
+ if (failureOrder < Infinity) throw failure;
20297
20341
  return positions;
20298
20342
  }
20299
20343
  /** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
@@ -20364,37 +20408,42 @@ var Chunk = class _Chunk {
20364
20408
  failed = true;
20365
20409
  }
20366
20410
  }
20367
- for (const { index, blockState: blockState3, biome } of perBlocks) {
20368
- let object4;
20369
- try {
20370
- object4 = await this.scene.addBlock(blockState3, {
20371
- mergeMeshes: true,
20372
- instanceMeshes: true,
20373
- maxInstanceCount: 2e3,
20374
- initialState: this.data.stateAt(this.data.idAt(index)).properties,
20375
- variantPosition: this.positionAt(index),
20376
- biome,
20377
- biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20378
- });
20379
- object4.setPosition(new import_three47.Vector3(
20380
- this.x * 256 + index % 16 * 16,
20381
- this.y * 256 + Math.floor(index / 256) * 16,
20382
- this.z * 256 + Math.floor(index / 16) % 16 * 16
20383
- ));
20384
- this.renderedBlocks.get(index).object = object4;
20411
+ const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new import_three47.Vector3(
20412
+ this.x * 256 + index % 16 * 16,
20413
+ this.y * 256 + Math.floor(index / 256) * 16,
20414
+ this.z * 256 + Math.floor(index / 16) % 16 * 16
20415
+ ), {
20416
+ mergeMeshes: true,
20417
+ instanceMeshes: true,
20418
+ maxInstanceCount: 2e3,
20419
+ initialState: this.data.stateAt(this.data.idAt(index)).properties,
20420
+ variantPosition: this.positionAt(index),
20421
+ biome,
20422
+ biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20423
+ })));
20424
+ for (const [i, { index }] of perBlocks.entries()) {
20425
+ const result = objects[i];
20426
+ if (result.status === "fulfilled") {
20427
+ this.renderedBlocks.get(index).object = result.value;
20385
20428
  this.objectBlocks.add(index);
20386
- } catch (error) {
20429
+ } else {
20387
20430
  this.data.assign(index, 0);
20388
20431
  this.fluidCells[index] = 0;
20389
20432
  this.renderedBlocks.delete(index);
20390
20433
  this.objectBlocks.delete(index);
20391
- object4?.removeFromScene();
20392
- if (!failed) failure = error;
20434
+ if (!failed) failure = result.reason;
20393
20435
  failed = true;
20394
20436
  }
20395
20437
  }
20396
20438
  if (failed) throw failure;
20397
20439
  }
20440
+ createBlockObject(blockState3, position, options) {
20441
+ return this.scene.addSceneObject(blockState3, () => {
20442
+ const object4 = new BlockObject(blockState3, options);
20443
+ object4.position.copy(position);
20444
+ return object4;
20445
+ }, options);
20446
+ }
20398
20447
  positionAt(index) {
20399
20448
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
20400
20449
  }