Alex Prut Alex Prut - 2 months ago 26
Javascript Question

THREE.TextureLoader() loads the same texture multiple times (i.e. 20 times or more)

The THREE.TextureLoader() behaves in an unexpected and erroneous way. The load() function of the class tries/loads the same assets multiple times (i.e. 20 times or more).

Below in figure is illustrated this behaviour using the browser console:

enter image description here

The code used to load and use textures follows next:

Element.prototype.createShaderMaterial = function (uniforms, vertexShader, fragmentShader) {
var loader = new THREE.TextureLoader();
uniforms.texture.value = loader.load(this.texture);

return new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
wireframe: true
});
};


You can also find a live preview here: https://alexprut.github.io/earth-defender/
and the game code here: https://github.com/alexprut/earth-defender/tree/master/client/js

What is done wrong? Why the same image is loaded multiple times?

Answer

The fact is that THREE.TextureLoader() do not cache by default the textures, but I think it should, or at least ask you if not to do it. That means that if you have 400 objects with the same texture the library will make 400 HTTP request for the same assets.

Below is a simple solution (Singleton pattern / Module pattern) cache module:

var TextureLoader = (function () {
    var _instance = null;

    var Loader = function () {
        var _loader = new THREE.TextureLoader();
        var _cache = [];

        function _cachePush(elem, val) {
            _cache.push({
                element: elem,
                value: val
            });
        }

        function _cacheSearch(elem) {
            for (var i = 0; i < _cache.length; i++) {
                if (_cache[i].element === elem) {
                    return _cache[i].value;
                }
            }

            return false;
        }

        function load(texture) {
            var match = _cacheSearch(texture);

            if (match) {
                return match;
            }

            var val = _loader.load(texture);
            _cachePush(texture, val);

            return val;
        }

        return {
            load: load
        }
    };

    function getInstance() {
        return (_instance) ? _instance : _instance = Loader();
    }

    return {
        getInstance: getInstance
    }
})();

To use and cache the texture you need to call:

TextureLoader.getInstance().load(texture);