normalizing euler angles rotation vector - c#

I need to display the rotation in Euler angles of an object's certain axis.
I am aware that retrieving the rotation of an object in Euler angles gives inconsistent results, some of which can be solved by simply using modulo 360 on the result. however one permutation that unity sometimes does when assigning a vector with the value of "transform.localRotation.eulerAngles" is instead of retrieving the Vector3 "V", it retrieves "(180, 180, 180) - V".
to my understanding, "(180, 180, 180) - V" does not result in the same real world rotation as V, unlike "(180, 180, 180) + V" which does leave the actual rotation unaffected.
what is the explanation for the phenomenon, and what is the best way of normalizing an Euler angles rotation vector assuming I know the desired and feasible value of one of its axes? (for example, to normalize it such that all of it's values are mod 360 and it's Z axis equals 0 assuming it does have a representation in which Z = 0)

I don't know about the first part of the question (it is different enough to be its own question imo) but I can answer your second one.
So, you have these inputs :
Quaternion desiredRotation;
float knownZ;
And you're trying to find Vector3 eulers where eulers.z is approximately knownZ and Quaternion.Euler(eulers) == desiredRotation.
Here's the procedure I would use:
First, determine the up direction rotated by desiredRotation and the up and right direction rotated by a roll of knownZ:
Vector3 upDirEnd = desiredRotation * Vector3.up;
Quaternion rollRotation = Quaternion.Euler(0,0,knownZ);
Vector3 upDirAfterRoll = rollRotation * Vector3.up;
Vector3 rightDirAfterRoll = rollRotation * Vector3.right;
We know the local up direction after desiredRotation is applied and that the only thing that can adjust the up direction after the roll knownZ is applied is the rotation done by the euler pitch component. So, if we can calculate the angle from upDirAfterRoll to upDirEnd as measured around the rightDirAfterRoll axis...
float determinedX = Vector3.SignedAngle(upDirAfterRoll, upDirEnd, rightDirAfterRoll);
// Normalizing determinedX
determinedX = (determinedX + 360f) % 360f;
...we can determine the x component of eulers!
Then, we do the same with the yaw component of eulers to make the new forward direction line up with the end forward direction:
Vector3 forwardDirEnd = desiredRotation * Vector3.forward;
Quaternion rollAndPitchRotation = Quaternion.Euler(determinedX, 0, knownZ);
Vector3 forwardDirAfterRollAndPitch = rollAndPitchRotation * Vector3.forward;
Vector3 upDirAfterRollAndPitch = upDirEnd; // unnecessary but here for clarity
float determinedY = Vector3.SignedAngle(forwardDirAfterRollAndPitch, forwardDirEnd, upDirAfterRollAndPitch );
// Normalizing determinedY
determinedY = (determinedY + 360f) % 360f;
Vector3 eulers = new Vector3(determinedX, determinedY, knownZ);
To ensure that the given quaternion can be made with the given component, you could check if the axes given to SignedAngle actually can rotate the input vector to the target vector, or you can just compare the calculated eulers and the given quaternion:
Quaternion fromEuler = Quaternion.Euler(eulerAngles);
if (fromEuler==desiredRotation)
{
// use eulerAngles here
}
else
{
// component and quaternion incompatible
}
Hopefully that helps.

I'm not quite sure I understand your question correctly, but the euler angles just represent the angles of 3 rotations applied around the 3 axis in a specific order, right? So why would you normalize it by adding 180 everywhere? You should bring each angle individually into the range 0-360 by modulo-ing them.
Your question seems to imply that you can obtain any orientation by only rotating around two axis instead of three... is that what you are trying to achieve?
Using quaternions could possibly help you, in fact an orientation can be defined with just 4 scalar values: an axis and an angle

Related

how do I rotate a direction Vector3 upwards by an angle in unity

this is my direction vector
new Vector3(target.transform.position.x - projectile.position.x, 0, target.transform.position.z - projectile.position.z).normalized
I tried multiplying it by Quaternion.AngleAxis(45, Vector3.up) but that simply doesn't work
All other orientations like Vector3.left, right, etc. don't help either
The only thing I could observe is the way that the angle changes when I move the target or projectile
You were close. Use cross product to get the axis you need, use that in AngleAxis, then finally apply that rotation to your starting direction:
Vector3 RotateTowardsUp(Vector3 start, float angle)
{
// if you know start will always be normalized, can skip this step
start.Normalize();
Vector3 axis = Vector3.Cross(start, Vector3.up);
// handle case where start is colinear with up
if (axis == Vector3.zero) axis = Vector3.right;
return Quaternion.AngleAxis(angle, axis) * start;
}

Unity - Apply two local rotations to object (trying to recreate the rotation of a controller joystick into a 3D mesh joystick)

