33#include "utility/graphic/field_of_view.hpp"
34#include "utility/graphic/pose.hpp"
37namespace utility::graphic
44 template<
typename ViewComponentType>
54 template<CanBeViewComponent ViewComponentType>
class View
65 ViewComponentType _nearPlane;
66 ViewComponentType _farPlane;
77 static void validatePerspective(ViewComponentType verticalFovRadians,
78 ViewComponentType aspectRatio)
80 if (verticalFovRadians <= ViewComponentType {}
82 >= std::numbers::pi_v<ViewComponentType>) {
83 throw std::invalid_argument(
84 "View vertical FOV must be in range (0, pi) radians");
87 if (aspectRatio <= ViewComponentType {}) {
88 throw std::invalid_argument(
89 "View aspect ratio must be positive");
101 makeSymmetricFieldOfView(ViewComponentType verticalFovRadians,
102 ViewComponentType aspectRatio)
104 const ViewComponentType halfVertical =
105 verticalFovRadians / ViewComponentType { 2 };
107 const ViewComponentType halfHorizontal =
108 std::atan(std::tan(halfVertical) * aspectRatio);
111 halfVertical, -halfVertical, -halfHorizontal, halfHorizontal);
120 static void validateOrientation(
124 if (math::dot(forward, forward) == ViewComponentType {}) {
125 throw std::invalid_argument(
126 "View forward vector must be non-zero");
128 if (math::dot(up, up) == ViewComponentType {}) {
129 throw std::invalid_argument(
"View up vector must be non-zero");
131 if (math::dot(math::cross(forward, up), math::cross(forward, up))
132 == ViewComponentType {}) {
133 throw std::invalid_argument(
134 "View forward and up vectors must not be collinear");
147 static_cast<ViewComponentType
>(normalized.x),
148 static_cast<ViewComponentType
>(normalized.y),
149 static_cast<ViewComponentType
>(normalized.z),
150 static_cast<ViewComponentType
>(normalized.w)
162 const auto q = normalizedRotationComponents();
164 const ViewComponentType s = q[3];
166 return u * (ViewComponentType { 2 } * math::dot(u, vector))
167 + vector.operator*(s * s - math::dot(u, u))
168 + math::cross(u, vector) * (ViewComponentType { 2 } * s);
179 , _nearPlane(ViewComponentType { 1.0 })
180 , _farPlane(ViewComponentType { 1000.0 })
197 ViewComponentType nearPlane, ViewComponentType farPlane)
198 : _pose(std::
move(pose))
199 , _fieldOfView(std::
move(fov))
200 , _viewportSize(std::
move(viewportSize))
201 , _nearPlane(nearPlane)
202 , _farPlane(farPlane)
261 return math::normalize(
263 ViewComponentType {}, ViewComponentType {},
264 ViewComponentType { -1 } }));
273 return math::normalize(rotateVectorByRotation(
275 ViewComponentType { 1 },
276 ViewComponentType {} }));
287 ViewComponentType aspectRatio)
289 validatePerspective(verticalFovRadians, aspectRatio);
291 makeSymmetricFieldOfView(verticalFovRadians, aspectRatio);
300 const ViewComponentType vertical = std::tan(_fieldOfView.
getUp())
301 - std::tan(_fieldOfView.
getDown());
302 const ViewComponentType horizontal =
304 - std::tan(_fieldOfView.
getLeft());
305 if (vertical == ViewComponentType {}) {
306 return ViewComponentType {};
308 return horizontal / vertical;
317 _fieldOfView = fieldOfView;
335 return math::normalize(rotateVectorByRotation(
337 ViewComponentType {},
338 ViewComponentType {} }));
348 ViewComponentType farDistance)
350 if (nearDistance <= ViewComponentType { 0 }) {
351 throw std::invalid_argument(
"Near plane must be > 0");
353 if (farDistance <= nearDistance) {
354 throw std::invalid_argument(
"Far plane must be > near plane");
357 _nearPlane = nearDistance;
358 _farPlane = farDistance;
407 ViewComponentType ndcY)
const
409 const auto tx0 = std::tan(_fieldOfView.
getLeft());
410 const auto tx1 = std::tan(_fieldOfView.
getRight());
411 const auto ty0 = std::tan(_fieldOfView.
getDown());
412 const auto ty1 = std::tan(_fieldOfView.
getUp());
414 const ViewComponentType horizontalOffset =
415 ((ndcX + ViewComponentType { 1 }) * ViewComponentType { 0.5 })
419 const ViewComponentType verticalOffset =
420 ((ndcY + ViewComponentType { 1 }) * ViewComponentType { 0.5 })
428 math::normalize(rayDirection));
440 if (point.x < 0 || point.y < 0 || point.x >= _viewportSize.x
441 || point.y >= _viewportSize.y) {
442 throw std::out_of_range(
443 "View point is outside of viewport bounds");
446 const ViewComponentType ndcX =
447 (
static_cast<ViewComponentType
>(point.x) / _viewportSize.x)
448 * ViewComponentType { 2 }
449 - ViewComponentType { 1 };
451 const ViewComponentType ndcY = ViewComponentType { 1 }
452 - (
static_cast<ViewComponentType
>(point.y) / _viewportSize.y)
453 * ViewComponentType { 2 };
482 ViewComponentType epsilon = ViewComponentType {
483 1e-6 })
const noexcept
504 return _pose == other._pose
505 && _fieldOfView.
getUp() == other._fieldOfView.getUp()
506 && _fieldOfView.
getDown() == other._fieldOfView.getDown()
507 && _fieldOfView.
getLeft() == other._fieldOfView.getLeft()
508 && _fieldOfView.
getRight() == other._fieldOfView.getRight();
518 return !(*
this == other);
528 _viewportSize = viewportSize;
537 return _viewportSize;
550 glm::translate(glm::identity<glm::mat4>(),
551 glm::vec3(position.x, position.y, position.z))
552 * glm::mat4_cast(glm::quat(orientation.w, orientation.x,
553 orientation.y, orientation.z))) };
592 const ViewComponentType tanLeft = std::tan(_fieldOfView.
getLeft());
593 const ViewComponentType tanRight =
595 const ViewComponentType tanUp = std::tan(_fieldOfView.
getUp());
596 const ViewComponentType tanDown = std::tan(_fieldOfView.
getDown());
598 const ViewComponentType width = tanRight - tanLeft;
599 const ViewComponentType height = tanUp - tanDown;
601 if (width <= ViewComponentType { 0 }
602 || height <= ViewComponentType { 0 }) {
603 throw std::invalid_argument(
604 "View frustum width and height must be positive");
607 if (_farPlane <= _nearPlane) {
608 throw std::invalid_argument(
609 "View far plane must be greater than near plane");
614 projection[0][0] =
static_cast<ViewComponentType
>(2) / width;
615 projection[1][1] =
static_cast<ViewComponentType
>(2) / height;
616 projection[2][0] = (tanRight + tanLeft) / width;
617 projection[2][1] = (tanUp + tanDown) / height;
618 projection[2][2] = -_farPlane / (_farPlane - _nearPlane);
619 projection[2][3] = -
static_cast<ViewComponentType
>(1);
621 -(_farPlane * _nearPlane) / (_farPlane - _nearPlane);
622 projection[3][3] =
static_cast<ViewComponentType
>(0);
626 projection[1][1] *=
static_cast<ViewComponentType
>(-1);
627 projection[2][1] *=
static_cast<ViewComponentType
>(-1);
658 using ViewF = View<float>;
663 using ViewD = View<double>;
671 std::ostream &operator<<(std::ostream &stream,
const ViewF &view);
679 std::ostream &operator<<(std::ostream &stream,
const ViewD &view);
Field of view representation in radians for up, down, left, and right directions.
Type getLeft(void) const noexcept
Get left field-of-view in radians.
Type getUp(void) const noexcept
Get up field-of-view in radians.
Type getDown(void) const noexcept
Get down field-of-view in radians.
Type getRight(void) const noexcept
Get right field-of-view in radians.
bool isSymmetric(Type epsilon=Type { 1e-6 }) const noexcept
Check if this FOV is symmetric around vertical and horizontal axes.
Orientation normalized(void) const noexcept
Return a normalized orientation.
Template class representing a pose (Position + Orientation).
const Orientation< PoseComponentType > & getOrientation(void) const noexcept
Get the orientation component.
const Position< PoseComponentType > & getPosition(void) const noexcept
Get the position component.
Pose & translate(const Position< PoseComponentType > &offset) noexcept
Translate pose position by an offset.
Position in 3D space represented as a vector of three PositionComponentType components (x,...
Geometric ray with arithmetic component type and fixed dimension.
3D perspective view with floating-point components.
Ray< ViewComponentType > viewPointToRay(const math::Vector< ViewComponentType, 2 > &point) const
Create a world-space ray from viewport pixel coordinates.
utility::math::Matrix< ViewComponentType, 4, 4 > toViewMatrix(void) const
Convert view to a GLM-compatible 4x4 view matrix.
void setClippingPlanes(ViewComponentType nearDistance, ViewComponentType farDistance)
Set near and far clipping plane distances.
bool isFieldOfViewSymmetric(ViewComponentType epsilon=ViewComponentType { 1e-6 }) const noexcept
Check whether current field-of-view is symmetric.
void move(const math::Vector< ViewComponentType, 3 > &offset)
Translate view position by an offset.
~View(void)=default
Destructor.
View & operator=(View &&other) noexcept=default
Move assignment operator.
utility::math::Matrix< ViewComponentType, 4, 4 > getProjectionMatrix() const
Build a perspective projection matrix from per-side field of view angles.
View(const View &other)=default
Copy constructor.
math::Vector< ViewComponentType, 3 > getUp(void) const
Get normalized up direction.
void setFlipY(bool flip) noexcept
Set whether the projection matrix inverts the Y axis.
bool isFlipY(void) const noexcept
Get whether the projection matrix inverts the Y axis.
View & operator=(const View &other)=default
Copy assignment operator.
ViewComponentType getNearPlane() const noexcept
Get near clipping plane distance.
ViewComponentType getVerticalFovRadians() const noexcept
Get total vertical field-of-view in radians.
View(void)
Default constructor with common perspective defaults.
ViewComponentType getHorizontalFovRadians() const noexcept
Get total horizontal field-of-view in radians.
void setPose(const Pose< ViewComponentType > &pose)
Set view world-space pose.
Ray< ViewComponentType > viewRay(ViewComponentType ndcX, ViewComponentType ndcY) const
Create a world-space ray from normalized device coordinates.
ViewComponentType getFarPlane() const noexcept
Get far clipping plane distance.
View(Pose< ViewComponentType > pose, utility::graphic::FieldOfView< ViewComponentType > fov, math::Vector< ViewComponentType, 2 > viewportSize, ViewComponentType nearPlane, ViewComponentType farPlane)
Construct view from explicit quaternion orientation.
void setViewportSize(const math::Vector< ViewComponentType, 2 > &viewportSize)
Set viewport size in pixels.
Ray< ViewComponentType > centerRay(void) const
Create a ray going through the center of the viewport.
View(View &&other) noexcept=default
Move constructor.
Pose< ViewComponentType > getPose(void) const
Get view world-space pose.
ViewComponentType getAspectRatio() const
Get aspect ratio (width/height).
void setPerspective(ViewComponentType verticalFovRadians, ViewComponentType aspectRatio)
Set perspective projection parameters.
View moved(const math::Vector< ViewComponentType, 3 > &offset) const
Return translated view copy.
math::Vector< ViewComponentType, 3 > getRight(void) const
Compute view right direction from orientation basis.
bool operator==(const View &other) const noexcept
Equality comparison.
math::Vector< ViewComponentType, 2 > getViewportSize(void) const
Get viewport size in pixels.
bool operator!=(const View &other) const noexcept
Inequality comparison.
math::Vector< ViewComponentType, 3 > getForward(void) const
Get normalized forward direction.
void setFieldOfView(const FieldOfView< ViewComponentType > &fieldOfView)
Set per-direction field-of-view values.
FieldOfView< ViewComponentType > getFieldOfView(void) const
Get field-of-view values.
M x N matrix class inheriting from glm::mat<Cols, Rows, Type>.
3D vector class inheriting from glm::vec3.
Concept to constrain field-of-view component type.
Concept to constrain pose component type.
Concept to ensure the type can be used as a ray component.
Concept to ensure the type can be used as a view component.
Geometric ray template declaration.