module Geomancy.Vulkan.Projection
  ( perspective
  , infinitePerspective
  , orthoOffCenter
  ) where

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

perspective
  :: Integral side
  => Float
  -> Float -> Float
  -> side -> side
  -> Transform
perspective :: forall side.
Integral side =>
Float -> Float -> Float -> side -> side -> Transform
perspective Float
fovRads Float
near Float
far side
width side
height = forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
  Float
x Float
0   Float
0   Float
0
  Float
0 Float
y   Float
0   Float
0
  Float
0 Float
0   Float
z Float
w23
  Float
0 Float
0 Float
w32   Float
1

  where
    x :: Float
x = Float
cotFoV forall a. Num a => a -> a -> a
* Float
aspectX
    y :: Float
y = -Float
cotFoV
    z :: Float
z = Float
far forall a. Fractional a => a -> a -> a
/ (Float
near forall a. Num a => a -> a -> a
- Float
far)
    w23 :: Float
w23 = Float
near forall a. Num a => a -> a -> a
* Float
far forall a. Fractional a => a -> a -> a
/ (Float
near forall a. Num a => a -> a -> a
- Float
far)
    w32 :: Float
w32 = -Float
1

    cotFoV :: Float
cotFoV = forall a. Fractional a => a -> a
recip forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Floating a => a -> a
tan forall a b. (a -> b) -> a -> b
$ Float
0.5 forall a. Num a => a -> a -> a
* Float
fovRads

    aspectX :: Float
aspectX = forall a b. (Integral a, Num b) => a -> b
fromIntegral side
height forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral side
width

infinitePerspective
  :: Integral side
  => Float
  -> side
  -> side
  -> Transform
infinitePerspective :: forall side. Integral side => Float -> side -> side -> Transform
infinitePerspective Float
fovRads side
width side
height = forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
  Float
x   Float
0   Float
0  Float
0
  Float
0 (-Float
y)  Float
0  Float
0
  Float
0   Float
0 (-Float
1) Float
w
  Float
0   Float
0 (-Float
1) Float
0
  where
    (Float
x, Float
y) =
      if side
width forall a. Ord a => a -> a -> Bool
> side
height then
        ( Float
cotFoV forall a. Fractional a => a -> a -> a
/ Float
camAspect
        , Float
cotFoV
        )
      else
        ( Float
cotFoV
        , Float
cotFoV forall a. Num a => a -> a -> a
* Float
camAspect
        )
    camAspect :: Float
camAspect = forall a b. (Integral a, Num b) => a -> b
fromIntegral side
width forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral side
height
    cotFoV :: Float
cotFoV = forall a. Fractional a => a -> a
recip forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Floating a => a -> a
tan forall a b. (a -> b) -> a -> b
$ Float
0.5 forall a. Num a => a -> a -> a
* Float
fovRads

    w :: Float
w = -Float
2 forall a. Num a => a -> a -> a
* Float
near
    near :: Float
near = Float
1forall a. Fractional a => a -> a -> a
/Float
128 -- 2048

orthoOffCenter :: Integral side => Float -> Float -> side -> side -> Transform
orthoOffCenter :: forall side.
Integral side =>
Float -> Float -> side -> side -> Transform
orthoOffCenter Float
near Float
far side
width side
height = forall a.
Coercible Mat4 a =>
Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> Float
-> a
colMajor
  Float
x Float
0 Float
0 Float
0
  Float
0 Float
y Float
0 Float
0
  Float
0 Float
0 Float
z Float
w
  Float
0 Float
0 Float
0 Float
1

  where
    x :: Float
x = Float
2 forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral side
width
    y :: Float
y = Float
2 forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral side
height
    z :: Float
z = Float
1 forall a. Fractional a => a -> a -> a
/ (Float
far forall a. Num a => a -> a -> a
- Float
near)

    w :: Float
w = Float
near forall a. Num a => a -> a -> a
* (Float
near forall a. Num a => a -> a -> a
- Float
far)