Skip to content

Commit

Permalink
[Math/MathUtils] Added computeOrthonormalBasis()
Browse files Browse the repository at this point in the history
- This computes the 2 remaining vectors using the given one to form an orthonormal basis
  • Loading branch information
Razakhel committed Nov 17, 2023
1 parent 8421e75 commit bf9b0cb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 8 deletions.
39 changes: 31 additions & 8 deletions include/RaZ/Math/MathUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
#ifndef RAZ_MATHUTILS_HPP
#define RAZ_MATHUTILS_HPP

#include "RaZ/Math/Vector.hpp"

#include <algorithm>
#include <cassert>
#include <type_traits>

namespace Raz {

template <typename T, std::size_t Size>
class Vector;

namespace MathUtils {
namespace Raz::MathUtils {

/// Computes the linear interpolation between two values, according to a coefficient.
/// \tparam T Type to compute the interpolation with.
Expand Down Expand Up @@ -137,8 +134,34 @@ constexpr T smootherstep(T minThresh, T maxThresh, T value) noexcept {
return smootherstep(clampedVal);
}

} // namespace MathUtils
/// Computes an [orthonormal basis from a single vector](https://graphics.pixar.com/library/OrthonormalB/), according to the right hand rule.
///
/// Note that the example diagram below represents one possible solution; the vectors may not necessarily be in these directions relatively to the input.
///
/// axis3
/// ^
/// |
/// +---> axis2
/// /
/// v
/// input
///
/// \tparam T Type of the basis' vectors.
/// \param input Base vector from which to compute the basis. Must be normalized.
/// \param axis2 Second vector of the computed basis.
/// \param axis3 Third vector of the computed basis.
template <typename T>
void computeOrthonormalBasis(const Vec3<T>& input, Vec3<T>& axis2, Vec3<T>& axis3) {
static_assert(std::is_floating_point_v<T>, "Error: Vectors must be of a floating-point type to compute an orthonormal basis from.");

const T sign = std::copysign(static_cast<T>(1), input.z());
const T a = static_cast<T>(-1) / (sign + input.z());
const T b = input.x() * input.y() * a;

axis2 = Vec3<T>(static_cast<T>(1) + sign * input.x() * input.x() * a, sign * b, -sign * input.x());
axis3 = Vec3<T>(b, sign + input.y() * input.y() * a, -input.y());
}

} // namespace Raz
} // namespace Raz::MathUtils

#endif // RAZ_MATHUTILS_HPP
27 changes: 27 additions & 0 deletions tests/src/RaZ/Math/MathUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,30 @@ TEST_CASE("MathUtils smootherstep range") {
CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 5.f + std::numeric_limits<float>::epsilon()) == 1.f);
CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 10.f) == 1.f);
}

TEST_CASE("MathUtils orthonormal basis") {
Raz::Vec3f axis2;
Raz::Vec3f axis3;

Raz::MathUtils::computeOrthonormalBasis(Raz::Axis::X, axis2, axis3);
CHECK(axis2 == -Raz::Axis::Z);
CHECK(axis3 == Raz::Axis::Y);

Raz::MathUtils::computeOrthonormalBasis(Raz::Axis::Y, axis2, axis3);
CHECK(axis2 == Raz::Axis::X);
CHECK(axis3 == -Raz::Axis::Z);

Raz::MathUtils::computeOrthonormalBasis(Raz::Axis::Z, axis2, axis3);
CHECK(axis2 == Raz::Axis::X);
CHECK(axis3 == Raz::Axis::Y);

Raz::MathUtils::computeOrthonormalBasis(Raz::Vec3f(1.f).normalize(), axis2, axis3);
CHECK(axis2 == Raz::Vec3f(0.788675f, -0.211324856f, -0.577350259f));
CHECK(axis3 == Raz::Vec3f(-0.211324856f, 0.788675f, -0.577350259f));
CHECK(axis2.dot(axis3) == 0.f);

Raz::MathUtils::computeOrthonormalBasis(Raz::Vec3f(-1.f).normalize(), axis2, axis3);
CHECK(axis2 == Raz::Vec3f(0.788675f, -0.211324856f, -0.577350259f));
CHECK(axis3 == Raz::Vec3f(0.211324856f, -0.788675f, 0.577350259f));
CHECK(axis2.dot(axis3) == 0.f);
}

0 comments on commit bf9b0cb

Please sign in to comment.