How to animate triangle vertices in a-frame - aframe

I am trying to animate an individual vertex of a triangle primitive in a-frame using the animation tag and am having some strange results
In one case, If I try to animate as I would expect to, changing the x,y,z values like so...
<a-triangle
vertex-a="-1 1 -5"
vertex-b="-1 1 -10"
vertex-c="4 1 -10"
color="blue"
material="side: double">
<a-animation
attribute="vertex-b"
from="-1 1 -10"
to="-1 5 -10"
dur="4000"
>
</a-animation>
</a-triangle>
Fiddle https://jsfiddle.net/k7fbmo9k/
...I get the following error
'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is
NaN. The "position" attribute is likely to have NaN values.'
However, If I only offer a single value like so..
<a-triangle
vertex-a="-1 1 -5"
vertex-b="-1 1 -10"
vertex-c="4 1 -10"
color="blue"
material="side: double">
<a-animation
attribute="vertex-b"
from="1"
to="5"
dur="4000"
>
</a-animation>
</a-triangle>
Fiddle https://jsfiddle.net/k7fbmo9k/1/
..it seems to 'work' as in no errors and there is animation but I have no control over it.
Am I doing something wrong?
Thanks for any advice as ever

I have a solution. Using the animation component https://www.npmjs.com/package/aframe-animation-component
the vertices will animate as expected. See example below
<a-triangle
vertex-a="-1 1 -5"
vertex-b="-1 1 -10"
vertex-c="4 1 -10"
color="blue"
material="side: double"
animation="property: vertex-b; to: -1 5 -10; dur: 1000">
</a-triangle>

Try attribute="geometry.vertexB". It might not work to animate the primitive attributes, so we animate the geometry component directly.
Otherwise, if you are comfortable enough, it is better to animate vertices at a more advanced level for performance. You can use a vertex shader.
Or you can manipulate the vertices manually:
var geometry = triangleEl.getObject3D('mesh').geometry;
geometry.attributes.position[0] += 0.1;
geometry.attributes.position[1] += 0.1;
geometry.attributes.position[2] += 0.1;
geometry.attributes.position.needsUpdate = true;
And to animate, do it in a tick handler:
AFRAME.registerComponent('animate-vertex', {
tick: function (t) {
// Animate vertex position until desired position using `t` to interpolate time.
}
});

Related

how to curve a plan entity to match a sphere surface?

