Browse Source

image loading error handling and other tweaks

Alexander Rose 2 years ago
parent
commit
08d736ecdc

+ 1 - 1
src/extensions/backgrounds/typings.d.ts

@@ -5,6 +5,6 @@
  */
 
 declare module '*.jpg' {
-    const value: any;
+    const value: string;
     export = value;
 }

+ 18 - 15
src/mol-canvas3d/passes/background.ts

@@ -137,20 +137,20 @@ export class BackgroundPass {
         const f = props.faces.params;
         if (!f.nx || !f.ny || !f.nz || !f.px || !f.py || !f.pz) {
             this.clearSkybox();
-            if (onload) onload(false);
+            onload?.(false);
             return;
         }
         if (!this.skybox || !tf || !areSkyboxTexturePropsEqual(props.faces, this.skybox.props.faces)) {
             this.clearSkybox();
-            const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, () => {
-                if (this.skybox) this.skybox.loaded = true;
-                if (onload) onload(true);
+            const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, errored => {
+                if (this.skybox) this.skybox.loaded = !errored;
+                onload?.(true);
             });
             this.skybox = { texture, props: { ...props }, assets, loaded: false };
             ValueCell.update(this.renderable.values.tSkybox, texture);
             this.renderable.update();
         } else {
-            if (onload) onload(false);
+            onload?.(false);
         }
         if (!this.skybox) return;
 
@@ -188,20 +188,20 @@ export class BackgroundPass {
     private updateImage(props: ImageProps, onload?: (loaded: boolean) => void) {
         if (!props.source.params) {
             this.clearImage();
-            if (onload) onload(false);
+            onload?.(false);
             return;
         }
         if (!this.image || !this.image.props.source.params || !areImageTexturePropsEqual(props.source, this.image.props.source)) {
             this.clearImage();
-            const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, () => {
-                if (this.image) this.image.loaded = true;
-                if (onload) onload(true);
+            const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, errored => {
+                if (this.image) this.image.loaded = !errored;
+                onload?.(true);
             });
             this.image = { texture, props: { ...props }, asset, loaded: false };
             ValueCell.update(this.renderable.values.tImage, texture);
             this.renderable.update();
         } else {
-            if (onload) onload(false);
+            onload?.(false);
         }
         if (!this.image) return;
 
@@ -248,7 +248,7 @@ export class BackgroundPass {
         if (props.variant.name === 'off') {
             this.clearSkybox();
             this.clearImage();
-            if (onload) onload(false);
+            onload?.(false);
             return;
         } else if (props.variant.name === 'skybox') {
             this.clearImage();
@@ -260,12 +260,12 @@ export class BackgroundPass {
             this.clearSkybox();
             this.clearImage();
             this.updateGradient(props.variant.params.topColor, props.variant.params.bottomColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
-            if (onload) onload(false);
+            onload?.(false);
         } else if (props.variant.name === 'radialGradient') {
             this.clearSkybox();
             this.clearImage();
             this.updateGradient(props.variant.params.centerColor, props.variant.params.edgeColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
-            if (onload) onload(false);
+            onload?.(false);
         }
 
         const { x, y, width, height } = camera.viewport;
@@ -363,7 +363,7 @@ function areSkyboxTexturePropsEqual(facesA: SkyboxProps['faces'], facesB: Skybox
     return getSkyboxHash(facesA) === getSkyboxHash(facesB);
 }
 
-function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: () => void): { texture: Texture, assets: Asset[] } {
+function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: (errored?: boolean) => void): { texture: Texture, assets: Asset[] } {
     const cubeAssets = getCubeAssets(assetManager, faces);
     const cubeFaces = getCubeFaces(assetManager, cubeAssets);
     const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz];
@@ -387,13 +387,16 @@ function areImageTexturePropsEqual(sourceA: ImageProps['source'], sourceB: Image
     return getImageHash(sourceA) === getImageHash(sourceB);
 }
 
-function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: () => void): { texture: Texture, asset: Asset } {
+function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: (errored?: boolean) => void): { texture: Texture, asset: Asset } {
     const texture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
     const img = new Image();
     img.onload = () => {
         texture.load(img);
         onload?.();
     };
+    img.onerror = () => {
+        onload?.(true);
+    };
     const asset = source.name === 'url'
         ? Asset.getUrlAsset(assetManager, source.params)
         : source.params!;

+ 1 - 1
src/mol-canvas3d/passes/image.ts

@@ -64,7 +64,7 @@ export class ImagePass {
         this.setSize(1024, 768);
     }
 
-    async updateBackground() {
+    updateBackground() {
         return new Promise<void>(resolve => {
             this.drawPass.postprocessing.background.update(this.camera, this.props.postprocessing.background, () => {
                 resolve();

+ 14 - 9
src/mol-gl/webgl/texture.ts

@@ -441,7 +441,7 @@ export function getCubeTarget(gl: GLRenderingContext, side: CubeSide): number {
     }
 }
 
-export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipmaps: boolean, onload?: () => void): Texture {
+export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipmaps: boolean, onload?: (errored?: boolean) => void): Texture {
     const target = gl.TEXTURE_CUBE_MAP;
     const filter = gl.LINEAR;
     const internalFormat = gl.RGBA;
@@ -482,17 +482,22 @@ export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipm
             gl.texImage2D(cubeTarget, level, internalFormat, format, type, image);
 
             loadedCount += 1;
-            if (loadedCount === 6 && !destroyed) {
-                if (mipmaps) {
-                    gl.generateMipmap(target);
-                    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
-                } else {
-                    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter);
+            if (loadedCount === 6) {
+                if (!destroyed) {
+                    if (mipmaps) {
+                        gl.generateMipmap(target);
+                        gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
+                    } else {
+                        gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter);
+                    }
+                    gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter);
                 }
-                gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter);
-                if (onload) onload();
+                onload?.(destroyed);
             }
         });
+        image.addEventListener('error', () => {
+            onload?.(true);
+        });
     });
 
     let destroyed = false;