-->

main-navbar

Showing posts with label Quaternions. Show all posts
Showing posts with label Quaternions. Show all posts

Saturday, October 19, 2013

A word on Quaternions and doing complex rotations - Solution

In my older post on Quaternions, I described a problem, but failed to show the solution. Someone commented on that, so I felt inclined to do a full post describing the solution. This post is mostly code, and what it does, just a warning to those reading.

The problem was as follows:
How do you align the faces of two objects that are facing different directions on objects facing diferent directions?

Note the setup here; a side is one of the faces that are being oriented, the gameObject is the object the side is apart of. A side has its own custom class with information about it(where it is relative to the object, its orientation, its number of geometric sides, etc).

Step 1 - Create a rotation variable that you are going to manipulate. Assign it to the gameObjects current rotation:

var rot: Quaternion = gameObject.transform.rotation; 

Step 2 - Create a function that will manipulate the rotation to point it toward the target direction, how I did it is shown below:

function GetLookRot(side: Side, rotation: Quaternion){
var rot: Quaternion;
if(rotation == null) rot = gameObject.transform.rotation;
//assigns rotation for manipulation
else rot = rotation;
//the location of the side relative to the center of the 
//object, which is the same thing as a vector that starts 
//at 0,0,0 and passes through the location of the side.
var fromLocalDir: Vector3 = (location);
//target global location (of the target side)
var target: Vector3;
//this basically finds the vector that starts at the 
//target gameobjects center and passes through the target 
//sides center, all in global coordinates
target = (side.gameObject.transform.rotation
*(-side.location))
+gameObject.transform.position;
//takes the global vector described above and makes its 
//position local, its rotation is kept global.
var toGlobalDir: Vector3 = target 
- gameObject.transform.position;
//updates the first vector by modifying it from local 
//rotation to global rotation, while still keeping its 
//local position.
var fromGlobalDir: Vector3 = rot * fromLocalDir; 
//so now we have two vectors, the first points in the 
//current direction the side is pointing, and the second 
//points in the opposite direction the target side is 
//pointing, because we want the sides to face each other, 
//rather then simply point in the same direction.
//after that we get a rotation using unity's 
//FromToRotation to get a Quaternion that describes the 
//rotation of going from the first direction to the second 
//direction, this rotation is then combined with the 
//gameobjects current orientation, by multiplying, to get a 
//rotation that points the current side to be facing the 
//target side.
return Quaternion.FromToRotation(fromGlobalDir, toGlobalDir) 
* rot; 
}



Step 3 - The next function assumes that the sides are parallel, this takes them and aligns their sides. Because it handles rotations, the sides do not actually have to be parallel at yet, just the rotation you feed this function would have to put them parallel. This step is a bit more complicated....

 //returns a rotation that would rotate so the face is aligned 
//(ie: so all the vertexes of both sides are at same pos)
//with the target face, rotates along the normal axis of the 
//side. rotates current, or if given rotation, rotates that, 
//target rotation is given through the passed side
function GetAxisRot(side: Side, rotation: Quaternion){
var rot: Quaternion;
if(rotation == null) rot = gameObject.transform.rotation;
//assigns rotation for manipulation
else rot = rotation;
//def axis is a vector that originates at the center of the 
//side and passes through a vertex on the side. This is 
//used to represent the sides rotation around the y axis
//if the side was in a horizontal position
var direction: Vector3 = (rot*(defAxis));
//this is an array that will be used to hold the diferent
//possible vectors.
var targets: Vector3[] = new Vector3[side.polySides];
//sets the array of axies
for(i = 0; i < polySides; i++){
if (i == 0) targets[i] = side.gameObject.transform.rotation*(side.defAxis);
else{
targets[i] = side.gameObject.transform.rotation*(Quaternion.AngleAxis((360/side.polySides)*i, side.location) * (side.defAxis));
}
//shows each axis for visualization
Debug.DrawRay(side.gameObject.transform.position, targets[i], Color.red);
}
//shows each axis for visualization
Debug.DrawRay(gameObject.transform.position, direction, Color.red);
//the angle between the closest direction of available directions to target,     //and the target
var angle1: float = 360;
//the angle difernce between the direction that comes before the above and the   //target
var angle2: float = 360;
//the angle diference between target direction and current direction in +-       //degrees
var angle: float;
//assigns the above
var j: int;
for(i = 0; i < polySides; i++){
if (i == 0) j = polySides-1;
else j = i-1;
if(angle1 > Vector3.Angle(direction, targets[i])) {
angle1 = Vector3.Angle(direction, targets[i]); 
angle2 = Vector3.Angle(direction, targets[j]);
}
}
if(angle2 < 360/polySides) angle = -angle1;
else angle = angle1;
//applies final rotation
var finalRot: Quaternion = rot*(Quaternion.AngleAxis(angle, location));

return finalRot;
}

