Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
AffineTransform3Semantics.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <optional>
6#include <type_traits>
7
12
13namespace mv::math
14{
15 template <typename T>
16 requires std::is_floating_point_v<T>
18 const AffineTransform3<T>& transform, const Point3<T>& point) noexcept
19 {
21 }
22
23 template <typename T>
24 requires std::is_floating_point_v<T>
25 [[nodiscard]] inline std::optional<Direction3<T>> TryTransformDirection(
27 const Direction3<T>& direction) noexcept
28 {
31 }
32
33 namespace detail
34 {
35 // Cofactor/inverse-transpose derivation: Eric Lengyel, "Transforming
36 // Normals" (2024). The boolean distinguishes plane/covector
37 // orientation from parameterized-surface winding under reflections.
38 template <typename T>
39 [[nodiscard]] inline std::optional<Normal3<T>> TryTransformNormalImpl(
41 const Normal3<T>& normal,
42 bool preserveHalfSpaceOrientation) noexcept
43 {
44 using Calculation =
45 std::conditional_t<(sizeof(T) < sizeof(double)), double, T>;
46
47 const Vec3<T>& axisX = transform.AxisX();
48 const Vec3<T>& axisY = transform.AxisY();
49 const Vec3<T>& axisZ = transform.AxisZ();
50
51 const Calculation ax = static_cast<Calculation>(axisX.X());
52 const Calculation ay = static_cast<Calculation>(axisX.Y());
53 const Calculation az = static_cast<Calculation>(axisX.Z());
54 const Calculation bx = static_cast<Calculation>(axisY.X());
55 const Calculation by = static_cast<Calculation>(axisY.Y());
56 const Calculation bz = static_cast<Calculation>(axisY.Z());
57 const Calculation cx = static_cast<Calculation>(axisZ.X());
58 const Calculation cy = static_cast<Calculation>(axisZ.Y());
59 const Calculation cz = static_cast<Calculation>(axisZ.Z());
60
61 const Calculation cofactorXx = by * cz - bz * cy;
62 const Calculation cofactorXy = bz * cx - bx * cz;
63 const Calculation cofactorXz = bx * cy - by * cx;
64 const Calculation cofactorYx = cy * az - cz * ay;
65 const Calculation cofactorYy = cz * ax - cx * az;
66 const Calculation cofactorYz = cx * ay - cy * ax;
67 const Calculation cofactorZx = ay * bz - az * by;
68 const Calculation cofactorZy = az * bx - ax * bz;
69 const Calculation cofactorZz = ax * by - ay * bx;
72
73 if (determinant == Calculation(0) || !std::isfinite(determinant))
74 {
75 return std::nullopt;
76 }
77
78 const Vec3<T>& value = normal.Vector();
79 const Calculation nx = static_cast<Calculation>(value.X());
80 const Calculation ny = static_cast<Calculation>(value.Y());
81 const Calculation nz = static_cast<Calculation>(value.Z());
84 ? Calculation(-1)
85 : Calculation(1);
95
96 const Calculation maximum =
97 std::max({std::abs(transformedX), std::abs(transformedY),
98 std::abs(transformedZ)});
99 if (!(maximum > Calculation(0)) || !std::isfinite(maximum))
100 {
101 return std::nullopt;
102 }
103
107 const Calculation length = std::sqrt(transformedX * transformedX +
110 if (!(length > Calculation(0)) || !std::isfinite(length))
111 {
112 return std::nullopt;
113 }
114
115 return Normal3<T>::TryFrom(
116 Vec3<T>(static_cast<T>(transformedX / length),
117 static_cast<T>(transformedY / length),
118 static_cast<T>(transformedZ / length)));
119 }
120 } // namespace detail
121
122 // Treat a normal as a plane covector: the positive half-space transforms
123 // with the object, including a direction flip under a reflection. This is
124 // the default expected by lighting normals and transformed planes.
125 template <typename T>
126 requires std::is_floating_point_v<T>
127 [[nodiscard]] inline std::optional<Normal3<T>> TryTransformNormal(
128 const AffineTransform3<T>& transform, const Normal3<T>& normal) noexcept
129 {
131 }
132
133 // Treat a normal as the cross product of an ordered tangent pair. Under a
134 // reflection this intentionally differs by a sign from TryTransformNormal
135 // so parameterized-surface winding is preserved explicitly.
136 template <typename T>
137 requires std::is_floating_point_v<T>
138 [[nodiscard]] inline std::optional<Normal3<T>>
144
145 template <typename T>
146 requires std::is_floating_point_v<T>
148 const RigidTransform3<T>& transform) noexcept
149 {
150 return AffineTransform3<T>(
151 Rotate(transform.Rotation(), Vec3<T>(T(1), T(0), T(0))),
152 Rotate(transform.Rotation(), Vec3<T>(T(0), T(1), T(0))),
153 Rotate(transform.Rotation(), Vec3<T>(T(0), T(0), T(1))),
154 transform.Translation());
155 }
156} // namespace mv::math
static std::optional< Direction3 > TryFrom(const Vec3< T > &value) noexcept
static std::optional< Normal3 > TryFrom(const Vec3< T > &value) noexcept
Definition Normal3.hpp:46
static Point3 FromVector(const Vec3< T > &value) noexcept
Definition Point3.hpp:28
std::optional< Normal3< T > > TryTransformNormalImpl(const AffineTransform3< T > &transform, const Normal3< T > &normal, bool preserveHalfSpaceOrientation) noexcept
std::optional< Normal3< T > > TryTransformNormal(const AffineTransform3< T > &transform, const Normal3< T > &normal) noexcept
Vec3< T > TransformVector(const AffineTransform3< T > &transform, const Vec3< T > &vector) noexcept
std::optional< Normal3< T > > TryTransformOrientedSurfaceNormal(const AffineTransform3< T > &transform, const Normal3< T > &normal) noexcept
AffineTransform3< T > ToAffine(const RigidTransform3< T > &transform) noexcept
std::optional< Direction3< T > > TryTransformDirection(const AffineTransform3< T > &transform, const Direction3< T > &direction) noexcept
Vec3< T > TransformPoint(const AffineTransform3< T > &transform, const Vec3< T > &point) noexcept
Vec3< T > Rotate(const Rotation3< T > &rotation, const Vec3< T > &vector) noexcept