Add WRot SLerp and axis-angle ctor.
These allow more complex calculations to be made using rotations.
This commit is contained in:
@@ -24,6 +24,9 @@ namespace OpenRA
|
|||||||
// Internal calculations use the quaternion form
|
// Internal calculations use the quaternion form
|
||||||
readonly int x, y, z, w;
|
readonly int x, y, z, w;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a rotation from euler angles.
|
||||||
|
/// </summary>
|
||||||
public WRot(WAngle roll, WAngle pitch, WAngle yaw)
|
public WRot(WAngle roll, WAngle pitch, WAngle yaw)
|
||||||
{
|
{
|
||||||
Roll = roll;
|
Roll = roll;
|
||||||
@@ -48,6 +51,21 @@ namespace OpenRA
|
|||||||
w = (int)((cr * cp * cy + sr * sp * sy) / 1048576);
|
w = (int)((cr * cp * cy + sr * sp * sy) / 1048576);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a rotation from an axis and angle.
|
||||||
|
/// The axis is expected to be normalized to length 1024
|
||||||
|
/// </summary>
|
||||||
|
public WRot(WVec axis, WAngle angle)
|
||||||
|
{
|
||||||
|
// Angles increase clockwise
|
||||||
|
x = axis.X * new WAngle(-angle.Angle / 2).Sin() / 1024;
|
||||||
|
y = axis.Y * new WAngle(-angle.Angle / 2).Sin() / 1024;
|
||||||
|
z = axis.Z * new WAngle(-angle.Angle / 2).Sin() / 1024;
|
||||||
|
w = new WAngle(-angle.Angle / 2).Cos();
|
||||||
|
|
||||||
|
(Roll, Pitch, Yaw) = QuaternionToEuler(x, y, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
WRot(int x, int y, int z, int w)
|
WRot(int x, int y, int z, int w)
|
||||||
{
|
{
|
||||||
this.x = x;
|
this.x = x;
|
||||||
@@ -55,6 +73,11 @@ namespace OpenRA
|
|||||||
this.z = z;
|
this.z = z;
|
||||||
this.w = w;
|
this.w = w;
|
||||||
|
|
||||||
|
(Roll, Pitch, Yaw) = QuaternionToEuler(x, y, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static (WAngle, WAngle, WAngle) QuaternionToEuler(int x, int y, int z, int w)
|
||||||
|
{
|
||||||
// Theoretically 1024 squared, but may differ slightly due to rounding
|
// Theoretically 1024 squared, but may differ slightly due to rounding
|
||||||
var lsq = x * x + y * y + z * z + w * w;
|
var lsq = x * x + y * y + z * z + w * w;
|
||||||
|
|
||||||
@@ -64,9 +87,11 @@ namespace OpenRA
|
|||||||
var sycp = 2 * (w * z + x * y);
|
var sycp = 2 * (w * z + x * y);
|
||||||
var cycp = lsq - 2 * (y * y + z * z);
|
var cycp = lsq - 2 * (y * y + z * z);
|
||||||
|
|
||||||
Roll = -WAngle.ArcTan(srcp, crcp);
|
var roll = -WAngle.ArcTan(srcp, crcp);
|
||||||
Pitch = -(Math.Abs(sp) >= 1024 ? new WAngle(Math.Sign(sp) * 256) : WAngle.ArcSin(sp));
|
var pitch = -(Math.Abs(sp) >= 1024 ? new WAngle(Math.Sign(sp) * 256) : WAngle.ArcSin(sp));
|
||||||
Yaw = -WAngle.ArcTan(sycp, cycp);
|
var yaw = -WAngle.ArcTan(sycp, cycp);
|
||||||
|
|
||||||
|
return (roll, pitch, yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
WRot(int x, int y, int z, int w, WAngle roll, WAngle pitch, WAngle yaw)
|
WRot(int x, int y, int z, int w, WAngle roll, WAngle pitch, WAngle yaw)
|
||||||
@@ -168,5 +193,32 @@ namespace OpenRA
|
|||||||
public override bool Equals(object obj) { return obj is WRot && Equals((WRot)obj); }
|
public override bool Equals(object obj) { return obj is WRot && Equals((WRot)obj); }
|
||||||
|
|
||||||
public override string ToString() { return Roll + "," + Pitch + "," + Yaw; }
|
public override string ToString() { return Roll + "," + Pitch + "," + Yaw; }
|
||||||
|
|
||||||
|
public static WRot SLerp(in WRot a, in WRot b, int mul, int div)
|
||||||
|
{
|
||||||
|
// This implements the standard spherical linear interpolation
|
||||||
|
// between two quaternions, accounting for OpenRA's integer math
|
||||||
|
// conventions and WRot always using (nearly) normalized quaternions
|
||||||
|
var dot = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||||
|
var flip = dot >= 0 ? 1 : -1;
|
||||||
|
|
||||||
|
// a and b describe the same rotation
|
||||||
|
if (flip * dot >= 1024 * 1024)
|
||||||
|
return a;
|
||||||
|
|
||||||
|
var theta = WAngle.ArcCos(dot / 1024);
|
||||||
|
var s1 = new WAngle((div - mul) * theta.Angle / div).Sin();
|
||||||
|
var s2 = new WAngle(mul * theta.Angle / div).Sin();
|
||||||
|
var s3 = theta.Sin();
|
||||||
|
|
||||||
|
var x = ((long)a.x * s1 + flip * b.x * s2) / s3;
|
||||||
|
var y = ((long)a.y * s1 + flip * b.y * s2) / s3;
|
||||||
|
var z = ((long)a.z * s1 + flip * b.z * s2) / s3;
|
||||||
|
var w = ((long)a.w * s1 + flip * b.w * s2) / s3;
|
||||||
|
|
||||||
|
// Normalize to 1024 == 1.0
|
||||||
|
var l = Exts.ISqrt(x * x + y * y + z * z + w * w);
|
||||||
|
return new WRot((int)(1024 * x / l), (int)(1024 * y / l), (int)(1024 * z / l), (int)(1024 * w / l));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user