Step 4 - Once you have both functions you run them like this:

var rot: Quaternion = gameObject.transform.rotation; rot = GetLookRot(side, rot); rot = GetAxisRot(side, rot); return rot;

And that is how I did it, hopefully this can be of some use to someone :)

Saturday, July 6, 2013

A word on Quaternions and doing complex rotations

Its been some time since I last made a post here. Not for lack of working, but rather of lack of something to post. For the past several months I have been working quite a bit in Unity3d on Quaternions.

I will talk about that below; the first point is that I haven't made many posts here. To keep whoever bothers following this blog on my project interested and up to date, I will write a blog post (to the best of my ability; post is still not guaranteed) every Saturday with some info on what has been worked on/ updated etc. And for those that even that is not enough, I plan on making a twitter account with daily updates on progress. Now as a warning to those who wish to read on, below is a rant/very long post on quaternions which some may find fascinating and others boring.

Quaternions are what unity uses to describe rotations. Unity also has something called eulerangles, which  has the more easily interpreted 0-360 notation that we are used to using for rotations. The main reason unity uses quaternions as the underlying way of calculating an objects rotation though, is because quaternions don't suffer from something called gimbal lock. The major disadvantage of quaternions, is that their representation is very complex, so much so in fact that quaternions are best simply referred to as variables instead of their individual values. For a mathematical explanation of quaternions, here is a good explanation that might give you a little bit of an idea of what the values actually mean. However, the explanation went a bit over my head, and the variable method of representation is sufficient.

My problem with quaternions started when I tried to do something a bit complex, and all at once. The idea was to add a hook function for rotating one side of a module to be facing another side of another module.  So I setup something simple for selecting the sides that needed to face each other. I set it up so all that was needed was the new rotation to rotate too.

Each object stores its side as a rotation and position relative too the center of the module it is on. This greatly reduces lag from object creation and destruction compared to when I had an entire GameObject representing each side, but it added a complication; instead of rotating one module to be facing another, I had to rotate one rotation of a modules side to be facing another rotation of another modules side.

In the below example side one is selected to rotate facing side 3, and the goal was to rotate it so it looked like side 2; both sides plains are parallel, and the corners of the modules are aligned.
My first solution to get a rotation that would rotate in this way was to try rotating all at once, and i came up with this line of code:  

gameObject.transform.rotation*Quaternion.Euler(orbit))*(Quaternion.Inverse(Quaternion.Euler(side.orbit+Vector3(0, 0, 180)))
note: orbit is the sides relative rotation in euler angles to the module it is on. Quaternion.Euler converts it to a quaternion.

This line is the result from hours of trial and error and was by no means the most efficient time I spent. However, it still worked to provide the desired rotation. I am honestly still not entirely sure why it works, but it does, so I was fine with it. That is, until I tried to rotate the resulting rotation. 

At some point I realized I needed to rotate the returned rotation, the reason being that if you connect modules into a "loop" where there are connections connecting in a loop pattern, the assembly will destroy itself. In the example below the red shows the "loop" that would be created. The blue shows the corner of the side that would rotate toward the other corner of the other triangle if the above line of code was used to provide the target rotation.
If a joint is added with this rotation as the target, then the assembly literally flies apart. So as a solution I attempted to use the angle axis function to rotate the resulting rotation in a way that would return the correct target rotation. Only problem was, using the angle axis function seriously messed up the rotation; it rotated on some arbitrary axis as a result of the original rotation calculations. I spent a long time attempting to rectify this, but nothing worked and I was forced to attempt a different approach.

I was back to square one and I did what I should have done from the start: rotate in steps. Eventually I came up with a rotation that would align the faces of the two objects, which was really a modified LookAt function. From there I was able to use angle axis to rotate the previous rotation to the final rotation. This resulted in the desired rotation of the modules.

To sum up: Do quaternion rotations in steps; it will save you a LOT of trouble, and will certainly not cause you to learn a million things about quaternions you never needed to know.
note: the last part of the above sentence is not guaranteed not to occur. 

Edit: The details (with the actual code) on how this was solved can be found here.