In my world I build walls that are actually cubes but with one side made very small (so it’s no longer a cube but it looks like a wall/panel). One and only one face of the cube gets an image assigned to it, loaded via aTHREE.TextureLoader().load()
call using a URL to a JPG file.
I want to be able to update the images of a wall. Is it enough just to delete the existing MeshBasicMaterial
object assigned to the surface of the cube that has the image object, and replace it with a new one received from another call to THREE.TextureLoader().load()
? Or are there other steps I need to take to make sure I clean up memory property? I’ll be replacing images a lot so I need to be really diligent in this if I want to avoid memory leaks.
Note: If there is an overall easier, faster, or safer way to replace the image of an object from a URL, please let me know!
This is the code I use to replace an asset now. Is it correct and is it enough to completely clean up an earlier loaded MeshBasicMaterial
instance?
// If there is an existing asset, then remove it.
if (self[surfaceName] && self[surfaceName].materialContent !== null) {
// Is it a ThreeJS object or any other object that has a dispose method?
if (typeof self[surfaceName].materialContent.dispose !== undefined)
// Yes. Call its dispose method.
self[surfaceName].materialContent.dispose();
}
Below is the code I use to create the material for a wall in the first place:
/**
* 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;
}