minerender 2.1.0-alpha.7 → 2.1.0-alpha.9

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.
@@ -64,6 +64,7 @@ declare abstract class PersistentCache<B = unknown> {
64
64
  static open(name: string): PersistentCache;
65
65
  protected constructor(backing: B);
66
66
  abstract get<T>(key: string): Promise<T>;
67
+ /** Loads a missing entry and stores only non-nullish results. */
67
68
  getOrLoad<T>(key: string, loader: (key: string) => Promise<T>): Promise<T>;
68
69
  abstract put<T>(key: string, value: T): Promise<T>;
69
70
  abstract delete<T>(key: string): Promise<void>;
@@ -852,6 +853,7 @@ declare class EntityObject extends SceneObject {
852
853
  constructor(entity: EntityModel, options?: Partial<EntityObjectOptions>);
853
854
  init(): Promise<void>;
854
855
  dispose(): void;
856
+ private get textureKey();
855
857
  protected loadTextures(): Promise<void>;
856
858
  protected createMeshes(force?: boolean): void;
857
859
  protected applyTextures(): Promise<void>;
@@ -1212,6 +1214,10 @@ declare class Caching {
1212
1214
  static readonly mergedModelCache: AsyncLoadingCache<CacheKey, Model>;
1213
1215
  static readonly modelTextureAtlasCache: AsyncLoadingCache<CacheKey, TextureAtlas>;
1214
1216
  static readonly blockStateCache: AsyncLoadingCache<CacheKey, BlockState>;
1217
+ static readonly listAssetCache: AsyncLoadingCache<CacheKey, ListAsset>;
1218
+ static readonly defaultBlockStatesCache: AsyncLoadingCache<CacheKey, DefaultBlockStates>;
1219
+ static readonly entityModelsCache: AsyncLoadingCache<CacheKey, EntityModels>;
1220
+ private static createAssetCache;
1215
1221
  /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */
1216
1222
  private static get all();
1217
1223
  static clear(): void;
@@ -1511,6 +1517,9 @@ declare class Skins {
1511
1517
  }
1512
1518
 
1513
1519
  declare class TextureLoader {
1520
+ private static readonly failedTextures;
1521
+ /** Reports whether image loading failed, so cached textures can be retried. */
1522
+ static hasFailed(texture: Texture): boolean;
1514
1523
  protected static createTexture(): Texture;
1515
1524
  static loadInBackground(src: string, format?: PixelFormat, rotation?: number): Texture;
1516
1525
  static load(src: string, format?: PixelFormat, rotation?: number): Texture;
@@ -1620,7 +1629,7 @@ declare class BrowserCache extends PersistentCache<LocalForage> {
1620
1629
  *
1621
1630
  * `image-size` pulls in `fs`/`path` at module scope, so it can only live in the Node build.
1622
1631
  * Everything MineRender loads is a PNG in practice; GIF and JPEG are handled as a courtesy and
1623
- * anything else falls through to an empty result (callers treat that as 0x0).
1632
+ * anything else falls through to an empty result, which ImageLoader rejects.
1624
1633
  */
1625
1634
  declare function probeImageSize(data: Uint8Array): ImageSizeInfo;
1626
1635
 
@@ -64,6 +64,7 @@ declare abstract class PersistentCache<B = unknown> {
64
64
  static open(name: string): PersistentCache;
65
65
  protected constructor(backing: B);
66
66
  abstract get<T>(key: string): Promise<T>;
67
+ /** Loads a missing entry and stores only non-nullish results. */
67
68
  getOrLoad<T>(key: string, loader: (key: string) => Promise<T>): Promise<T>;
68
69
  abstract put<T>(key: string, value: T): Promise<T>;
69
70
  abstract delete<T>(key: string): Promise<void>;
@@ -852,6 +853,7 @@ declare class EntityObject extends SceneObject {
852
853
  constructor(entity: EntityModel, options?: Partial<EntityObjectOptions>);
853
854
  init(): Promise<void>;
854
855
  dispose(): void;
856
+ private get textureKey();
855
857
  protected loadTextures(): Promise<void>;
856
858
  protected createMeshes(force?: boolean): void;
857
859
  protected applyTextures(): Promise<void>;
@@ -1212,6 +1214,10 @@ declare class Caching {
1212
1214
  static readonly mergedModelCache: AsyncLoadingCache<CacheKey, Model>;
1213
1215
  static readonly modelTextureAtlasCache: AsyncLoadingCache<CacheKey, TextureAtlas>;
1214
1216
  static readonly blockStateCache: AsyncLoadingCache<CacheKey, BlockState>;
1217
+ static readonly listAssetCache: AsyncLoadingCache<CacheKey, ListAsset>;
1218
+ static readonly defaultBlockStatesCache: AsyncLoadingCache<CacheKey, DefaultBlockStates>;
1219
+ static readonly entityModelsCache: AsyncLoadingCache<CacheKey, EntityModels>;
1220
+ private static createAssetCache;
1215
1221
  /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */
1216
1222
  private static get all();
1217
1223
  static clear(): void;
@@ -1511,6 +1517,9 @@ declare class Skins {
1511
1517
  }
1512
1518
 
1513
1519
  declare class TextureLoader {
1520
+ private static readonly failedTextures;
1521
+ /** Reports whether image loading failed, so cached textures can be retried. */
1522
+ static hasFailed(texture: Texture): boolean;
1514
1523
  protected static createTexture(): Texture;
1515
1524
  static loadInBackground(src: string, format?: PixelFormat, rotation?: number): Texture;
1516
1525
  static load(src: string, format?: PixelFormat, rotation?: number): Texture;
@@ -1620,7 +1629,7 @@ declare class BrowserCache extends PersistentCache<LocalForage> {
1620
1629
  *
1621
1630
  * `image-size` pulls in `fs`/`path` at module scope, so it can only live in the Node build.
1622
1631
  * Everything MineRender loads is a PNG in practice; GIF and JPEG are handled as a courtesy and
1623
- * anything else falls through to an empty result (callers treat that as 0x0).
1632
+ * anything else falls through to an empty result, which ImageLoader rejects.
1624
1633
  */
1625
1634
  declare function probeImageSize(data: Uint8Array): ImageSizeInfo;
1626
1635
 
@@ -28,14 +28,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  mod
29
29
  ));
30
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
- var __decorateClass = (decorators, target, key, kind) => {
32
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
33
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
34
- if (decorator = decorators[i])
35
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
36
- if (kind && result) __defProp(target, key, result);
37
- return result;
38
- };
39
31
 
40
32
  // ../../node_modules/ts-deepmerge/dist/index.js
41
33
  var require_dist = __commonJS({
@@ -308,11 +300,14 @@ var _PersistentCache = class _PersistentCache {
308
300
  static open(name) {
309
301
  return Env.provider.openCache(name, _PersistentCache.VERSION);
310
302
  }
303
+ /** Loads a missing entry and stores only non-nullish results. */
311
304
  async getOrLoad(key, loader) {
312
305
  let v = await this.get(key);
313
306
  if (typeof v === "undefined" || v === null) {
314
307
  v = await loader(key);
315
- await this.put(key, v);
308
+ if (typeof v !== "undefined" && v !== null) {
309
+ await this.put(key, v);
310
+ }
316
311
  }
317
312
  return v;
318
313
  }
@@ -548,7 +543,10 @@ function serializeMeshKey(key) {
548
543
  // src/cache/Caching.ts
549
544
  var import_loading_cache = require("@inventivetalent/loading-cache");
550
545
  var import_time = require("@inventivetalent/time");
551
- var Caching = class {
546
+ var _Caching = class _Caching {
547
+ static createAssetCache() {
548
+ return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
549
+ }
552
550
  /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */
553
551
  static get all() {
554
552
  return [
@@ -565,7 +563,10 @@ var Caching = class {
565
563
  this.rawModelCache,
566
564
  this.mergedModelCache,
567
565
  this.modelTextureAtlasCache,
568
- this.blockStateCache
566
+ this.blockStateCache,
567
+ this.listAssetCache,
568
+ this.defaultBlockStatesCache,
569
+ this.entityModelsCache
569
570
  ];
570
571
  }
571
572
  static clear() {
@@ -591,20 +592,24 @@ var Caching = class {
591
592
  };
592
593
  }
593
594
  };
594
- Caching.rawImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(5)).buildAsync();
595
- Caching.imageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
596
- Caching.canvasImageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
597
- Caching.wrappedImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
598
- Caching.boxGeometryCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(20)).build();
599
- Caching.textureCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
600
- Caching.materialCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
601
- Caching.boxMeshCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
602
- Caching.textureAssetCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
603
- Caching.textureMetaCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
604
- Caching.rawModelCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
605
- Caching.mergedModelCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
606
- Caching.modelTextureAtlasCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
607
- Caching.blockStateCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
595
+ _Caching.rawImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(5)).buildAsync();
596
+ _Caching.imageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
597
+ _Caching.canvasImageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
598
+ _Caching.wrappedImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
599
+ _Caching.boxGeometryCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(20)).build();
600
+ _Caching.textureCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
601
+ _Caching.materialCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
602
+ _Caching.boxMeshCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).build();
603
+ _Caching.textureAssetCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
604
+ _Caching.textureMetaCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
605
+ _Caching.rawModelCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
606
+ _Caching.mergedModelCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
607
+ _Caching.modelTextureAtlasCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
608
+ _Caching.blockStateCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
609
+ _Caching.listAssetCache = _Caching.createAssetCache();
610
+ _Caching.defaultBlockStatesCache = _Caching.createAssetCache();
611
+ _Caching.entityModelsCache = _Caching.createAssetCache();
612
+ var Caching = _Caching;
608
613
 
609
614
  // src/Geometries.ts
610
615
  var Geometries = class _Geometries {
@@ -912,29 +917,41 @@ var ImageLoader = class _ImageLoader {
912
917
  static async loadAsync(src) {
913
918
  return new Promise((resolve, reject) => {
914
919
  const image = this._createImage();
915
- image.src = src;
916
920
  image.onload = () => resolve(image);
917
- image.onerror = (err) => reject(err);
921
+ image.onerror = (err) => reject(err instanceof Error ? err : new Error("Failed to decode image"));
922
+ image.src = src;
918
923
  });
919
924
  }
920
925
  static loadElement(src, onload, onerr) {
921
926
  const image = this._createImage();
922
- image.src = src;
923
927
  if (onload)
924
928
  image.onload = onload;
925
929
  if (onerr)
926
930
  image.onerror = onerr;
931
+ image.src = src;
927
932
  return image;
928
933
  }
929
934
  static async loadData(src) {
930
935
  console.debug(p2, "loadData", src);
931
- return await this.infoToData(await this.getInfo(src));
936
+ const image = await this.loadCanvasData(src);
937
+ return image.data.getImageData(0, 0, image.width, image.height);
932
938
  }
933
939
  static async loadCanvasData(src) {
934
- return await this.infoToCanvasData(await this.getInfo(src));
940
+ const keyStr = serializeImageKey({ src });
941
+ const info = this.getInfo(src);
942
+ const cached = Caching.rawImageCache.getIfPresent(keyStr);
943
+ try {
944
+ return await this.infoToCanvasData(await info);
945
+ } catch (err) {
946
+ if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
947
+ Caching.rawImageCache.invalidate(keyStr);
948
+ }
949
+ throw err;
950
+ }
935
951
  }
936
952
  static async infoToCanvasData(info) {
937
- const image = await _ImageLoader.loadAsync(info.src);
953
+ const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
954
+ const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
938
955
  const canvas = createCanvas(info.width, info.height);
939
956
  const context = canvas.getContext("2d");
940
957
  context.drawImage(image, 0, 0);
@@ -945,11 +962,8 @@ var ImageLoader = class _ImageLoader {
945
962
  };
946
963
  }
947
964
  static async infoToData(info) {
948
- const image = await _ImageLoader.loadAsync(info.src);
949
- const canvas = createCanvas(info.width, info.height);
950
- const context = canvas.getContext("2d");
951
- context.drawImage(image, 0, 0);
952
- return context.getImageData(0, 0, canvas.width, canvas.height);
965
+ const image = await this.infoToCanvasData(info);
966
+ return image.data.getImageData(0, 0, image.width, image.height);
953
967
  }
954
968
  static async getData(src) {
955
969
  const keyStr = serializeImageKey({ src });
@@ -964,13 +978,16 @@ var ImageLoader = class _ImageLoader {
964
978
  });
965
979
  }
966
980
  static async processResponse(response) {
967
- const src = response.config.url;
981
+ const src = response.config?.url;
968
982
  const data = import_buffer.Buffer.from(response.data);
969
983
  const { width, height, type } = Env.provider.imageSize(data);
984
+ if (!width || !height) {
985
+ throw new Error("Invalid or unsupported image dimensions");
986
+ }
970
987
  return {
971
988
  src,
972
- width: width || 0,
973
- height: height || 0,
989
+ width,
990
+ height,
974
991
  type,
975
992
  data
976
993
  };
@@ -987,15 +1004,7 @@ var ImageLoader = class _ImageLoader {
987
1004
  return Requests.genericRequest({
988
1005
  url: src,
989
1006
  responseType: "arraybuffer"
990
- }).then(this.processResponse).catch((err) => {
991
- return {
992
- src,
993
- width: 0,
994
- height: 0,
995
- type: void 0,
996
- data: import_buffer.Buffer.from([0])
997
- };
998
- });
1007
+ }).then(this.processResponse);
999
1008
  }
1000
1009
  static async getInfo(src) {
1001
1010
  const keyStr = serializeImageKey({ src });
@@ -1017,16 +1026,22 @@ var ImageLoader = class _ImageLoader {
1017
1026
 
1018
1027
  // src/texture/TextureLoader.ts
1019
1028
  var TextureLoader = class {
1029
+ /** Reports whether image loading failed, so cached textures can be retried. */
1030
+ static hasFailed(texture) {
1031
+ return this.failedTextures.has(texture);
1032
+ }
1020
1033
  static createTexture() {
1021
1034
  return new import_three4.Texture();
1022
1035
  }
1023
1036
  static loadInBackground(src, format = import_three4.RGBAFormat, rotation = 0) {
1024
1037
  const texture = this.createTexture();
1025
1038
  texture.colorSpace = THREE.SRGBColorSpace;
1026
- const image = ImageLoader.loadElement(src);
1027
- image.onload = function() {
1039
+ const image = ImageLoader.loadElement(src, () => {
1028
1040
  texture.needsUpdate = true;
1029
- };
1041
+ }, (error) => {
1042
+ this.failedTextures.add(texture);
1043
+ console.warn("Failed to load texture", src, error);
1044
+ });
1030
1045
  texture.image = image;
1031
1046
  texture.format = format;
1032
1047
  texture.rotation = rotation;
@@ -1036,14 +1051,18 @@ var TextureLoader = class {
1036
1051
  const texture = new import_three4.Texture();
1037
1052
  texture.colorSpace = THREE.SRGBColorSpace;
1038
1053
  ImageLoader.getData(src).then((image) => {
1039
- texture.needsUpdate = true;
1040
1054
  texture.image = image;
1055
+ texture.needsUpdate = true;
1056
+ }).catch((error) => {
1057
+ this.failedTextures.add(texture);
1058
+ console.warn("Failed to load texture", src, error);
1041
1059
  });
1042
1060
  texture.format = format;
1043
1061
  texture.rotation = rotation;
1044
1062
  return Textures.initTextureProps(texture);
1045
1063
  }
1046
1064
  };
1065
+ TextureLoader.failedTextures = /* @__PURE__ */ new WeakSet();
1047
1066
 
1048
1067
  // src/texture/Textures.ts
1049
1068
  var THREE2 = __toESM(require("three"));
@@ -1067,6 +1086,10 @@ var Textures = class _Textures {
1067
1086
  }
1068
1087
  static getImage(key) {
1069
1088
  const keyStr = serializeTextureKey(key);
1089
+ const cached = Caching.textureCache.peek(keyStr);
1090
+ if (cached && TextureLoader.hasFailed(cached)) {
1091
+ Caching.textureCache.invalidate(keyStr);
1092
+ }
1070
1093
  return Caching.textureCache.get(keyStr, (k) => {
1071
1094
  return _Textures.createImage(key);
1072
1095
  });
@@ -1211,6 +1234,10 @@ var _Materials = class _Materials {
1211
1234
  }
1212
1235
  static getImage(key) {
1213
1236
  const keyStr = serializeMaterialKey(key);
1237
+ const map = Caching.materialCache.peek(keyStr)?.map;
1238
+ if (map && TextureLoader.hasFailed(map)) {
1239
+ Caching.materialCache.invalidate(keyStr);
1240
+ }
1214
1241
  return Caching.materialCache.get(keyStr, (k) => {
1215
1242
  return _Materials.createImage(key);
1216
1243
  });
@@ -1364,9 +1391,6 @@ var ModelMerger = class {
1364
1391
  }
1365
1392
  };
1366
1393
 
1367
- // src/assets/Models.ts
1368
- var import_typescript_memoize = require("typescript-memoize");
1369
-
1370
1394
  // src/assets/source/parser/AssetParsers.ts
1371
1395
  var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1372
1396
  AssetParser2["MODEL"] = "model";
@@ -1380,14 +1404,14 @@ var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1380
1404
  })(AssetParser || {});
1381
1405
 
1382
1406
  // src/assets/Models.ts
1383
- var _Models = class _Models {
1407
+ var Models = class _Models {
1384
1408
  // opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
1385
1409
  // the library, and would require an EnvProvider before any entry has had a chance to register
1386
1410
  static get PERSISTENT_CACHE() {
1387
1411
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
1388
1412
  }
1389
1413
  static async getItemList() {
1390
- return AssetLoader.get(new AssetKey(
1414
+ const key = new AssetKey(
1391
1415
  DEFAULT_NAMESPACE,
1392
1416
  "_list",
1393
1417
  "models",
@@ -1395,7 +1419,10 @@ var _Models = class _Models {
1395
1419
  "assets",
1396
1420
  ".json",
1397
1421
  DEFAULT_ROOT
1398
- ), "list" /* LIST */).then((r) => r?.files ?? []);
1422
+ );
1423
+ return Caching.listAssetCache.get(key.serialize(), () => {
1424
+ return AssetLoader.get(key, "list" /* LIST */);
1425
+ }).then((r) => r?.files ?? []);
1399
1426
  }
1400
1427
  static async loadAndMerge(key) {
1401
1428
  const model = await this.getRaw(key);
@@ -1442,19 +1469,15 @@ var _Models = class _Models {
1442
1469
  return this.PERSISTENT_CACHE.clear();
1443
1470
  }
1444
1471
  };
1445
- __decorateClass([
1446
- (0, import_typescript_memoize.Memoize)()
1447
- ], _Models, "getItemList", 1);
1448
- var Models = _Models;
1449
1472
 
1450
1473
  // src/assets/BlockStates.ts
1451
- var import_typescript_memoize2 = require("typescript-memoize");
1452
1474
  var BlockStates = class {
1453
1475
  static get PERSISTENT_CACHE() {
1454
1476
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
1455
1477
  }
1478
+ // BlockState names are hardcoded
1456
1479
  static async getList() {
1457
- return AssetLoader.get(new AssetKey(
1480
+ const key = new AssetKey(
1458
1481
  DEFAULT_NAMESPACE,
1459
1482
  "_list",
1460
1483
  "blockstates",
@@ -1462,11 +1485,16 @@ var BlockStates = class {
1462
1485
  "assets",
1463
1486
  ".json",
1464
1487
  DEFAULT_ROOT
1465
- ), "list" /* LIST */).then((r) => r?.files ?? []);
1488
+ );
1489
+ return Caching.listAssetCache.get(key.serialize(), () => {
1490
+ return AssetLoader.get(key, "list" /* LIST */);
1491
+ }).then((r) => r?.files ?? []);
1466
1492
  }
1467
1493
  static async getDefaultStates() {
1468
1494
  const key = AssetKey.parse("blockstates", "minerender:defaultBlockStates");
1469
- return AssetLoader.get(key, "json" /* JSON */);
1495
+ return Caching.defaultBlockStatesCache.get(key.serialize(), () => {
1496
+ return AssetLoader.get(key, "json" /* JSON */);
1497
+ });
1470
1498
  }
1471
1499
  static async getDefaultState(key) {
1472
1500
  const defaultStates = await this.getDefaultStates();
@@ -1505,12 +1533,6 @@ var BlockStates = class {
1505
1533
  await this.PERSISTENT_CACHE.clear();
1506
1534
  }
1507
1535
  };
1508
- __decorateClass([
1509
- (0, import_typescript_memoize2.Memoize)()
1510
- ], BlockStates, "getList", 1);
1511
- __decorateClass([
1512
- (0, import_typescript_memoize2.Memoize)()
1513
- ], BlockStates, "getDefaultStates", 1);
1514
1536
  var DefaultBlockStates = class {
1515
1537
  };
1516
1538
 
@@ -1868,11 +1890,21 @@ var ModelTextures = class {
1868
1890
  return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
1869
1891
  }
1870
1892
  static async get(key) {
1871
- const asset = await this.preload(key);
1872
- if (asset) {
1873
- return ImageLoader.infoToCanvasData(asset);
1893
+ const keyStr = key.serialize();
1894
+ const pending = this.preload(key);
1895
+ const cached = Caching.textureAssetCache.getIfPresent(keyStr);
1896
+ try {
1897
+ const asset = await pending;
1898
+ if (asset) {
1899
+ return await ImageLoader.infoToCanvasData(asset);
1900
+ }
1901
+ return void 0;
1902
+ } catch (err) {
1903
+ if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
1904
+ Caching.textureAssetCache.invalidate(keyStr);
1905
+ }
1906
+ throw err;
1874
1907
  }
1875
- return void 0;
1876
1908
  }
1877
1909
  static async preload(key) {
1878
1910
  const keyStr = key.serialize();
@@ -2550,17 +2582,20 @@ function shutdown() {
2550
2582
  }
2551
2583
 
2552
2584
  // src/assets/Entities.ts
2553
- var import_typescript_memoize3 = require("typescript-memoize");
2554
2585
  var Entities = class {
2555
2586
  static async getBlockEntityModels() {
2556
2587
  const key = AssetKey.parse("models", "minerender:blockEntityModels");
2557
2588
  console.log(key);
2558
- return AssetLoader.get(key, "json" /* JSON */);
2589
+ return Caching.entityModelsCache.get(key.serialize(), () => {
2590
+ return AssetLoader.get(key, "json" /* JSON */);
2591
+ });
2559
2592
  }
2560
2593
  static async getEntityModels() {
2561
2594
  const key = AssetKey.parse("models", "minerender:entityModels");
2562
2595
  console.log(key);
2563
- return AssetLoader.get(key, "json" /* JSON */);
2596
+ return Caching.entityModelsCache.get(key.serialize(), () => {
2597
+ return AssetLoader.get(key, "json" /* JSON */);
2598
+ });
2564
2599
  }
2565
2600
  // BlockEntity names are hardcoded
2566
2601
  static async getBlockList() {
@@ -2606,12 +2641,6 @@ var Entities = class {
2606
2641
  };
2607
2642
  }
2608
2643
  };
2609
- __decorateClass([
2610
- (0, import_typescript_memoize3.Memoize)()
2611
- ], Entities, "getBlockEntityModels", 1);
2612
- __decorateClass([
2613
- (0, import_typescript_memoize3.Memoize)()
2614
- ], Entities, "getEntityModels", 1);
2615
2644
  var EntityModels = class {
2616
2645
  };
2617
2646
  var BlockEntityModels = class {
@@ -3063,8 +3092,8 @@ var EntityObject = class extends SceneObject {
3063
3092
  dispose() {
3064
3093
  super.dispose();
3065
3094
  }
3066
- async loadTextures() {
3067
- this.imageData = await ModelTextures.get(new AssetKey(
3095
+ get textureKey() {
3096
+ return new AssetKey(
3068
3097
  this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
3069
3098
  this.entity.key.path,
3070
3099
  //TODO: texture may differ from entity name; most of them are in subdirectories for multiple variants etc.
@@ -3072,7 +3101,10 @@ var EntityObject = class extends SceneObject {
3072
3101
  "entity",
3073
3102
  "assets",
3074
3103
  ".png"
3075
- ));
3104
+ );
3105
+ }
3106
+ async loadTextures() {
3107
+ this.imageData = await ModelTextures.get(this.textureKey);
3076
3108
  }
3077
3109
  //TODO: abstract this, since extracted model data can be used for entities, blocks, players
3078
3110
  createMeshes(force = false) {
@@ -3105,20 +3137,23 @@ var EntityObject = class extends SceneObject {
3105
3137
  this.meshesCreated = true;
3106
3138
  }
3107
3139
  async applyTextures() {
3108
- const textureAsset = await ModelTextures.preload(new AssetKey(
3109
- this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
3110
- this.entity.key.path,
3111
- "textures",
3112
- "entity",
3113
- "assets",
3114
- ".png"
3115
- ));
3116
- if (textureAsset) {
3117
- let mat = Materials.getImage({ texture: { src: textureAsset.src } });
3118
- this.iterateAllMeshes((mesh) => {
3119
- mesh.material = mat;
3120
- });
3121
- }
3140
+ const assetKeyStr = this.textureKey.serialize();
3141
+ const keyStr = `entity:${assetKeyStr}`;
3142
+ let mat = Caching.materialCache.getIfPresent(keyStr);
3143
+ if (!mat) {
3144
+ const pending = this.loadTextures();
3145
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
3146
+ await pending;
3147
+ if (!this.imageData) return;
3148
+ const canvas = this.imageData.data.canvas;
3149
+ const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
3150
+ mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
3151
+ this.imageData = void 0;
3152
+ }
3153
+ this.iterateAllMeshes((mesh) => {
3154
+ mesh.material = mat;
3155
+ });
3156
+ this.notifyDirty();
3122
3157
  }
3123
3158
  };
3124
3159
  EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, {});