import glm

# We assume all the spaces up to and including view space to feature a Y-axis pointing upwards.
# Screen space in Vulkan, however, has the +Y-axis pointing downwards, +X to the left, and +Z into the screen.
# We are staying in right-handed (RH) coordinate systems throughout ALL the spaces.
# Therefore, we are representing the coordinates from here on -- actually exactly BETWEEN View Space and
# Clip Space -- in a coordinate system which is rotated 180 around X, having Y point down, still RH.
let rotateAroundXFrom_RH_Yup_to_RH_Ydown*: Mat4[float32] = 
 mat4f(  vec4f(1.float32,  0,  0,  0)
      , -vec4f(0.float32,  1,  0,  0)
      , -vec4f(0.float32,  0,  1,  0)
      ,  vec4f(0.float32,  0,  0,  1)
      )
let rotateAroundXFrom_RH_Yup_to_RH_Ydown_Inverse* = inverse rotateAroundXFrom_RH_Yup_to_RH_Ydown

proc ortho*(left, right, bottom, top, near, far: float32): Mat4f = 
 var m = mat4[float32](1.0)
 let 
  rl = right - left
  tb = top - bottom
  fn = far - near
 
 m[0][0] = 2.float32 / rl
 m[1][1] = 2.float32 / tb
 m[2][2] = 1.float32 / fn
 m[3][0] = -(right + left) / rl
 m[3][1] = -(top + bottom) / tb
 m[3][2] = -(near) / fn

 result = m * rotateAroundXFrom_RH_Yup_to_RH_Ydown_Inverse

proc lookAtRH*( eye
              , center
              , up: Vec3[float32]
              ): Mat4[float32] = 

 let 
  f = normalize(center - eye)
  s = normalize(cross(f, up))
  u = cross(s, f)

 result[0][0] = s.x
 result[1][0] = s.y
 result[2][0] = s.z
 result[0][1] = u.x
 result[1][1] = u.y
 result[2][1] = u.z
 result[0][2] = -f.x
 result[1][2] = -f.y
 result[2][2] = -f.z
 result[3][0] = -dot(s, eye)
 result[3][1] = -dot(u, eye)
 result[3][2] =  dot(f, eye)
 
proc perspectiveLH*[T]( fovy, aspect, zNear, zFar:T): Mat4[T] =
    let tanHalfFovy = tan(fovy / T(2))
    result = mat4[T](0.0)
    result[0,0] = T(1) / (aspect * tanHalfFovy)
    result[1,1] = T(1) / (tanHalfFovy)
    result[2,2] = (zFar + zNear) / (zFar - zNear)
    result[2,3] = T(1)
    result[3,2] = - (T(2) * zFar * zNear) / (zFar - zNear)

proc perspectiveRH*[T]( fovy, aspect, zNear, zFar:T): Mat4f =
 let tanHalfFovy = tan(fovy / T(2))
 result = mat4[T](0.0)
 result[0,0] = T(1) / (aspect * tanHalfFovy)
 result[1,1] = T(1) / (tanHalfFovy)
 result[2,3] = T(-1) 
 result[2,2] = -(zFar + zNear) / (zFar - zNear)
 result[3,2] = -(T(2) * zFar * zNear) / (zFar - zNear)