Calculating angles between two points in 3d space - c#

I have two points (cube game object) in Unity, P1(x,y,z) and P2(x,y,z) and I have to set the MainCamera position and rotation along the vector between P2P1.
I tried different methods but was not successful.
All suggestions are welcomed.
Thanks.

We can find angle between 2 vectors according the dot production.
angle = arccos ( a * b / |a| * |b| );
where:
a * b = ax * bx + ay * by + az * bz
|a| = sqrt( ax * ax + ay * ay + az * az )
|b| = sqrt( bx * bx + by * by + bz * bz )
Or just use this method: http://docs.unity3d.com/ScriptReference/Vector3.Angle.html

Related

Quaternion vector roation

My quaternion math is a bit rusty, and I'm trying to figure out which of the following implementations is more correct...
Looking here you can see Microsoft's version of transforming a vector by a quaternion: https://referencesource.microsoft.com/#System.Numerics/System/Numerics/Vector3.cs,347
Here is the code, and it has clearly been optimized:
public static Vector3 Transform(Vector3 value, Quaternion rotation)
{
float x2 = rotation.X + rotation.X;
float y2 = rotation.Y + rotation.Y;
float z2 = rotation.Z + rotation.Z;
float wx2 = rotation.W * x2;
float wy2 = rotation.W * y2;
float wz2 = rotation.W * z2;
float xx2 = rotation.X * x2;
float xy2 = rotation.X * y2;
float xz2 = rotation.X * z2;
float yy2 = rotation.Y * y2;
float yz2 = rotation.Y * z2;
float zz2 = rotation.Z * z2;
return new Vector3(
value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2) + value.Z * (xz2 + wy2),
value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2) + value.Z * (yz2 - wx2),
value.X * (xz2 - wy2) + value.Y * (yz2 + wx2) + value.Z * (1.0f - xx2 - yy2));
}
However, this gives different results to my own (less optimized) implementation:
public static Vector3 Transform(this Vector3 value, Quaternion rotation)
{
var q = new Quaternion(value.X, value.Y, value.Z, 0.0f);
var res = rotation.Conjugate() * q * rotation;
return new Vector3(res.X, res.Y, res.Z);
}
public static Quaternion operator *(Quaternion value1, Quaternion value2)
{
// 9 muls, 27 adds
var tmp_00 = (value1.Z - value1.Y) * (value2.Y - value2.Z);
var tmp_01 = (value1.W + value1.X) * (value2.W + value2.X);
var tmp_02 = (value1.W - value1.X) * (value2.Y + value2.Z);
var tmp_03 = (value1.Y + value1.Z) * (value2.W - value2.X);
var tmp_04 = (value1.Z - value1.X) * (value2.X - value2.Y);
var tmp_05 = (value1.Z + value1.X) * (value2.X + value2.Y);
var tmp_06 = (value1.W + value1.Y) * (value2.W - value2.Z);
var tmp_07 = (value1.W - value1.Y) * (value2.W + value2.Z);
var tmp_08 = tmp_05 + tmp_06 + tmp_07;
var tmp_09 = (tmp_04 + tmp_08) * 0.5f;
return new Quaternion(
tmp_01 + tmp_09 - tmp_08,
tmp_02 + tmp_09 - tmp_07,
tmp_03 + tmp_09 - tmp_06,
tmp_00 + tmp_09 - tmp_05
);
}
Since these do not give the same results, one of them must be wrong, but which one is it and why?
My own implementation seems to work in the cases that I'm trying to use it in, and the MS implementation seems to be broken, but I would be surprised if it were actually incorrect, since I think it is very widely used...
Thanks! :)
There are two different conventions for rotating a vector via a quaternion, left chain and right chain. E.g.,
vnew = q * v * conjugate(q) <-- This is left chain (successive rotations stack up on the left)
vnew = conjugate(q) * v * q <-- This is right chain (successive rotations stack up on the right)
Left chain convention is typically used for active rotations, where the quaternion is being used to rotate a vector within a coordinate frame. I.e., v and vnew are two different vectors coordinatized within the same coordinate frame.
Right chain convention is typically used for passive rotations, e.g. quaternions that represent coordinate system transformations. I.e., v and vnew are actually the same vector, just coordinatized in two different coordinate frames.
The MS code you show above is a left chain convention, but your code is a right chain convention. Hence the different results.
Both conventions are valid and have their uses, but you need to be very careful when pulling code from online sources. You need to verify what the convention used by the code is in order to use it correctly. And you need to ensure the convention matches how you are using the quaternions in your particular application.

