217 lines
6.7 KiB
HLSL
217 lines
6.7 KiB
HLSL
#ifndef QUATERNION_HLSL_
|
|
#define QUATERNION_HLSL_
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct Quaternion
|
|
{
|
|
float4 value;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Identity()
|
|
{
|
|
Quaternion rv;
|
|
rv.value = float4(0, 0, 0, 1);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static float3 Rotate(Quaternion q, float3 v)
|
|
{
|
|
float3 t = 2 * cross(q.value.xyz, v);
|
|
float3 rv = v + q.value.w * t + cross(q.value.xyz, t);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Multiply(Quaternion a, Quaternion b)
|
|
{
|
|
Quaternion rv;
|
|
rv.value = float4(a.value.wwww * b.value + (a.value.xyzx * b.value.wwwx + a.value.yzxy * b.value.zxyy) * float4(1.0f, 1.0f, 1.0f, -1.0f) - a.value.zxyz * b.value.yzxz);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion ShortestRotation(Quaternion from, Quaternion to)
|
|
{
|
|
Quaternion rv;
|
|
uint4 sign = asuint(dot(from.value, to.value)) & 0x80000000;
|
|
rv.value = asfloat(sign ^ asuint(to.value));
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion AxisAngle(float3 axis, float angle)
|
|
{
|
|
float sina, cosa;
|
|
sincos(0.5f * angle, sina, cosa);
|
|
Quaternion rv;
|
|
rv.value = float4(axis * sina, cosa);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Normalize(Quaternion q)
|
|
{
|
|
float4 x = q.value;
|
|
Quaternion rv;
|
|
rv.value = rsqrt(dot(x, x)) * x;
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Inverse(Quaternion q)
|
|
{
|
|
float4 a = q.value;
|
|
Quaternion rv;
|
|
rv.value = rcp(dot(a, a)) * a * float4(-1.0f, -1.0f, -1.0f, 1.0f);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Conjugate(Quaternion q)
|
|
{
|
|
Quaternion rv;
|
|
rv.value = q.value * float4(-1, -1, -1, 1);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion NormalizeSafe(Quaternion q)
|
|
{
|
|
float FLT_MIN_NORMAL = 1.175494351e-38F;
|
|
float len = dot(q.value, q.value);
|
|
Quaternion rv;
|
|
rv.value = float4(0, 0, 0, 1);
|
|
|
|
if (len > FLT_MIN_NORMAL)
|
|
{
|
|
rv.value = q.value * rsqrt(len);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion FromMatrix(float3x3 m)
|
|
{
|
|
Quaternion rv;
|
|
|
|
float3 u = m._m00_m10_m20;
|
|
float3 v = m._m01_m11_m21;
|
|
float3 w = m._m02_m12_m22;
|
|
|
|
uint u_sign = asuint(u.x) & 0x80000000;
|
|
float t = v.y + asfloat(asuint(w.z) ^ u_sign);
|
|
uint1 u_mask1 = (int)u_sign >> 31;
|
|
uint1 t_mask1 = asint(t) >> 31;
|
|
uint4 u_mask = u_mask1.xxxx;
|
|
uint4 t_mask = t_mask1.xxxx;
|
|
uint4 u_mask_inv = uint4(~u_mask1.x, ~u_mask1.x, ~u_mask1.x, ~u_mask1.x);
|
|
uint4 t_mask_inv = uint4(~t_mask1.x, ~t_mask1.x, ~t_mask1.x, ~t_mask1.x);
|
|
|
|
float tr = 1.0f + abs(u.x);
|
|
|
|
uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
|
|
|
|
rv.value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
|
|
|
|
rv.value = asfloat((asuint(rv.value) & u_mask_inv) | (asuint(rv.value.zwxy) & u_mask));
|
|
rv.value = asfloat((asuint(rv.value.wzyx) & t_mask_inv) | (asuint(rv.value) & t_mask));
|
|
rv.value = normalize(rv.value);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float3x3 ToRotationMatrix()
|
|
{
|
|
float4 value2 = value * 2;
|
|
uint3 npn = uint3(0x80000000, 0x00000000, 0x80000000);
|
|
uint3 nnp = uint3(0x80000000, 0x80000000, 0x00000000);
|
|
uint3 pnn = uint3(0x00000000, 0x80000000, 0x80000000);
|
|
float3 c0 = value2.y * asfloat(asuint(value.yxw) ^ npn) - value2.z * asfloat(asuint(value.zwx) ^ pnn) + float3(1, 0, 0);
|
|
float3 c1 = value2.z * asfloat(asuint(value.wzy) ^ nnp) - value2.x * asfloat(asuint(value.yxw) ^ npn) + float3(0, 1, 0);
|
|
float3 c2 = value2.x * asfloat(asuint(value.zwx) ^ pnn) - value2.y * asfloat(asuint(value.wzy) ^ nnp) + float3(0, 0, 1);
|
|
|
|
float3x3 rv = float3x3(c0, c1, c2);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion EulerXYZ(float3 eulerAngles)
|
|
{
|
|
float3 sinV, cosV;
|
|
sincos(0.5f * eulerAngles, sinV, cosV);
|
|
Quaternion rv;
|
|
rv.value = float4(sinV.xyz, cosV.x) * cosV.yxxy * cosV.zzyz + sinV.yxxy * sinV.zzyz * float4(cosV.xyz, sinV.x) * float4(-1.0f, 1.0f, -1.0f, 1.0f);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static float4 ChangeSign(float4 a, float4 b)
|
|
{
|
|
return asfloat(asuint(a) ^ asuint(b) & 0x80000000);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Scale(Quaternion q, float s)
|
|
{
|
|
Quaternion rv;
|
|
rv.value = q.value * s;
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Nlerp(Quaternion a, Quaternion b, float t)
|
|
{
|
|
Quaternion rv;
|
|
rv.value = normalize(a.value + t * (ChangeSign(b.value, dot(a.value, b.value)) - a.value));
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Quaternion Slerp(Quaternion a, Quaternion b, float t)
|
|
{
|
|
float dt = dot(a.value, b.value);
|
|
if (dt < 0)
|
|
{
|
|
dt = -dt;
|
|
b.value = -b.value;
|
|
}
|
|
|
|
if (dt < 0.9995f)
|
|
{
|
|
float angle = acos(dt);
|
|
float s = rsqrt(1 - dt * dt);
|
|
float w1 = sin(angle * (1 - t)) * s;
|
|
float w2 = sin(angle * t) * s;
|
|
Quaternion rv;
|
|
rv.value = a.value * w1 + b.value * w2;
|
|
return rv;
|
|
}
|
|
|
|
return Nlerp(a, b, t);
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#endif
|
|
|
|
|