nsys-gl-utils 0.11.3

OpenGL and graphics utilities
//! 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_traits as num;
use math_utils::vek;
use crate::camera3d;

/// Convenience method that calls `math::orthographic_rh_no` on the given
/// `FrustmPlanes` struct
#[inline]
pub fn projection_mat_orthographic <S : num::real::Real> (
  ortho : &vek::FrustumPlanes <S>
) -> math::Matrix4 <S> {
  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 as f32 / (0.5 * screen_dimensions.x as f32) - 1.0,
    screen_coord.0.y as f32 / (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)
  ])
}