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:
The code used to load and use textures follows next:
var Element = function (texture) {
this.texture = texture;
};
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
});
};
For debugging purposes 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
[x] r80
[x] All of them
[ ] Internet Explorer
[x] All of them
I believe the loader is working in the intended way. Its loading the meteor textures 200 times because you are asking it to. The loader could be modified to automatically cache and return assets that you ask it to load multiple times, but this may not be wanted in all cases. The browser will be managing the cache so if all the headers on the image file are correct, the browser will return cached versions of the image each time.
In your code:
this.maxMeteorietes = config.maxMeteorietes || 200;
Game.prototype.createMeteorites = function (numMeteorites) {
var meteorites = new THREE.Object3D();
for (var i = 0; i < numMeteorites; i++) {
var meteorite = new Meteorite().create(
this.createUniforms(),
this.createVertexShader(),
this.createFragmentShader()
);
....
}
Meteorite.prototype.create = function (uniforms, vertexShader, fragmentShader) {
return new THREE.Mesh(
new THREE.SphereGeometry(5, 5, 5),
//This line is called 200 times, and as such your loader.load() function will be called 200 times.
this.createShaderMaterial(uniforms, vertexShader, fragmentShader)
);
};
I would consider adding something like the following:
var cache = [];
var loader = new THREE.TextureLoader(); //don't need a local version of this object
Element.prototype.createShaderMaterial = function (uniforms, vertexShader, fragmentShader) {
if(cache[this.texture]){
return cache[this.texture]; //cache[this.texture].clone();
}
uniforms.texture.value = loader.load(this.texture);
var shader = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
wireframe: true
});
cache[this.texture] = shader;
return shader;
};
So you manage a cache of materials that get loaded from an array rather than regenerated everytime. This will work well if all the asteroids have the same material, or use the .clone() method if you want the materials to be different.
See if that helps out.
@calrk thanks it solves the problem.
I still believe THREE.TextureLoader()
behaves in an unexpected way, it should ask you if you want to cache or not the textures, e.g.:
var loader = THREE.TextureLoader();
loader(texture, cache = false);
I think by default the loader should cache the textures for performance reasons (if I'm wrong please let me know why).
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);
XHRLoader
which is called from TextureLoader
via ImageLoader
uses global object Cache
but default Cache.enabled
is false
.
If you set THREE.Cache.enabled = true;
right after loading three.js
it'd work.
Most helpful comment
XHRLoader
which is called fromTextureLoader
viaImageLoader
uses global objectCache
but default
Cache.enabled
isfalse
.If you set
THREE.Cache.enabled = true;
right after loadingthree.js
it'd work.
https://github.com/mrdoob/three.js/blob/2f469f327a10c7780c9bc69f876f9ed5049587f2/src/loaders/XHRLoader.js#L22
https://github.com/mrdoob/three.js/blob/f65e669af99feb518e31756d793a9688a2578fbd/src/loaders/Cache.js#L9
6834