minerender 2.1.0-alpha.140 → 2.1.0-alpha.142

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.
@@ -309,6 +309,9 @@ var init_Caching = __esm({
309
309
  static {
310
310
  this.blockEntityIndexCache = _Caching.createAssetCache();
311
311
  }
312
+ static {
313
+ this.entityDatasetVersionCache = _Caching.createAssetCache();
314
+ }
312
315
  static {
313
316
  this.unihexCache = _Caching.createAssetCache();
314
317
  }
@@ -353,6 +356,7 @@ var init_Caching = __esm({
353
356
  this.entityTextureCache,
354
357
  this.entityAnimationCache,
355
358
  this.blockEntityIndexCache,
359
+ this.entityDatasetVersionCache,
356
360
  this.fontCache,
357
361
  this.unihexCache,
358
362
  this.bannerPatternCache,
@@ -3331,23 +3335,39 @@ var init_MinecraftCubeTexture = __esm({
3331
3335
  }
3332
3336
  });
3333
3337
 
3338
+ // src/assets/_versions/DatasetVersions.ts
3339
+ var parseRelease, compare2, nearestRelease;
3340
+ var init_DatasetVersions = __esm({
3341
+ "src/assets/_versions/DatasetVersions.ts"() {
3342
+ parseRelease = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3343
+ compare2 = (left, right) => {
3344
+ for (let i = 0; i < 3; i++) {
3345
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
3346
+ if (difference) return difference;
3347
+ }
3348
+ return 0;
3349
+ };
3350
+ nearestRelease = (requestedVersion, published) => {
3351
+ const requested = parseRelease(requestedVersion);
3352
+ if (!requested) return void 0;
3353
+ const versions = published.map((name) => ({ name, number: parseRelease(name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3354
+ const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3355
+ const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3356
+ return selected?.name;
3357
+ };
3358
+ }
3359
+ });
3360
+
3334
3361
  // src/assets/MineRenderData.ts
3335
- var object2, release, compare2, MineRenderData;
3362
+ var object2, MineRenderData;
3336
3363
  var init_MineRenderData = __esm({
3337
3364
  "src/assets/MineRenderData.ts"() {
3365
+ init_DatasetVersions();
3338
3366
  init_AssetKey();
3339
3367
  init_AssetLoader();
3340
3368
  init_AssetParsers();
3341
3369
  init_Caching();
3342
3370
  object2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
3343
- release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3344
- compare2 = (left, right) => {
3345
- for (let i = 0; i < 3; i++) {
3346
- const difference = (left[i] ?? 0) - (right[i] ?? 0);
3347
- if (difference) return difference;
3348
- }
3349
- return 0;
3350
- };
3351
3371
  MineRenderData = class {
3352
3372
  /** Loads one registry. Missing files and invalid data reject the request. */
3353
3373
  static async get(dataset, root) {
@@ -3377,9 +3397,9 @@ var init_MineRenderData = __esm({
3377
3397
  const scope = AssetLoader.persistentScope;
3378
3398
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
3379
3399
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
3380
- let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
3400
+ let manifest = await this.manifest(root, requestRoot !== void 0 && parseRelease(requestedVersion) ? requestedVersion : void 0, requestRoot);
3381
3401
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
3382
- const requested = release(requestedVersion);
3402
+ const requested = parseRelease(requestedVersion);
3383
3403
  if (!requested) {
3384
3404
  throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
3385
3405
  }
@@ -3391,14 +3411,12 @@ var init_MineRenderData = __esm({
3391
3411
  }
3392
3412
  return value.versions;
3393
3413
  });
3394
- const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3395
- const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3396
- const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3414
+ const selected = nearestRelease(requestedVersion, index.map((entry) => entry.name));
3397
3415
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
3398
- const resolvedRoot = `${base}/${selected.name}`;
3399
- manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
3400
- if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
3401
- return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
3416
+ const resolvedRoot = `${base}/${selected}`;
3417
+ manifest = await this.manifest(resolvedRoot, selected, resolvedRoot);
3418
+ if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected}`);
3419
+ return { requestedVersion, version: selected, root: resolvedRoot, manifest };
3402
3420
  });
3403
3421
  }
3404
3422
  static async manifest(root, expectedVersion, requestRoot) {
@@ -4520,118 +4538,12 @@ var init_Biomes = __esm({
4520
4538
  }
4521
4539
  });
4522
4540
 
4523
- // src/assets/BlockEntities.ts
4524
- import { Matrix4 as Matrix42 } from "three";
4525
- var log9, BlockEntities;
4526
- var init_BlockEntities = __esm({
4527
- "src/assets/BlockEntities.ts"() {
4528
- init_AssetKey();
4529
- init_AssetLoader();
4530
- init_source();
4531
- init_Caching();
4532
- init_log();
4533
- log9 = logger("BlockEntities");
4534
- BlockEntities = class {
4535
- /**
4536
- * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4537
- * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4538
- * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4539
- */
4540
- static async getIndex(root) {
4541
- const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4542
- key.rootType = void 0;
4543
- const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4544
- try {
4545
- return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4546
- } catch (error) {
4547
- log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4548
- return {};
4549
- }
4550
- });
4551
- return index ?? {};
4552
- }
4553
- /** The index entry of a block ID such as `minecraft:chest`. */
4554
- static entry(index, block) {
4555
- const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4556
- return Array.isArray(entry?.parts) ? entry : void 0;
4557
- }
4558
- /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4559
- static resolve(index, block, state = {}) {
4560
- const entry = this.entry(index, block);
4561
- if (!entry) return void 0;
4562
- const value = (property) => String(state[property]).toLowerCase();
4563
- const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4564
- if (!parts.length) return void 0;
4565
- let rotation = 0;
4566
- if (entry.rotation) {
4567
- const selected = value(entry.rotation.property);
4568
- rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4569
- if (!Number.isFinite(rotation)) rotation = 0;
4570
- }
4571
- return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4572
- }
4573
- /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4574
- static matrix(resolved, matrix = new Matrix42()) {
4575
- return matrix.makeTranslation(8, 0, 8).multiply(new Matrix42().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix42().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4576
- }
4577
- };
4578
- }
4579
- });
4580
-
4581
- // src/assets/DecoratedPots.ts
4582
- var POTTERY_SHERDS, DecoratedPots;
4583
- var init_DecoratedPots = __esm({
4584
- "src/assets/DecoratedPots.ts"() {
4585
- init_AssetKey();
4586
- POTTERY_SHERDS = Object.freeze([
4587
- "angler",
4588
- "archer",
4589
- "arms_up",
4590
- "blade",
4591
- "brewer",
4592
- "burn",
4593
- "danger",
4594
- "explorer",
4595
- "flow",
4596
- "friend",
4597
- "guster",
4598
- "heart",
4599
- "heartbreak",
4600
- "howl",
4601
- "miner",
4602
- "mourner",
4603
- "plenty",
4604
- "prize",
4605
- "scrape",
4606
- "sheaf",
4607
- "shelter",
4608
- "skull",
4609
- "snort"
4610
- ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4611
- DecoratedPots = class {
4612
- /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4613
- static getSideTextures(value, root) {
4614
- const items = value === void 0 ? [] : value;
4615
- if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4616
- const textures = items.map((item) => {
4617
- if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4618
- const id = item.includes(":") ? item : `minecraft:${item}`;
4619
- return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4620
- });
4621
- return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4622
- side,
4623
- new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4624
- ]));
4625
- }
4626
- };
4627
- }
4628
- });
4629
-
4630
4541
  // src/assets/Entities.ts
4631
4542
  var Entities;
4632
4543
  var init_Entities = __esm({
4633
4544
  "src/assets/Entities.ts"() {
4634
4545
  init_AssetKey();
4546
+ init_DatasetVersions();
4635
4547
  init_AssetLoader();
4636
4548
  init_source();
4637
4549
  init_Caching();
@@ -4639,11 +4551,36 @@ var init_Entities = __esm({
4639
4551
  init_MineRenderError();
4640
4552
  init_ModelTextures();
4641
4553
  Entities = class {
4554
+ /**
4555
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
4556
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
4557
+ * Textures keep the requested root.
4558
+ */
4559
+ static async getDatasetRoot(root) {
4560
+ const requestRoot = (root ?? AssetLoader.ROOT).replace(/\/+$/, "");
4561
+ const requestedVersion = requestRoot.slice(requestRoot.lastIndexOf("/") + 1);
4562
+ const base = requestRoot.slice(0, requestRoot.lastIndexOf("/"));
4563
+ const selected = await Caching.entityDatasetVersionCache.get(root ?? `sources:${AssetLoader.ROOT}`, async () => {
4564
+ const key = new AssetKey("entity-models", "version", void 0, void 0, void 0, ".json", root);
4565
+ key.rootType = void 0;
4566
+ if (!parseRelease(requestedVersion) || await AssetLoader.get(key, "json" /* JSON */) !== void 0) return requestedVersion;
4567
+ const indexKey = new AssetKey("entity-models", "versions", void 0, void 0, void 0, ".json", base);
4568
+ indexKey.rootType = void 0;
4569
+ const index = await AssetLoader.get(indexKey, "json" /* JSON */);
4570
+ if (index === void 0) return requestedVersion;
4571
+ if (!index || typeof index !== "object" || Array.isArray(index) || !Array.isArray(index.versions) || !index.versions.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.name === "string")) {
4572
+ throw new MineRenderError(`Invalid entity model version index at ${base}/entity-models/versions.json`);
4573
+ }
4574
+ return nearestRelease(requestedVersion, index.versions.map((entry) => entry.name)) ?? requestedVersion;
4575
+ });
4576
+ return selected === requestedVersion ? root : `${base}/${selected}`;
4577
+ }
4642
4578
  /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
4643
4579
  static async getEntityList() {
4580
+ const root = await this.getDatasetRoot();
4644
4581
  const collect = async (path) => {
4645
4582
  const prefix2 = path ? `${path}/` : "";
4646
- const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
4583
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json", root);
4647
4584
  const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
4648
4585
  if (!list2) return [];
4649
4586
  const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
@@ -4654,7 +4591,7 @@ var init_Entities = __esm({
4654
4591
  }
4655
4592
  static async getModelFile(modelKey) {
4656
4593
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4657
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4594
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4658
4595
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4659
4596
  }
4660
4597
  /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
@@ -4715,7 +4652,7 @@ var init_Entities = __esm({
4715
4652
  */
4716
4653
  static async getAnimations(modelKey) {
4717
4654
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4718
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4655
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
4719
4656
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4720
4657
  return file?.animations;
4721
4658
  }
@@ -4757,6 +4694,115 @@ var init_Entities = __esm({
4757
4694
  }
4758
4695
  });
4759
4696
 
4697
+ // src/assets/BlockEntities.ts
4698
+ import { Matrix4 as Matrix42 } from "three";
4699
+ var log9, BlockEntities;
4700
+ var init_BlockEntities = __esm({
4701
+ "src/assets/BlockEntities.ts"() {
4702
+ init_AssetKey();
4703
+ init_Entities();
4704
+ init_AssetLoader();
4705
+ init_source();
4706
+ init_Caching();
4707
+ init_log();
4708
+ log9 = logger("BlockEntities");
4709
+ BlockEntities = class {
4710
+ /**
4711
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4712
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4713
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4714
+ */
4715
+ static async getIndex(root) {
4716
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4717
+ key.rootType = void 0;
4718
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4719
+ try {
4720
+ key.root = await Entities.getDatasetRoot(root);
4721
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4722
+ } catch (error) {
4723
+ log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4724
+ return {};
4725
+ }
4726
+ });
4727
+ return index ?? {};
4728
+ }
4729
+ /** The index entry of a block ID such as `minecraft:chest`. */
4730
+ static entry(index, block) {
4731
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4732
+ return Array.isArray(entry?.parts) ? entry : void 0;
4733
+ }
4734
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4735
+ static resolve(index, block, state = {}) {
4736
+ const entry = this.entry(index, block);
4737
+ if (!entry) return void 0;
4738
+ const value = (property) => String(state[property]).toLowerCase();
4739
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4740
+ if (!parts.length) return void 0;
4741
+ let rotation = 0;
4742
+ if (entry.rotation) {
4743
+ const selected = value(entry.rotation.property);
4744
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4745
+ if (!Number.isFinite(rotation)) rotation = 0;
4746
+ }
4747
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4748
+ }
4749
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4750
+ static matrix(resolved, matrix = new Matrix42()) {
4751
+ return matrix.makeTranslation(8, 0, 8).multiply(new Matrix42().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix42().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4752
+ }
4753
+ };
4754
+ }
4755
+ });
4756
+
4757
+ // src/assets/DecoratedPots.ts
4758
+ var POTTERY_SHERDS, DecoratedPots;
4759
+ var init_DecoratedPots = __esm({
4760
+ "src/assets/DecoratedPots.ts"() {
4761
+ init_AssetKey();
4762
+ POTTERY_SHERDS = Object.freeze([
4763
+ "angler",
4764
+ "archer",
4765
+ "arms_up",
4766
+ "blade",
4767
+ "brewer",
4768
+ "burn",
4769
+ "danger",
4770
+ "explorer",
4771
+ "flow",
4772
+ "friend",
4773
+ "guster",
4774
+ "heart",
4775
+ "heartbreak",
4776
+ "howl",
4777
+ "miner",
4778
+ "mourner",
4779
+ "plenty",
4780
+ "prize",
4781
+ "scrape",
4782
+ "sheaf",
4783
+ "shelter",
4784
+ "skull",
4785
+ "snort"
4786
+ ].map((pattern2) => `minecraft:${pattern2}_pottery_sherd`));
4787
+ DecoratedPots = class {
4788
+ /** Reads up to four item IDs in back, left, right, front order; missing and unmapped items use plain sides. */
4789
+ static getSideTextures(value, root) {
4790
+ const items = value === void 0 ? [] : value;
4791
+ if (!Array.isArray(items) || items.length > 4) throw new Error("pot_decorations must be an array of at most four item IDs");
4792
+ const textures = items.map((item) => {
4793
+ if (!isResourceLocation(item)) throw new Error("Pot decorations must be item identifiers");
4794
+ const id = item.includes(":") ? item : `minecraft:${item}`;
4795
+ return POTTERY_SHERDS.includes(id) ? id.slice("minecraft:".length).replace(/_sherd$/, "_pattern") : "decorated_pot_side";
4796
+ });
4797
+ return Object.fromEntries(["back", "left", "right", "front"].map((side, index) => [
4798
+ side,
4799
+ new AssetKey("minecraft", `decorated_pot/${textures[index] ?? "decorated_pot_side"}`, "textures", "entity", "assets", ".png", root)
4800
+ ]));
4801
+ }
4802
+ };
4803
+ }
4804
+ });
4805
+
4760
4806
  // src/assets/_fonts/Unihex.ts
4761
4807
  var Unihex_exports = {};
4762
4808
  __export(Unihex_exports, {
@@ -17835,7 +17881,7 @@ var AnvilWorldSource = class {
17835
17881
  read.waiters++;
17836
17882
  return new Promise((resolve, reject) => {
17837
17883
  let finished = false;
17838
- const release2 = () => {
17884
+ const release = () => {
17839
17885
  if (finished) return false;
17840
17886
  finished = true;
17841
17887
  signal?.removeEventListener("abort", abort);
@@ -17843,7 +17889,7 @@ var AnvilWorldSource = class {
17843
17889
  return true;
17844
17890
  };
17845
17891
  const abort = () => {
17846
- if (!release2()) return;
17892
+ if (!release()) return;
17847
17893
  if (!read.waiters && !read.settled) {
17848
17894
  if (this.pending.get(key) === read) this.pending.delete(key);
17849
17895
  read.controller.abort(signal.reason);
@@ -17853,10 +17899,10 @@ var AnvilWorldSource = class {
17853
17899
  signal?.addEventListener("abort", abort, { once: true });
17854
17900
  read.promise.then(
17855
17901
  (data) => {
17856
- if (release2()) resolve(data);
17902
+ if (release()) resolve(data);
17857
17903
  },
17858
17904
  (error) => {
17859
- if (release2()) reject(error);
17905
+ if (release()) reject(error);
17860
17906
  }
17861
17907
  );
17862
17908
  if (signal?.aborted) abort();
@@ -17878,7 +17924,7 @@ var AnvilWorldSource = class {
17878
17924
  };
17879
17925
 
17880
17926
  // src/world/Chunk.ts
17881
- import { Color as Color13, Vector3 as Vector320 } from "three";
17927
+ import { Color as Color13, Vector3 as Vector321 } from "three";
17882
17928
  init_BlockStates();
17883
17929
  init_AssetKey();
17884
17930
 
@@ -19138,7 +19184,7 @@ init_BlockEntities();
19138
19184
  // src/world/SectionMesh.ts
19139
19185
  init_CanvasCompat();
19140
19186
  init_Materials();
19141
- import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as DoubleSide8, Float32BufferAttribute as Float32BufferAttribute10, FrontSide as FrontSide4, Group as Group3, Mesh as Mesh12, Uint32BufferAttribute } from "three";
19187
+ import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as DoubleSide8, DynamicDrawUsage, Float32BufferAttribute as Float32BufferAttribute10, FrontSide as FrontSide4, Group as Group3, Matrix4 as Matrix413, Mesh as Mesh12, Uint32BufferAttribute, Vector3 as Vector320 } from "three";
19142
19188
 
19143
19189
  // src/world/SectionGeometry.ts
19144
19190
  init_FluidQuads();
@@ -19465,6 +19511,9 @@ var SectionWorker = class _SectionWorker {
19465
19511
  };
19466
19512
 
19467
19513
  // src/world/SectionMesh.ts
19514
+ var BLOCK_SIZE = 16;
19515
+ var QUAD_VERTICES = 4;
19516
+ var QUAD_INDICES = 6;
19468
19517
  var templateCache = /* @__PURE__ */ new WeakMap();
19469
19518
  var WATER_TINT = new Color12(4159204).toArray();
19470
19519
  function toInput(entries, maxAtlasSize, fluids, lighting) {
@@ -19501,8 +19550,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19501
19550
  if (!data) {
19502
19551
  const indices = template.geometry.getIndex();
19503
19552
  const position = template.geometry.getAttribute("position");
19504
- const quads = indices.count / 6;
19505
- if (indices.count % 6 !== 0 || position.count !== quads * 4) {
19553
+ const quads = indices.count / QUAD_INDICES;
19554
+ if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
19506
19555
  throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
19507
19556
  }
19508
19557
  data = {
@@ -19512,8 +19561,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19512
19561
  uvs: new Float32Array(quads * 8),
19513
19562
  uvBounds: new Float32Array(quads * 16),
19514
19563
  colors: new Float32Array(quads * 12),
19515
- shade: new Float32Array(quads * 4),
19516
- indices: new Uint16Array(quads * 6),
19564
+ shade: new Float32Array(quads * QUAD_VERTICES),
19565
+ indices: new Uint16Array(quads * QUAD_INDICES),
19517
19566
  cullFaces: new Uint8Array(template.cullFaces),
19518
19567
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
19519
19568
  atlas: 0,
@@ -19525,7 +19574,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19525
19574
  const bounds = template.geometry.getAttribute("uvBounds");
19526
19575
  const color2 = template.geometry.getAttribute("color");
19527
19576
  const shade = template.geometry.getAttribute("shade");
19528
- for (let vertex = 0; vertex < quads * 4; vertex++) {
19577
+ for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
19529
19578
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19530
19579
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
19531
19580
  data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
@@ -19538,7 +19587,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19538
19587
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19539
19588
  data.shade[vertex] = shade?.getX(vertex) ?? 1;
19540
19589
  }
19541
- for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19590
+ for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
19542
19591
  templateCache.set(template, data);
19543
19592
  }
19544
19593
  id = input.templateData.length;
@@ -19571,6 +19620,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19571
19620
  }
19572
19621
  return { input, atlases: atlases2 };
19573
19622
  }
19623
+ function sortTransparentQuads(mesh) {
19624
+ const index = mesh.geometry.getIndex();
19625
+ if (index.count <= QUAD_INDICES) return;
19626
+ index.setUsage(DynamicDrawUsage);
19627
+ const count = index.count / QUAD_INDICES;
19628
+ let source, centers, depths, order;
19629
+ const view = new Matrix413(), localCamera = new Vector320(), key = new Vector320(), previous = new Vector320();
19630
+ let previousOrthographic;
19631
+ mesh.onBeforeRender = (_renderer, _scene, camera) => {
19632
+ localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
19633
+ const orthographic = !!camera.isOrthographicCamera;
19634
+ if (orthographic) {
19635
+ view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
19636
+ key.set(view.elements[2], view.elements[6], view.elements[10]);
19637
+ } else {
19638
+ key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
19639
+ }
19640
+ if (orthographic === previousOrthographic && key.equals(previous)) return;
19641
+ previous.copy(key);
19642
+ previousOrthographic = orthographic;
19643
+ if (!centers) {
19644
+ source = new Uint32Array(index.array);
19645
+ centers = new Float32Array(count * 3);
19646
+ depths = new Float32Array(count);
19647
+ order = Uint32Array.from({ length: count }, (_, quad) => quad);
19648
+ const positions = mesh.geometry.getAttribute("position");
19649
+ for (let quad = 0; quad < count; quad++) {
19650
+ for (let axis = 0; axis < 3; axis++) {
19651
+ let center = 0;
19652
+ for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
19653
+ centers[quad * 3 + axis] = center / QUAD_VERTICES;
19654
+ }
19655
+ }
19656
+ }
19657
+ for (let quad = 0; quad < count; quad++) {
19658
+ const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
19659
+ depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
19660
+ }
19661
+ order.sort((a, b) => depths[a] - depths[b] || a - b);
19662
+ if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
19663
+ for (let offset = 0; offset < count; offset++) {
19664
+ for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
19665
+ index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
19666
+ }
19667
+ }
19668
+ index.needsUpdate = true;
19669
+ };
19670
+ }
19574
19671
  var SectionMesh = class _SectionMesh extends Group3 {
19575
19672
  constructor() {
19576
19673
  super(...arguments);
@@ -19580,7 +19677,11 @@ var SectionMesh = class _SectionMesh extends Group3 {
19580
19677
  /** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
19581
19678
  getBlockIndex(hit) {
19582
19679
  if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
19583
- return this.blockIndices.get(hit.object)?.[Math.floor(hit.faceIndex / 2)];
19680
+ const mesh = hit.object;
19681
+ const owners = this.blockIndices.get(mesh);
19682
+ if (!owners) return void 0;
19683
+ const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
19684
+ return owners[Math.floor(vertex / QUAD_VERTICES)];
19584
19685
  }
19585
19686
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19586
19687
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
@@ -19616,13 +19717,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
19616
19717
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19617
19718
  material.side = FrontSide4;
19618
19719
  if (page.layer === 2) {
19720
+ material.depthWrite = !material.transparent;
19619
19721
  material.side = DoubleSide8;
19620
19722
  material.forceSinglePass = true;
19621
- material.depthWrite = !material.transparent;
19622
19723
  }
19623
19724
  material.vertexColors = true;
19624
19725
  const texture = material.uniforms?.map?.value ?? material.map;
19625
19726
  const mesh = new Mesh12(geometry, material);
19727
+ if (transparent) sortTransparentQuads(mesh);
19626
19728
  section.add(mesh);
19627
19729
  section.blockIndices.set(mesh, page.blockIndices);
19628
19730
  const unsubscribe = [];
@@ -19681,7 +19783,7 @@ var Chunk = class _Chunk {
19681
19783
  this.sectionBlocks = /* @__PURE__ */ new Map();
19682
19784
  this.hiddenBlocks = /* @__PURE__ */ new Set();
19683
19785
  this.fluidCells = new Uint8Array(4096);
19684
- this.blockPosition = new Vector320();
19786
+ this.blockPosition = new Vector321();
19685
19787
  this.meshDirty = false;
19686
19788
  this.meshGeneration = 0;
19687
19789
  this.blockChanges = /* @__PURE__ */ new Set();
@@ -19741,7 +19843,7 @@ var Chunk = class _Chunk {
19741
19843
  const info = owner.chunk.renderedBlocks.get(owner.index);
19742
19844
  if (!info) continue;
19743
19845
  return {
19744
- position: new Vector320(
19846
+ position: new Vector321(
19745
19847
  owner.chunk.x * 16 + owner.index % 16,
19746
19848
  owner.chunk.y * 16 + Math.floor(owner.index / 256),
19747
19849
  owner.chunk.z * 16 + Math.floor(owner.index / 16) % 16
@@ -19756,18 +19858,18 @@ var Chunk = class _Chunk {
19756
19858
  }
19757
19859
  getBlockAt(posOrX, y, z) {
19758
19860
  if (typeof posOrX == "number") {
19759
- return this.getBlockAt(new Vector320(posOrX, y, z));
19861
+ return this.getBlockAt(new Vector321(posOrX, y, z));
19760
19862
  }
19761
19863
  if (isTripleArray(posOrX)) {
19762
- return this.getBlockAt(new Vector320(posOrX[0], posOrX[1], posOrX[2]));
19864
+ return this.getBlockAt(new Vector321(posOrX[0], posOrX[1], posOrX[2]));
19763
19865
  }
19764
19866
  const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
19765
19867
  const index = _Chunk.chunkPosToBlockIndex(pos);
19766
19868
  return this.renderedBlocks.get(index);
19767
19869
  }
19768
19870
  getBiomeAt(posOrX, y, z) {
19769
- if (typeof posOrX === "number") return this.getBiomeAt(new Vector320(posOrX, y, z));
19770
- if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector320(...posOrX));
19871
+ if (typeof posOrX === "number") return this.getBiomeAt(new Vector321(posOrX, y, z));
19872
+ if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector321(...posOrX));
19771
19873
  const pos = this.worldPosToChunkPos(posOrX);
19772
19874
  _Chunk.chunkPosToBlockIndex(pos);
19773
19875
  return this.biomeAt ? this.biomeAt(posOrX.x, posOrX.y, posOrX.z) : this.biomes?.[Math.floor(pos.x / 4) + Math.floor(pos.z / 4) * 4 + Math.floor(pos.y / 4) * 16];
@@ -19845,17 +19947,17 @@ var Chunk = class _Chunk {
19845
19947
  this.hiddenBlocks.add(index);
19846
19948
  this.renderedBlocks.get(index)?.object?.setVisible(false);
19847
19949
  }
19848
- const positions = blocks.map(({ index }) => new Vector320(...this.positionAt(index)));
19950
+ const positions = blocks.map(({ index }) => new Vector321(...this.positionAt(index)));
19849
19951
  if (this.onBlocksChanged) await this.onBlocksChanged(positions, deferCulling);
19850
19952
  else await this.rebuildSectionMesh();
19851
19953
  }
19852
19954
  }
19853
19955
  async setBlockAt(posOrX, yOrBlock, z, block) {
19854
19956
  if (typeof posOrX == "number") {
19855
- return this.setBlockAt(new Vector320(posOrX, yOrBlock, z), block);
19957
+ return this.setBlockAt(new Vector321(posOrX, yOrBlock, z), block);
19856
19958
  }
19857
19959
  if (isTripleArray(posOrX)) {
19858
- return this.setBlockAt(new Vector320(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
19960
+ return this.setBlockAt(new Vector321(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
19859
19961
  }
19860
19962
  const worldPos = posOrX;
19861
19963
  const pos = this.worldPosToChunkPos(worldPos);
@@ -19928,7 +20030,7 @@ var Chunk = class _Chunk {
19928
20030
  this.fluidCells[index] = 0;
19929
20031
  this.renderedBlocks.delete(index);
19930
20032
  this.objectBlocks.delete(index);
19931
- const worldPos = new Vector320(
20033
+ const worldPos = new Vector321(
19932
20034
  this.x * 16 + index % 16,
19933
20035
  this.y * 16 + Math.floor(index / 256),
19934
20036
  this.z * 16 + Math.floor(index / 16) % 16
@@ -20066,7 +20168,7 @@ var Chunk = class _Chunk {
20066
20168
  biome,
20067
20169
  biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20068
20170
  });
20069
- object4.setPosition(new Vector320(
20171
+ object4.setPosition(new Vector321(
20070
20172
  this.x * 256 + index % 16 * 16,
20071
20173
  this.y * 256 + Math.floor(index / 256) * 16,
20072
20174
  this.z * 256 + Math.floor(index / 16) % 16 * 16
@@ -20373,7 +20475,7 @@ var Chunk = class _Chunk {
20373
20475
  async clear(notify = true) {
20374
20476
  if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
20375
20477
  const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
20376
- new Vector320(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
20478
+ new Vector321(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
20377
20479
  )) : [];
20378
20480
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
20379
20481
  this.sectionBlocks.clear();
@@ -20409,14 +20511,14 @@ var Chunk = class _Chunk {
20409
20511
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
20410
20512
  }
20411
20513
  worldPosToChunkPos(pos) {
20412
- return new Vector320(
20514
+ return new Vector321(
20413
20515
  pos.x - this.x * 16,
20414
20516
  pos.y - this.y * 16,
20415
20517
  pos.z - this.z * 16
20416
20518
  );
20417
20519
  }
20418
20520
  chunkPosToWorldPos(pos) {
20419
- return new Vector320(
20521
+ return new Vector321(
20420
20522
  this.x * 16 + pos.x,
20421
20523
  this.y * 16 + pos.y,
20422
20524
  this.z * 16 + pos.z