gl_utils/
graphics.rs

1//! General graphics-related utility functions
2//!
3//! # Coordinates
4//!
5//! The `gl_Position` output of the last GLSL vertex processing stage (vertex
6//! shader, tessellation shader, or geometry shader) is in *clip space*, that is
7//! 4D homogeneous (projective) coordinates (x, y, z, w). During vertex
8//! post-processing, clip space coordinates are transformed to normalized device
9//! coordinates (NDCs) by perspective divide and clipped to the range
10//! [-1.0, 1.0] in the x, y, and z coorinates. Finally the viewport transform
11//! takes NDCs and outputs *screen space* (or *window space*) coordinates, which
12//! are 2D device coordinates + a 1D depth coordinate, on which rasterization is
13//! performed to produce fragments.
14//!
15//! **2D**
16//!
17//! 2D x,y coordinates normalized to [-1.0, 1.0] can be passed through to vertex
18//! post-processing unmodified. This is the case with shader pipelines that
19//! specify inputs are in "ClipSpace"; the pass-thru vertex shader will add a z
20//! component of 0.0 and a w component of 1.0.
21//!
22//! (Note: as of v0.6.0 none of the default draw2d resources use clip space
23//! rendering pipelines)
24//!
25//! Even though the ultimate output of vertex post-processing is 2D screen space
26//! (device) coordinates, in order to render screen-space input vertices, they
27//! must be transformed to clip space by vertex processing. Two utility
28//! functions are provided to convert between screen space and normalized
29//! coordinates: `graphics::screen_2d_to_ndc_2d` and
30//! `graphics::ndc_2d_to_screen_2d`. Both require screen dimensions as input.
31
32use math_utils as math;
33use math_utils::num_traits as num;
34use math_utils::vek;
35use crate::camera3d;
36
37/// Convenience method that calls `math::orthographic_rh_no` on the given
38/// `FrustmPlanes` struct
39#[inline]
40pub fn projection_mat_orthographic <S : num::real::Real> (
41  ortho : &vek::FrustumPlanes <S>
42) -> math::Matrix4 <S> {
43  math::Matrix4::orthographic_rh_no (*ortho)
44}
45
46/// Convenience method calling `Matrix4::perspective_rh_no` to construct a
47/// right-handed perspective matrix with -1 to 1 clip planes, equivalent to the
48/// `gluPerspective` function.
49///
50/// This will transform points in right-handed camera (view, eye) space
51/// (negative Z axis into the scene, positive Y axis 'up') into left-handed 4D
52/// homogenous clip space where the Z axis is reversed with positive Z into the
53/// screen while X and Y orientations remain unchanged.
54#[inline]
55pub fn projection_mat_perspective <S> (
56  perspective_fov : &camera3d::PerspectiveFov <S>
57) -> math::Matrix4 <S> where
58  S : num::real::Real + num::FloatConst + std::fmt::Debug
59{
60  math::Matrix4::perspective_rh_no (
61    perspective_fov.fovy.0,
62    perspective_fov.aspect,
63    perspective_fov.near,
64    perspective_fov.far
65  )
66}
67
68/// Convert screen coordinate to OpenGL NDC based on a given screen resolution.
69///
70/// # Examples
71///
72/// ```
73/// # extern crate gl_utils;
74/// # extern crate math_utils as math;
75/// # fn main () {
76/// # use gl_utils::graphics::screen_2d_to_ndc_2d;
77/// assert_eq!(
78///   screen_2d_to_ndc_2d ([640, 480].into(), [320.0, 240.0].into()),
79///   [0.0, 0.0].into()
80/// );
81/// math::approx::assert_relative_eq!(
82///   screen_2d_to_ndc_2d ([640, 480].into(), [480.0, 264.0].into()),
83///   [0.5, 0.1].into()
84/// );
85/// assert_eq!(
86///   screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 0.0].into()),
87///   [-1.0, -1.0].into()
88/// );
89/// assert_eq!(
90///   screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 480.0].into()),
91///   [-1.0, 1.0].into()
92/// );
93/// assert_eq!(
94///   screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 0.0].into()),
95///   [1.0, -1.0].into()
96/// );
97/// assert_eq!(
98///   screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 480.0].into()),
99///   [1.0, 1.0].into()
100/// );
101/// # }
102/// ```
103
104pub fn screen_2d_to_ndc_2d (
105  screen_dimensions : math::Vector2 <u16>,
106  screen_coord      : math::Point2 <f32>
107) -> math::Point2 <f32> {
108  math::Point2::from ([
109    screen_coord.0.x as f32 / (0.5 * screen_dimensions.x as f32) - 1.0,
110    screen_coord.0.y as f32 / (0.5 * screen_dimensions.y as f32) - 1.0
111  ])
112}
113
114/// Maps OpenGL NDC coordinates to screen coordinates based on a given screen
115/// resolution.
116///
117/// # Examples
118///
119/// ```
120/// # extern crate gl_utils;
121/// # extern crate math_utils as math;
122/// # fn main () {
123/// # use gl_utils::graphics::ndc_2d_to_screen_2d;
124/// assert_eq!(
125///   ndc_2d_to_screen_2d ([640, 480].into(), [0.0, 0.0].into()),
126///   [320.0, 240.0].into()
127/// );
128/// assert_eq!(
129///   ndc_2d_to_screen_2d ([640, 480].into(), [0.5, 0.1].into()),
130///   [480.0, 264.0].into()
131/// );
132/// assert_eq!(
133///   ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, -1.0].into()),
134///   [0.0, 0.0].into()
135/// );
136/// assert_eq!(
137///   ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, 1.0].into()),
138///   [0.0, 480.0].into()
139/// );
140/// assert_eq!(
141///   ndc_2d_to_screen_2d ([640, 480].into(), [1.0, -1.0].into()),
142///   [640.0, 0.0].into()
143/// );
144/// assert_eq!(
145///   ndc_2d_to_screen_2d ([640, 480].into(), [1.0, 1.0].into()),
146///   [640.0, 480.0].into()
147/// );
148/// # }
149/// ```
150
151pub fn ndc_2d_to_screen_2d (
152  screen_dimensions : math::Vector2 <u16>,
153  ndc_coord         : math::Point2 <f32>
154) -> math::Point2 <f32> {
155  math::Point2::from ([
156    0.5 * (screen_dimensions.x as f32) * (1.0 + ndc_coord.0.x),
157    0.5 * (screen_dimensions.y as f32) * (1.0 + ndc_coord.0.y)
158  ])
159}