i'm kinda new to aframe
i have a question : with the aframe-position-spherical-component, i managed to position my element in 360deg
but how can i "curve" my element? (to match the sphering display) (btw my element are
<html>
<head>
<script src="/lib/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-position-spherical-component/index.js"></script>
</head>
<body>
<a-scene>
<a-image src="/static/cat.jpeg" width="3" height="1.5" rotation="0 180 0" position-spherical="10 90 180"></a-image>
</a-scene>
</body>
</html>
first attempt to solve this :
for now i managed to get something near what i want, but the value were found manually and aren't perfect
<a-curvedimage src="/static/cat.jpeg" width="3" height="1.5" theta-length="64" radius="3" theta-start="-32" rotation="0 180 0" position-spherical="10 90 180"></a-curvedimage>
second attempt (with the a-sphere solution) :
kinda work, but image are mirrored, and adding a click event like to show an image bigger is difficult to achieve
<a-assets>
<img id="cat" src="/static/cat.jpeg" />
</a-assets>
<a-box scale="0.1 0.1 0.1" color="red"></a-box>
<a-sphere radius="10" geometry="phiLength: 20; thetaLength: 14.12; thetaStart: 65" rotation="0 210 0" material="side: back; shader: flat; src: #cat"></a-sphere>
<a-sphere radius="10" geometry="phiLength: 20; thetaLength: 14.12; thetaStart: 65" rotation="0 240 0" material="side: back; shader: flat; src: #cat"></a-sphere>
<a-sphere radius="10" geometry="phiLength: 20; thetaLength: 14.12; thetaStart: 65" rotation="0 270 0" material="side: back; shader: flat; src: #cat"></a-sphere>
<a-sphere radius="10" geometry="phiLength: 20; thetaLength: 14.12; thetaStart: 65" rotation="0 300 0" material="side: back; shader: flat; src: #cat"></a-sphere>
<a-curvedimage> is based on a cylinder (source) so it may not fit well.
So how about actually using the sphere geometry ?
tldr fiddle here
Geometry
You could make it look like a <a-curvedimage> using theta and psi properties of a sphere:
<a-sphere geometry='thetaStart: 45; thetaLength: 45; psiLength: 45'></a-sphere>
This should result in a <a-curvedimage>ish plane, but also curved in the vertical axis. Play around with the psi and theta to see more triangle, or diamond shaped geometries.
Fitting into a sphere
This seems like a job for a custom component ! If you didn't use them before, check out the link, otherwise the component below simply copies the sphere radius and position and uses their values for the image.
AFRAME.registerComponent('foo', {
schema: {
// we'll use it to provide the sphere
target: {type: selector}
},
init: function() {
let sphere = this.data.target
// make sure the sphere radius and transform is identical:
this.el.setAttribute('radius', sphere.getAttribute('radius'))
this.el.setAttribute('position', sphere.getAttribute('position'))
}
})
And simply use it like this:
<!-- the background with some position and radius -->
<a-sphere id='background'></a-sphere>
<!-- the inner sphere -->
<a-sphere foo='target: #background'></a-sphere>
Z-Fighting
You should notice that the image is not visible, or it's distorted. By now we have two spheres with identical size and transforms so the renderer won't know which one is in front of the another.
You could deal with this easily - by changing the radius for the inner sphere:
this.el.setAttribute('radius', sphere.getAttribute('radius') * 0.95)
Or you could move the inner sphere a bit towards the center - like in the provided fiddle:
// grab the inner sphere's mesh
let mesh = this.el.getObject3D('mesh')
// we need an axis - I'd substract the mesh's center from the spheres center
let axis = sphere.getAttribute('position').clone()
axis.add(mesh.geometry.boundingSphere.center.clone().multiplyScalar(-1))
axis.normalize();
// move the inner sphere a bit along the axis
this.el.object3D.translateOnAxis(axis, 0.05)
Enlarging the image on click
Usually we'd use the scale attribute, but here we can manipulate the phi and theta values to make the image bigger. Also you should bring the image to front when enlarged, to prevent z-fighting between images:
this.el.addEventListener('click', e => {
this.clicked = !this.clicked
// preset values depending if the image is clicked or not
let thetaLength = this.clicked ? 65 : 45
let thetaStart = this.clicked ? 35 : 45
let psiLength = this.clicked ? 65 : 45
let psiStart = this.clicked ? -10 : 0
let scale = this.clicked ? 0.95 : 1
// apply the new geometry
this.el.setAttribute('geometry', {
'thetaLength': thetaLength,
'thetaStart': thetaStart,
'phiLength': psiLength,
'phiStart' : psiStart
})
this.el.setAttribute('radius', sphere.getAttribute('radius') * scale)
})
fiddle here. It would be better to keep these values in variables (base value, and a delta), but i hope this way it's easier to get the idea.

Begin:click not working in Aframe v 0.8.0

I'm trying to rotate an object on click and I'm using cursor listener to catch the touch event.
Currently I'm using animation-component.js for animating the object. However, even though im able to animate the button with loop: true but begin:click is not working
<a-entity cursor-listener id="butt"
position="-.0.4 -0.1 0.5"
obj-model="obj: #but"
scale="1 1 1"
rotation="0 0 0"
animation__rotate="begin:click; property: rotation; to: 0 360 0 "
>
Read the API. There is no begin property.
https://github.com/ngokevin/kframe/tree/master/components/animation#api
There is a startEvents property.

Animation Component Property for 360° Rotation

Does anyone perhaps know the animation component property that I would add to a model in a scene to make it 'rotate' 360° (as in, when a car rotates on a circular platform at a motor show or something)? Would ideally be adding it to this and would hope to be able to make looping true or false:
<a-entity gltf-model="#id" scale="0.5 0.5 0.5" rotation="0 90 0" animation__scale="property: scale; dir: alternate; dur: 200; easing: easeInSine; loop: false; to: 1.2 1 1.2">
</a-entity>
Had a look at the anime.js readme but couldn't seem to find it.
Thank you
You can use the basic animation component:
<a-animation attribute="rotation"
dur="2000"
fill="forwards"
to="0 360 0"
repeat="indefinite"
easing="linear"
></a-animation>
The attribute is rotation, set the angle to 0 360 0, and set the easing to linear so it is smooth.
or use ngoKevin's animation component:
animation__rot="property:rotation;
dur:3000;
to:0 360 0;
loop: true;
easing:linear;"
check it out here: https://jsfiddle.net/gL0pkgz7/.

How to fire A-FRAME collision event when two boxes intersect each other?

I am trying to detect collision of two boxes in A-FRAME v. 0.5.0. I use raycaster example:
https://aframe.io/docs/0.5.0/components/raycaster.html#whitelisting-entities-to-test-for-intersection
but for me it only works with cursor intersecting one of the meshes.
As it is written, raycaster detects when line created from a starting point to a certain direction intersects desired mesh (here marked with a collidable class). It appears that start of this collision-detecting line is somehow set on the camera or on a cursor but not on one of the boxes. How to reassign this starting point?
Before initializing scene I added component:
AFRAME.registerComponent('collider-check', {
dependencies: ['raycaster'],
init: function () {
console.log("we have component");
this.el.addEventListener('raycaster-intersected', function () {
console.log('Player hit something!');
});
},
});
and then A-FRAME entities
<a-entity id="player" collider-check >
<a-entity id="rc"
raycaster="objects: .collidable"
geometry="primitive: box; width: 0.5; height: 4; depth: 0.5"
material="shader: flat; color:gray"
position="0 -0.9 0"
rotation="90 0 0" ></a-entity>
</a-entity>
<a-entity id="inmotion" class="collidable"
geometry="primitive: box; width: 0.5; height: 4; depth: 0.5"
position="1 0 0"
material="shader: flat; color: #00CCDD">
<a-animation id="canim"
attribute="position"
dur="2000"
from ="-2 -1 0"
to="2 0 0.5"
fill="forwards"
direction="alternate"
repeat="indefinite">
</a-animation>
</a-entity>
Here is jsfiddle with the example;
https://jsfiddle.net/Suiseki/9ggs6x4m/2/
Using the raycaster is one way to check for collisions in 3D space, but it's best if one of those shapes is a ray/line. If you have two 3D objects, it's easier to use bounding box or bounding sphere collisions, without a raycaster. Here are example implementations of each:
aabb-collider
sphere-collider
Both will file hit events on the elements when they collide. Example usage, for a slightly different case.

Flush Material Update to HTML in Aframe

var obj = document.querySelector("#sphere");
obj.components.material.data.roughness = 0.3;
obj.components.material.update(object);
obj.components.material.flushToDOM();
//I don't think object matters for this use,
// it is only useful for shaders I believe.
I am trying to "flushToDOM()", but that doesn't seem to update the entity html material at all it is still stuck at initial settings...
<a-sphere id="sphere" position="0 0.25 -4.5" radius="1.25" color="#712ef0" transparent="true" material="color:#712ef0;transparent:true;metalness:0.3;opacity:1" geometry="primitive:sphere;radius:1.25" rotation="0 0 0" scale="1 1 1">
You should be using setAttribute('material', 'roughness', 0.3).
But there might be a bug with flushing "sub-components" like shaders and geometries. Will check out the issue.

Resources