I am making a scene that generates entities from code after parsing a json file. Everything is working, except the texture loading.
To simplify the problem, I rebuilt the minimal requirements in this glitch https://glitch.com/~generate-scene
<script>
AFRAME.registerComponent("make-spheres", {
init: function(){
var sceneEl = document.querySelector('a-scene');
var entityEl = document.createElement('a-entity');
entityEl.setAttribute('id', 'sphere1');
entityEl.setAttribute('geometry', { primitive: 'sphere', radius: 0.5});
entityEl.setAttribute('material', { color: 'white', src: 'assets/pano_1.png', opacity: 0.5});
entityEl.setAttribute('position', "0 0.25 0");
entityEl.setAttribute('animation__opacity',{ property: "components.material.material.opacity", from: 0.1, to: 1, dur: 1500, loop: true, dir:"alternate"});
entityEl.setAttribute('animation__pos1',{ property: 'position', to: "0 2 0", from: "0 0.85 0", dur: 1500, easing: "linear", loop: true, dir:"alternate"});
entityEl.setAttribute('do-something-once-loaded', '');
sceneEl.appendChild(entityEl);
}
});
</script>
<body>
<a-scene>
<a-entity id="setup" make-spheres></a-entity>
and in here, the texture loads and everything works.
But in my larger project, the dynamic load component code, which is buried inside of a fetch promise, the texture fails to load and throws errors and warnings.
HTTP load failed with status 404. Load of media resource http://localhost:3000/assets/pano_1.png failed. panomaker.html
components:texture:warn $s is not a valid video assets/pano_1.png
AFRAME.registerComponent("init-spheres", {
init: function(){
let el = this.el;
let self = this;
self.parent = el; // Ref to 3D entity that will be the parent of all newly created entities (spheres, buttons, etc).
// load the json file used to populate the scene
fetch("../data.json")
.then(function(response){
return response.json(); // convert data to json object
})
.then(function(body) {
// When data has been converted, start generating 3D
//console.log('fetch data response: ', body);
let panosArray = body.data;
// Create the pano objects from the json data
self.panos = getPanoramas(panosArray);
...
// creating sphere panos
self.panos.forEach(function(pano){
// Create the pano spheres.
pano['sphere'] = document.createElement("a-entity");
pano.sphere.setAttribute('id', "panoSphere_"+pano.id);
pano.sphere.setAttribute('geometry', {primitive: 'sphere', radius: pano.avgDistance});
pano.sphere.setAttribute('position', pano.position.x.toString()+' '+pano.position.y.toString()+' '+pano.position.z.toString() );
pano.sphere.setAttribute('scale', "-1 1 1");
pano.sphere.setAttribute('visible', true);
// set the src to the pano texture. src: pano.texture, opacity: 1, shader: "flat", side:"back", npot: true, blending: "normal",
let texName = "assets/"+pano.texture;
pano.sphere.setAttribute('material', {color: "#ffffff", src: 'assets/pano_1.png', transparent: true } ); //src: texName,
self.sceneEl.appendChild(pano.sphere);
});
Why does it fail to load in the larger project, but work just fine in smaller project?
It would seem that the problem is that the texture is not ready to be loaded, or perhaps something is preventing it from loading. I am puzzled.
I notice that there are 2 ways to load a texture in AFrame.
1) use a direct path in the material: src attribute
material="src: texture.png"
2) create the asset as an img element in and refer to its id tag in the material: src attribute.
<a-assets>
<img id="my-texture" src="texture.png">
</a-assets>
material="src: #my-texture"
Both work, but the 2nd method is preferred, as it preloads the texture, prevents errors. My code above is using method 1, which works in simple version and fails in more complex project. So I am presuming, that if I can create an a-asset img source, in code, then this might work. But I haven't found a way to make it work yet. I am trying this
// Create the image assets for the scene
let assets = document.querySelector('a-assets');
// Create the pano textures.
let name = 'panotex_'+pano.id;
let texture = document.createElement('img');
texture.setAttribute('id', name);
texture.onload = function(){
console.log('texture loaded');
}
texture.onerror = function(){
console.log('texture failed');
}
let texPath = "assets/"+pano.texture;
texture.setAttribute('src', texPath);
assets.appendChild(texture);
But this results in texture failing to load and throwing 'texture failed' in console.
What is the best way to preload a texture, append it to a-assets, and execute a handler function that will run when the textures are loaded?