frustum :: Float -> Float -> Float -> Float -> Vec3 -> Vec3 -> Vec3 -> Frustum
frustum viewAngle aspectRatio nearDistance farDistance position lookat up =
  Frustum [ (pl ntr ntl ftl)
          , (pl nbl nbr fbr)
          , (pl ntl nbl fbl)
          , (pl nbr ntr fbr)
          , (pl ntl ntr nbr)
          , (pl ftr ftl fbl)
          ] ntl ntr nbl nbr ftl ftr fbl fbr
  where
    pl a b c = (n,d)
      where
        n = normalize $ (c - b) `crossprod` (a - b)
        d = -(n `dotprod` b)
    m a v = scalarMul a v
    ang2rad = pi / 180
    tang    = tan $ viewAngle * ang2rad * 0.5
    nh  = nearDistance * tang
    nw  = nh * aspectRatio
    fh  = farDistance * tang
    fw  = fh * aspectRatio
    z   = normalize $ position - lookat
    x   = normalize $ up `crossprod` z
    y   = z `crossprod` x

    nc  = position - m nearDistance z
    fc  = position - m farDistance z

    ntl = nc + m nh y - m nw x
    ntr = nc + m nh y + m nw x
    nbl = nc - m nh y - m nw x
    nbr = nc - m nh y + m nw x

    ftl = fc + m fh y - m fw x
    ftr = fc + m fh y + m fw x
    fbl = fc - m fh y - m fw x
    fbr = fc - m fh y + m fw x
frustum :: Float -> Float -> Float -> Float -> Vec3 -> Vec3 -> Vec3 -> Frustum
frustum viewAngle aspectRatio nearDistance farDistance position lookat up =
  Frustum [ (pl ntr ntl ftl)
          , (pl nbl nbr fbr)
          , (pl ntl nbl fbl)
          , (pl nbr ntr fbr)
          , (pl ntl ntr nbr)
          , (pl ftr ftl fbl)
          ] ntl ntr nbl nbr ftl ftr fbl fbr
  where
    pl a b c = (n,d)
      where
        n = normalize $ (c - b) `crossprod` (a - b)
        d = -(n `dotprod` b)
    m a v = scalarMul a v
    ang2rad = pi / 180
    tang    = tan $ viewAngle * ang2rad * 0.5
    nh  = nearDistance * tang
    nw  = nh * aspectRatio
    fh  = farDistance * tang
    fw  = fh * aspectRatio
    z   = normalize $ position - lookat
    x   = normalize $ up `crossprod` z
    y   = z `crossprod` x

    nc  = position - m nearDistance z
    fc  = position - m farDistance z

    ntl = nc + m nh y - m nw x
    ntr = nc + m nh y + m nw x
    nbl = nc - m nh y - m nw x
    nbr = nc - m nh y + m nw x

    ftl = fc + m fh y - m fw x
    ftr = fc + m fh y + m fw x
    fbl = fc - m fh y - m fw x
    fbr = fc - m fh y + m fw x
frustum viewAngle aspectRatio nearDistance farDistance position lookat up =
  Frustum [ (pl ntr ntl ftl)
          , (pl nbl nbr fbr)
          , (pl ntl nbl fbl)
          , (pl nbr ntr fbr)
          , (pl ntl ntr nbr)
          , (pl ftr ftl fbl)
          ] ntl ntr nbl nbr ftl ftr fbl fbr
  where
    pl a b c = (n,d)
      where
        n = normalize $ (c - b) `crossprod` (a - b)
        d = -(n `dotprod` b)
    m a v = scalarMul a v
    ang2rad = pi / 180
    tang    = tan $ viewAngle * ang2rad * 0.5
    nh  = nearDistance * tang
    nw  = nh * aspectRatio
    fh  = farDistance * tang
    fw  = fh * aspectRatio
    z   = normalize $ position - lookat
    x   = normalize $ up `crossprod` z
    y   = z `crossprod` x

    nc  = position - m nearDistance z
    fc  = position - m farDistance z

    ntl = nc + m nh y - m nw x
    ntr = nc + m nh y + m nw x
    nbl = nc - m nh y - m nw x
    nbr = nc - m nh y + m nw x

    ftl = fc + m fh y - m fw x
    ftr = fc + m fh y + m fw x
    fbl = fc - m fh y - m fw x
    fbr = fc - m fh y + m fw x