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, vek};
34use crate::camera3d;
35
36/// Convenience method that calls `math::orthographic_rh_no` on the given
37/// `FrustmPlanes` struct
38#[inline]
39pub fn projection_mat_orthographic <S : num::real::Real> (
40 ortho : &vek::FrustumPlanes <S>
41) -> math::Matrix4 <S> {
42 math::Matrix4::orthographic_rh_no (*ortho)
43}
44
45/// Convenience method calling `Matrix4::perspective_rh_no` to construct a
46/// right-handed perspective matrix with -1 to 1 clip planes, equivalent to the
47/// `gluPerspective` function.
48///
49/// This will transform points in right-handed camera (view, eye) space
50/// (negative Z axis into the scene, positive Y axis 'up') into left-handed 4D
51/// homogenous clip space where the Z axis is reversed with positive Z into the
52/// screen while X and Y orientations remain unchanged.
53#[inline]
54pub fn projection_mat_perspective <S> (
55 perspective_fov : &camera3d::PerspectiveFov <S>
56) -> math::Matrix4 <S> where
57 S : num::real::Real + num::FloatConst + std::fmt::Debug
58{
59 math::Matrix4::perspective_rh_no (
60 perspective_fov.fovy.0,
61 perspective_fov.aspect,
62 perspective_fov.near,
63 perspective_fov.far
64 )
65}
66
67/// Convert screen coordinate to OpenGL NDC based on a given screen resolution.
68///
69/// # Examples
70///
71/// ```
72/// # extern crate gl_utils;
73/// # extern crate math_utils as math;
74/// # fn main () {
75/// # use gl_utils::graphics::screen_2d_to_ndc_2d;
76/// assert_eq!(
77/// screen_2d_to_ndc_2d ([640, 480].into(), [320.0, 240.0].into()),
78/// [0.0, 0.0].into()
79/// );
80/// math::approx::assert_relative_eq!(
81/// screen_2d_to_ndc_2d ([640, 480].into(), [480.0, 264.0].into()),
82/// [0.5, 0.1].into()
83/// );
84/// assert_eq!(
85/// screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 0.0].into()),
86/// [-1.0, -1.0].into()
87/// );
88/// assert_eq!(
89/// screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 480.0].into()),
90/// [-1.0, 1.0].into()
91/// );
92/// assert_eq!(
93/// screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 0.0].into()),
94/// [1.0, -1.0].into()
95/// );
96/// assert_eq!(
97/// screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 480.0].into()),
98/// [1.0, 1.0].into()
99/// );
100/// # }
101/// ```
102
103pub fn screen_2d_to_ndc_2d (
104 screen_dimensions : math::Vector2 <u16>,
105 screen_coord : math::Point2 <f32>
106) -> math::Point2 <f32> {
107 math::Point2::from ([
108 screen_coord.0.x / (0.5 * screen_dimensions.x as f32) - 1.0,
109 screen_coord.0.y / (0.5 * screen_dimensions.y as f32) - 1.0
110 ])
111}
112
113/// Maps OpenGL NDC coordinates to screen coordinates based on a given screen
114/// resolution.
115///
116/// # Examples
117///
118/// ```
119/// # extern crate gl_utils;
120/// # extern crate math_utils as math;
121/// # fn main () {
122/// # use gl_utils::graphics::ndc_2d_to_screen_2d;
123/// assert_eq!(
124/// ndc_2d_to_screen_2d ([640, 480].into(), [0.0, 0.0].into()),
125/// [320.0, 240.0].into()
126/// );
127/// assert_eq!(
128/// ndc_2d_to_screen_2d ([640, 480].into(), [0.5, 0.1].into()),
129/// [480.0, 264.0].into()
130/// );
131/// assert_eq!(
132/// ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, -1.0].into()),
133/// [0.0, 0.0].into()
134/// );
135/// assert_eq!(
136/// ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, 1.0].into()),
137/// [0.0, 480.0].into()
138/// );
139/// assert_eq!(
140/// ndc_2d_to_screen_2d ([640, 480].into(), [1.0, -1.0].into()),
141/// [640.0, 0.0].into()
142/// );
143/// assert_eq!(
144/// ndc_2d_to_screen_2d ([640, 480].into(), [1.0, 1.0].into()),
145/// [640.0, 480.0].into()
146/// );
147/// # }
148/// ```
149
150pub fn ndc_2d_to_screen_2d (
151 screen_dimensions : math::Vector2 <u16>,
152 ndc_coord : math::Point2 <f32>
153) -> math::Point2 <f32> {
154 math::Point2::from ([
155 0.5 * (screen_dimensions.x as f32) * (1.0 + ndc_coord.0.x),
156 0.5 * (screen_dimensions.y as f32) * (1.0 + ndc_coord.0.y)
157 ])
158}