minerender 2.1.0-alpha.7 → 2.1.0-alpha.8

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.
@@ -853,6 +853,7 @@ declare class EntityObject extends SceneObject {
853
853
  constructor(entity: EntityModel, options?: Partial<EntityObjectOptions>);
854
854
  init(): Promise<void>;
855
855
  dispose(): void;
856
+ private get textureKey();
856
857
  protected loadTextures(): Promise<void>;
857
858
  protected createMeshes(force?: boolean): void;
858
859
  protected applyTextures(): Promise<void>;
@@ -1512,6 +1513,9 @@ declare class Skins {
1512
1513
  }
1513
1514
 
1514
1515
  declare class TextureLoader {
1516
+ private static readonly failedTextures;
1517
+ /** Reports whether image loading failed, so cached textures can be retried. */
1518
+ static hasFailed(texture: Texture): boolean;
1515
1519
  protected static createTexture(): Texture;
1516
1520
  static loadInBackground(src: string, format?: PixelFormat, rotation?: number): Texture;
1517
1521
  static load(src: string, format?: PixelFormat, rotation?: number): Texture;
@@ -853,6 +853,7 @@ declare class EntityObject extends SceneObject {
853
853
  constructor(entity: EntityModel, options?: Partial<EntityObjectOptions>);
854
854
  init(): Promise<void>;
855
855
  dispose(): void;
856
+ private get textureKey();
856
857
  protected loadTextures(): Promise<void>;
857
858
  protected createMeshes(force?: boolean): void;
858
859
  protected applyTextures(): Promise<void>;
@@ -1512,6 +1513,9 @@ declare class Skins {
1512
1513
  }
1513
1514
 
1514
1515
  declare class TextureLoader {
1516
+ private static readonly failedTextures;
1517
+ /** Reports whether image loading failed, so cached textures can be retried. */
1518
+ static hasFailed(texture: Texture): boolean;
1515
1519
  protected static createTexture(): Texture;
1516
1520
  static loadInBackground(src: string, format?: PixelFormat, rotation?: number): Texture;
1517
1521
  static load(src: string, format?: PixelFormat, rotation?: number): Texture;
@@ -874,29 +874,41 @@ var ImageLoader = class _ImageLoader {
874
874
  static async loadAsync(src) {
875
875
  return new Promise((resolve, reject) => {
876
876
  const image = this._createImage();
877
- image.src = src;
878
877
  image.onload = () => resolve(image);
879
- image.onerror = (err) => reject(err);
878
+ image.onerror = (err) => reject(err instanceof Error ? err : new Error("Failed to decode image"));
879
+ image.src = src;
880
880
  });
881
881
  }
882
882
  static loadElement(src, onload, onerr) {
883
883
  const image = this._createImage();
884
- image.src = src;
885
884
  if (onload)
886
885
  image.onload = onload;
887
886
  if (onerr)
888
887
  image.onerror = onerr;
888
+ image.src = src;
889
889
  return image;
890
890
  }
891
891
  static async loadData(src) {
892
892
  console.debug(p2, "loadData", src);
893
- return await this.infoToData(await this.getInfo(src));
893
+ const image = await this.loadCanvasData(src);
894
+ return image.data.getImageData(0, 0, image.width, image.height);
894
895
  }
895
896
  static async loadCanvasData(src) {
896
- return await this.infoToCanvasData(await this.getInfo(src));
897
+ const keyStr = serializeImageKey({ src });
898
+ const info = this.getInfo(src);
899
+ const cached = Caching.rawImageCache.getIfPresent(keyStr);
900
+ try {
901
+ return await this.infoToCanvasData(await info);
902
+ } catch (err) {
903
+ if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
904
+ Caching.rawImageCache.invalidate(keyStr);
905
+ }
906
+ throw err;
907
+ }
897
908
  }
898
909
  static async infoToCanvasData(info) {
899
- const image = await _ImageLoader.loadAsync(info.src);
910
+ const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
911
+ const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
900
912
  const canvas = createCanvas2(info.width, info.height);
901
913
  const context = canvas.getContext("2d");
902
914
  context.drawImage(image, 0, 0);
@@ -907,11 +919,8 @@ var ImageLoader = class _ImageLoader {
907
919
  };
908
920
  }
909
921
  static async infoToData(info) {
910
- const image = await _ImageLoader.loadAsync(info.src);
911
- const canvas = createCanvas2(info.width, info.height);
912
- const context = canvas.getContext("2d");
913
- context.drawImage(image, 0, 0);
914
- return context.getImageData(0, 0, canvas.width, canvas.height);
922
+ const image = await this.infoToCanvasData(info);
923
+ return image.data.getImageData(0, 0, image.width, image.height);
915
924
  }
916
925
  static async getData(src) {
917
926
  const keyStr = serializeImageKey({ src });
@@ -926,13 +935,17 @@ var ImageLoader = class _ImageLoader {
926
935
  });
927
936
  }
928
937
  static async processResponse(response) {
929
- const src = response.config.url;
938
+ var _a;
939
+ const src = (_a = response.config) == null ? void 0 : _a.url;
930
940
  const data = import_buffer.Buffer.from(response.data);
931
941
  const { width, height, type } = Env.provider.imageSize(data);
942
+ if (!width || !height) {
943
+ throw new Error("Invalid or unsupported image dimensions");
944
+ }
932
945
  return {
933
946
  src,
934
- width: width || 0,
935
- height: height || 0,
947
+ width,
948
+ height,
936
949
  type,
937
950
  data
938
951
  };
@@ -949,15 +962,7 @@ var ImageLoader = class _ImageLoader {
949
962
  return Requests.genericRequest({
950
963
  url: src,
951
964
  responseType: "arraybuffer"
952
- }).then(this.processResponse).catch((err) => {
953
- return {
954
- src,
955
- width: 0,
956
- height: 0,
957
- type: void 0,
958
- data: import_buffer.Buffer.from([0])
959
- };
960
- });
965
+ }).then(this.processResponse);
961
966
  }
962
967
  static async getInfo(src) {
963
968
  const keyStr = serializeImageKey({ src });
@@ -979,16 +984,22 @@ var ImageLoader = class _ImageLoader {
979
984
 
980
985
  // src/texture/TextureLoader.ts
981
986
  var TextureLoader = class {
987
+ /** Reports whether image loading failed, so cached textures can be retried. */
988
+ static hasFailed(texture) {
989
+ return this.failedTextures.has(texture);
990
+ }
982
991
  static createTexture() {
983
992
  return new import_three4.Texture();
984
993
  }
985
994
  static loadInBackground(src, format = import_three4.RGBAFormat, rotation = 0) {
986
995
  const texture = this.createTexture();
987
996
  texture.colorSpace = THREE.SRGBColorSpace;
988
- const image = ImageLoader.loadElement(src);
989
- image.onload = function() {
997
+ const image = ImageLoader.loadElement(src, () => {
990
998
  texture.needsUpdate = true;
991
- };
999
+ }, (error) => {
1000
+ this.failedTextures.add(texture);
1001
+ console.warn("Failed to load texture", src, error);
1002
+ });
992
1003
  texture.image = image;
993
1004
  texture.format = format;
994
1005
  texture.rotation = rotation;
@@ -998,14 +1009,18 @@ var TextureLoader = class {
998
1009
  const texture = new import_three4.Texture();
999
1010
  texture.colorSpace = THREE.SRGBColorSpace;
1000
1011
  ImageLoader.getData(src).then((image) => {
1001
- texture.needsUpdate = true;
1002
1012
  texture.image = image;
1013
+ texture.needsUpdate = true;
1014
+ }).catch((error) => {
1015
+ this.failedTextures.add(texture);
1016
+ console.warn("Failed to load texture", src, error);
1003
1017
  });
1004
1018
  texture.format = format;
1005
1019
  texture.rotation = rotation;
1006
1020
  return Textures.initTextureProps(texture);
1007
1021
  }
1008
1022
  };
1023
+ TextureLoader.failedTextures = /* @__PURE__ */ new WeakSet();
1009
1024
 
1010
1025
  // src/texture/Textures.ts
1011
1026
  var THREE2 = __toESM(require("three"));
@@ -1029,6 +1044,10 @@ var Textures = class _Textures {
1029
1044
  }
1030
1045
  static getImage(key) {
1031
1046
  const keyStr = serializeTextureKey(key);
1047
+ const cached = Caching.textureCache.peek(keyStr);
1048
+ if (cached && TextureLoader.hasFailed(cached)) {
1049
+ Caching.textureCache.invalidate(keyStr);
1050
+ }
1032
1051
  return Caching.textureCache.get(keyStr, (k) => {
1033
1052
  return _Textures.createImage(key);
1034
1053
  });
@@ -1172,7 +1191,12 @@ var _Materials = class _Materials {
1172
1191
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1173
1192
  }
1174
1193
  static getImage(key) {
1194
+ var _a;
1175
1195
  const keyStr = serializeMaterialKey(key);
1196
+ const map = (_a = Caching.materialCache.peek(keyStr)) == null ? void 0 : _a.map;
1197
+ if (map && TextureLoader.hasFailed(map)) {
1198
+ Caching.materialCache.invalidate(keyStr);
1199
+ }
1176
1200
  return Caching.materialCache.get(keyStr, (k) => {
1177
1201
  return _Materials.createImage(key);
1178
1202
  });
@@ -1830,11 +1854,21 @@ var ModelTextures = class {
1830
1854
  return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
1831
1855
  }
1832
1856
  static async get(key) {
1833
- const asset = await this.preload(key);
1834
- if (asset) {
1835
- return ImageLoader.infoToCanvasData(asset);
1857
+ const keyStr = key.serialize();
1858
+ const pending = this.preload(key);
1859
+ const cached = Caching.textureAssetCache.getIfPresent(keyStr);
1860
+ try {
1861
+ const asset = await pending;
1862
+ if (asset) {
1863
+ return await ImageLoader.infoToCanvasData(asset);
1864
+ }
1865
+ return void 0;
1866
+ } catch (err) {
1867
+ if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
1868
+ Caching.textureAssetCache.invalidate(keyStr);
1869
+ }
1870
+ throw err;
1836
1871
  }
1837
- return void 0;
1838
1872
  }
1839
1873
  static async preload(key) {
1840
1874
  const keyStr = key.serialize();
@@ -3028,9 +3062,9 @@ var EntityObject = class extends SceneObject {
3028
3062
  dispose() {
3029
3063
  super.dispose();
3030
3064
  }
3031
- async loadTextures() {
3065
+ get textureKey() {
3032
3066
  var _a;
3033
- this.imageData = await ModelTextures.get(new AssetKey(
3067
+ return new AssetKey(
3034
3068
  ((_a = this.entity.key) == null ? void 0 : _a.namespace) ?? DEFAULT_NAMESPACE,
3035
3069
  this.entity.key.path,
3036
3070
  //TODO: texture may differ from entity name; most of them are in subdirectories for multiple variants etc.
@@ -3038,7 +3072,10 @@ var EntityObject = class extends SceneObject {
3038
3072
  "entity",
3039
3073
  "assets",
3040
3074
  ".png"
3041
- ));
3075
+ );
3076
+ }
3077
+ async loadTextures() {
3078
+ this.imageData = await ModelTextures.get(this.textureKey);
3042
3079
  }
3043
3080
  //TODO: abstract this, since extracted model data can be used for entities, blocks, players
3044
3081
  createMeshes(force = false) {
@@ -3071,21 +3108,23 @@ var EntityObject = class extends SceneObject {
3071
3108
  this.meshesCreated = true;
3072
3109
  }
3073
3110
  async applyTextures() {
3074
- var _a;
3075
- const textureAsset = await ModelTextures.preload(new AssetKey(
3076
- ((_a = this.entity.key) == null ? void 0 : _a.namespace) ?? DEFAULT_NAMESPACE,
3077
- this.entity.key.path,
3078
- "textures",
3079
- "entity",
3080
- "assets",
3081
- ".png"
3082
- ));
3083
- if (textureAsset) {
3084
- let mat = Materials.getImage({ texture: { src: textureAsset.src } });
3085
- this.iterateAllMeshes((mesh) => {
3086
- mesh.material = mat;
3087
- });
3088
- }
3111
+ const assetKeyStr = this.textureKey.serialize();
3112
+ const keyStr = `entity:${assetKeyStr}`;
3113
+ let mat = Caching.materialCache.getIfPresent(keyStr);
3114
+ if (!mat) {
3115
+ const pending = this.loadTextures();
3116
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
3117
+ await pending;
3118
+ if (!this.imageData) return;
3119
+ const canvas = this.imageData.data.canvas;
3120
+ const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
3121
+ mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
3122
+ this.imageData = void 0;
3123
+ }
3124
+ this.iterateAllMeshes((mesh) => {
3125
+ mesh.material = mat;
3126
+ });
3127
+ this.notifyDirty();
3089
3128
  }
3090
3129
  };
3091
3130
  EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, {});