I would like to recreate one on one the rotation of the real life controller joystick (i.e. 360 controller) into a 3D joystick mesh (that resembles the 360 controller one).
I thought about doing it by rotating the joystick in the X axis according to the magnitude of the input (mapping it to a min and max rotation in the X axis). And then figure the angle of the input and apply it to the Y axis of the 3D joystick.
This is the code I have, the joystick tilts properly in the X axis but the rotation in the Y axis doesn't work:
public void SetStickRotation(Vector2 stickInput)
{
float magnitude = stickInput.magnitude;
// This function converts the magnitude to a range between the min and max rotation I want to apply to the 3D stick in the X axis
float rotationX = Utils.ConvertRange(0.0f, 1.0f, m_StickRotationMinX, m_StickRotationMaxX, magnitude);
float angle = Mathf.Atan2(stickInput.x, stickInput.y);
// I try to apply both rotations to the 3D model
m_Stick.localEulerAngles = new Vector3(rotationX, angle, 0.0f);
}
I am not sure why is not working or even if I am doing it the right way (i.e. perhaps there is a more optimal way to achieve it).
Many thanks for your input.
I would recommend rotating it by an amount determined by the magnitude around a single axis determined by the direction. This will avoid the joystick spinning around, which would be especially noticeable in cases of asymmetric joysticks such as pilots joysticks:
Explanation in comments:
public void SetStickRotation(Vector2 stickInput)
{
/////////////////////////////////////////
// CONSTANTS (consider making a field) //
/////////////////////////////////////////
float maxRotation = 35f; // can rotate 35 degrees from neutral position (up)
///////////
// LOGIC //
///////////
// Convert input to x/z plane
Vector3 stickInput3 = new Vector3(stickInput.x, 0f, stickInput.y);
// determine axis of rotation to produce that direction
Vector3 axisOfRotation = Vector3.Cross(Vector3.up, stickInput3);
// determine angle of rotation
float angleOfRotation = maxRotation * Mathf.Min(1f, stickInput.magnitude);
// apply that rotation to the joystick as a local rotation
transform.localRotation = Quaternion.AngleAxis(angleOfRotation, axisOfRotation);
}
This will work for joysticks where:
the direction from its axle to its end is the local up direction,
it should have zero (identity) rotation on neutral input, and
stickInput with y=0 should rotate the knob around the stick's forward/back axis, and stickInput with x=0 should rotate the knob around the stick's left/right axis.
Figure out the problem, atan2 returns the angle in radiants, however the code assumes it is euler degrees, as soon as I did the conversion it worked well.
I put the code here if anyone is interested (not the change in the atan2 function):
public void SetStickRotation(Vector2 stickInput)
{
float magnitude = stickInput.magnitude;
// This function converts the magnitude to a range between the min and max rotation I want to apply to the 3D stick in the X axis
float rotationX = Utils.ConvertRange(0.0f, 1.0f, m_StickRotationMinX, m_StickRotationMaxX, magnitude);
float angle = Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg;
// Apply both rotations to the 3D model
m_Stick.localEulerAngles = new Vector3(rotationX, angle, 0.0f);
}

Find slope in degrees between two vector3

Hi,
I found a large number of references but without being able to adapt them to my needs.
As per attached figures I have my character in a given position. Below the character's feet is a new plane (). With the mouse wheel I move the character up along the Y axis and the plane moves with it. Then I drag the character to any position and I join the three vector3s with Gizmos lines. Now I need to know the slope in degrees between the starting point (the red point) and the new position of the character. I tried to use Vector3.Angle or Atan2 and many examples found around but all return different values when you rotate the character despite the slope is always the same. For example charAngle = Vector3.Angle (initialCharPos - character.transform.position, Vector3.left) returns the correct value only in that certain direction and I can get the 4 points left, right, forward, back. But for directions other than these? I was wondering if for each of the 360 points it is necessary to make checks based on the direction or if there is a faster way to get this value.
You can use Vector3.Angle, you just need to take it between the down direction & the direction from the new feet position to the start feet position, and subtract the result from 90:
Vector3 newFeetPosition;
Vector3 startFeetPosition;
// direction of "down", could be different in a zero g situation for instance
Vector3 downDirection = Vector3.down:
float slopeDegrees = 90f - Vector3.Angle(newFeetPosition - startFeetPosition, downDirection);
If you need the rise/run for other reasons, you can get them in the process of calculating the angle yourself using vector math:
Vector3 newFeetPosition;
Vector3 startFeetPosition;
// direction of "up", could be different in a zero g situation for instance
Vector3 upDirection = Vector3.up:
Vector3 feetDiff = newFeetPosition - startFeetPosition:
float riseMagnitude = Vector3.Dot(feetDiff, upDirection);
Vector3 riseVector = riseMagnitude * upDirection;
float runMagnitude = (feetDiff - riseVector).magnitude;
float slopeDegrees = Mathf.Rad2Deg * Mathf.Atan2(riseMagnitude, runMagnitude);

