MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ViewTransform.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <glm/gtc/matrix_transform.hpp>
4
5namespace MayaFlux::Kinesis {
6
7/**
8 * @struct ViewTransform
9 * @brief View and projection matrices as a named push constant slot
10 *
11 * Two glm::mat4 fields (128 bytes total) that map directly to a vertex
12 * shader push constant block. No Camera class, no scene graph, no
13 * transform hierarchy. The third dimension is not special: it is a
14 * push constant slot that the user may optionally supply, which
15 * enables depth testing and provides view/projection matrices to the
16 * vertex shader.
17 *
18 * Construction uses free functions (look_at, perspective, ortho) or
19 * direct assignment. Both matrices can be driven by signal graph node
20 * outputs for audio-reactive or algorithmically controlled viewpoints.
21 */
23 glm::mat4 view { 1.0F };
24 glm::mat4 projection { 1.0F };
25};
26
27static_assert(sizeof(ViewTransform) == 128,
28 "ViewTransform must be exactly 128 bytes (Vulkan minimum push constant size)");
29
30/**
31 * @brief Construct view matrix from eye position, target, and up vector
32 * @param eye Observer position in source space
33 * @param target Point the observer is looking at
34 * @param up Up direction (default: positive Y)
35 * @return ViewTransform with view matrix set, projection identity
36 */
37[[nodiscard]] inline ViewTransform look_at(
38 const glm::vec3& eye,
39 const glm::vec3& target,
40 const glm::vec3& up = glm::vec3(0.0F, 1.0F, 0.0F))
41{
42 return { .view = glm::lookAt(eye, target, up) };
43}
44
45/**
46 * @brief Construct perspective projection matrix
47 * @param fov_radians Vertical field of view in radians
48 * @param aspect Width / height aspect ratio
49 * @param near Near clip plane distance
50 * @param far Far clip plane distance
51 * @return ViewTransform with projection matrix set, view identity
52 */
53[[nodiscard]] inline ViewTransform perspective(
54 float fov_radians,
55 float aspect,
56 float near,
57 float far)
58{
59 return { .projection = glm::perspective(fov_radians, aspect, near, far) };
60}
61
62/**
63 * @brief Construct orthographic projection matrix
64 * @param left Left clipping plane
65 * @param right Right clipping plane
66 * @param bottom Bottom clipping plane
67 * @param top Top clipping plane
68 * @param near Near clipping plane (default: -1.0)
69 * @param far Far clipping plane (default: 1.0)
70 * @return ViewTransform with projection matrix set, view identity
71 */
72[[nodiscard]] inline ViewTransform ortho(
73 float left, float right,
74 float bottom, float top,
75 float near = -1.0F, float far = 1.0F)
76{
77 return { .projection = glm::ortho(left, right, bottom, top, near, far) };
78}
79
80/**
81 * @brief Construct complete ViewTransform from look-at and perspective parameters
82 * @param eye Observer position in source space
83 * @param target Point the observer is looking at
84 * @param fov_radians Vertical field of view in radians
85 * @param aspect Width / height aspect ratio
86 * @param near Near clip plane distance
87 * @param far Far clip plane distance
88 * @param up Up direction (default: positive Y)
89 * @return ViewTransform with both view and projection matrices set
90 */
92 const glm::vec3& eye,
93 const glm::vec3& target,
94 float fov_radians,
95 float aspect,
96 float near,
97 float far,
98 const glm::vec3& up = glm::vec3(0.0F, 1.0F, 0.0F))
99{
100 return {
101 .view = glm::lookAt(eye, target, up),
102 .projection = glm::perspective(fov_radians, aspect, near, far)
103 };
104}
105
106/**
107 * @brief Convert window pixel coordinates to NDC.
108 * @param window_x X in window space [0, width]
109 * @param window_y Y in window space [0, height] (top-left origin)
110 * @param width Window width in pixels
111 * @param height Window height in pixels
112 * @return NDC (x, y, 0) in [-1, +1], center origin, +Y up
113 */
114[[nodiscard]] inline glm::vec3 to_ndc(
115 double window_x, double window_y,
116 uint32_t width, uint32_t height)
117{
118 return {
119 (static_cast<float>(window_x) / static_cast<float>(width)) * 2.0F - 1.0F,
120 1.0F - (static_cast<float>(window_y) / static_cast<float>(height)) * 2.0F,
121 0.0F
122 };
123}
124
125/**
126 * @brief Convert window pixel coordinates to NDC, corrected for aspect ratio.
127 *
128 * The shorter dimension maps to [-1, +1]; the longer dimension extends beyond.
129 * Circles drawn in NDC space remain circular in window space.
130 */
131[[nodiscard]] inline glm::vec3 to_ndc_aspect(
132 double window_x, double window_y,
133 uint32_t width, uint32_t height)
134{
135 const float aspect = static_cast<float>(width) / static_cast<float>(height);
136 float x = (static_cast<float>(window_x) / static_cast<float>(width)) * 2.0F - 1.0F;
137 float y = 1.0F - (static_cast<float>(window_y) / static_cast<float>(height)) * 2.0F;
138 if (aspect >= 1.0F) {
139 x *= aspect; // width is longer: extend X beyond [-1,+1]
140 } else {
141 y /= aspect; // height is longer: extend Y beyond [-1,+1]
142 }
143 return { x, y, 0.0F };
144}
145
146/**
147 * @brief Convert NDC to window pixel coordinates.
148 * @param ndc NDC position (z ignored)
149 * @param width Window width in pixels
150 * @param height Window height in pixels
151 * @return Window coordinates (top-left origin, +Y down)
152 */
153[[nodiscard]] inline glm::vec2 to_window(
154 const glm::vec3& ndc,
155 uint32_t width, uint32_t height)
156{
157 return {
158 (ndc.x + 1.0F) * 0.5F * static_cast<float>(width),
159 (1.0F - ndc.y) * 0.5F * static_cast<float>(height)
160 };
161}
162
163/**
164 * @brief Aspect ratio from pixel dimensions.
165 */
166[[nodiscard]] inline float aspect_ratio(uint32_t width, uint32_t height)
167{
168 return static_cast<float>(width) / static_cast<float>(height);
169}
170
171/**
172 * @brief Check if a window-space point is within bounds.
173 */
174[[nodiscard]] inline bool in_bounds(
175 double window_x, double window_y,
176 uint32_t width, uint32_t height)
177{
178 return window_x >= 0.0
179 && window_x < static_cast<double>(width)
180 && window_y >= 0.0
181 && window_y < static_cast<double>(height);
182}
183
184} // namespace MayaFlux::Kinesis
uint32_t width
ViewTransform look_at(const glm::vec3 &eye, const glm::vec3 &target, const glm::vec3 &up=glm::vec3(0.0F, 1.0F, 0.0F))
Construct view matrix from eye position, target, and up vector.
glm::vec2 to_window(const glm::vec3 &ndc, uint32_t width, uint32_t height)
Convert NDC to window pixel coordinates.
ViewTransform look_at_perspective(const glm::vec3 &eye, const glm::vec3 &target, float fov_radians, float aspect, float near, float far, const glm::vec3 &up=glm::vec3(0.0F, 1.0F, 0.0F))
Construct complete ViewTransform from look-at and perspective parameters.
float aspect_ratio(uint32_t width, uint32_t height)
Aspect ratio from pixel dimensions.
ViewTransform ortho(float left, float right, float bottom, float top, float near=-1.0F, float far=1.0F)
Construct orthographic projection matrix.
bool in_bounds(double window_x, double window_y, uint32_t width, uint32_t height)
Check if a window-space point is within bounds.
glm::vec3 to_ndc(double window_x, double window_y, uint32_t width, uint32_t height)
Convert window pixel coordinates to NDC.
glm::vec3 to_ndc_aspect(double window_x, double window_y, uint32_t width, uint32_t height)
Convert window pixel coordinates to NDC, corrected for aspect ratio.
ViewTransform perspective(float fov_radians, float aspect, float near, float far)
Construct perspective projection matrix.
View and projection matrices as a named push constant slot.