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.
@@ -758,29 +758,41 @@ var ImageLoader = class _ImageLoader {
758
758
  static async loadAsync(src) {
759
759
  return new Promise((resolve, reject) => {
760
760
  const image = this._createImage();
761
- image.src = src;
762
761
  image.onload = () => resolve(image);
763
- image.onerror = (err) => reject(err);
762
+ image.onerror = (err) => reject(err instanceof Error ? err : new Error("Failed to decode image"));
763
+ image.src = src;
764
764
  });
765
765
  }
766
766
  static loadElement(src, onload, onerr) {
767
767
  const image = this._createImage();
768
- image.src = src;
769
768
  if (onload)
770
769
  image.onload = onload;
771
770
  if (onerr)
772
771
  image.onerror = onerr;
772
+ image.src = src;
773
773
  return image;
774
774
  }
775
775
  static async loadData(src) {
776
776
  console.debug(p2, "loadData", src);
777
- return await this.infoToData(await this.getInfo(src));
777
+ const image = await this.loadCanvasData(src);
778
+ return image.data.getImageData(0, 0, image.width, image.height);
778
779
  }
779
780
  static async loadCanvasData(src) {
780
- return await this.infoToCanvasData(await this.getInfo(src));
781
+ const keyStr = serializeImageKey({ src });
782
+ const info = this.getInfo(src);
783
+ const cached = Caching.rawImageCache.getIfPresent(keyStr);
784
+ try {
785
+ return await this.infoToCanvasData(await info);
786
+ } catch (err) {
787
+ if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
788
+ Caching.rawImageCache.invalidate(keyStr);
789
+ }
790
+ throw err;
791
+ }
781
792
  }
782
793
  static async infoToCanvasData(info) {
783
- const image = await _ImageLoader.loadAsync(info.src);
794
+ const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
795
+ const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
784
796
  const canvas = createCanvas(info.width, info.height);
785
797
  const context = canvas.getContext("2d");
786
798
  context.drawImage(image, 0, 0);
@@ -791,11 +803,8 @@ var ImageLoader = class _ImageLoader {
791
803
  };
792
804
  }
793
805
  static async infoToData(info) {
794
- const image = await _ImageLoader.loadAsync(info.src);
795
- const canvas = createCanvas(info.width, info.height);
796
- const context = canvas.getContext("2d");
797
- context.drawImage(image, 0, 0);
798
- return context.getImageData(0, 0, canvas.width, canvas.height);
806
+ const image = await this.infoToCanvasData(info);
807
+ return image.data.getImageData(0, 0, image.width, image.height);
799
808
  }
800
809
  static async getData(src) {
801
810
  const keyStr = serializeImageKey({ src });
@@ -810,13 +819,16 @@ var ImageLoader = class _ImageLoader {
810
819
  });
811
820
  }
812
821
  static async processResponse(response) {
813
- const src = response.config.url;
822
+ const src = response.config?.url;
814
823
  const data = Buffer2.from(response.data);
815
824
  const { width, height, type } = Env.provider.imageSize(data);
825
+ if (!width || !height) {
826
+ throw new Error("Invalid or unsupported image dimensions");
827
+ }
816
828
  return {
817
829
  src,
818
- width: width || 0,
819
- height: height || 0,
830
+ width,
831
+ height,
820
832
  type,
821
833
  data
822
834
  };
@@ -833,15 +845,7 @@ var ImageLoader = class _ImageLoader {
833
845
  return Requests.genericRequest({
834
846
  url: src,
835
847
  responseType: "arraybuffer"
836
- }).then(this.processResponse).catch((err) => {
837
- return {
838
- src,
839
- width: 0,
840
- height: 0,
841
- type: void 0,
842
- data: Buffer2.from([0])
843
- };
844
- });
848
+ }).then(this.processResponse);
845
849
  }
