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.
@@ -1319,7 +1319,7 @@ var Models = class _Models {
1319
1319
  }
1320
1320
  static async getItemModel(key) {
1321
1321
  const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
1322
- const model = await this.PERSISTENT_CACHE.getOrLoad(itemKey.serialize(), async () => {
1322
+ const model = await this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(itemKey.serialize()), async () => {
1323
1323
  const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1324
1324
  if (!result) return void 0;
1325
1325
  if (result.key.assetType !== "items") return { ...result.asset, key };
@@ -1365,7 +1365,7 @@ var Models = class _Models {
1365
1365
  }
1366
1366
  const keyStr = key.serialize();
1367
1367
  return Caching.rawModelCache.get(keyStr, (k) => {
1368
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1368
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1369
1369
  return AssetLoader.get(key, "model" /* MODEL */);
1370
1370
  });
1371
1371
  }).then((asset) => {
@@ -1437,7 +1437,7 @@ var BlockStates = class {
1437
1437
  }
1438
1438
  const keyStr = key.serialize();
1439
1439
  return Caching.blockStateCache.get(keyStr, (k) => {
1440
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1440
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1441
1441
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1442
1442
  });
1443
1443
  }).then((asset) => {
@@ -1483,6 +1483,14 @@ var Assets = class {
1483
1483
  var AssetSource = class {
1484
1484
  constructor() {
1485
1485
  }
1486
+ /**
1487
+ * Identifies this source's content across sessions for persistent caching. Sources that
1488
+ * cannot tell whether their content changed (an unnamed archive, for example) return
1489
+ * undefined, which keeps their results out of long-lived caches.
1490
+ */
1491
+ get cacheId() {
1492
+ return void 0;
1493
+ }
1486
1494
  };
1487
1495
  var AssetLoadError = class extends Error {
1488
1496
  constructor(source, key, path, cause) {
@@ -1771,6 +1779,9 @@ var HostedAssetSource = class _HostedAssetSource extends AssetSource {
1771
1779
  get root() {
1772
1780
  return this._root;
1773
1781
  }
1782
+ get cacheId() {
1783
+ return `hosted:${this._root}`;
1784
+ }
1774
1785
  get options() {
1775
1786
  return this._options;
1776
1787
  }
@@ -1786,9 +1797,10 @@ var HostedAssetSource = class _HostedAssetSource extends AssetSource {
1786
1797
  // src/assets/source/archive/BrowserArchiveProxy.ts
1787
1798
  import { BlobReader, BlobWriter, ZipReader } from "@zip.js/zip.js";
1788
1799
  var BrowserArchiveProxy = class {
1789
- constructor(blob) {
1800
+ constructor(blob, id) {
1790
1801
  this._blob = blob;
1791
1802
  this._reader = new ZipReader(new BlobReader(this._blob));
1803
+ this.id = id ?? (typeof File !== "undefined" && blob instanceof File ? `${blob.name}:${blob.size}:${blob.lastModified}` : void 0);
1792
1804
  }
1793
1805
  async getEntries() {
1794
1806
  return (await this._reader.getEntries()).map((e) => {
@@ -1813,6 +1825,10 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1813
1825
  static blob(blob) {
1814
1826
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob));
1815
1827
  }
1828
+ get cacheId() {
1829
+ const id = this._archiveProxy.id;
1830
+ return id === void 0 ? void 0 : `archive:${id}`;
1831
+ }
1816
1832
  async getEntries() {
1817
1833
  return this._archiveProxy.getEntries();
1818
1834
  }
@@ -1855,6 +1871,7 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1855
1871
 
1856
1872
  // src/assets/AssetLoader.ts
1857
1873
  var p4 = prefix("AssetLoader");
1874
+ var FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
1858
1875
  var AssetLoader = class {
1859
1876
  static {
1860
1877
  this.ROOT = DEFAULT_ROOT;
@@ -1862,6 +1879,10 @@ var AssetLoader = class {
1862
1879
  static {
1863
1880
  this._SOURCES = [];
1864
1881
  }
1882
+ static {
1883
+ /** Used when a registered source cannot identify its content, so nothing persists past this session. */
1884
+ this.SESSION_SCOPE = `session:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1885
+ }
1865
1886
  static get version() {
1866
1887
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
1867
1888
  }
@@ -1876,6 +1897,28 @@ var AssetLoader = class {
1876
1897
  }
1877
1898
  Caching.clear();
1878
1899
  }
1900
+ /**
1901
+ * Scope for persistent cache keys. Empty with only the default vanilla sources, so their
1902
+ * entries stay valid across sessions; otherwise it names every added source (resource packs,
1903
+ * mirrors) so their results never masquerade as vanilla assets after a reload.
1904
+ */
1905
+ static get persistentScope() {
1906
+ const parts = [];
1907
+ for (const { key, source } of this._SOURCES) {
1908
+ const id = source.cacheId;
1909
+ if (key === "mcassets" && id === `hosted:${this.ROOT}`) continue;
1910
+ if (key === "mcassets-fallback" && id === `hosted:${FALLBACK_ROOT}`) continue;
1911
+ if (id === void 0) return this.SESSION_SCOPE;
1912
+ parts.push(`${key}=${id}`);
1913
+ }
1914
+ return parts.join("|");
1915
+ }
1916
+ /** Prefixes a persistent cache key with the current source scope. */
1917
+ static persistentKey(key) {
1918
+ const scope = this.persistentScope;
1919
+ return scope ? `${scope}
1920
+ ${key}` : key;
1921
+ }
1879
1922
  static addSource(key, source, override = true) {
1880
1923
  if (override) {
1881
1924
  const existing = this.removeSource(key);
@@ -1897,7 +1940,7 @@ var AssetLoader = class {
1897
1940
  return void 0;
1898
1941
  }
1899
1942
  static {
1900
- this.addSource("mcassets-fallback", new HostedAssetSource("https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master", { retryDefaults: false }));
1943
+ this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
1901
1944
  this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
1902
1945
  }
1903
1946
  static async getAll(key, parser) {
@@ -1979,7 +2022,7 @@ var ModelTextures = class {
1979
2022
  }
1980
2023
  const keyStr = key.serialize();
1981
2024
  return Caching.textureMetaCache.get(keyStr, (k) => {
1982
- return this.PERSISTENT_META_CACHE.getOrLoad(keyStr, (k1) => {
2025
+ return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1983
2026
  return AssetLoader.get(key, "meta" /* META */).then((asset) => {
1984
2027
  if (asset)
1985
2028
  asset.key = key;
@@ -4659,7 +4702,9 @@ var BlockObject = class _BlockObject extends SceneObject {
4659
4702
  if (typeof variant.y !== "undefined") {
4660
4703
  rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4661
4704
  }
4662
- const model = await Models.getMerged(AssetKey.parse("models", variant.model));
4705
+ const modelKey = AssetKey.parse("models", variant.model);
4706
+ modelKey.root = this.blockState.key?.root;
4707
+ const model = await Models.getMerged(modelKey);
4663
4708
  const options = {
4664
4709
  ...this.options,
4665
4710
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
@@ -5451,56 +5496,12 @@ var baseSkinCoordinates = {
5451
5496
  }
5452
5497
  },
5453
5498
  cape: {
5454
- //TODO
5455
- east: {
5456
- uv: [
5457
- 0,
5458
- 36,
5459
- 4,
5460
- 48
5461
- ]
5462
- },
5463
- west: {
5464
- uv: [
5465
- 8,
5466
- 36,
5467
- 12,
5468
- 48
5469
- ]
5470
- },
5471
- north: {
5472
- uv: [
5473
- 4,
5474
- 36,
5475
- 8,
5476
- 48
5477
- ]
5478
- },
5479
- south: {
5480
- uv: [
5481
- 12,
5482
- 36,
5483
- 16,
5484
- 48
5485
- ]
5486
- },
5487
- up: {
5488
- uv: [
5489
- 4,
5490
- 32,
5491
- 8,
5492
- 36
5493
- ]
5494
- },
5495
- down: {
5496
- uv: [
5497
- 8,
5498
- 32,
5499
- 12,
5500
- 36
5501
- ],
5502
- rotation: 180
5503
- }
5499
+ east: { uv: [0, 1, 1, 17] },
5500
+ west: { uv: [11, 1, 12, 17] },
5501
+ north: { uv: [1, 1, 11, 17] },
5502
+ south: { uv: [12, 1, 22, 17] },
5503
+ up: { uv: [11, 1, 1, 0] },
5504
+ down: { uv: [21, 0, 11, 1] }
5504
5505
  }
5505
5506
  };
5506
5507
  var classicSkinTextureCoordinates = {
@@ -5583,7 +5584,7 @@ var slimSkinGeometries = {
5583
5584
  var import_ts_deepmerge7 = __toESM(require_dist());
5584
5585
 
5585
5586
  // src/skin/SkinTextures.ts
5586
- import { DataTexture, DoubleSide as DoubleSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5587
+ import { DataTexture, DoubleSide as DoubleSide2, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5587
5588
  var SkinTextures = class {
5588
5589
  static async get(src, legacy) {
5589
5590
  const image = await ImageLoader.getData(src);
@@ -5599,14 +5600,27 @@ var SkinTextures = class {
5599
5600
  const key = `skin:${serializeImageKey({ src })}:${isLegacy}`;
5600
5601
  const material = Caching.materialCache.get(key, () => {
5601
5602
  const pixels = isLegacy ? this.normalizeLegacy(image, scale) : new Uint8Array(image.data);
5602
- const texture = Textures.initTextureProps(new DataTexture(pixels, image.width, image.width));
5603
- texture.colorSpace = SRGBColorSpace3;
5604
- texture.flipY = true;
5605
- texture.needsUpdate = true;
5606
- return new MeshBasicMaterial3({ map: texture, transparent: true, side: DoubleSide2, alphaTest: 0.5 });
5603
+ return this.createMaterial(pixels, image.width, image.width);
5607
5604
  });
5608
5605
  return { material, slim, legacy: isLegacy };
5609
5606
  }
5607
+ static async getCape(src) {
5608
+ const image = await ImageLoader.getData(src);
5609
+ const scale = image.width / 64;
5610
+ if (!Number.isInteger(scale) || scale < 1 || image.height !== image.width / 2) {
5611
+ throw new MineRenderError(`Invalid cape dimensions ${image.width}x${image.height}; expected 64x32 or an integer-scaled equivalent`);
5612
+ }
5613
+ return Caching.materialCache.get(`cape:${serializeImageKey({ src })}`, () => {
5614
+ return this.createMaterial(new Uint8Array(image.data), image.width, image.height, false);
5615
+ });
5616
+ }
5617
+ static createMaterial(pixels, width, height, transparent = true) {
5618
+ const texture = Textures.initTextureProps(new DataTexture(pixels, width, height));
5619
+ texture.colorSpace = SRGBColorSpace3;
5620
+ texture.flipY = true;
5621
+ texture.needsUpdate = true;
5622
+ return new MeshBasicMaterial3({ map: texture, transparent, side: transparent ? DoubleSide2 : FrontSide2, alphaTest: transparent ? 0.5 : 0 });
5623
+ }
5610
5624
  static isSlim(image, scale) {
5611
5625
  for (const [x, y] of [[46, 52], [54, 20]]) {
5612
5626
  for (let row = y * scale; row < (y + 12) * scale; row++) {
@@ -5669,6 +5683,7 @@ var SkinObject = class extends SceneObject {
5669
5683
  this.slim = false;
5670
5684
  this.detectedSlim = false;
5671
5685
  this.skinLoad = 0;
5686
+ this.capeLoad = 0;
5672
5687
  this.options = (0, import_ts_deepmerge7.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5673
5688
  this.slim = this.options.slim ?? false;
5674
5689
  }
@@ -5738,6 +5753,7 @@ var SkinObject = class extends SceneObject {
5738
5753
  this.detectedSlim = texture.slim;
5739
5754
  this.updateSlim(this.options.slim ?? this.detectedSlim);
5740
5755
  for (const part of SKIN_PARTS) {
5756
+ if (part === "cape" /* CAPE */) continue;
5741
5757
  const mesh = this.getMeshByName(part);
5742
5758
  if (mesh) {
5743
5759
  mesh.material = texture.material;
@@ -5745,7 +5761,34 @@ var SkinObject = class extends SceneObject {
5745
5761
  }
5746
5762
  this.notifyDirty();
5747
5763
  }
5748
- //TODO: cape
5764
+ /** Load a vanilla cape texture, or pass undefined to remove the cape. */
5765
+ async setCapeTexture(src) {
5766
+ const load = ++this.capeLoad;
5767
+ if (src === void 0) {
5768
+ const group = this.getGroupByName("cape" /* CAPE */);
5769
+ if (group) this.remove(group);
5770
+ this.notifyDirty();
5771
+ return;
5772
+ }
5773
+ const material = await SkinTextures.getCape(src);
5774
+ if (load !== this.capeLoad) return;
5775
+ const mesh = this.getMeshByName("cape" /* CAPE */);
5776
+ if (mesh) {
5777
+ mesh.material = material;
5778
+ } else {
5779
+ const group = this.createAndAddGroup("cape" /* CAPE */, 0, 24, 2);
5780
+ group.rotation.set(toRadians(-6), Math.PI, 0);
5781
+ const geometry = this._getBoxGeometryFromDimensions(
5782
+ classicSkinGeometries.cape,
5783
+ classicSkinTextureCoordinates.cape,
5784
+ [64, 32],
5785
+ [64, 32]
5786
+ );
5787
+ const cape = this.createAndAddMesh("cape" /* CAPE */, group, geometry, material);
5788
+ cape.position.set(0, -8, -0.5);
5789
+ }
5790
+ this.notifyDirty();
5791
+ }
5749
5792
  /** Select the arm model, or pass undefined to use texture detection. */
5750
5793
  setSlim(slim) {
5751
5794
  this.options.slim = slim;
@@ -5773,6 +5816,7 @@ var SkinObject = class extends SceneObject {
5773
5816
  }
5774
5817
  dispose() {
5775
5818
  this.skinLoad++;
5819
+ this.capeLoad++;
5776
5820
  super.dispose();
5777
5821
  }
5778
5822
  //TODO: layer toggles