minerender 2.1.0-alpha.28 → 2.1.0-alpha.29

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.
@@ -1312,7 +1312,7 @@ var Models = class _Models {
1312
1312
  }
1313
1313
  static async getItemModel(key) {
1314
1314
  const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
1315
- const model = await this.PERSISTENT_CACHE.getOrLoad(itemKey.serialize(), async () => {
1315
+ const model = await this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(itemKey.serialize()), async () => {
1316
1316
  const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1317
1317
  if (!result) return void 0;
1318
1318
  if (result.key.assetType !== "items") return { ...result.asset, key };
@@ -1358,7 +1358,7 @@ var Models = class _Models {
1358
1358
  }
1359
1359
  const keyStr = key.serialize();
1360
1360
  return Caching.rawModelCache.get(keyStr, (k) => {
1361
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1361
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1362
1362
  return AssetLoader.get(key, "model" /* MODEL */);
1363
1363
  });
1364
1364
  }).then((asset) => {
@@ -1430,7 +1430,7 @@ var BlockStates = class {
1430
1430
  }
1431
1431
  const keyStr = key.serialize();
1432
1432
  return Caching.blockStateCache.get(keyStr, (k) => {
1433
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1433
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1434
1434
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1435
1435
  });
1436
1436
  }).then((asset) => {
@@ -1476,6 +1476,14 @@ var Assets = class {
1476
1476
  var AssetSource = class {
1477
1477
  constructor() {
1478
1478
  }
1479
+ /**
1480
+ * Identifies this source's content across sessions for persistent caching. Sources that
1481
+ * cannot tell whether their content changed (an unnamed archive, for example) return
1482
+ * undefined, which keeps their results out of long-lived caches.
1483
+ */
1484
+ get cacheId() {
1485
+ return void 0;
1486
+ }
1479
1487
  };
1480
1488
  var AssetLoadError = class extends Error {
1481
1489
  constructor(source, key, path, cause) {
@@ -1688,6 +1696,9 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1688
1696
  get root() {
1689
1697
  return this._root;
1690
1698
  }
1699
+ get cacheId() {
1700
+ return `hosted:${this._root}`;
1701
+ }
1691
1702
  get options() {
1692
1703
  return this._options;
1693
1704
  }
@@ -1764,9 +1775,10 @@ var HostedAssetSource = _HostedAssetSource;
1764
1775
  // src/assets/source/archive/BrowserArchiveProxy.ts
1765
1776
  import { BlobReader, BlobWriter, ZipReader } from "@zip.js/zip.js";
1766
1777
  var BrowserArchiveProxy = class {
1767
- constructor(blob) {
1778
+ constructor(blob, id) {
1768
1779
  this._blob = blob;
1769
1780
  this._reader = new ZipReader(new BlobReader(this._blob));
1781
+ this.id = id ?? (typeof File !== "undefined" && blob instanceof File ? `${blob.name}:${blob.size}:${blob.lastModified}` : void 0);
1770
1782
  }
1771
1783
  async getEntries() {
1772
1784
  return (await this._reader.getEntries()).map((e) => {
@@ -1791,6 +1803,10 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1791
1803
  static blob(blob) {
1792
1804
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob));
1793
1805
  }
1806
+ get cacheId() {
1807
+ const id = this._archiveProxy.id;
1808
+ return id === void 0 ? void 0 : `archive:${id}`;
1809
+ }
1794
1810
  async getEntries() {
1795
1811
  return this._archiveProxy.getEntries();
1796
1812
  }
@@ -1833,6 +1849,7 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1833
1849
 
1834
1850
  // src/assets/AssetLoader.ts
1835
1851
  var p4 = prefix("AssetLoader");
1852
+ var FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
1836
1853
  var _AssetLoader = class _AssetLoader {
1837
1854
  static get version() {
1838
1855
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
@@ -1848,6 +1865,28 @@ var _AssetLoader = class _AssetLoader {
1848
1865
  }
1849
1866
  Caching.clear();
1850
1867
  }
1868
+ /**
1869
+ * Scope for persistent cache keys. Empty with only the default vanilla sources, so their
1870
+ * entries stay valid across sessions; otherwise it names every added source (resource packs,
1871
+ * mirrors) so their results never masquerade as vanilla assets after a reload.
1872
+ */
1873
+ static get persistentScope() {
1874
+ const parts = [];
1875
+ for (const { key, source } of this._SOURCES) {
1876
+ const id = source.cacheId;
1877
+ if (key === "mcassets" && id === `hosted:${this.ROOT}`) continue;
1878
+ if (key === "mcassets-fallback" && id === `hosted:${FALLBACK_ROOT}`) continue;
1879
+ if (id === void 0) return this.SESSION_SCOPE;
1880
+ parts.push(`${key}=${id}`);
1881
+ }
1882
+ return parts.join("|");
1883
+ }
1884
+ /** Prefixes a persistent cache key with the current source scope. */
1885
+ static persistentKey(key) {
1886
+ const scope = this.persistentScope;
1887
+ return scope ? `${scope}
1888
+ ${key}` : key;
1889
+ }
1851
1890
  static addSource(key, source, override = true) {
1852
1891
  if (override) {
1853
1892
  const existing = this.removeSource(key);
@@ -1909,7 +1948,9 @@ var _AssetLoader = class _AssetLoader {
1909
1948
  };
1910
1949
  _AssetLoader.ROOT = DEFAULT_ROOT;
1911
1950
  _AssetLoader._SOURCES = [];
1912
- _AssetLoader.addSource("mcassets-fallback", new HostedAssetSource("https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master", { retryDefaults: false }));
1951
+ /** Used when a registered source cannot identify its content, so nothing persists past this session. */
1952
+ _AssetLoader.SESSION_SCOPE = `session:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1953
+ _AssetLoader.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
1913
1954
  _AssetLoader.addSource("mcassets", new HostedAssetSource(_AssetLoader.ROOT, { retryDefaults: false }));
1914
1955
  var AssetLoader = _AssetLoader;
1915
1956
 
@@ -1952,7 +1993,7 @@ var ModelTextures = class {
1952
1993
  }
1953
1994
  const keyStr = key.serialize();
1954
1995
  return Caching.textureMetaCache.get(keyStr, (k) => {
1955
- return this.PERSISTENT_META_CACHE.getOrLoad(keyStr, (k1) => {
1996
+ return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1956
1997
  return AssetLoader.get(key, "meta" /* META */).then((asset) => {
1957
1998
  if (asset)
1958
1999
  asset.key = key;
@@ -4609,7 +4650,9 @@ var _BlockObject = class _BlockObject extends SceneObject {
4609
4650
  if (typeof variant.y !== "undefined") {
4610
4651
  rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4611
4652
  }
4612
- const model = await Models.getMerged(AssetKey.parse("models", variant.model));
4653
+ const modelKey = AssetKey.parse("models", variant.model);
4654
+ modelKey.root = this.blockState.key?.root;
4655
+ const model = await Models.getMerged(modelKey);
4613
4656
  const options = {
4614
4657
  ...this.options,
4615
4658
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
@@ -5405,56 +5448,12 @@ var baseSkinCoordinates = {
5405
5448
  }
5406
5449
  },
5407
5450
  cape: {
5408
- //TODO
5409
- east: {
5410
- uv: [
5411
- 0,
5412
- 36,
5413
- 4,
5414
- 48
5415
- ]
5416
- },
5417
- west: {
5418
- uv: [
5419
- 8,
5420
- 36,
5421
- 12,
5422
- 48
5423
- ]
5424
- },
5425
- north: {
5426
- uv: [
5427
- 4,
5428
- 36,
5429
- 8,
5430
- 48
5431
- ]
5432
- },
5433
- south: {
5434
- uv: [
5435
- 12,
5436
- 36,
5437
- 16,
5438
- 48
5439
- ]
5440
- },
5441
- up: {
5442
- uv: [
5443
- 4,
5444
- 32,
5445
- 8,
5446
- 36
5447
- ]
5448
- },
5449
- down: {
5450
- uv: [
5451
- 8,
5452
- 32,
5453
- 12,
5454
- 36
5455
- ],
5456
- rotation: 180
5457
- }
5451
+ east: { uv: [0, 1, 1, 17] },
5452
+ west: { uv: [11, 1, 12, 17] },
5453
+ north: { uv: [1, 1, 11, 17] },
5454
+ south: { uv: [12, 1, 22, 17] },
5455
+ up: { uv: [11, 1, 1, 0] },
5456
+ down: { uv: [21, 0, 11, 1] }
5458
5457
  }
5459
5458
  };
5460
5459
  var classicSkinTextureCoordinates = {
@@ -5537,7 +5536,7 @@ var slimSkinGeometries = {
5537
5536
  var import_ts_deepmerge7 = __toESM(require_dist());
5538
5537
 
5539
5538
  // src/skin/SkinTextures.ts
5540
- import { DataTexture, DoubleSide as DoubleSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5539
+ import { DataTexture, DoubleSide as DoubleSide2, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5541
5540
  var SkinTextures = class {
5542
5541
  static async get(src, legacy) {
5543
5542
  const image = await ImageLoader.getData(src);
@@ -5553,14 +5552,27 @@ var SkinTextures = class {
5553
5552
  const key = `skin:${serializeImageKey({ src })}:${isLegacy}`;
5554
5553
  const material = Caching.materialCache.get(key, () => {
5555
5554
  const pixels = isLegacy ? this.normalizeLegacy(image, scale) : new Uint8Array(image.data);
5556
- const texture = Textures.initTextureProps(new DataTexture(pixels, image.width, image.width));
5557
- texture.colorSpace = SRGBColorSpace3;
5558
- texture.flipY = true;
5559
- texture.needsUpdate = true;
5560
- return new MeshBasicMaterial3({ map: texture, transparent: true, side: DoubleSide2, alphaTest: 0.5 });
5555
+ return this.createMaterial(pixels, image.width, image.width);
5561
5556
  });
5562
5557
  return { material, slim, legacy: isLegacy };
5563
5558
  }
5559
+ static async getCape(src) {
5560
+ const image = await ImageLoader.getData(src);
5561
+ const scale = image.width / 64;
5562
+ if (!Number.isInteger(scale) || scale < 1 || image.height !== image.width / 2) {
5563
+ throw new MineRenderError(`Invalid cape dimensions ${image.width}x${image.height}; expected 64x32 or an integer-scaled equivalent`);
5564
+ }
5565
+ return Caching.materialCache.get(`cape:${serializeImageKey({ src })}`, () => {
5566
+ return this.createMaterial(new Uint8Array(image.data), image.width, image.height, false);
5567
+ });
5568
+ }
5569
+ static createMaterial(pixels, width, height, transparent = true) {
5570
+ const texture = Textures.initTextureProps(new DataTexture(pixels, width, height));
5571
+ texture.colorSpace = SRGBColorSpace3;
5572
+ texture.flipY = true;
5573
+ texture.needsUpdate = true;
5574
+ return new MeshBasicMaterial3({ map: texture, transparent, side: transparent ? DoubleSide2 : FrontSide2, alphaTest: transparent ? 0.5 : 0 });
5575
+ }
5564
5576
  static isSlim(image, scale) {
5565
5577
  for (const [x, y] of [[46, 52], [54, 20]]) {
5566
5578
  for (let row = y * scale; row < (y + 12) * scale; row++) {
@@ -5623,6 +5635,7 @@ var SkinObject = class extends SceneObject {
5623
5635
  this.slim = false;
5624
5636
  this.detectedSlim = false;
5625
5637
  this.skinLoad = 0;
5638
+ this.capeLoad = 0;
5626
5639
  this.options = (0, import_ts_deepmerge7.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5627
5640
  this.slim = this.options.slim ?? false;
5628
5641
  }
@@ -5692,6 +5705,7 @@ var SkinObject = class extends SceneObject {
5692
5705
  this.detectedSlim = texture.slim;
5693
5706
  this.updateSlim(this.options.slim ?? this.detectedSlim);
5694
5707
  for (const part of SKIN_PARTS) {
5708
+ if (part === "cape" /* CAPE */) continue;
5695
5709
  const mesh = this.getMeshByName(part);
5696
5710
  if (mesh) {
5697
5711
  mesh.material = texture.material;
@@ -5699,7 +5713,34 @@ var SkinObject = class extends SceneObject {
5699
5713
  }
5700
5714
  this.notifyDirty();
5701
5715
  }
5702
- //TODO: cape
5716
+ /** Load a vanilla cape texture, or pass undefined to remove the cape. */
5717
+ async setCapeTexture(src) {
5718
+ const load = ++this.capeLoad;
5719
+ if (src === void 0) {
5720
+ const group = this.getGroupByName("cape" /* CAPE */);
5721
+ if (group) this.remove(group);
5722
+ this.notifyDirty();
5723
+ return;
5724
+ }
5725
+ const material = await SkinTextures.getCape(src);
5726
+ if (load !== this.capeLoad) return;
5727
+ const mesh = this.getMeshByName("cape" /* CAPE */);
5728
+ if (mesh) {
5729
+ mesh.material = material;
5730
+ } else {
5731
+ const group = this.createAndAddGroup("cape" /* CAPE */, 0, 24, 2);
5732
+ group.rotation.set(toRadians(-6), Math.PI, 0);
5733
+ const geometry = this._getBoxGeometryFromDimensions(
5734
+ classicSkinGeometries.cape,
5735
+ classicSkinTextureCoordinates.cape,
5736
+ [64, 32],
5737
+ [64, 32]
5738
+ );
5739
+ const cape = this.createAndAddMesh("cape" /* CAPE */, group, geometry, material);
5740
+ cape.position.set(0, -8, -0.5);
5741
+ }
5742
+ this.notifyDirty();
5743
+ }
5703
5744
  /** Select the arm model, or pass undefined to use texture detection. */
5704
5745
  setSlim(slim) {
5705
5746
  this.options.slim = slim;
@@ -5727,6 +5768,7 @@ var SkinObject = class extends SceneObject {
5727
5768
  }
5728
5769
  dispose() {
5729
5770
  this.skinLoad++;
5771
+ this.capeLoad++;
5730
5772
  super.dispose();
5731
5773
  }
5732
5774
  //TODO: layer toggles