How to make a semi-circular movement?

Suppose I have a car in position P0 and I want to move it to position P1, just like these 4 examples:
The linear distance between P0 and P1 is d and the perpendicular maximum height the movement reaches is d/3. I want to simulate this clockwise semi-circular movement from P0 to P1.
Suppose dir = P1 - P0 (length d) and perp is the vector (of length d/3) perpendicular to dir.
Suppose t = 0 is the beginning of the semi-circular movement and t = 1 is the end, how can I measure the angle and the position of the car at t = i?
We have to find angle of this arc and circle center.
At first find radius.
R^2 = (d/2)^2 + (R-d/3)^2 //pythagorean
R = 13/24 * d
Now angle
half_angle = arcsin(12/13) ~ 67.4 degrees
angle = 2 * half_angle ~ 135 degrees = 2.35 radians
Normalize perp vector
uperp = perp / len(perp)
Get circle center
M = (P0 + P1)/2 //chord middle
C = M + uperp * 5/24 * d
Starting angle
A0 = atan2(P0.Y-C.Y, P0.X-C.X)
And finally coordinates
Car.X = C.X + R * Cos(A0 + t * angle)
Car.Y = C.Y + R * Sin(A0 + t * angle)
In Unity this would look like:
Vector2 startPosition;
Vector2 endPosition;
Vector2 perp;
float t;
float d = (endPosition - startPosition).magnitude;
float radius = 13f/24f * d;
float angle = 2f * Mathf.Asin(12f/13f);
Vector2 uperp = perp.normalized;
Vector2 M = (startPosition+endPosition)*0.5f;
Vector2 C = M + uperp * 5f/24f * d;
float A0 = Mathf.Atan2(startPosition.y-C.y, startPosition.x-C.x);
float At = A0 + t * angle;
Vector2 newPos = C + radius * new Vector2(Mathf.Cos(At), Mathf.Sin(At));

Calculate new X,Y position based on angle center x,y is facing for a radar

I have a radar in my game. Right now the radar works using a static X,Y graph that does not rotate based on how you are facing in my game.
I want the X, Y positions on the radar to rotate based on the direction you are facing. Monsters in the front of you will always appear at the top of the radar.
Right now, the radar is static and monsters north of you will always appear north on the radar.
Currently, I have math like this:
int x = (int)(x * Math.Cos(Math.PI * angle / 180) + y * Math.Sin(Math.PI * angle / 180));
int y = (int)(y * Math.Cos(Math.PI * angle / 180) - x * Math.Sin(Math.PI * angle / 180));
Then I add the center X and center Y to the integers above, but the graph is weird and only seems to work from 359~ degree angle. I have tried adding my center X and center Y before calculating it, but it is always off.
Other code I found online differentiated based on the degrees/angle.
if (angle >= 0 && angle <= 180)
{
x = (int)(x * Math.Cos(Math.PI * angle / 180) - y * Math.Sin(Math.PI * angle / 180));
y = (int)(x * Math.Sin(Math.PI * angle / 180) + y * Math.Cos(Math.PI * angle / 180));
}
else
{
x = (int)(x * Math.Cos(Math.PI * angle / 180) - y * Math.Sin(Math.PI * angle / 180));
y = (int)(x * Math.Sin(Math.PI * angle / 180) + y * Math.Cos(Math.PI * angle / 180));
}
What is the correct formula I need to take the Center X(cX), center Y(cY), the angle I am facing and the target X(tX), target Y(tY) to relocate the targets X & Y.
the obvious answer is to use the same rotation code that rotates your point of view to rotate the radar coordinates, but I guess you rejected that for some reason.
all that example code is wrong x is being overwritten and then used to find the new value for y. That's never going to work.
The basic basic rotation formula looks like this:
x2 = (int)(x * Math.Cos(Math.PI * angle / 180) - y * Math.Sin(Math.PI * angle / 180));
y2 = (int)(x * Math.Sin(Math.PI * angle / 180) + y * Math.Cos(Math.PI * angle / 180));
but if the payer moves away from 0,0 you'll want
x2 = (int)((x - player_x) * Math.Cos(Math.PI * angle / 180) - (y - player_y) * Math.Sin(Math.PI * angle / 180));
y2 = (int)((x - player_x) * Math.Sin(Math.PI * angle / 180) + (y - player_y) * Math.Cos(Math.PI * angle / 180));
that will keep 0,0 on the radar centred on the player
dpending on how your game measures angles you might need to swap the sign on the Sin terms .

