Since the texture object store the texture information in the .image property, we have to use callback function even the image data is already in the memory like the following:
var imageDataInBase64 = '.........';
var image = new Image();
image.src = `data:image/png;base64' + imageDataInBase64;
var texture = new THREE.Texture();
texture.image = image;
image.onload = function() {
texture.needsUpdate = true;
};
It will be very convenient if we can construct a texture object directly and synchronously without any callbacks when the image data is already in the memory. (IMHO, It's also good to de-couple the logic of loading data and using data from the software engineering perspective.)
By the way, there are many cases when a texture image data is already there in the memory, such as:
One solution I've come up with is using TextureLoader.load() which returns Texture.
var url = 'data:image/png;base64' + imageDataInBase64;
var loader = new THREE.TextureLoader();
var texture = loader.load( url );
Isn't this good enough?
Actually, it is not good enough since essentially it is asynchronous.
That means texture is not complete just after var texture = loader.load( url );.
But you don't need any callbacks in the user code.
Would you please explain a bit more what you wanna do with synchronous texture generation?
According to the docs (https://threejs.org/docs/#api/loaders/TextureLoader), the callback is needed to make sure to use the texture after it is loaded successfully.
// instantiate a loader
var loader = new THREE.TextureLoader();
// load a resource
loader.load(
// resource URL
'textures/land_ocean_ice_cloud_2048.jpg',
// Function when resource is loaded
function ( texture ) {
// do something with the texture
var material = new THREE.MeshBasicMaterial( {
map: texture
} );
},
// Function called when download progresses
function ( xhr ) {
console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
// Function called when download errors
function ( xhr ) {
console.log( 'An error happened' );
}
);
What I want to achieve is :
var texture = new THREE.Texture();
texture.loadSomeDataFromMyComputerMemory(....);
// Now the texture is ready to use.
var material = new THREE.MeshBasicMaterial( {map: texture} );
Is this what you're trying to do?
var image = new Image();
image.src = 'data:image/png;base64' + imageDataInBase64;
var texture = new THREE.Texture( image );
texture.needsUpdate = true;
The whole asynchronousness of the Image API is a javascript thing.
What @mrdoob wrote works in my environment.
And even if you need image asynchronously loaded, I think this would work
var url = 'data:image/png;base64' + imageDataInBase64;
var loader = new THREE.TextureLoader();
var texture = loader.load( url );
var material = new THREE.MeshBasicMaterial( {map: texture} );
because TextureLoader sets texture.needsUpdate = true; when image is loaded.
I suppose we should update TextureLoader.load documentation. callbacks should be optional.
https://threejs.org/docs/#api/loaders/TextureLoader.load
In general you need onError callback unless loading image success is guaranteed tho.
What @mrdoob wrote works in my environment.
I don't think that is guaranteed. You have to be careful not to set theneedsUpdate flag too early, in the event that render() is called before the image loads.
var image = new Image();
image.src = 'data:image/png;base64' + imageDataInBase64;
var texture = new THREE.Texture( image );
image.onload = function() {
texture.needsUpdate = true;
};
Curious to know that, image can be loaded asynchronously with
image.src = 'data:image/png;base64' + imageDataInBase64;
?
@WestLangley You are right. Just setting needsUpdate flag is not enough. texture.needsUpdate = true; has to be put in the onload callback.
The key is the HTML Image is an asynchronous thing. It should be awesome if the texture can be independent of HTMLImage.
More comments:
A good example which can support both asynchronous and synchronous calling is ObjectLoader(https://threejs.org/docs/index.html#api/loaders/ObjectLoader). When some data is already there, then we just need to use it without any callback.
var loader = new THREE.ObjectLoader();
loader.load(
// resource URL
"models/json/example.json",
// pass the loaded data to the onLoad function.
//Here it is assumed to be an object
function ( obj ) {
//add the loaded object to the scene
scene.add( obj );
},
// Function called when download progresses
function ( xhr ) {
console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
// Function called when download errors
function ( xhr ) {
console.error( 'An error happened' );
}
);
// Alternatively, to parse a previously loaded JSON structure
var object = loader.parse( a_json_object );
scene.add( object );
The key is the HTML Image is an asynchronous thing. It should be awesome if the texture can be independent of HTMLImage.
Why not using DataTexture? This allows you to setup your texture in a synchronous way. You only have to transfer the (base64) image data into an array buffer/typed array.
@mrdoob Not sure why DataTexture was not originally suggested but I think this is a proper solution for this use case.
Why not using DataTexture?
How can a DataTexture be used in the synchronous case ?
Most helpful comment
I don't think that is guaranteed. You have to be careful not to set the
needsUpdateflag too early, in the event thatrender()is called before the image loads.