Qbik
Qbik

Reputation: 6147

Loading three.js texture in react as base64 string

I'm using server to generate base64 string representation of charts. Then load it to react component by fetching data with POST request. Data loads to react state but scene renders before texture is loaded. I've read Is there a way to wait for THREE.TextureLoader.load() to finish? , should I also use promise ? I would like to communicate with use of react state, because final communication is going to be more complicated then in below example.

const Comp= () => {
    const [state, setState] = useState(null)

    // basic three.js scene setting
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
    camera.position.set(75, 25, 50);

    camera.lookAt(scene.position);
    scene.add(camera);
    camera.add(new THREE.PointLight(0xffffff, 1, Infinity));

    var cubeGeo = new THREE.BoxBufferGeometry(3,3, 0.0001);

    // fetching data
    const getFile = (event) => {
        fetch('http://127.0.0.1:5000/getFile',
            {
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                method: 'POST'
            })
            .then(response => response.json())
            .then(response => setState(response.state))
    }
    
    // fetch data when loading
    useEffect(
        () => getFile(), []
    )

    // use effect when change of state happens
    // (it depends on [state])
    useEffect(
        () => {
            // state contains image data as string representation of base64 
            var texture = new THREE.TextureLoader().load("data:image/jpeg;base64," + state);
            var mat = new THREE.MeshBasicMaterial({ map: texture });

            var cubes = []
            const n = 100;
            const V = 111;
    
            // rest is irrelevant
            for (var i = 0; i < n; i++) {
                var mesh = new THREE.Mesh(cubeGeo, mat);

                cubes[i] = mesh
                var x = V * (Math.random() - .5);
                var y = V * (Math.random() - .5);
                var z = V * (Math.random() - .5);
                var r = Math.sqrt(x ** 2 + y ** 2 + z ** 2) / 20
                x /= r
                y /= r
                z /= r
                // console.log(r, x,y,z)
                cubes[i].position.x = x;
                cubes[i].position.y = y;
                cubes[i].position.z = z;
                scene.add(cubes[i]);
            }
        }, [state])

        return (
            <p ref={() => animate()} />
        )
}

export default Comp;

Upvotes: 0

Views: 1487

Answers (2)

Juan Sevillano
Juan Sevillano

Reputation: 56

I know is not the proper answer but you should take a look to R3F React-three-fiber

https://github.com/pmndrs/react-three-fiber

and it has useLoader on its api:

https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#useloader

where you'll be able to do:

useLoader(loader: THREE.Loader, url: string , extensions?, xhr?)

Upvotes: 2

TheJim01
TheJim01

Reputation: 8866

TextureLoader.load uses callbacks for onLoad, onError, and onProgress.

So if you want to make the scene wait until the texture is loaded, don't start rendering until onLoad or onError is called.

Example:

let url = "...";
let loader = new THREE.TextureLoader();

function onLoad(tex){
  // start rendering now
}

function onError(){
  // start rendering now?
}

function onProgress(xhr){
  //...
}

loader.load(url, onLoad, onError, onProgress);

In recent versions of three.js, there is also a loadAsync function, which returns a Promise. With this, you can construct your application to leverage async/await to wait for the texture to finish loading.

Pseudo-code:

// set up your scene, etc., then...

async function run(){
  let url = "...";
  let loader = new THREE.TextureLoader();

  function onProgress(xhr){
    //...
  }

  await loader.loadAsync(url, onProgress); // or use .then/.catch

  // start rendering now
}

run();

Three.js r127

Upvotes: 3

Related Questions