packages feed

geomancy-0.4.0.0: test/Spec/Vulkan.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}

module Spec.Vulkan (tests) where

import Geomancy
import Hedgehog
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testPropertyNamed)

import Linear (M44, V3(..), V4(..))

import Geomancy.DMat4 qualified
import Geomancy.DTransform qualified as DTransform
import Geomancy.Mat4 qualified
import Geomancy.Mat4 ((!*))
import Geomancy.Transform qualified as Transform
import Geomancy.Vulkan.DProjection qualified as DProjection
import Geomancy.Vulkan.DView qualified as DView
import Geomancy.Vulkan.Projection qualified as Projection
import Geomancy.Vulkan.View qualified as View

import Support.Ball (Ball, enclosed, exactly)
import Support.Fixtures (quarterTurn, toNDC)
import Support.Gen (genDVec3, genVec3)
import Support.Linear (entries, toM44, toM44D)
import Support.Reference.Mat4 qualified as Ref.Mat4
import Support.Reference.Vulkan qualified as Ref.Vulkan

tests :: TestTree
tests = testGroup "Vulkan"
  [ testGroup "Projection"
      [ testPropertyNamed "reverseDepthRH" "prop_projection_reverseDepthRH" prop_projection_reverseDepthRH
      , testPropertyNamed "orthoRH_identity" "prop_projection_orthoRH_identity" prop_projection_orthoRH_identity
      , testPropertyNamed "reverseDepthOrthoRH" "prop_projection_reverseDepthOrthoRH" prop_projection_reverseDepthOrthoRH
      ]
  , testGroup "View"
      [ testPropertyNamed "lookAtRH_identity" "prop_view_lookAtRH_identity" prop_view_lookAtRH_identity
      ]
  , testGroup "DProjection"
      [ testPropertyNamed "dprojection" "prop_dprojection" prop_dprojection
      ]
  , testGroup "DView"
      [ testPropertyNamed "lookAtRH_identity" "prop_dview_lookAtRH_identity" prop_dview_lookAtRH_identity
      ]
  ]

prop_projection_reverseDepthRH :: Property
prop_projection_reverseDepthRH = withTests 1 $ property do
  let
    zNear = 0.1
    Transform p = Projection.reverseDepthRH quarterTurn zNear 800 600
    pBall = Ref.Vulkan.reverseDepthRH quarterTurn (exactly zNear) 800 600
  annotateShow p
  let
    -- stating the expected in transposed order for testing
    Transform expected = Geomancy.Mat4.rowMajor
      0.7500 0.0000 0.0000 0.0000
      0.0000 1.0000 0.0000 0.0000
      0.0000 0.0000 0.0000 1.0000
      0.0000 0.0000  zNear 0.0000
  annotateShow expected
  replicate 16 Nothing === enclosed (entries pBall) (entries (toM44 p))
  replicate 16 Nothing === enclosed (entries pBall) (entries (toM44 expected))

  let
    centerNearIn = vec4 0 0 zNear 1
    centerNearOut = p !* centerNearIn
    centerNearNDC = toNDC centerNearOut
  annotateShow (centerNearIn, centerNearOut)
  vec3 0 0 1 === centerNearNDC

prop_projection_orthoRH_identity :: Property
prop_projection_orthoRH_identity = withTests 1 $ property do
  let Transform pNative = Projection.orthoRH 0 1 2 2
  annotateShow pNative
  let pBall = Ref.Vulkan.orthoRH 0 1 2 2 :: M44 (Ball Float)
  replicate 16 Nothing === enclosed (entries pBall) (entries (toM44 pNative))
  replicate 16 Nothing === enclosed (entries pBall) (entries (toM44 mempty))

prop_projection_reverseDepthOrthoRH :: Property
prop_projection_reverseDepthOrthoRH = withTests 1 $ property do
  let t = Projection.reverseDepthOrthoRH 1 10 800 600
  annotateShow t

  vec4 0 0 1 1 === t !* vec4 0 0 1 1 -- center/near
  vec4 0 0 0 1 === t !* vec4 0 0 10 1 -- center/far
  vec4 1 1 0 1 === t !* vec4 400 300 10 1 -- edge/far
  vec4 (-1/400) (-1/300) 1 1 === t !* vec4 (-1) (-1) 1 1 -- step/near
  vec4 (1/400) (1/300) 0 1 === t !* vec4 1 1 10 1 -- step/far

prop_view_lookAtRH_identity :: Property
prop_view_lookAtRH_identity = property do
  let alreadyThere = View.lookAtRH_ (vec3 0 0 0) (vec3 0 0 1)
  annotateShow alreadyThere

  p <- forAll genVec3

  p === Transform.apply p alreadyThere

prop_dprojection :: Property
prop_dprojection = withTests 1 $ property do
  let
    zNear = 0.1
    DTransform p = DProjection.reverseDepthRH quarterTurn zNear 800 600
    centerNear = p Geomancy.DMat4.!* dvec4 0 0 zNear 1
    pBall = Ref.Vulkan.reverseDepthRH quarterTurn (exactly zNear) 800 600
    V4 xBall yBall zBall wBall = Ref.Mat4.apply pBall (V4 0 0 (exactly zNear) 1)
    centerNearNDCBall = V3 (xBall / wBall) (yBall / wBall) (zBall / wBall)
  withDVec4 centerNear \x y z w -> do
    replicate 3 Nothing === enclosed centerNearNDCBall (V3 (x/w) (y/w) (z/w))
    replicate 3 Nothing === enclosed centerNearNDCBall (V3 0 0 1 :: V3 Double)

  let
    DTransform o = DProjection.orthoRH 0 1 2 2
    oBall = Ref.Vulkan.orthoRH 0 1 2 2 :: M44 (Ball Double)
  replicate 16 Nothing === enclosed (entries oBall) (entries (toM44D o))
  replicate 16 Nothing === enclosed (entries oBall) (entries (toM44D mempty))

  let t = DProjection.reverseDepthOrthoRH 1 10 800 600
  dvec4 0 0 1 1 === t Geomancy.DMat4.!* dvec4 0 0 1 1
  dvec4 0 0 0 1 === t Geomancy.DMat4.!* dvec4 0 0 10 1
  dvec4 1 1 0 1 === t Geomancy.DMat4.!* dvec4 400 300 10 1

prop_dview_lookAtRH_identity :: Property
prop_dview_lookAtRH_identity = property do
  let alreadyThere = DView.lookAtRH_ (dvec3 0 0 0) (dvec3 0 0 1)
  p <- forAll genDVec3
  p === DTransform.apply p alreadyThere