more restructuring including separated frustum and added quaternion slerp

This commit is contained in:
Hartmut Seichter 2024-07-15 00:42:49 +02:00
parent f3365d0669
commit 51bc5203ae
6 changed files with 136 additions and 38 deletions

View file

@ -28,7 +28,7 @@
namespace pw {
template <typename T> struct axisangle {
template <typename T> struct axisangle final {
using value_type = T;
using axis_type = vector3<T>;

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 1999-2021 Hartmut Seichter
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#ifndef PW_CORE_FRUSTUM_HPP
#define PW_CORE_FRUSTUM_HPP
#include <pw/core/globals.hpp>
namespace pw {
template <typename Scalar> struct frustum final {
Scalar left{-1}, right{1}, bottom{-1}, top{1}, z_near{-1}, z_far{1};
static constexpr auto make_perspective_symmetric(Scalar fov_h_deg,
Scalar aspect_ratio,
Scalar z_near,
Scalar z_far) -> frustum {
const auto tangent_half{std::tan(pw::deg_to_rad(fov_h_deg / 2))};
const auto top{tangent_half * z_near};
const auto right{top * aspect_ratio};
return {.left = -right,
.right = right,
.bottom = -top,
.top = top,
.z_near = z_near,
.z_far = z_far};
}
static constexpr auto make_orthographic_symmetric(Scalar scale,
Scalar aspect_ratio,
Scalar z_near,
Scalar z_far) -> frustum {
return {.left = -scale,
.right = scale,
.bottom = -scale * aspect_ratio,
.top = scale * aspect_ratio,
.z_near = z_near,
.z_far = z_far};
}
};
} // namespace pw
#endif

View file

@ -23,7 +23,6 @@
#ifndef PW_CORE_MATRIX_HPP
#define PW_CORE_MATRIX_HPP
#include <cstddef>
#include <pw/core/globals.hpp>
#include <pw/core/math.hpp>
#include <pw/core/vector.hpp>

View file

@ -26,41 +26,10 @@
#include <pw/core/math.hpp>
#include <pw/core/matrix.hpp>
#include <pw/core/vector.hpp>
#include <pw/core/frustum.hpp>
namespace pw {
template <typename Scalar> struct frustum final {
Scalar left{-1}, right{1}, bottom{-1}, top{1}, z_near{-1}, z_far{1};
static constexpr auto make_perspective_symmetric(Scalar fov_h_deg,
Scalar aspect_ratio,
Scalar z_near,
Scalar z_far) -> frustum {
const auto tangent_half{std::tan(pw::deg_to_rad(fov_h_deg / 2))};
const auto top{tangent_half * z_near};
const auto right{top * aspect_ratio};
return {.left = -right,
.right = right,
.bottom = -top,
.top = top,
.z_near = z_near,
.z_far = z_far};
}
static constexpr auto make_orthographic_symmetric(Scalar scale,
Scalar aspect_ratio,
Scalar z_near,
Scalar z_far) -> frustum {
return {.left = -scale,
.right = scale,
.bottom = -scale * aspect_ratio,
.top = scale * aspect_ratio,
.z_near = z_near,
.z_far = z_far};
}
};
struct matrix_transform {
template <typename Scalar>

View file

@ -23,8 +23,8 @@
#ifndef PW_CORE_QUATERNION_HPP
#define PW_CORE_QUATERNION_HPP
#include <pw/core/matrix.hpp>
#include <pw/core/axisangle.hpp>
#include <pw/core/matrix.hpp>
#include <pw/core/vector.hpp>
#include <concepts>
@ -83,6 +83,10 @@ template <std::floating_point Scalar> struct quaternion final {
return conjugate() / this->norm();
}
constexpr auto dot(const quaternion& b) const noexcept -> Scalar {
return q_.dot(b.q_);
}
constexpr auto to_matrix() const noexcept -> matrix<Scalar, 3, 3> {
const Scalar xx = x() * x();
@ -119,9 +123,61 @@ template <std::floating_point Scalar> struct quaternion final {
}
static constexpr auto lerp(const quaternion& a, const quaternion& b,
const Scalar& t) -> quaternion {
const Scalar& t) -> quaternion {
return {value_type::lerp(a.q_, b.q_, t).normalized()};
}
/**
* @note: a and b need to be normalized
*/
static constexpr auto
slerp(const quaternion& a, const quaternion& b, const Scalar& t,
const Scalar& eps = Scalar{0.001}) -> quaternion {
// Calculate angle between them.
const Scalar cos_half_theta{a.dot(b)};
// if qa=qb or qa=-qb then theta = 0 and we can return a
if (std::fabs(cos_half_theta) >= Scalar{1}) {
return a;
}
// Calculate temporary values.
const Scalar half_theta{std::acos(cos_half_theta)};
const Scalar sin_half_theta{
std::sqrt(Scalar{1} - cos_half_theta * cos_half_theta)};
// if theta = 180 degrees then result is not fully defined
// we could rotate around any axis normal to a or b
const auto is_pi = std::fabs(sin_half_theta) < eps;
// now do the lerp either halfway or across unit sphere
const Scalar ratio_a{is_pi ? Scalar{0.5}
: std::sin((Scalar{1} - t) * half_theta) /
sin_half_theta};
const Scalar ratio_b{is_pi ? Scalar{0.5}
: std::sin(t * half_theta) / sin_half_theta};
return {
(a.x() * ratio_a + b.x() * ratio_b), // x
(a.y() * ratio_a + b.y() * ratio_b), // y
(a.z() * ratio_a + b.z() * ratio_b), // z
(a.w() * ratio_a + b.w() * ratio_b) // w
};
}
static constexpr auto
from_axisangle(const axisangle<Scalar>& aa) -> quaternion {
const auto sin_half_angle{std::sin(aa.angle / Scalar{2})};
return {
aa.axis.x() * sin_half_angle, // x
aa.axis.y() * sin_half_angle, // y
aa.axis.z() * sin_half_angle, // z
std::cos(aa.angle / Scalar{2}) // w
};
}
};
// deduction guide for quaternion

View file

@ -13,6 +13,10 @@ auto main() -> int {
pw::debug::d() << "q1 = quaternion{1,2,3,4} -> \n"
<< pw::serialize::to_string(q1);
q1 = q1.normalized();
pw::debug::d() << "q1 = quaternion{1,2,3,4}.normalized() -> \n"
<< pw::serialize::to_string(q1);
auto q0_x_q1 = q0 * q1;
pw::debug::d() << "q0 * q1 -> \n" << pw::serialize::to_string(q0_x_q1);
@ -28,10 +32,15 @@ auto main() -> int {
pw::debug::d() << "q1.conjugate().inverse() -> \n"
<< pw::serialize::to_string(q1_conj_inv);
auto lerp_q0_q1_half = decltype(q0)::lerp(q0, q1, 0.5f);
pw::debug::d() << "quaternion::lerp(q0,q1,0.5) -> \n"
pw::debug::d() << "quaternion::dot(q0,q1) -> " << q0.dot(q1);
auto lerp_q0_q1_half = decltype(q0)::lerp(q0, q1, 0.3f);
pw::debug::d() << "quaternion::lerp(q0,q1,0.3) -> \n"
<< pw::serialize::to_string(lerp_q0_q1_half);
auto slerp_q0_q1_half = decltype(q0)::slerp(q0, q1, 0.3f);
pw::debug::d() << "quaternion::slerp(q0,q1,0.3) -> \n"
<< pw::serialize::to_string(slerp_q0_q1_half);
return 0;
}