Aframe object not loading - aframe

I'm trying to load an object via AFRAME-React
let modelAttributes = {
obj: '#' + id + '_obj',
mtl: '#' + id + '_mtl',
}
return(
<Entity {...commonAttribute}>
<a-entity obj-model={modelAttributes}></a-entity>
</Entity>
)
The assets are there in the asset manager
<a-asset-item id="hotspot869_obj" src="/cdn/hotspot_869_MODEL_530b2710-15d4-11e8-817d-45ca437b9a19.obj" preload="auto"></a-asset-item>
<a-asset-item id="hotspot869_mtl" src="/cdn/hotspot_869_MODEL_aaf40910-15d4-11e8-899b-93b7b4edc285.mtl" preload="auto"></a-asset-item>
I get a load of warnings
core:schema:warn Unknown property `0` for component/system `obj-model`.
Any ideas why my model isn't loading?
EDIT it loads if I do
return( <Entity {...commonAttribute} obj-model={modelAttributes}>)
But I do need it to have a parent container, any ideas why this would be?

I have wrapped it in an entity.
The problem was there was no scale value.. an meaningful error would be helpful.

Related

animating a custom attribute in a custom component

I am attempting to animate a material property that is buried inside of a gltf-model (that has many objects). I can find the parameter and animate it with a tick event, and it looks like this
https://glitch.com/~uv-reveal-wave
It almost works, but animating with formulas is a nightmare. Instead I want to control it with the animation component. Unfortunately, exposing the parameter directly seems impossible, as I have to use getObject3D.traverse() just to locate the object, and then get the parameter.
Instead I have made a custom attribute (in schema) and and animating that attribute. And in the update function, I'm using that attribute to drive the buried parameter. It should work, but I can't seem to get a response in the update function. Iow, my custom attribute is not animating.
AFRAME.registerComponent('matclipplane', { // attached to gltf-model tree
schema:{
clipHeightA:{type: 'number', default: 0}
},
init: function () {
let el = this.el;
let comp = this;
comp.treeModels = [];
el.addEventListener('model-loaded', function(ev){
let mesh = el.getObject3D('mesh');
mesh.traverse(function(node){
if (node.isMesh){
comp.treeModels.push(node);
}
comp.modelLoaded = true;
});
...
update: function(){
console.log("update"); // returns nothing. no update from
animating attributes clipHeightA
let comp = this;
if (comp.modelLoaded){
comp.treeModels[1].material.map.offset.y =
this.data.clipHeightA;
}
}
...
AFRAME.registerComponent("click-listener", { // attached to box button
schema:{
dir:{type:"boolean", default: false}
},
init: function(){
let el=this.el;
let data = this.data;
el.addEventListener("click", function(evt){
let tree = document.querySelector('#tree');
data.dir = !data.dir;
if(data.dir){
el.setAttribute("material",'color', 'orange');
tree.emit("grow");
} else {
el.setAttribute('material','color','#332211');
tree.emit('shrink');
}
});
}
});
<a-entity id="tree" gltf-model="#tree" scale="5 5 5"
animation__grow="property: clipHeightA; from: 0; to: 1;
startEvents: grow; dur: 500"
matclipplane></a-entity>
<a-entity id="button" geometry="primitive: box" material="color:orange;
metalness: 0.5" scale="0.2 0.2 0.2" class="clickable" click-listener></a-
entity>
<a-entity id="mouseCursor" cursor="rayOrigin: mouse" raycaster="objects:
.clickable"></a-entity>
here is the glitch in progress
https://glitch.com/~uv-reveal
Clicking on the orange cube should launch the uv reveal animation. I expect that calling a custom component attribute should trigger an update event, but the update does not log in the console.
Not sure how to animate a parameter inside a gltf.
Do I have to implement the animejs component directly inside my custom component?
Or can this be done with the animate component?
According to the docs you can animate properties through
object3D like animation="property: object3D.x"
components like animation="property: components.myComp.myProp"
I don't see it mentioned (although it's used in the docs) but it also works when you provide the property as the components attribute:
animation__grow="property: matclipplane.clipHeightA;"
glitch here. You can see update is being called.
Also tree was an id of both the asset and the component, therefore grow was emitted on the asset item.

How to access the interior of gltf-model object3D.children.material in AFrame

I'm loading a gltf-model into aframe and some materials on some objects need adjusting. I am attempting to isolate and manipulate them by directly accessing the object3D property of the entity that contains the gltf component. The part of the object3D tree that I need to access is the .children Array. When I log that part to the console, it is an empty array, but I can twirl it down in the console and see the object properties I need. How do I access this in my script? the .children property is returning an empty array.
You can see my project here:
http://www.sensorium.love/experiments/yamashiro/walkthroughlit2/bonsaiLightsTest.html
The small black rectangle with the flare texture on it is one of many planes from the gltf. The large flare in the background is a primitive I made in aframe with the material as I would like it to be. I attempted to assign this to another plane object in my gltf, and it did not render. It should be applied to the child.
If you inspect the console, you can see where I've logged this children array. It is an empty array, and yet if you twirl down the arrow next to it, you can see the underlying data I'm trying to access. But I can't understand how to access that in my script.
AFRAME.registerComponent('flareplanes',{
init:function(){
let l1 = document.querySelector('#lta1');
let lm3D1 = l1.object3D;
console.log(lm3D1);
let lmc = lm3D1.children;
console.log(lmc);
for(let propName in lmc){
console.log(lmc[propName]);
}
}
});
<a-scene>
<a-assets>
<a-asset-item id="bonsailights" src="BonsaiLights.glb" ></a-asset-item>
<img id="flare" src="assets/ledFlare.png"></a-asset-item>
</a-assets>
<a-entity id="lta1" gltf-part="src: #bonsailights;
part:BonsaiBendDLeafLiteL_01"></a-entity>
<a-entity id="lta2" gltf-part="src: #bonsailights; part:BonsaiBendDLeafLiteL_02" material="src: #flare; shader: flat; opacity: 0.99; blending: additive"></a-entity>
<a-entity id="plane" geometry="primitive: plane" position="1.0 1.6 2" rotation="0 180 0" material="src: #flare; shader: flat; opacity: 0.99; blending: additive" flareplanes></a-entity>
</a-scene>
let lmc = lm3D1.children;
console.log(lmc); // Array empty
console.log(lmc[0]); //undefined
// yet, in the console, twirling the arrow reveals the object I need to //access. It appears that this object is entry 0 in the array, but accessing //directly fails. How do I access this object in my script?
Try iterating through the mesh children, not the object3Ds:
var mesh = el.getObject3D('mesh');
mesh.traverse(node => {
if (node.isMesh) {
console.log(node.material)
}
});
Here's a glitch in which I access child materials to manipulate the opacity.
If the el.getObject3D('mesh') is null, try waiting for the model-loaded event:
handleModel: function() {
let mesh = this.el.getObject3D('mesh')
if (!mesh) {
this.el.addEventListener('model-loaded', this.handleModel.bind(this)
return
}
// the model should be loaded by this point
}

reference to normalMap in material returning undefined

I am attempting to make a simple demo, using dat.GUI to manipulate a material on an Aframe primitive . On the entity tag, I create a material, an assign a texture to the normal slot.
<a-entity id="circle" geometry="primitive:circle; radius: 2; segments: 96"
rotation="-90 0 0"
material="color: #335500; transparent: true; opacity: 0.9;
normalMap: #watertex; side: double"
water ></a-entity>
Then, in a custom component, I access its THREEjs mesh and material.
I am able to get a reference to the material, but when I try to access the normalMap, it returns undefined.
I added a listener, to wait for the "loaded" event. I am not using gltf, so "model-loaded" is unnecessary.
I used getObject3D("mesh"), and traversed the mesh, but still, the normal map still returns undefined. The puzzling part is that in the console, I log the material, and can clearly see the normalMap. But when log it, it returns undefined.
AFRAME.registerComponent("water",{
init: function(){
let el = this.el;
//console.log(el);
let comp = this;
this.counter=0;
el.addEventListener("loaded", function(ev){
console.log('model loaded');
let mesh = el.getObject3D('mesh');
if (!mesh){return;}
mesh.traverse(function(node){
if (node.isMesh){
console.log(node);
console.log(node.material);
console.log(node.material.normalMap); <-- returns
undefined
}
});
});
},
});
<a-entity id="circle" geometry="primitive:circle; radius: 2; segments: 96"
rotation="-90 0 0"
material="color: #335500; transparent: true; opacity: 0.9;
normalMap: #watertex; side: double"
water ></a-entity>
I am surprised that a reference to a material shows the normal map
console.log(node.material);
but a direct reference to the normal map fails
console.log(node.material.normalMap);
I suspect that perhaps something is strange with texture maps made with aframe not being accessible from Threejs, but I don't know why or how to test this.
Ultimately I want to use datGUI to control params in the materials.
I notice that AFrame material component does not expose all the parameters of threejs materials. Also accessing some aframe material parameters with datGUI seem a bit buggy. So this is why I want to access threejs directly.
Here is a link to the project on my server, where you can see the console logs.
http://sensorium.love/experiments/demos/water/waterDemo1.html
and here is a glitch if you want to play with the code
https://glitch.com/~water-demo
It seems to be a race, where you try to access the normalMap just before it's applied.
There is a materialtextureloaded event (docs) which seems to do the trick:
el.addEventListener('materialtextureloaded', e => {
// nomalMap is accessible here
})
Otherwise you can also use a timeout or interval to determine when the normalMap isn't null.
I've seen ar.js do:
let timer = setInterval(e => {
if (!someVal) return;
clearInterval(timer);
// here someVal isn't null or undefined
}, 500)
Glitch here.
I'm pretty sure you could see the normalMap in the console, because the expression gets evaluated once you click the arrow in the browser console. On chrome there's a blue "i" which says "value below was evaluated just now".

How is properly to make pool with 10 <a-sphere>s A-frame

Have a task: create 10 sphere objects,
put them in pool; on each click got each sphere from pool
and show to user at cursor intersection point.
Problem: can't figure how to properly create and after this, put it to pool. Please check code below.
Currently each sphere create dynamicly like this: (in a-scene on click event)
let {x, y, z} = event.detail.intersection.point
sceneEl.insertAdjacentHTML('beforeend',
`<a-sphere data-coords="[${x}, ${y}, ${z}]" data-clickable position="${x} ${y} ${z}" radius="32.0" color="#eee"></a-sphere>`)
need in further work to get each a-sphere object from pool.
Layout:
<a-scene id="scene" pool__sphere="mixin: sphere; size: 10" main-scene class="ascene" cursor="rayOrigin: mouse" raycaster="zobjects: a-sky">
....
<!-- Asset-s from them want to create each a-sphere -->
<a-assets>
<a-mixin id="gray" color="#eee" ></a-mixin>
<a-mixin id="radius" radius="32.0" ></a-mixin>
<a-mixin id="sphere" geometry="primitive: sphere"></a-mixin>
</a-assets>
Pool creation:
AFRAME.registerComponent('main-scene', {
init() {
//here maybe needed to create each a-sphere object
//end add each to pool,
//then on each scene click, needed to get one by one sphere from pool
//pool creation
let sceneEl = this.el
var el = sceneEl.components
sceneEl.play();
//pool logs 10 empty objects {} if console.log('pool with spheres', el.pool__sphere)
el.pool__sphere.returnEntity(el);
console.log('entity', el)
},
// console.log('el', el)
play (){
}
})
Maybe it's me, but not got how exactly do it
There no clear example for obj. creating in doc. only for object get from pool
please look: https://github.com/aframevr/aframe/blob/master/docs/components/pool.md
I'm not sure if the question is about <a-sphere> pools, or creating objects before "pooling" them, so:
1) You don't need to manually create the objects which are supposed to be pooled.
2) The "template" for the pooled entites is defined by the mixin attribute. Any component (geometry, material, models, custom ones) needs to be defined within the given mixin.
Source code here. So, with a simple declaration:
<a-scene pool__spheres='mixin: ball; size: 10'>
<a-assets>
<a-mixin id="ball" geometry='primitive: sphere' material="color: red">
</a-mixin>
the pool component will already create 10 <a-entity>ies, all using the #ball mixin.
You only need to grab the entities from the pool on click:
this.el.addEventListener('click', e => {
let pool = this.el.sceneEl.components["pool__spheres"]
let sphere = pool.requestEntity();
});
and return them to the pool at some point:
let pool = this.el.sceneEl.components["pool__spheres"]
pool.returnEntity(this.el)
Check it out in this fiddle.

A-frame Want click on entity shows other entity

I have an entity and I want when I click or point with the a-frame cursor that is changes the attribute visible of another entity to true.
<a-entity id="pug" gltf-model="#pug" position="-1.75 0.0035 3"
scale="0.01 0.01 0.01" rotation="0 -11 0" >shadow
event-set__down="_event: mousedown; scale: 1.2 1.2 1.2"
event-set__up="_event: mouseup; scale: 1 1 1"
event-set__leave="_event: mouseleave; scale: 1 1 1">
</a-entity>
If I have that the interaction with the cursor works but I don't how (or even if) I can make it affect the attribute of another entity.
I want that :
<script>
$(document).ready(function(){
$("#pug").mouseenter(function(){
$('#bubble').attr('visible', 'true');
});
});
</script>
I do have cursor in my camera but I don't know... I'm new to A-Frame and I must be missing something :/ Thanks!
Keep in mind, that changing CSS properties won't affect the rendered entities. The best way to change properties is by using entity.setAttribute("attribute", "value")
I'm not sure if you can do a switch (visible / invisible) using the event-set component, but you can make an entity visible, or invisible by setting the visible attribute:
<a-entity event-set__click="_target: a-cylinder; visible: false;"></a-entity>
check it out here.
But i would recommend creating an a-frame component. You can check them out in the docs, in this case it looks like this:
AFRAME.registerComponent("foo", {
init: function() {
this.el.addEventListener("click", (e) => {
let cylinder = document.querySelector("a-cylinder")
cylinder.setAttribute("visible", !cylinder.getAttribute("visible"))
})
}
})
HTML:
<a-entity foo>
quite simple:
1) The AFRAME.registerComponent("foo" bit "declares" the component.
2) The init: function() function is executed when the component is initialized
3) Inside the click listener, I've made a simple toggle, which sets the visibility to the opposite of the actual value. If its visible, switch it to !visible = false.
Check it out here.

Resources