Three.js: Feature request: create texture object without any callbacks when data is in memory

Created on 25 Oct 2017  路  13Comments  路  Source: mrdoob/three.js

Description of the problem

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:

  1. The image data is generated by code in the memory;
  2. The image data is loaded from a local database/disk when in Electron/NW.js applications.
Three.js version
  • [ ] Dev
  • [X ] r87
  • [ ] ...
Browser
  • [x] All of them
  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer
OS
  • [x] All of them
  • [ ] Windows
  • [ ] macOS
  • [ ] Linux
  • [ ] Android
  • [ ] iOS
Hardware Requirements (graphics card, VR Device, ...)

Most helpful comment

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;
};

All 13 comments

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 ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fuzihaofzh picture fuzihaofzh  路  3Comments

akshaysrin picture akshaysrin  路  3Comments

Bandit picture Bandit  路  3Comments

ghost picture ghost  路  3Comments

scrubs picture scrubs  路  3Comments