Optimize Quat_FromMtx()
This commit is contained in:
parent
8f3fda7986
commit
c2226e2182
@ -2,54 +2,61 @@
|
|||||||
|
|
||||||
C3D_FQuat Quat_FromMtx(const C3D_Mtx* m)
|
C3D_FQuat Quat_FromMtx(const C3D_Mtx* m)
|
||||||
{
|
{
|
||||||
//Taken from Gamasutra:
|
// Original algorithm taken from here (with some optimizations):
|
||||||
//http://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php
|
// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
|
||||||
//Expanded upon from:
|
// Layout of the algorithm:
|
||||||
//http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
|
// First, we select a large (non-zero!) component "P" in the output quaternion (q)
|
||||||
|
// (we can test this just by looking at the diagonal of the matrix)
|
||||||
|
// Second, we calculate q' which is our desired output quaternion (q) scaled by 4P
|
||||||
|
// (this can be done with simple additions; the 4P factor is large and non-zero thanks to above)
|
||||||
|
// Third, we normalize q' to finally obtain q
|
||||||
|
// (this will work because normalize(kq) = q for any k scalar and q unit quaternion)
|
||||||
|
|
||||||
//Variables we need.
|
|
||||||
float trace, sqrtTrace;
|
|
||||||
C3D_FQuat q;
|
C3D_FQuat q;
|
||||||
|
C3D_FVec diagonal = FVec4_New(m->r[0].x, m->r[1].y, m->r[2].z, 1.0f);
|
||||||
|
|
||||||
//Check the main diagonal of the passed-in matrix for positive/negative signs.
|
// Check if x^2 + y^2 >= z^2 + w^2
|
||||||
trace = m->r[0].x + m->r[1].y + m->r[2].z;
|
if (diagonal.z <= 0.0f)
|
||||||
if (trace > 0.0f)
|
|
||||||
{
|
{
|
||||||
//Diagonal is positive.
|
// Check if |x| >= |y|
|
||||||
sqrtTrace = sqrtf(trace + 1.0f);
|
if (diagonal.x >= diagonal.y)
|
||||||
q.w = sqrtTrace / 2.0f;
|
{
|
||||||
sqrtTrace = 0.5 / sqrtTrace;
|
// X case
|
||||||
q.x = (m->r[1].z - m->r[2].y) * sqrtTrace;
|
q.x = diagonal.w + diagonal.x - diagonal.y - diagonal.z;
|
||||||
q.y = (m->r[2].x - m->r[0].z) * sqrtTrace;
|
q.y = m->r[1].x + m->r[0].y;
|
||||||
q.z = (m->r[0].y - m->r[1].x) * sqrtTrace;
|
q.z = m->r[2].x + m->r[0].z;
|
||||||
|
q.w = m->r[2].y - m->r[1].z;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Diagonal is negative or equals to zero. We need to identify which major diagonal element has the greatest value.
|
// Y case
|
||||||
if (m->r[0].x > m->r[1].y && m->r[0].x > m->r[2].z)
|
q.x = m->r[1].x + m->r[0].y;
|
||||||
{
|
q.y = diagonal.w - diagonal.x + diagonal.y - diagonal.z;
|
||||||
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[0].x - m->r[1].y - m->r[2].z);
|
q.z = m->r[2].y + m->r[1].z;
|
||||||
q.w = (m->r[2].y - m->r[1].z) / sqrtTrace;
|
q.w = m->r[0].z - m->r[2].x;
|
||||||
q.x = 0.25f * sqrtTrace;
|
|
||||||
q.y = (m->r[0].y - m->r[1].x) / sqrtTrace;
|
|
||||||
q.z = (m->r[0].z - m->r[2].x) / sqrtTrace;
|
|
||||||
}
|
}
|
||||||
else if (m->r[1].y > m->r[2].z)
|
|
||||||
{
|
|
||||||
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[1].y - m->r[0].x - m->r[2].z);
|
|
||||||
q.w = (m->r[0].z - m->r[2].x) / sqrtTrace;
|
|
||||||
q.x = (m->r[0].y - m->r[1].x) / sqrtTrace;
|
|
||||||
q.y = 0.25f * sqrtTrace;
|
|
||||||
q.z = (m->r[1].z - m->r[2].y) / sqrtTrace;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[2].z - m->r[0].x - m->r[1].y);
|
// Check if |z| >= |w|
|
||||||
q.w = (m->r[1].x - m->r[0].y) / sqrtTrace;
|
if (-diagonal.x >= diagonal.y)
|
||||||
q.x = (m->r[0].z - m->r[2].x) / sqrtTrace;
|
{
|
||||||
q.y = (m->r[1].z - m->r[2].y) / sqrtTrace;
|
// Z case
|
||||||
q.z = 0.25f * sqrtTrace;
|
q.x = m->r[2].x + m->r[0].z;
|
||||||
|
q.y = m->r[2].y + m->r[1].z;
|
||||||
|
q.z = diagonal.w - diagonal.x - diagonal.y + diagonal.z;
|
||||||
|
q.w = m->r[1].x - m->r[0].y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// W case
|
||||||
|
q.x = m->r[2].y - m->r[1].z;
|
||||||
|
q.y = m->r[0].z - m->r[2].x;
|
||||||
|
q.z = m->r[1].x - m->r[0].y;
|
||||||
|
q.w = diagonal.w + diagonal.x + diagonal.y + diagonal.z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return q;
|
|
||||||
|
// Normalize the quaternion
|
||||||
|
return Quat_Normalize(q);
|
||||||
}
|
}
|
||||||
|
@ -953,13 +953,15 @@ check_quaternion(generator_t &gen, distribution_t &dist)
|
|||||||
|
|
||||||
// check conversion to matrix
|
// check conversion to matrix
|
||||||
{
|
{
|
||||||
C3D_FQuat q = randomQuat(gen, dist);
|
C3D_FQuat q = Quat_Normalize(randomQuat(gen, dist));
|
||||||
glm::quat g = loadQuat(q);
|
glm::quat g = loadQuat(q);
|
||||||
|
|
||||||
C3D_Mtx m;
|
C3D_Mtx m;
|
||||||
Mtx_FromQuat(&m, q);
|
Mtx_FromQuat(&m, q);
|
||||||
|
|
||||||
assert(m == glm::mat4_cast(g));
|
assert(m == glm::mat4_cast(g));
|
||||||
|
|
||||||
|
C3D_FQuat q2 = Quat_FromMtx(&m);
|
||||||
|
assert(q2 == q || q2 == FVec4_Negate(q));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user