- Home /
How to combine Quaternions?
I try to rotate object according normal rotation of terrain. More over this object should move. And I need to combine z and x from normal of terrain and y which rotates to target. I tried this but result is quiet weird:
function getTerrainRot(myTarget:Vector3,terrPos: Vector3,modelRot:Quaternion){
var terrHit: RaycastHit;
var dirQuat: Quaternion;
var newPos: Vector3;
if(Physics.Raycast (terrPos, -Vector3.up,terrHit, 10.0))
{
newPos = getPosAndTerrainY(myTarget) - terrPos;
normal_q = Quaternion.FromToRotation(Vector3.up, terrHit.normal);
terr_q = normal_q * Quaternion.AngleAxis(modelRot.eulerAngles.x, terrHit.normal);
dirQuat = Quaternion.LookRotation(newPos);
terr_q = Quaternion(terr_q.x,dirQuat.y,terr_q.z,dirQuat.w);
}
return Quaternion.Slerp(modelRot, terr_q, Time.deltaTime);
}
Thanks
Answer by aldonaletto · Sep 09, 2011 at 04:31 AM
You're having weird results because you've messed with the components of a Quaternion - despite their names, they have nothing to do with the axes x, y and z.
If you want the character to keep looking to newPos direction while following the surface normal, you can use LookRotation(newPos, normal) - the second parameter indicates the up direction. About the surface normal: it's better to keep a curNormal variable and Lerp it to the surface normal (or Vector3.up, if no terrain hit) to smooth the character orientation:
var curNormal = Vector3.up;
function getTerrainRot(myTarget:Vector3,terrPos: Vector3): Quaternion; { var terrHit: RaycastHit; var newPos: Vector3; var normal = Vector3.up; if(Physics.Raycast (terrPos, -curNormal, terrHit, 10.0)){ normal = terrHit.normal; } curNormal = Vector3.Lerp(curNormal, normal, 2*Time.deltaTime);
newPos = getPosAndTerrainY(myTarget) - terrPos; return Quaternion.LookRotation(newPos, curNormal); }
EDITED: You're right: LookRotation in this case doesn't work as expected - it prefers to look at the target direction and completely ignores the normal!
Let's go back to the basics: calculate a rotation from Vector3.up to curNormal (as you already did), than multiply by the horizontal rotation from Vector3.forward to the target direction (multiplication in the Quaternion world actually combines the rotations):
var curNormal = Vector3.up;
function getTerrainRot(myTarget:Vector3,terrPos: Vector3): Quaternion { var terrHit: RaycastHit; var newPos: Vector3; var normal = Vector3.up; // default normal = up if (Physics.Raycast (terrPos, -curNormal, terrHit, 10.0)){ normal = terrHit.normal; // use the surface normal, if any } // smoothly follow the surface normal curNormal = Vector3.Lerp(curNormal, normal, 2*Time.deltaTime);
newPos = myTarget - terrPos; // get the direction to look at newPos.y = 0; // zero y to keep only the horizontal direction // calculate rotation to normal var rot = Quaternion.FromToRotation(Vector3.up, curNormal); // combine with the horizontal rotation rot = rot * Quaternion.FromToRotation(Vector3.forward, newPos); return rot; }
When are you writing us a 'Quaternions for Unity developers' blog Al?
Not work (. See here: http://img194.imageshack.us/img194/9264/movement.jpg
The problem is that LookAt really prefers to look at the target, completely ignoring the normal. I edited my answer to do things in the traditional quaternion way.
@$$anonymous$$eltdown, that's a tempting idea - but I still have to learn a lot of secrets of these four headed monsters before doing that!