I have a target for an animation rigging IK system that i’m trying to simultaneously align it’s forward vector with a surface normal while also having its upward vector follow the direction the mouse is moving.
this works on a flat mesh fine, but once the normal of the plane changes, the upwards vector is effected. you can see this in the video here about 12 seconds in:
bfg4g4
what i’m trying to do is maintain the direction of the upward vector when the forward vector changes, so that hand isn’t awkwardly flipping directions everytime the hand moves to a surface with a different normal.
here is the code i’m currently using, CalculateLookDirection() gets the direction of the mouse:
In case anyone was curious here’s the code for the mouse rotation:
private Vector3 CalculateLookDirection() {
Vector3 mousePos = Mouse.current.position.ReadValue();
mousePos.z = mainCamera.farClipPlane * 5f;
var cam_rot = mainCamera.ScreenPointToRay(mousePos).direction.normalized;
var ray = new Ray(target.transform.position, cam_rot);
return ray.direction;
}
I really have no clue what to do here, I’ve tried doing the rotations separately, I’ve tried multiplying them, no matter what when the forward vector of the target changes it always rotates in a way that disregards the current upwards direction
It’s unclear what “normalRay” is, and from the code you shared, CalculateLookDirection seems to be using a ray from the camera into the scene, which doesn’t make much sense as a up direction.
normal ray is just a ray from the ray cast point in the direction of the normal that the target aligns its forward vector to Ray normalRay = new Ray(hit.point, hit.normal);
and with the calculate look direction function, what i’m trying to do is have the up vector of the target aligned with the direction that the mouse is moving, so that the tip of the hand is following the mouse basically. which with my current setup seems to work if the surface is flat, but once that forward vector of the target changes, the up vector of the target is effected and the direction it was going in previously is not maintained.
maybe that doesn’t make sense for the way i’m getting the mouse direction to be an up vector, would you suggest changing something about how i do that part?
If you have a normal pointing forward, you can think of it as a plane normal. This plane must contain the other two vectors, the question is where exactly. Well if we say that your original vector is ‘forward’ we know the relationship it has to the other two. So one method to come up with the basis vectors would be to grasp the rotation of the ‘forward’ vector and then apply this to the plane.
The rotation is represented by a quaternion that can be found via
var rot = Quaternion.FromToRotation(Vector3.forward, myForward);
and then
var myUp = rot * Vector3.up;
var myRight = rot * Vector3.right;
There are two caveats:
with Quaternion.FromToRotation if myForward is exactly Vector3.back then this rotation is not defined. You can explicitly test for this.
applying quaternion to myRight is excessive because you already have two vectors.
Therefore
// expects forward as a unit vector
void computeBasisVectorsFromForward(Vector3 forward, ref Vector3 up, ref Vector3 right) {
if(Math.Abs(forward.z + 1f) < 1E-7f) {
up = Vector3.down;
right = Vector3.left;
return;
}
up = Quaternion.FromToRotation(Vector3.forward, forward) * Vector3.up;
right = Vector3.Cross(forward, up);
}
There are other ways to do this, but this is still light and comprehensible.
edit:
You can find more information on AngleAxis and FromToRotation here .
thanks for the explanation, so after reading through that and trying to apply it to my situation, i can calculate the basis up vector after aligning my forward vector to the normal that i want, and then in a separate step rotate that up vector to direction i want?
am i incorrect in these assumptions?
It is likely that the method above will mess your rolling angle, depending on what you need.
You can also check out OrthoNormalize which likely does this on a more fundamental level.
I did this numerous times, and frankly it’s always something slightly different, so I don’t have a factory standard. You can also produce your own perp function, which is probably what OrthoNormalize does anyway.
Here’s a more elaborate example
/// <summary> Returns a vector that is orthogonal (or orthonormal)
/// to input vector. </summary>
static public Vector3 Perp(this Vector3 v, bool normalize = true) {
var sqr = v.x * v.x + v.y * v.y;
if(sqr > 0f) { // (0,0,1) x (x,y,z)
var im = !normalize? 1f : 1f / Mathx.Sqrt(sqr);
return new Vector3(-v.y * im, v.x * im, 0f);
} else { // (1,0,0) x (x,y,z)
sqr = v.y * v.y + v.z * v.z;
var im = !normalize? 1f : 1f / Mathx.Sqrt(sqr);
return new Vector3(0f, -v.z * im, v.y * im);
}
}
(if you supply a unit vector, set normalize to false, to avoid the square roots)
This condenses a lot of 3D algebra into an optimal computation of an orthonormal (similar to how in 2D you just do (-y, x) however in 3D it gets a bit more convoluted).
I can’t tell because I don’t get your exact use case, the method simply takes an arbitrary unit vector, treats it as forward, and produces basis orthonormals from it.
In any case, none of these methods will respect your horizon line, which is why LookRotation exists.
I think @lordofduct had a deconstructed LookRotation somewhere, but the point is that it if you don’t maintain the up vector that is relevant for your world, you get a rotation that likes to tilt (roll) which is completely unnatural to how cameras (eg) typically track motion. Most people simply stick a world Vector3.up here, but I really don’t understand your specific use case (maybe it’s just me, I’m not implying you explained it badly).
I guess a better explanation of my problem, is that i want my targets forward vector to always be perpendicular to the surface that it is raycasting to. so basically the hand should always be parallel with the surface. but then i also want the upwards vector of the target to basically be following the direction that the mouse is moving in, so that the hand is rotating across its z axis (i think?) only to the direction of the mouse. but at no point should the forward vector not be aligned with the surface normal.
I guess my problem most likely lies in my CalculateLookDirection function where I’m trying to get the mouse direction. it seems like I’m not getting the proper up vector, so even though i’m using LookRotation the up direction I want to maintain when transitioning across surfaces isn’t respected.
I’m not sure which deconstruction you’re referring to… but here is my QuaternionUtil class:
I have a AltForwardLookRotation method, this behaves like LookRotation, but if your forward/up axes are not the standard +z/+y axes. Good if any of your bones are oriented differently than the standard Unity (common for models importanted from software with different orientation standards like 3D Studio Max).
As well as FaceRotation, this ensure that your rotation maintains some up vector, but attempts to face in some generation direction of forward, even if that forward isn’t orthogonal to up.
@stor314
Ok, do you have a surface normal at your disposal?
You have your hand direction vector.
To produce a vector that is perpendicular to both, you do a cross product. That’s all you need. That should be your up direction for this context, if I’m getting you right.
thank you @orionsyndrome and @lordofduct for your help and the amazing package, what ended up working for me was a variation of the AltForwardLookRotation in @lordofduct 's package.
and my function call looks like target.transform.rotation = QuaternionUtil.AltForwardLookRotation (-normalRay.direction, Vector3.forward, CalculateLookDirection());
So really what ended up working for me was the following operation: