This patch implements the matrix types (i.e:float4x4) by making heavy usage of templating. All matrix functions are now outside of the vector classes (inside the blender::math namespace) and are not vector size dependent for the most part. ###Motivations The goal/motivations of this rewrite are the same as the Vector C++ API (D13791): - Template everything for making it work with any types and avoid code duplication. - Use functional style instead of Object Oriented function call to allow a simple compatibility layer with GLSL syntax (see T103026 for more details). - Allow most convenient constructor syntax and accessors (array subscript `matrix[c][r]`, or component alias `matrix.y.z`). - Make it cover all features the current C API supports for adoption. - Keep compilation time and debug performance somehow acceptable. ###Consideration: - The new `MatView` class can be generated by `my_float.view<NumCol, NumRow, StartCol, StartRow>()` (with the last 2 being optionnal). This one allows modifying parts of the source matrix in place. It isn't pretty and duplicates a lot of code, but it is needed mainly to replace `normalize_m4`. At least I think it is a good starting point that can refined further. - An exhaustive list of missing `BLI_math_matrix.h` functions from the new API can be found here P3373. - This adds new Rotation types in order to have a clean API. This will be extended when we port the full Rotation API. The types are made so that they don't allow implicit down-casting to their vector representation. - Some functions make direct use of the Eigen library, bypassing the Eigen C API defined in `intern/eigen`. Its use is contained inside `math_matrix.cc`. There is conflicting opinion wether we should use it more so I contained its usage to almost the tasks as in the C API for now. Reviewed By: sergey, JacquesLucke, HooglyBoogly, Severin, brecht Differential Revision: https://developer.blender.org/D16625
243 lines
6.6 KiB
C++
243 lines
6.6 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include "BLI_math_matrix.hh"
|
|
#include "BLI_math_rotation_types.hh"
|
|
#include "BLI_math_vector.hh"
|
|
|
|
namespace blender::math {
|
|
|
|
/**
|
|
* Generic function for implementing slerp
|
|
* (quaternions and spherical vector coords).
|
|
*
|
|
* \param t: factor in [0..1]
|
|
* \param cosom: dot product from normalized vectors/quats.
|
|
* \param r_w: calculated weights.
|
|
*/
|
|
template<typename T> inline vec_base<T, 2> interpolate_dot_slerp(const T t, const T cosom)
|
|
{
|
|
const T eps = T(1e-4);
|
|
|
|
BLI_assert(IN_RANGE_INCL(cosom, T(-1.0001), T(1.0001)));
|
|
|
|
vec_base<T, 2> w;
|
|
/* Within [-1..1] range, avoid aligned axis. */
|
|
if (LIKELY(math::abs(cosom) < (T(1) - eps))) {
|
|
const T omega = math::acos(cosom);
|
|
const T sinom = math::sin(omega);
|
|
|
|
w[0] = math::sin((T(1) - t) * omega) / sinom;
|
|
w[1] = math::sin(t * omega) / sinom;
|
|
}
|
|
else {
|
|
/* Fallback to lerp */
|
|
w[0] = T(1) - t;
|
|
w[1] = t;
|
|
}
|
|
return w;
|
|
}
|
|
|
|
template<typename T>
|
|
inline detail::Quaternion<T> interpolate(const detail::Quaternion<T> &a,
|
|
const detail::Quaternion<T> &b,
|
|
T t)
|
|
{
|
|
using Vec4T = vec_base<T, 4>;
|
|
BLI_assert(is_unit_scale(Vec4T(a)));
|
|
BLI_assert(is_unit_scale(Vec4T(b)));
|
|
|
|
Vec4T quat = Vec4T(a);
|
|
T cosom = dot(Vec4T(a), Vec4T(b));
|
|
/* Rotate around shortest angle. */
|
|
if (cosom < T(0)) {
|
|
cosom = -cosom;
|
|
quat = -quat;
|
|
}
|
|
|
|
vec_base<T, 2> w = interpolate_dot_slerp(t, cosom);
|
|
|
|
return detail::Quaternion<T>(w[0] * quat + w[1] * Vec4T(b));
|
|
}
|
|
|
|
} // namespace blender::math
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Template implementations
|
|
* \{ */
|
|
|
|
namespace blender::math::detail {
|
|
|
|
#ifdef DEBUG
|
|
# define BLI_ASSERT_UNIT_QUATERNION(_q) \
|
|
{ \
|
|
auto rot_vec = static_cast<vec_base<T, 4>>(_q); \
|
|
T quat_length = math::length_squared(rot_vec); \
|
|
if (!(quat_length == 0 || (math::abs(quat_length - 1) < 0.0001))) { \
|
|
std::cout << "Warning! " << __func__ << " called with non-normalized quaternion: size " \
|
|
<< quat_length << " *** report a bug ***\n"; \
|
|
} \
|
|
}
|
|
#else
|
|
# define BLI_ASSERT_UNIT_QUATERNION(_q)
|
|
#endif
|
|
|
|
template<typename T> AxisAngle<T>::AxisAngle(const vec_base<T, 3> &axis, T angle)
|
|
{
|
|
T length;
|
|
axis_ = math::normalize_and_get_length(axis, length);
|
|
if (length > 0.0f) {
|
|
angle_cos_ = math::cos(angle);
|
|
angle_sin_ = math::sin(angle);
|
|
angle_ = angle;
|
|
}
|
|
else {
|
|
*this = identity();
|
|
}
|
|
}
|
|
|
|
template<typename T> AxisAngle<T>::AxisAngle(const vec_base<T, 3> &from, const vec_base<T, 3> &to)
|
|
{
|
|
BLI_assert(is_unit_scale(from));
|
|
BLI_assert(is_unit_scale(to));
|
|
|
|
/* Avoid calculating the angle. */
|
|
angle_cos_ = dot(from, to);
|
|
axis_ = normalize_and_get_length(cross(from, to), angle_sin_);
|
|
|
|
if (angle_sin_ <= FLT_EPSILON) {
|
|
if (angle_cos_ > T(0)) {
|
|
/* Same vectors, zero rotation... */
|
|
*this = identity();
|
|
}
|
|
else {
|
|
/* Colinear but opposed vectors, 180 rotation... */
|
|
axis_ = normalize(orthogonal(from));
|
|
angle_sin_ = T(0);
|
|
angle_cos_ = T(-1);
|
|
}
|
|
}
|
|
}
|
|
template<typename T>
|
|
AxisAngleNormalized<T>::AxisAngleNormalized(const vec_base<T, 3> &axis, T angle) : AxisAngle<T>()
|
|
{
|
|
BLI_assert(is_unit_scale(axis));
|
|
this->axis_ = axis;
|
|
this->angle_ = angle;
|
|
this->angle_cos_ = math::cos(angle);
|
|
this->angle_sin_ = math::sin(angle);
|
|
}
|
|
|
|
template<typename T> Quaternion<T>::operator EulerXYZ<T>() const
|
|
{
|
|
using Mat3T = MatBase<T, 3, 3>;
|
|
const Quaternion<T> &quat = *this;
|
|
BLI_ASSERT_UNIT_QUATERNION(quat)
|
|
Mat3T unit_mat = math::from_rotation<Mat3T>(quat);
|
|
return math::to_euler<T, true>(unit_mat);
|
|
}
|
|
|
|
template<typename T> EulerXYZ<T>::operator Quaternion<T>() const
|
|
{
|
|
const EulerXYZ<T> &eul = *this;
|
|
const T ti = eul.x * T(0.5);
|
|
const T tj = eul.y * T(0.5);
|
|
const T th = eul.z * T(0.5);
|
|
const T ci = math::cos(ti);
|
|
const T cj = math::cos(tj);
|
|
const T ch = math::cos(th);
|
|
const T si = math::sin(ti);
|
|
const T sj = math::sin(tj);
|
|
const T sh = math::sin(th);
|
|
const T cc = ci * ch;
|
|
const T cs = ci * sh;
|
|
const T sc = si * ch;
|
|
const T ss = si * sh;
|
|
|
|
Quaternion<T> quat;
|
|
quat.x = cj * cc + sj * ss;
|
|
quat.y = cj * sc - sj * cs;
|
|
quat.z = cj * ss + sj * cc;
|
|
quat.w = cj * cs - sj * sc;
|
|
return quat;
|
|
}
|
|
|
|
template<typename T> Quaternion<T>::operator AxisAngle<T>() const
|
|
{
|
|
const Quaternion<T> &quat = *this;
|
|
BLI_ASSERT_UNIT_QUATERNION(quat)
|
|
|
|
/* Calculate angle/2, and sin(angle/2). */
|
|
T ha = math::acos(quat.x);
|
|
T si = math::sin(ha);
|
|
|
|
/* From half-angle to angle. */
|
|
T angle = ha * 2;
|
|
/* Prevent division by zero for axis conversion. */
|
|
if (math::abs(si) < 0.0005) {
|
|
si = 1.0f;
|
|
}
|
|
|
|
vec_base<T, 3> axis = vec_base<T, 3>(quat.y, quat.z, quat.w) / si;
|
|
if (math::is_zero(axis)) {
|
|
axis[1] = 1.0f;
|
|
}
|
|
return AxisAngleNormalized<T>(axis, angle);
|
|
}
|
|
|
|
template<typename T> AxisAngle<T>::operator Quaternion<T>() const
|
|
{
|
|
BLI_assert(math::is_unit_scale(axis_));
|
|
|
|
/** Using half angle identities: sin(angle / 2) = sqrt((1 - angle_cos) / 2) */
|
|
T sine = math::sqrt(T(0.5) - angle_cos_ * T(0.5));
|
|
const T cosine = math::sqrt(T(0.5) + angle_cos_ * T(0.5));
|
|
|
|
if (angle_sin_ < 0.0) {
|
|
sine = -sine;
|
|
}
|
|
|
|
Quaternion<T> quat;
|
|
quat.x = cosine;
|
|
quat.y = axis_.x * sine;
|
|
quat.z = axis_.y * sine;
|
|
quat.w = axis_.z * sine;
|
|
return quat;
|
|
}
|
|
|
|
template<typename T> EulerXYZ<T>::operator AxisAngle<T>() const
|
|
{
|
|
/* Use quaternions as intermediate representation for now... */
|
|
return AxisAngle<T>(Quaternion<T>(*this));
|
|
}
|
|
|
|
template<typename T> AxisAngle<T>::operator EulerXYZ<T>() const
|
|
{
|
|
/* Use quaternions as intermediate representation for now... */
|
|
return EulerXYZ<T>(Quaternion<T>(*this));
|
|
}
|
|
|
|
/* Using explicit template instantiations in order to reduce compilation time. */
|
|
extern template AxisAngle<float>::operator EulerXYZ<float>() const;
|
|
extern template AxisAngle<float>::operator Quaternion<float>() const;
|
|
extern template EulerXYZ<float>::operator AxisAngle<float>() const;
|
|
extern template EulerXYZ<float>::operator Quaternion<float>() const;
|
|
extern template Quaternion<float>::operator AxisAngle<float>() const;
|
|
extern template Quaternion<float>::operator EulerXYZ<float>() const;
|
|
|
|
extern template AxisAngle<double>::operator EulerXYZ<double>() const;
|
|
extern template AxisAngle<double>::operator Quaternion<double>() const;
|
|
extern template EulerXYZ<double>::operator AxisAngle<double>() const;
|
|
extern template EulerXYZ<double>::operator Quaternion<double>() const;
|
|
extern template Quaternion<double>::operator AxisAngle<double>() const;
|
|
extern template Quaternion<double>::operator EulerXYZ<double>() const;
|
|
|
|
} // namespace blender::math::detail
|
|
|
|
/** \} */
|