Texture marked for update but image is undefined?

I’ve been having a persistent problem with my image display elements (cubes) not showing the assigned JPG/PNG on their surfaces, usually on the first load of my ThreeJS world from a browser with an empty browser cache. At first, I assumed that this was some kind of a CDN distribution problem with my server, because after you reload the world (host page) once or twice, everything displays fine. This led me to believe, incorrectly, that it was CDN issue.

However, my internet went out for a few days and then I discovered something strange. Please remember that at the time this happened I was working off a local copy of my resources so loading and display of images should have been unaffected by the outage. Instead, it was way worse. Instead of about half or so of the images beeing missing, pretty much all of the JPG/PNG based picture displays (ThreeJS cubes) were blank. (See the screenshot below).

Now my intuition on the problem has completely reversed. It seems that loading the images remotely from my server actually helped reduce the problem. My current theory is that the longer load times on the elements that are loaded before the JPG/PNG surfaces are loaded, a list of videos hosted on my server, changes the timing of that code thus delaying the execution of my animation loop until some of those JPG/PNG surfaces are “ready” (i.e. - the loader callback function has succeeded).

The problem can’t be the CDN because there is no CDN involved when running in the local context. When I look in the browser debugger (Chrome DevTools) and I see a bunch of “Texture marked for update but image is undefined” errors coming from ThreeJS during the SetTexutre2D() method call (see screenshot with the call stack)…

I looked at these other posts on the this forum and Stack Overflow:

But I looked at my code that loads these images, and I am not setting needsUpdate to TRUE anywhere in it:

/**
 * Given a URL to an image, build a ThreeJS texture from it.
 *
 * @param {String} srcUrl - A URL to an image.
 * @param {Boolean} bIsRepeated - Whether or not the texture should be repeated.
 * @param {Object} theTexture - A ThreeJS texture object.
 *
 * @return {MeshBasicMaterial} - Returns a ThreeJS MeshBasicMaterial object
 *  built from the image at the given URL.
 */
function createMaterialFromImage(srcUrl, bIsRepeated=false) {
    const errPrefix = `(createMaterialFromImage) `;

    if (misc_shared_lib.isEmptySafeString(srcUrl))
        throw new Error(errPrefix + `The srcUrl parameter is empty.`);
    // Make sure an attempt to load a GIF file is not made with
    //  this function.
    if (srcUrl.toLowerCase().endsWith('.gif'))
        throw new Error(errPrefix + `The srcUrl parameter is a GIF file: ${srcUrl}.`);

    if (typeof bIsRepeated !== 'boolean')
    	throw new Error(errPrefix + `The value in the bIsRepeated parameter is not boolean.`);

    const threeJsMaterial = new THREE.MeshBasicMaterial();

    if (bVerbose) {
        console.info(`${errPrefix}Loading image: ${srcUrl}.`);
    }

    const loader = new THREE.TextureLoader().load(
        // resource URL
        srcUrl,
        // This function fires when the resource is loaded.
        function ( theTexture ) {
            // If the image is to be repeated, set the wrap
            //  properties to THREE.RepeatWrapping, otherwise
            //  use the default wrapping which is THREE.ClampToEdgeWrapping.
            theTexture.wrapS = bIsRepeated ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
            theTexture.wrapT = bIsRepeated ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;

            // Assign the texture value to the material map when the texture is loaded.
            threeJsMaterial.map = theTexture;

            if (bVerbose)
                console.info(`${errPrefix}Resource LOADED: ${srcUrl}.`);
        },
        // This function will be called as the download of an
        //  image progresses.
        function ( xhr ) {
            if (bVerbose) {
                const pctLoaded = xhr.loaded / xhr.total * 100;

                console.info(`${errPrefix}${pctLoaded}}% loaded.  Resource: ${srcUrl}.`);
            }
        },
        // This function will be called in the event of an error.
        function ( xhr ) {
            console.error( `${errPrefix} Download failed for resource: ${srcUrl}.`);
        }
    );

    // Return the threeJsMaterial we created the desired image.
    return threeJsMaterial;
}

Why am i getting this error? Should I not add the element to the scene until the async texture loader callback function has already fired?

Also, is this actually what is causing my problem with the blank cube surfaces in my ThreeJS world, or is it something else?

BROWSER DEBUGGER SCREEN SHOT 1:

BROWSER DEBUGGER SCREEN SHOT 2 (Call Stack):

Hello!

I would have to check out the source a bit closer, but from a search, it seems like this issue stems from calling needsUpdate outside of the async callback of TextureLoader. Three.js r72 - Texture marked for update but image is undefined? - Stack Overflow

I typically use a loading manager and deal with updates post asset resolution.

Hope this helps!

/cc

1 Like