How to get an angle between a direction faced and a point in 3D space

The information I have is the vertical angle of the player's vision, the horizontal angle to the point in question, the distance to that point and the point's vertical height.
I've figured out how to get the angle if the player isn't looking up or down (vertical angle) by using the following.
float GetVisionAngle(float angleHoriz, float angleVert, float distance, float height)
{
double A = Math.Cos(angleHoriz * (Math.PI / 180)) * distance);
double hypotenuse = Math.Sqrt(distance * distance + height * height);
return (float)(Math.Acos(A / hypotenuse) * (180 / Math.PI));
}
What I can't figure out is how to get that angle if the player's vision direction is modified by a vertical angle (looking up or down). I've been mulling this over in my head for a few days and I can't figure out a way to do it.
What I'm using this for is to generate a vision cone cutoff. When an object is checked for visibility, the information I have to work with is the angle of the object from the player, the distance to that object, and its height. This initial range check will return an angle from the player's direction of vision and determine whether the object is visible or not.
Here is a shot of the code in debug using the solution provided by #HABO
Unfortunately, it always results in a NaN error.
Converting the angles to radians before using them seems to fix a lot of the numerical errors. I don't understand the formula at the end that converts the previous numbers into the final angle though.
aH = Angle in the horizontal plane between the line of sight (LOS) and the object. (angleHoriz)
aV = Angle in the vertical plane of the LOS. (angleVert)
d = Distance to the object in the horizontal plane. (distance)
h = Height of the object above the horizontal plane. (height)
dO = Distance from the origin to the object.
= sqrt( d * d + h * h )
oH = Horizontal offset from the LOS to the object at the base of the wall.
= sin( aH ) * d
dH = Horizontal distance from the origin to the wall.
= cos( aH ) * d
hLOS = Height at which the LOS intersects the wall.
= tan( aV ) * dH
dLOS = Distance from the observer to the LOS at the wall.
= sqrt( dH * dH + hLOS * hLOS )
dW = Distance along the wall between the line of sight and the object.
= sqrt( oH * oH + ( h - hLOS ) * ( h - hLOS ) )
answer = acos( ( dLOS * dLOS + dO * dO - dW * dW ) / ( 2 * dLOS * dO ) )

How to emulate Vector3.TransformNormal

I am trying to emulate Vector3.TransformNormal without the DirectX library.
Is anyone able to explain how this function works, to allow me to recreate the function?
So far I know the inputs and have seen the description of what it does, but I don't know the calculations.
public static Vector3 TransformNormal(
Vector3 source,
Matrix sourceMatrix
)
This should do it (didn't test)
public Vector3 TransformNormal(Vector3 normal, Matrix matrix)
{
return new Vector3
{
X = normal.X * matrix.M11 + normal.Y * matrix.M21 + normal.Z * matrix.M31,
Y = normal.X * matrix.M12 + normal.Y * matrix.M22 + normal.Z * matrix.M32,
Z = normal.X * matrix.M13 + normal.Y * matrix.M23 + normal.Z * matrix.M33
};
}

Categories