mirror of
https://github.com/lubosz/overte.git
synced 2025-04-07 02:22:32 +02:00
Merge pull request #10579 from AndrewMeadows/fix-generateBasisVectors
Fix generateBasisVectors() helper
This commit is contained in:
commit
67970182aa
6 changed files with 112 additions and 36 deletions
|
@ -1135,9 +1135,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
}
|
||||
|
||||
glm::vec3 headUp = headQuat * Vectors::UNIT_Y;
|
||||
glm::vec3 z, y, x;
|
||||
generateBasisVectors(lookAtVector, headUp, z, y, x);
|
||||
glm::mat3 m(glm::cross(y, z), y, z);
|
||||
glm::vec3 z, y, zCrossY;
|
||||
generateBasisVectors(lookAtVector, headUp, z, y, zCrossY);
|
||||
glm::mat3 m(-zCrossY, y, z);
|
||||
glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));
|
||||
|
||||
glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
|
||||
|
|
|
@ -260,7 +260,7 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPo
|
|||
// there is room, so lets draw a nice bone
|
||||
|
||||
glm::vec3 uAxis, vAxis, wAxis;
|
||||
generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis);
|
||||
generateBasisVectors(boneAxis0, glm::vec3(1.0f, 0.0f, 0.0f), uAxis, vAxis, wAxis);
|
||||
|
||||
glm::vec3 boneBaseCorners[NUM_BASE_CORNERS];
|
||||
boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius));
|
||||
|
|
|
@ -49,7 +49,7 @@ const mat4 Matrices::Z_180 { createMatFromQuatAndPos(Quaternions::Z_180, Vectors
|
|||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
|
||||
float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
|
||||
float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
|
||||
|
||||
|
||||
// adjust signs if necessary
|
||||
if (cosa < 0.0f) {
|
||||
cosa = -cosa;
|
||||
|
@ -58,19 +58,19 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
|
|||
oz = -oz;
|
||||
ow = -ow;
|
||||
}
|
||||
|
||||
|
||||
// calculate coefficients; if the angle is too close to zero, we must fall back
|
||||
// to linear interpolation
|
||||
if ((1.0f - cosa) > EPSILON) {
|
||||
float angle = acosf(cosa), sina = sinf(angle);
|
||||
s0 = sinf((1.0f - proportion) * angle) / sina;
|
||||
s1 = sinf(proportion * angle) / sina;
|
||||
|
||||
|
||||
} else {
|
||||
s0 = 1.0f - proportion;
|
||||
s1 = proportion;
|
||||
}
|
||||
|
||||
|
||||
return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
|
||||
}
|
||||
|
||||
|
@ -105,10 +105,10 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm
|
|||
|
||||
int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
|
||||
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0f);
|
||||
|
||||
|
||||
uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);
|
||||
memcpy(buffer, &angleHolder, sizeof(uint16_t));
|
||||
|
||||
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
|
|||
quatParts[1] = floorf((quatNormalized.y + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[2] = floorf((quatNormalized.z + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[3] = floorf((quatNormalized.w + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
|
||||
|
||||
memcpy(buffer, &quatParts, sizeof(quatParts));
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
@ -133,12 +133,12 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
|
|||
int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
|
||||
uint16_t quatParts[4];
|
||||
memcpy(&quatParts, buffer, sizeof(quatParts));
|
||||
|
||||
|
||||
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
|
||||
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
|
||||
asinf(sy),
|
||||
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
|
||||
|
||||
|
||||
} else {
|
||||
// not a unique solution; x + z = atan2(-m21, m11)
|
||||
eulers = glm::vec3(
|
||||
|
@ -250,7 +250,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
PI_OVER_TWO,
|
||||
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
|
||||
}
|
||||
|
||||
|
||||
// adjust so that z, rather than y, is in [-pi/2, pi/2]
|
||||
if (eulers.z < -PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
|
@ -265,7 +265,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
eulers.y -= PI;
|
||||
}
|
||||
eulers.z += PI;
|
||||
|
||||
|
||||
} else if (eulers.z > PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
eulers.x += PI;
|
||||
|
@ -320,7 +320,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
for (int i = 0; i < 10; i++) {
|
||||
// store the results of the previous iteration
|
||||
glm::mat3 previous = upper;
|
||||
|
||||
|
||||
// compute average of the matrix with its inverse transpose
|
||||
float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
|
||||
float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
|
||||
|
@ -334,15 +334,15 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
|
||||
upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
|
||||
upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
|
||||
|
||||
|
||||
upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
|
||||
upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
|
||||
upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
|
||||
|
||||
|
||||
upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
|
||||
upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
|
||||
upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
|
||||
|
||||
|
||||
// compute the difference; if it's small enough, we're done
|
||||
glm::mat3 diff = upper - previous;
|
||||
if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
|
||||
|
@ -352,7 +352,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now that we have a nice orthogonal matrix, we can extract the rotation quaternion
|
||||
// using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
|
||||
float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
|
||||
|
@ -473,7 +473,7 @@ glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat&
|
|||
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
|
||||
}
|
||||
|
||||
// cancel out roll
|
||||
// cancel out roll
|
||||
glm::quat cancelOutRoll(const glm::quat& q) {
|
||||
glm::vec3 forward = q * Vectors::FRONT;
|
||||
return glm::quat_cast(glm::inverse(glm::lookAt(Vectors::ZERO, forward, Vectors::UP)));
|
||||
|
@ -538,17 +538,16 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
|
|||
uAxisOut = glm::normalize(primaryAxis);
|
||||
glm::vec3 normSecondary = glm::normalize(secondaryAxis);
|
||||
|
||||
// if secondaryAxis is parallel with the primaryAxis, pick another axis.
|
||||
// if normSecondary is parallel with the primaryAxis, pick another secondary.
|
||||
const float EPSILON = 1.0e-4f;
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
|
||||
// pick a better secondaryAxis.
|
||||
normSecondary = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
|
||||
normSecondary = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
|
||||
normSecondary = Vectors::UNIT_X;
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
|
||||
normSecondary = Vectors::UNIT_Y;
|
||||
}
|
||||
}
|
||||
|
||||
wAxisOut = glm::normalize(glm::cross(uAxisOut, secondaryAxis));
|
||||
wAxisOut = glm::normalize(glm::cross(uAxisOut, normSecondary));
|
||||
vAxisOut = glm::cross(wAxisOut, uAxisOut);
|
||||
}
|
||||
|
||||
|
|
|
@ -140,3 +140,77 @@ void GLMHelpersTests::testSimd() {
|
|||
}
|
||||
qDebug() << "Done ";
|
||||
}
|
||||
|
||||
void GLMHelpersTests::testGenerateBasisVectors() {
|
||||
{ // very simple case: primary along X, secondary is linear combination of X and Y
|
||||
glm::vec3 u(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 v(1.0f, 1.0f, 0.0f);
|
||||
glm::vec3 w;
|
||||
|
||||
generateBasisVectors(u, v, u, v, w);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_X, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_Y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(w, Vectors::UNIT_Z, EPSILON);
|
||||
}
|
||||
|
||||
{ // point primary along Y instead of X
|
||||
glm::vec3 u(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 v(1.0f, 1.0f, 0.0f);
|
||||
glm::vec3 w;
|
||||
|
||||
generateBasisVectors(u, v, u, v, w);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_Y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_X, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(w, -Vectors::UNIT_Z, EPSILON);
|
||||
}
|
||||
|
||||
{ // pass bad data (both vectors along Y). The helper will guess X for secondary.
|
||||
glm::vec3 u(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 v(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 w;
|
||||
|
||||
generateBasisVectors(u, v, u, v, w);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_Y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_X, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(w, -Vectors::UNIT_Z, EPSILON);
|
||||
}
|
||||
|
||||
{ // pass bad data (both vectors along X). The helper will guess X for secondary, fail, then guess Y.
|
||||
glm::vec3 u(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 v(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 w;
|
||||
|
||||
generateBasisVectors(u, v, u, v, w);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_X, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_Y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(w, Vectors::UNIT_Z, EPSILON);
|
||||
}
|
||||
|
||||
{ // general case for arbitrary rotation
|
||||
float angle = 1.234f;
|
||||
glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||
glm::quat rotation = glm::angleAxis(angle, axis);
|
||||
|
||||
// expected values
|
||||
glm::vec3 x = rotation * Vectors::UNIT_X;
|
||||
glm::vec3 y = rotation * Vectors::UNIT_Y;
|
||||
glm::vec3 z = rotation * Vectors::UNIT_Z;
|
||||
|
||||
// primary is along x
|
||||
// secondary is linear combination of x and y
|
||||
// tertiary is unknown
|
||||
glm::vec3 u = 1.23f * x;
|
||||
glm::vec3 v = 2.34f * x + 3.45f * y;
|
||||
glm::vec3 w;
|
||||
|
||||
generateBasisVectors(u, v, u, v, w);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(u, x, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(v, y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(w, z, EPSILON);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ private slots:
|
|||
void testEulerDecomposition();
|
||||
void testSixByteOrientationCompression();
|
||||
void testSimd();
|
||||
void testGenerateBasisVectors();
|
||||
};
|
||||
|
||||
float getErrorDifference(const float& a, const float& b);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "StorageTests.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
QTEST_MAIN(StorageTests)
|
||||
|
||||
using namespace storage;
|
||||
|
@ -32,8 +34,8 @@ void StorageTests::testConversion() {
|
|||
QFileInfo fileInfo(_testFile);
|
||||
QCOMPARE(fileInfo.exists(), false);
|
||||
}
|
||||
StoragePointer storagePointer = std::make_unique<MemoryStorage>(_testData.size(), _testData.data());
|
||||
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
|
||||
StoragePointer storagePointer = std::unique_ptr<MemoryStorage>(new MemoryStorage(_testData.size(), _testData.data()));
|
||||
QCOMPARE(storagePointer->size(), _testData.size());
|
||||
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
|
||||
// Convert to a file
|
||||
storagePointer = storagePointer->toFileStorage(_testFile);
|
||||
|
@ -42,12 +44,12 @@ void StorageTests::testConversion() {
|
|||
QCOMPARE(fileInfo.exists(), true);
|
||||
QCOMPARE(fileInfo.size(), (qint64)_testData.size());
|
||||
}
|
||||
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
|
||||
QCOMPARE(storagePointer->size(), _testData.size());
|
||||
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
|
||||
|
||||
// Convert to memory
|
||||
storagePointer = storagePointer->toMemoryStorage();
|
||||
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
|
||||
QCOMPARE(storagePointer->size(), _testData.size());
|
||||
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
|
||||
{
|
||||
// ensure the file is unaffected
|
||||
|
@ -58,13 +60,13 @@ void StorageTests::testConversion() {
|
|||
|
||||
// truncate the data as a new memory object
|
||||
auto newSize = _testData.size() / 2;
|
||||
storagePointer = std::make_unique<MemoryStorage>(newSize, storagePointer->data());
|
||||
QCOMPARE(storagePointer->size(), (quint64)newSize);
|
||||
storagePointer = std::unique_ptr<Storage>(new MemoryStorage(newSize, storagePointer->data()));
|
||||
QCOMPARE(storagePointer->size(), newSize);
|
||||
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
|
||||
|
||||
// Convert back to file
|
||||
storagePointer = storagePointer->toFileStorage(_testFile);
|
||||
QCOMPARE(storagePointer->size(), (quint64)newSize);
|
||||
QCOMPARE(storagePointer->size(), newSize);
|
||||
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
|
||||
{
|
||||
// ensure the file is truncated
|
||||
|
|
Loading…
Reference in a new issue