846
850
  static async getInfo(src) {
847
851
  const keyStr = serializeImageKey({ src });
@@ -863,16 +867,22 @@ var ImageLoader = class _ImageLoader {
863
867
 
864
868
  // src/texture/TextureLoader.ts
865
869
  var TextureLoader = class {
870
+ /** Reports whether image loading failed, so cached textures can be retried. */
871
+ static hasFailed(texture) {
872
+ return this.failedTextures.has(texture);
873
+ }
866
874
  static createTexture() {
867
875
  return new Texture();
868
876
  }
869
877
  static loadInBackground(src, format = RGBAFormat, rotation = 0) {
870
878
  const texture = this.createTexture();
871
879
  texture.colorSpace = THREE.SRGBColorSpace;
872
- const image = ImageLoader.loadElement(src);
873
- image.onload = function() {
880
+ const image = ImageLoader.loadElement(src, () => {
874
881
  texture.needsUpdate = true;
875
- };
882
+ }, (error) => {
883
+ this.failedTextures.add(texture);
884
+ console.warn("Failed to load texture", src, error);
885
+ });
876
886
  texture.image = image;
877
887
  texture.format = format;
878
888
  texture.rotation = rotation;
@@ -882,14 +892,18 @@ var TextureLoader = class {
882
892
  const texture = new Texture();
883
893
  texture.colorSpace = THREE.SRGBColorSpace;
884
894
  ImageLoader.getData(src).then((image) => {
885
- texture.needsUpdate = true;
886
895
  texture.image = image;
896
+ texture.needsUpdate = true;
897
+ }).catch((error) => {
898
+ this.failedTextures.add(texture);
899
+ console.warn("Failed to load texture", src, error);
887
900
  });
888
901
  texture.format = format;
889
902
  texture.rotation = rotation;
890
903
  return Textures.initTextureProps(texture);
891
904
  }
892
905
  };
906
+ TextureLoader.failedTextures = /* @__PURE__ */ new WeakSet();
893
907
 
894
908
  // src/texture/Textures.ts
895
909
  import * as THREE2 from "three";
@@ -913,6 +927,10 @@ var Textures = class _Textures {
913
927
  }
914
928
  static getImage(key) {
915
929
  const keyStr = serializeTextureKey(key);
930
+ const cached = Caching.textureCache.peek(keyStr);
931
+ if (cached && TextureLoader.hasFailed(cached)) {
932
+ Caching.textureCache.invalidate(keyStr);
933
+ }
916
934
  return Caching.textureCache.get(keyStr, (k) => {
917
935
  return _Textures.createImage(key);
918
936
  });
@@ -1057,6 +1075,10 @@ var _Materials = class _Materials {
1057
1075
  }
1058
1076
  static getImage(key) {
1059
1077
  const keyStr = serializeMaterialKey(key);
1078
+ const map = Caching.materialCache.peek(keyStr)?.map;
1079
+ if (map && TextureLoader.hasFailed(map)) {
1080
+ Caching.materialCache.invalidate(keyStr);
1081
+ }
1060
1082
  return Caching.materialCache.get(keyStr, (k) => {
1061
1083
  return _Materials.createImage(key);
1062
1084
  });
@@ -1714,11 +1736,21 @@ var ModelTextures = class {
1714
1736
  return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
1715
1737
  }
1716
1738
  static async get(key) {
1717
- const asset = await this.preload(key);
1718
- if (asset) {
1719
- return ImageLoader.infoToCanvasData(asset);
1739
+ const keyStr = key.serialize();
1740
+ const pending = this.preload(key);
1741
+ const cached = Caching.textureAssetCache.getIfPresent(keyStr);
1742
+ try {
1743
+ const asset = await pending;
1744
+ if (asset) {
1745
+ return await ImageLoader.infoToCanvasData(asset);
1746
+ }
1747
+ return void 0;
1748
+ } catch (err) {
1749
+ if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
1750
+ Caching.textureAssetCache.invalidate(keyStr);
1751
+ }
1752
+ throw err;
1720
1753
  }
1721
- return void 0;
1722
1754
  }
1723
1755
  static async preload(key) {
1724
1756
  const keyStr = key.serialize();
@@ -2909,8 +2941,8 @@ var EntityObject = class extends SceneObject {
2909
2941
  dispose() {
2910
2942
  super.dispose();
2911
2943
  }
2912
- async loadTextures() {
2913
- this.imageData = await ModelTextures.get(new AssetKey(
2944
+ get textureKey() {
2945
+ return new AssetKey(
2914
2946
  this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
2915
2947
  this.entity.key.path,
2916
2948
  //TODO: texture may differ from entity name; most of them are in subdirectories for multiple variants etc.
@@ -2918,7 +2950,10 @@ var EntityObject = class extends SceneObject {
2918
2950
  "entity",
2919
2951
  "assets",
2920
2952
  ".png"
2921
- ));
2953
+ );
2954
+ }
2955
+ async loadTextures() {
2956
+ this.imageData = await ModelTextures.get(this.textureKey);
2922
2957
  }
2923
2958
  //TODO: abstract this, since extracted model data can be used for entities, blocks, players
2924
2959
  createMeshes(force = false) {
@@ -2951,20 +2986,23 @@ var EntityObject = class extends SceneObject {
2951
2986
  this.meshesCreated = true;
2952
2987
  }
2953
2988
  async applyTextures() {
2954
- const textureAsset = await ModelTextures.preload(new AssetKey(
2955
- this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
2956
- this.entity.key.path,
2957
- "textures",
2958
- "entity",
2959
- "assets",
2960
- ".png"
2961
- ));
2962
- if (textureAsset) {
2963
- let mat = Materials.getImage({ texture: { src: textureAsset.src } });
2964
- this.iterateAllMeshes((mesh) => {
2965
- mesh.material = mat;
2966
- });
2967
- }
2989
+ const assetKeyStr = this.textureKey.serialize();
2990
+ const keyStr = `entity:${assetKeyStr}`;
2991
+ let mat = Caching.materialCache.getIfPresent(keyStr);
2992
+ if (!mat) {
2993
+ const pending = this.loadTextures();
2994
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
2995
+ await pending;
2996
+ if (!this.imageData) return;
2997
+ const canvas = this.imageData.data.canvas;
2998
+ const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
2999
+ mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
3000
+ this.imageData = void 0;
3001
+ }
3002
+ this.iterateAllMeshes((mesh) => {
3003
+ mesh.material = mat;
3004
+ });
3005
+ this.notifyDirty();
2968
3006
  }
2969
3007
  };
2970
3008
  EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, {});