Unity destination Vector based on rotation and the amount to move

I'm having an issue where I can't figure out the algorithm to find out the destination point based on objects rotation and the amount to move. I have to move to the direction of my rotation a certain amount, but I don't know how to calculate the destination point I end up being at. Example:
Object location = (0, 0)
Object rotation = 45
Amount to move = 4
with these variables the destination point would be (2.5, 2.5)
Example 2:
Object location = (0, 0)
Object rotation = 0
Amount to move = 4
and with these it would be (0, 4)
The problem is, I don't know how to calculate the destination point when I know those variables. I need an algorithm that will calculate the destination point, can somebody help with this? :)
Regards, Tuukka.
If this is a strictly algorithmic question where you want to calculate the destination point (i.e. no game object to move around, but abstract data), you can do this:
Consider the two-dimensional plane in cartesian coordinates, (i.e. the standard x/y system). Let O be an object at point (0,0). From your "destination point" (2.5, 2.5) I can assume that you want the following thing:
So 45° is the angle and 4 (amount to move) is the length of the line segment you want to move along. Starting from (0,0), this end point can be calculated using sine and cosine by using the formula for the polar representation of a point:
But actually, that image is wrong, which we'll see in the following computation. If the movement is along the line with a slope angle of 45°, you'd land a little bit elsewhere.
Anyways, for this example, alpha would be 45° which is pi/4 in radians (you get this by dividing by 180 and multiplying with pi), and the radius r would be 4 (the amount we want to move), so we'd have calculated the destination point as:
If the point is located anywhere in the room (not at (0,0) but at (x_0, y_0)), then you can still add it as an offset:
So in code you'd write:
public static Vector2 ComputeDestination(Vector2 origin, float amountToMove, float angle)
{
//convert degrees to radians
var rad = angle * Mathf.Deg2Rad;
//calculate end point
var end_point = origin + amountToMove * new Vector2(Mathf.Cos(rad), Mathf.Sin(rad));
return end_point;
}
float homMuchToMove = 4f;
float angle = 45f;
float pointX = Mathf.Cos (ConvertToRadians (angle)) * homMuchToMove;
float pointY = Mathf.Sin (ConvertToRadians (angle)) * homMuchToMove;
public float ConvertToRadians(float angle)
{
return (Mathf.PI / 180f) * angle;
}
For these values you will get both points at 2.828427f

Correct rotation with Quaternion

I have some problems with a rotating marble.
I've tried it with Matrix.CreateFromYawPitchRoll and Matrix.CreateRotation but there were some problems, I think it's due to the Gimbal lock effect.
So, I've tried using quaternions instead, but nothing changed.
When moving on only an axis it works fine, but when the rotation occurs on two different axes the marble still rotates on wrong axes.
Here's my code:
// declarations
Vector3 Position = Vector3.Zero;
Vector3 Rotation = Vector3.Zero;
Quaternion qRotation = Quaternion.Identity;
AbsoluteBoneTransforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(AbsoluteBoneTransforms);
In the Update method:
Position += speed;
Rotation = speed * MathHelper.ToRadians(-1.5f);
Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.Right, Rotation.Z) *
Quaternion.CreateFromAxisAngle(Vector3.Backward, Rotation.X);
qRotation *= rot;
And in the Draw method:
effect.World = AbsoluteBoneTransforms[mesh.ParentBone.Index] *
Matrix.CreateFromQuaternion(qRotation) * Matrix.CreateTranslation(Position);
What's wrong? Is it wrong to use Quaternion.CreateFromAxisAngle on multiple axes?
EDIT
I've tried calculating directly the axis of rotation of my marble, instead of using combination of multiple axes:
angle += speed.Length() * angularVelocity;
qRotation = Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle);
qRotation.Normalize();
angle is a float that keeps track of the current movement.
This solution doesn't seem to create Gimbal lock, but marble rotations aren't correct, it seems that the rotating speed is not constant, but became faster and slower over time, I can't understand why.
If I "concatenate" the quaternions I get every frame using
qRotation *= Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle)
the Gimbal lock effect is still visible.
Here's how I've tackled that:
I'm assuming speed is a vector representing the direction the ball is rolling and whose magnitude represents the rate it is traveling in that direction.
Vector3 axis = Vector3.Cross(speed, Vector3.Up);
float angle = speed.Length();//factor by delta time if neccesary
Quaternion rotationThisFrame = Quaternion.CreateFromAxisAngle(axis, angle * (1/radiusOfBall));
then you can concatenate that to your qRotation. Also, you may need to normalize your quaternion after concatenation.
Update: The correct answer to this question/thread was reversing the order that quaternions concatenate in. With respect to XNA, matrices combine left to right, quaternions combine right to left.

Categories

Resources