MayaFlux 1.0.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 Packed view, projection, and geometry matrices for rendering.
32 *
33 * Geometry acts first; all matrices default to identity.
34 */
37 glm::mat4 geometry { 1.0F };
38};
39
40static_assert(sizeof(RenderTransform) == 3 * sizeof(glm::mat4));
41
42/**
43 * @brief Construct view matrix from eye position, target, and up vector
44 * @param eye Observer position in source space
45 * @param target Point the observer is looking at
46 * @param up Up direction (default: positive Y)
47 * @return ViewTransform with view matrix set, projection identity
48 */
49[[nodiscard]] inline ViewTransform look_at(
50 const glm::vec3& eye,
51 const glm::vec3& target,
52 const glm::vec3& up = glm::vec3(0.0F, 1.0F, 0.0F))
53{
54 return { .view = glm::lookAt(eye, target, up) };
55}
56
57/**
58 * @brief Construct perspective projection matrix
59 * @param fov_radians Vertical field of view in radians
60 * @param aspect Width / height aspect ratio
61 * @param near Near clip plane distance
62 * @param far Far clip plane distance
63 * @return ViewTransform with projection matrix set, view identity
64 */
65[[nodiscard]] inline ViewTransform perspective(
66 float fov_radians,
67 float aspect,
68 float near,
69 float far)
70{
71 return { .projection = glm::perspective(fov_radians, aspect, near, far) };
72}
73
74/**
75 * @brief Construct orthographic projection matrix
76 * @param left Left clipping plane
77 * @param right Right clipping plane
78 * @param bottom Bottom clipping plane
79 * @param top Top clipping plane
80 * @param near Near clipping plane (default: -1.0)
81 * @param far Far clipping plane (default: 1.0)
82 * @return ViewTransform with projection matrix set, view identity
83 */
84[[nodiscard]] inline ViewTransform ortho(
85 float left, float right,
86 float bottom, float top,
87 float near = -1.0F, float far = 1.0F)
88{
89 return { .projection = glm::ortho(left, right, bottom, top, near, far) };
90}
91
92/**
93 * @brief Construct complete ViewTransform from look-at and perspective parameters
94 * @param eye Observer position in source space
95 * @param target Point the observer is looking at
96 * @param fov_radians Vertical field of view in radians
97 * @param aspect Width / height aspect ratio
98 * @param near Near clip plane distance
99 * @param far Far clip plane distance
100 * @param up Up direction (default: positive Y)
101 * @return ViewTransform with both view and projection matrices set
102 */
104 const glm::vec3& eye,
105 const glm::vec3& target,
106 float fov_radians,
107 float aspect,
108 float near,
109 float far,
110 const glm::vec3& up = glm::vec3(0.0F, 1.0F, 0.0F))
111{
112 return {
113 .view = glm::lookAt(eye, target, up),
114 .projection = glm::perspective(fov_radians, aspect, near, far)
115 };
116}
117
118/**
119 * @brief Convert window pixel coordinates to NDC.
120 * @param window_x X in window space [0, width]
121 * @param window_y Y in window space [0, height] (top-left origin)
122 * @param width Window width in pixels
123 * @param height Window height in pixels
124 * @return NDC (x, y, 0) in [-1, +1], center origin, +Y up
125 */
126[[nodiscard]] inline glm::vec3 to_ndc(
127 double window_x, double window_y,
128 uint32_t width, uint32_t height)
129{
130 return {
131 (static_cast<float>(window_x) / static_cast<float>(width)) * 2.0F - 1.0F,
132 1.0F - (static_cast<float>(window_y) / static_cast<float>(height)) * 2.0F,
133 0.0F
134 };
135}
136
137/**
138 * @brief Convert window pixel coordinates to NDC, corrected for aspect ratio.
139 *
140 * The shorter dimension maps to [-1, +1]; the longer dimension extends beyond.
141 * Circles drawn in NDC space remain circular in window space.
142 */
143[[nodiscard]] inline glm::vec3 to_ndc_aspect(
144 double window_x, double window_y,
145 uint32_t width, uint32_t height)
146{
147 const float aspect = static_cast<float>(width) / static_cast<float>(height);
148 float x = (static_cast<float>(window_x) / static_cast<float>(width)) * 2.0F - 1.0F;
149 float y = 1.0F - (static_cast<float>(window_y) / static_cast<float>(height)) * 2.0F;
150 if (aspect >= 1.0F) {
151 x *= aspect; // width is longer: extend X beyond [-1,+1]
152 } else {
153 y /= aspect; // height is longer: extend Y beyond [-1,+1]
154 }
155 return { x, y, 0.0F };
156}
157
158/**
159 * @brief Convert NDC to window pixel coordinates.
160 * @param ndc NDC position (z ignored)
161 * @param width Window width in pixels
162 * @param height Window height in pixels
163 * @return Window coordinates (top-left origin, +Y down)
164 */
165[[nodiscard]] inline glm::vec2 to_window(
166 const glm::vec3& ndc,
167 uint32_t width, uint32_t height)
168{
169 return {
170 (ndc.x + 1.0F) * 0.5F * static_cast<float>(width),
171 (1.0F - ndc.y) * 0.5F * static_cast<float>(height)
172 };
173}
174
175/**
176 * @brief Aspect ratio from pixel dimensions.
177 */
178[[nodiscard]] inline float aspect_ratio(uint32_t width, uint32_t height)
179{
180 return static_cast<float>(width) / static_cast<float>(height);
181}
182
183/**
184 * @brief Check if a window-space point is within bounds.
185 */
186[[nodiscard]] inline bool in_bounds(
187 double window_x, double window_y,
188 uint32_t width, uint32_t height)
189{
190 return window_x >= 0.0
191 && window_x < static_cast<double>(width)
192 && window_y >= 0.0
193 && window_y < static_cast<double>(height);
194}
195
196/**
197 * @brief Convert an NDC-space size (extent) to integer pixel dimensions.
198 *
199 * NDC spans 2.0 across each axis. An NDC size of (1.8, 0.1) on a 1280x720
200 * window resolves to (1152, 36) pixels. Both components are clamped to at
201 * least 1 pixel so degenerate textures and zero-extent images are avoided.
202 *
203 * Size counterpart to to_window(), which converts an NDC position. Use
204 * to_window for positions, ndc_size_to_pixels for extents (text image
205 * budgets, capture target dimensions, offscreen image sizes matched to
206 * an on-screen NDC region).
207 *
208 * @param ndc_size NDC extent (each component in [0, 2]).
209 * @param width Window width in pixels.
210 * @param height Window height in pixels.
211 * @return Integer pixel dimensions, each at least 1.
212 */
213[[nodiscard]] inline glm::uvec2 ndc_size_to_pixels(
214 const glm::vec2& ndc_size,
215 uint32_t width, uint32_t height)
216{
217 const auto px = static_cast<uint32_t>(ndc_size.x * 0.5F * static_cast<float>(width));
218 const auto py = static_cast<uint32_t>(ndc_size.y * 0.5F * static_cast<float>(height));
219 return { std::max(px, 1U), std::max(py, 1U) };
220}
221
222} // namespace MayaFlux::Kinesis
uint32_t py
uint32_t px
uint32_t width
uint32_t height
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.
glm::uvec2 ndc_size_to_pixels(const AABB2D &region, uint32_t width, uint32_t height)
Convert an NDC AABB's extent to integer pixel dimensions.
Definition Bounds.hpp:488
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.
Packed view, projection, and geometry matrices for rendering.
View and projection matrices as a named push constant slot.