Reputation: 6147
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
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
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