Sign in to follow this  
logunov

How to animate movement with quaternions?

Recommended Posts

Good day everyone, here is playground attempt: https://www.babylonjs-playground.com/#74G81U

I'm trying to make movement from point1 to point2 by arcus of sphere, and the core goal is to prevent gimbal lock in points of pi/2.
Example demonstrates my attemps to make movement from the end of red line to the end of green line though rotating with quaternions around their cross product.

The question is:

should start quaternion be from target.position.toQuaternion() ?
which property should be used in animation object?

Thank you for any advices and notes.

Theoretically, to rotate object around some axis via Quaternion I should make quaternion with rule (w, v), where w = cos(a/2), and v = n*(sin/a2), where a - is my angle, n - rotation axis, then convert it to the rotation matrix, and use this matrix to prevent gimble lock on the position transform.
But in animation it is not working, I do not understand why. I am sure I am doing something wrong with BABYLON API, but I can not find the problem.

Share this post


Link to post
Share on other sites

A common way to smoothly transition between two quaternion-based orientations is to use spherical linear interpolation (slerp, for short). I think this will do what you want. The quaternion object in Babylon.js even has a slerp method built-in:

https://doc.babylonjs.com/api/classes/babylon.quaternion

Edit: here's the Wikipedia article on slerp:

https://en.wikipedia.org/wiki/Slerp

Share this post


Link to post
Share on other sites

Thank you for notes, @entropy, it very useful and interesting.

I want to formalize my question a bit deeper. so the question is: "
Is babylon animation doing slerp with quaternions on their own, and which property I should pas to babylon animation to make movements with quaternions. or this stuff is not supported and I should code method, which will produce array of points, and pass this though keys to babylon animation with "position" property".

This question is a little bit confusing me, because I don't find any clear explanation in API how to do something like that.


Edit: I got additional question, if I passing "rotationQuaternion" parameter in animation constructor, then will it rotate around local axes or world? If I need to rotate object around some world axis, which parameter I should pas? For example: I put some object on sphere surface, with base rotation, how could I get his rotationQuaternion around origin (0,0,0)?  Or around some axe? 
Let's have 2 points on surface, p1 - start, p2 - finish. Then, cross(p1,p2) gives me normal, and I can rotate around normal to go from p1 to p2, but I need to get rotationQuaternion of object in p1 point, provided normal-axes from cross product, this should be startQuaternion for slerp function, and then, I can make second quaternion with rule: 

let angle = BABYLON.Vector3.GetAngleBetweenVectors(p1,p2,normal) //normal is cross(p1,p2)
let cos = Math.cos(angle/2)
let sin = Math.sin(angle/2)
let rotationAxis = normal //normal is croos (p1,p2)
normal.scale(sin/2)
let endQuaternion = new BABYLON.Quaternion(normal.x, normal.y, normal.z, cos)

Then, I should provide something with this 2 quaternions: 
 

let quaternionsArray = function(q1, q2, a) {
let result = [];
let step = 1/a;
for (let i = 0; i<a; i++){
 result.push(BABYLON.Quaternion.Slerp(q1,q2, step*i) //q1 - startQuat, q2 - endQuat
}
return result
}

And then make keys array for each quaternion of result array, but if I pas "rotationQuaternion" as parameter of animation constructor, it will rotate around local axes, am I right? 
So, there is two unclear points: how to get rotationQuaternion in global coordinates, and how to provide animation constructor to use rotationQuaternion in worldSpace, not local.
 

Share this post


Link to post
Share on other sites
2 hours ago, logunov said:

Edit: I got additional question, if I passing "rotationQuaternion" parameter in animation constructor, then will it rotate around local axes or world?

It will, presumably, rotate around both. How does one rotate around a local axis and not rotate around a global axis? Or vice versa?

When you say you are trying to rotate around world axes, do you mean you are trying to restrict the rotation about a particular world axis? Or, if you are trying to rotate an object around a point in space (say, the world origin), you'll need to update (simultaneously) the rotationQuaternion of the mesh (to change it's orientation) and the position vector of the mesh (to change its...well...position). The mesh's position itself is just a vector, and you can use quaternions to rotate that as well.

Edit: I think I understand your question better. When you assign a quaternion to a mesh, it applies the quaternion to the coordinates of the mesh (which are probably defined relative to a local origin...I think...not certain, though). To rotate around a global origin, you're probably back to updating both the orientation and position, as previously described.

Edit 2: I just checked, and I do believe my statement is correct: it rotates around the local origin based upon how the mesh was originally defined. You can check here,

https://www.babylonjs-playground.com/#IKU2U#1

where a cube is created and then shifted along the y-axis by a unit of -1. Then, when the cube is clicked, it still rotates around the local center of the cube, and not about the global origin. However, if before the rotation you

cube.bakeCurrentTransformIntoVertices();

then the initial translation, in effect, gets baked into the vertex locations. Now it rotates around the global origin:

https://www.babylonjs-playground.com/#IKU2U#2

Share this post


