module Geomancy.Vulkan.Projection
  ( reverseDepthRH
  , reverseDepthOrthoRH
  , orthoRH
  ) where

import Geomancy.Mat4 (colMajor)
import Geomancy.Transform (Transform(..))

-- | Construct a view-to-NDC transformation.
--
-- This will shove a camera frustum (an expanding pyramid) into a Vulkan "clip space" box
-- with the dimensions [-1; 1] left-to-right, [-1; 1] top-to-bottom, and [1; 0] into the screen.
--
-- That is, things further away in the view will be pulled into [0; 0; 0] point on the *back* of the box.
-- And things that on the near plane set by the argument to this function will be scaled to match
-- the aspect ratio and the field of view.
--
-- When using FoV @pi/2@ and @width@=@height the points with Z=near will keep their positions in the XY plane.
--
-- NOTE: To update your code using a vanilla perspective:
--
-- Change your depth buffer's clear value.
--    Instead of clearing to 1.0 (farthest), you now clear to 0.0.
-- Change your depth comparison function.
--    Instead of VK_COMPARE_OP_LESS, you must use VK_COMPARE_OP_GREATER.
reverseDepthRH
  :: Float
  -> Float
  -> Float -> Float
  -> Transform
reverseDepthRH :: Float -> Float -> Float -> Float -> Transform
reverseDepthRH Float
fovRads Float
zn Float
width Float
height =
  Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Transform
forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
    Float
sx  Float
0 Float
0  Float
0
    Float
0  Float
sy Float
0  Float
0
    Float
0   Float
0 Float
0 Float
zn
    Float
0   Float
0 Float
1  Float
0
  where
    sx :: Float
sx = Float
sy Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
recipAspect
      where
        recipAspect :: Float
recipAspect = Float
height Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
width
    sy :: Float
sy = Float
1 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float -> Float
forall a. Floating a => a -> a
tan (Float
fovRads Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
2)

{- | Vanilla orthographic projection centered on @0,0@

@orthoRH 0 1 2 2@ gives identity transform.
@orthoRH 0 1 800 600@ will map @vec3 400 300 0@ to @vec3 0 0 0@.
-}
orthoRH :: Float -> Float -> Float -> Float -> Transform
orthoRH :: Float -> Float -> Float -> Float -> Transform
orthoRH Float
near Float
far Float
width Float
height =
  Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Transform
forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
    Float
sx Float
0 Float
0 Float
0
    Float
0 Float
sy Float
0 Float
0
    Float
0  Float
0 Float
z Float
w
    Float
0  Float
0 Float
0 Float
1
  where
    sx :: Float
sx = Float
2 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
width
    sy :: Float
sy = Float
2 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
height
    z :: Float
z = Float
1 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
far Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
near)
    w :: Float
w = Float
near Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
near Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
far)

{- | Reverse-depth orthographic projection centered on @0,0@

Can be used in the same render pass with reverseDepthRH/VK_COMPARE_OP_GREATER pipelines.

@reverseDepthOrthoRH 0 1 800 600@ will map @vec3 400 300 0@ to @vec3 0 0 1@.
-}
reverseDepthOrthoRH :: Float -> Float -> Float -> Float -> Transform
reverseDepthOrthoRH :: Float -> Float -> Float -> Float -> Transform
reverseDepthOrthoRH Float
near Float
far Float
width Float
height =
  Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Transform
forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
    Float
sx Float
0 Float
0 Float
0
    Float
0 Float
sy Float
0 Float
0
    Float
0  Float
0 Float
z Float
w
    Float
0  Float
0 Float
0 Float
1
  where
    sx :: Float
sx = Float
2 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
width
    sy :: Float
sy = Float
2 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
height
    z :: Float
z = Float
1 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
near Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
far)
    w :: Float
w = Float
far Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
far Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
near)