nsys-gl-utils 0.17.0

OpenGL and graphics utilities
Documentation
//! General graphics-related utility functions
//!
//! # Coordinates
//!
//! The `gl_Position` output of the last GLSL vertex processing stage (vertex shader,
//! tessellation shader, or geometry shader) is in *clip space*, that is 4D homogeneous
//! (projective) coordinates (x, y, z, w). During vertex post-processing, clip space
//! coordinates are transformed to normalized device coordinates (NDCs) by perspective
//! divide and clipped to the range [-1.0, 1.0] in the x, y, and z coorinates. Finally
//! the viewport transform takes NDCs and outputs *screen space* (or *window space*)
//! coordinates, which are 2D device coordinates + a 1D depth coordinate, on which
//! rasterization is performed to produce fragments.
//!
//! **2D**
//!
//! 2D x,y coordinates normalized to [-1.0, 1.0] can be passed through to vertex
//! post-processing unmodified. This is the case with shader pipelines that specify
//! inputs are in "`ClipSpace`"; the pass-thru vertex shader will add a z component of
//! 0.0 and a w component of 1.0.
//!
//! (Note: as of v0.6.0 none of the default draw2d resources use clip space rendering
//! pipelines)
//!
//! Even though the ultimate output of vertex post-processing is 2D screen space
//! (device) coordinates, in order to render screen-space input vertices, they must be
//! transformed to clip space by vertex processing. Two utility functions are provided
//! to convert between screen space and normalized coordinates:
//! `graphics::screen_2d_to_ndc_2d` and `graphics::ndc_2d_to_screen_2d`. Both require
//! screen dimensions as input.

use math_utils as math;
use math_utils::{num, vek};
use crate::camera3d;

/// Convenience method that calls `math::orthographic_rh_no` on the given `FrustmPlanes`
/// struct
#[inline]
pub fn projection_mat_orthographic <S> (ortho : &vek::FrustumPlanes <S>)
  -> math::Matrix4 <S>
where S : num::real::Real {
  math::Matrix4::orthographic_rh_no (*ortho)
}

/// Convenience method calling `Matrix4::perspective_rh_no` to construct a right-handed
/// perspective matrix with -1 to 1 clip planes, equivalent to the `gluPerspective`
/// function.
///
/// This will transform points in right-handed camera (view, eye) space (negative Z axis
/// into the scene, positive Y axis 'up') into left-handed 4D homogenous clip space
/// where the Z axis is reversed with positive Z into the screen while X and Y
/// orientations remain unchanged.
#[inline]
pub fn projection_mat_perspective <S> (perspective_fov : &camera3d::PerspectiveFov <S>)
  -> math::Matrix4 <S>
where S : num::real::Real + num::FloatConst + std::fmt::Debug {
  math::Matrix4::perspective_rh_no (
    perspective_fov.fovy.0,
    perspective_fov.aspect,
    perspective_fov.near,
    perspective_fov.far
  )
}

/// Convert screen coordinate to OpenGL NDC based on a given screen resolution.
///
/// # Examples
///
/// ```
/// # extern crate gl_utils;
/// # extern crate math_utils as math;
/// # fn main () {
/// # use gl_utils::graphics::screen_2d_to_ndc_2d;
/// assert_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [320.0, 240.0].into()),
///   [0.0, 0.0].into()
/// );
/// math::approx::assert_relative_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [480.0, 264.0].into()),
///   [0.5, 0.1].into()
/// );
/// assert_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 0.0].into()),
///   [-1.0, -1.0].into()
/// );
/// assert_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 480.0].into()),
///   [-1.0, 1.0].into()
/// );
/// assert_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 0.0].into()),
///   [1.0, -1.0].into()
/// );
/// assert_eq!(
///   screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 480.0].into()),
///   [1.0, 1.0].into()
/// );
/// # }
/// ```

pub fn screen_2d_to_ndc_2d (
  screen_dimensions : math::Vector2 <u16>,
  screen_coord      : math::Point2 <f32>
) -> math::Point2 <f32> {
  math::Point2::from ([
    screen_coord.0.x / (0.5 * screen_dimensions.x as f32) - 1.0,
    screen_coord.0.y / (0.5 * screen_dimensions.y as f32) - 1.0
  ])
}

/// Maps OpenGL NDC coordinates to screen coordinates based on a given screen
/// resolution.
///
/// # Examples
///
/// ```
/// # extern crate gl_utils;
/// # extern crate math_utils as math;
/// # fn main () {
/// # use gl_utils::graphics::ndc_2d_to_screen_2d;
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [0.0, 0.0].into()),
///   [320.0, 240.0].into()
/// );
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [0.5, 0.1].into()),
///   [480.0, 264.0].into()
/// );
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, -1.0].into()),
///   [0.0, 0.0].into()
/// );
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, 1.0].into()),
///   [0.0, 480.0].into()
/// );
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [1.0, -1.0].into()),
///   [640.0, 0.0].into()
/// );
/// assert_eq!(
///   ndc_2d_to_screen_2d ([640, 480].into(), [1.0, 1.0].into()),
///   [640.0, 480.0].into()
/// );
/// # }
/// ```

pub fn ndc_2d_to_screen_2d (
  screen_dimensions : math::Vector2 <u16>,
  ndc_coord         : math::Point2 <f32>
) -> math::Point2 <f32> {
  math::Point2::from ([
    0.5 * (screen_dimensions.x as f32) * (1.0 + ndc_coord.0.x),
    0.5 * (screen_dimensions.y as f32) * (1.0 + ndc_coord.0.y)
  ])
}