Link to post
Share on other sites

Thx a lot, @entropy, your reseach was extremly useful to understand BABYLON API better, but I found a two more problems, because of this understanding:
1) This should work only for meshes, because, as example, camera have no method to convert vertices, so, if I need to move camera by this idea, it is impossible, because it have no verices and method (it is just note, your method is good)
2) This do not work, if I choose arbitrary axis. 
Here is playground, demonstrating #2 on your code: https://www.babylonjs-playground.com/#H22U9B#2

Look at lines #32, and #49 and #59 - I change axis, because I want to rotate around blue axis, and change start position + drawed all lines on the scene to make example more visible. 
My goal is to rotate around blue axis from end of red line (point1) to end of green line (point2), this is possible, because blue line is perpendicular to both green and red, so green and red forming surface (plane, I do not know termin on english). 

And I am shocked a bit, because, logically, your solution should work for any kind of axis, but it doesnt, and I can not understand why. Maybe, I missing some mathematical transformations, to make it work, but I do not know which math here should be used. 


p.s A simple question about math: why you multiplying quaternions? Where can I read about geometry transformations, which quaternion-multiplying produces? I simply can not understand why it needed and what it's doing. 


Again, my big thank for your help, you really make me progress a lot. 

Share this post


Link to post
Share on other sites

Yes, you understand correctly. Box emulates (simulates) the camera movement, I need to move camera around sphere surface, but any of the given BABYLON camera going in gimble lock in pi/2 so I can not use default babylon camera, so I choosed 2 points on sphere, and want to rotate camera with quaternion to prevent gimble lock, but the problem camera rotates around itself or do not rotating. 

This two points will be exactly on the same sphere.

var point1 = new BABYLON.Vector3.One()
var point2 = new BABYLON.Vector3(3/2,0.5,0.70710678118654752440084436210485);



More clearly explanation:
Purple line - how camera\mesh should move, looking at origin (center of the sphere)
Green line - point1
Red line - point2
Blue line - perpendicular (same as normal) to both OP and PQ where O is origin point,
Blue line defines that I can rotate around it though Quaternion. 
Edit: I need to note, if I change camera.position property with new data, it does not resolve the problem, because now in my project camera.position = parameter produce this problem, so I need to change camera.rotationQuaternion, to change it position around sphere surface to prevent any gimble locks as description of this property said.
Illustration_of_great-circle_distance_svg.thumb.png.5ef245ee4127b9bc99a0ca46c88500ff.png
 

Share this post


Link to post
Share on other sites

Thanks for the picture. That's much clearer to me now. The method I described above is likely your best option: you need to update the camera's position and rotation simultaneously. Your initial quaternion should be qi = (0,0,0,1), which represents no rotation; qf should represent a rotation about the desired axis (like you had in your example). Here's how to rotate a vector:

1. Write the initial position of the camera qpos = (x,y,z,0).

2. Let qs be the slerped quaternion between qi and qf for the current frame. The new position of the camera would be the x,y,z components of qs*qpos*qsc (where qsc is the conjugate of qs). Note the multiplication must be in that order.

3. Adjusting the orientation of the camera may be as simple as setting the target to the same point as you move the position. You might also have to rotate the up vector in a manner similar to step 2, or the look direction. Unfortunately, I'm a little unfamiliar with the babylon.js syntax, so I can't advice specifics. The API may already provide a more elegant solution, but this is the method I would try.

 

Share this post


Link to post
Share on other sites

Thank you, @entropy, I finally made a solution, take a look: https://www.babylonjs-playground.com/#PP962K#8

And the real camera example: https://www.babylonjs-playground.com/#PP962K#9

Answers on my questions in order:
1) To provide quaternion rotation you need use transformNode as pivot, putted in the center of the rotation. In the problem of mine it will be origin between point1, point2 and normal (green, red and blue lines)
2) Babylon animation component slerping quaternions on it's own, just need to put ANIMATIONTYPE_QUATERNION and "rotationQuaternion" parameters, as shown below
3) Before use quaternions, you should overwrite defaul transformNode.rotation via traformNode.rotationQuaternion, by default transformNode.rotationQuaternion is equals to null, so you need to put any quaternion to this before game starts or any calculations, this will need to make transformNode.lookAt(target:vector3) returning quaternions instead of vector3s (note: do not do that: transformNode.rotation = someQuaternion, just put transformNode.rotationQuaternion = someQuaternion); 
4) Then just create keys with start Quaternion and End quaternion, due to javascript compilator, you can not

transformNode.lookAt(point1)
start = transformNode.rotationQuaternion
transformNode.lookAt(point2)
end = transformNode.rotationQuaternion
transformNode.lookAt(point1)


Because it will simplify this (I tested), so just make a copy as in example above, and then dispose copy of this object
5) Create animation object and put arguments.
All works, gimble lock should be **** up. 
Thank you, @entropy, you really help me a lot! 

Note to descendants: this will work only for sphere, this is spherical mathematics, and this will work only for sphere with constant radius for animation time.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.