-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathQuaternionMaths.hpp
140 lines (122 loc) · 4.47 KB
/
QuaternionMaths.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
MessyBsp. BSP collision and loading example code.
Copyright (C) 2014 Richard Maxwell <[email protected]>
This file is part of MessyBsp
MessyBsp is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#pragma once
#include "Geometry.hpp"
#include <cmath>
// ///////////////////
// Operators
// ///////////////////
inline Quaternion& operator*=(Quaternion& lhs, const Quaternion& rhs)
{
lhs = Quaternion
{
(lhs.data[0] * rhs.data[0]) - (lhs.data[0] * rhs.data[1]) - (lhs.data[0] * rhs.data[2]) - (lhs.data[0] * rhs.data[3]),
(lhs.data[1] * rhs.data[1]) + (lhs.data[1] * rhs.data[0]) + (lhs.data[1] * rhs.data[3]) - (lhs.data[1] * rhs.data[2]),
(lhs.data[2] * rhs.data[2]) - (lhs.data[2] * rhs.data[3]) + (lhs.data[2] * rhs.data[0]) + (lhs.data[2] * rhs.data[1]),
(lhs.data[3] * rhs.data[3]) + (lhs.data[3] * rhs.data[2]) - (lhs.data[3] * rhs.data[1]) + (lhs.data[3] * rhs.data[0])
};
return lhs;
}
inline Quaternion operator*(Quaternion lhs, const Quaternion& rhs){ lhs *= rhs; return lhs; }
// ///////////////////
// Conversions
// ///////////////////
inline Quaternion ToQuaternion(const Vec3& axis, Radians angle)
{
// found my first GCC bug.
// float version of cos/sin arn't called resulting in doubles
// resulting in narrowing errors. Hack is the cast to double.
// RAM: TODO: Post bug report, find way of using float version.
return
{
Normalise(Vec4
{
static_cast<float>(axis.data[0] * sin(angle.value / 2.0f)),
static_cast<float>(axis.data[1] * sin(angle.value / 2.0f)),
static_cast<float>(axis.data[2] * sin(angle.value / 2.0f)),
static_cast<float>(cos(angle.value / 2.0f))
}).data
};
};
// From and to are unit vectors.
// The Quaternion is the rotation between them.
inline Quaternion ToQuaternion(const Vec3N& from, const Vec3N& to)
{
// http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors
// from and to are unit vectors.
auto w = Cross(from, to);
auto q = Vec4
{
w.data[0],
w.data[1],
w.data[2],
1.0f + DotF(to, from)
};
return {Normalise(q).data};
}
// ///////////////////
// Vector Return Maths
// ///////////////////
inline Quaternion Normalise(const Quaternion& lhs)
{
// Use Vec4's version.
return
{
Normalise(Vec4{lhs.data}).data
};
}
// commutative : yes
// constant velocity : no
// torque-minimal : yes
// Computation : cheap
inline Quaternion NLerp(const Quaternion& lhs, const Quaternion& rhs, float scale)
{
// Use Vec4's version.
return
{
Normalise(Lerp(Vec4{lhs.data}, Vec4{rhs.data}, scale)).data
};
}
// commutative : no
// constant velocity : yes
// torque-minimal : yes
// Computation : expensive (sin + acos)
inline Quaternion SLerp(const Quaternion& lhs, const Quaternion& rhs, float scale)
{
// Adapted from bullet3
auto Vec4lhs = Vec4{lhs.data};
auto Vec4rhs = Vec4{rhs.data};
auto magnitude = std::sqrt(MagnitudeF(Vec4lhs) * MagnitudeF(Vec4rhs));
// RAM: TODO: deal with quaternions that are too close. Ie magnitude !> 0
float product = DotF(Vec4lhs, Vec4rhs) / magnitude;
if (std::fabs(product) < 1.0f)
{
// Take care of long angle case see http://en.wikipedia.org/wiki/Slerp
auto sign = (product < 0) ? float(-1) : float(1);
auto theta = std::acos(sign * product);
auto s1 = std::sin(sign * scale * theta);
auto d = 1.0f / std::sin(theta);
auto s0 = std::sin((1.0f - scale) * theta);
return
{
(lhs.data[0] * s0 + rhs.data[0] * s1) * d,
(lhs.data[1] * s0 + rhs.data[1] * s1) * d,
(lhs.data[2] * s0 + rhs.data[2] * s1) * d,
(lhs.data[3] * s0 + rhs.data[3] * s1) * d
};
}
return lhs;
}