packages feed

gltf-loader-0.3.0.0: test/Text/GLTF/Loader/Internal/BufferAccessorSpec.hs

module Text.GLTF.Loader.Internal.BufferAccessorSpec (spec) where

import Text.GLTF.Loader.Internal.BufferAccessor
import Text.GLTF.Loader.Internal.Decoders
import Text.GLTF.Loader.Test.MkGltf

import Linear (V3(..))
import Data.Binary.Builder
import Data.Binary.Get (runGet)
import Data.Binary.Put (putFloatle, runPut)
import Data.ByteString.Lazy (fromStrict, toStrict)
import RIO
import RIO.Vector.Partial ((!))
import Test.Hspec
import qualified Codec.GlTF as GlTF
import qualified Codec.GlTF.Accessor as Accessor
import qualified Codec.GlTF.Buffer as Buffer
import qualified Codec.GlTF.BufferView as BufferView
import qualified Codec.GlTF.Image as Image
import qualified Codec.GlTF.URI as URI

spec :: Spec
spec = do
  let gltf = mkCodecGltf
      basePath = "."
  
  describe "loadBuffers" $ do
    it "Reads buffers from GlTF" $ do
      buffers <- loadBuffers gltf Nothing basePath
      
      let (GltfBuffer buffer') = buffers ! 0
          values = runGet (getScalar (fromIntegral <$> getUnsignedShort)) . fromStrict $ buffer'
      
      values `shouldBe` ([1..4] :: Vector Integer)

    it "Handles malformed URI" $ do
      let gltf' = gltf
            { GlTF.buffers = Just
                [ mkCodecBufferIndices { Buffer.uri = Just $ URI.URI "uh oh!" } ]
            }

      loadBuffers gltf' Nothing basePath `shouldThrow` anyException

    it "Handles no buffer" $ do
      let gltf' = gltf { GlTF.buffers = Nothing }
      buffers <- loadBuffers gltf' Nothing basePath

      buffers `shouldBe` []

    it "Handles a chunk buffer" $ do
      let chunk = mkCodecBufferChunk
      
          gltf' = gltf
            { GlTF.buffers = Just
              [ mkCodecBufferIndices { Buffer.uri = Nothing } ]
            }
      buffers <- loadBuffers gltf' (Just chunk) basePath
      
      let (GltfBuffer buffer') = buffers ! 0
          values = runGet (getScalar (fromIntegral <$> getUnsignedShort)) . fromStrict $ buffer'
      
      values `shouldBe` ([1..4] :: Vector Integer)
      
    it "Handles chunk + buffers" $ do
      let chunk = mkCodecBufferChunk
      
          gltf' = gltf
            { GlTF.buffers = Just
              [ mkCodecBufferIndices { Buffer.uri = Nothing },
                mkCodecBufferIndices
              ]
            }
      buffers <- loadBuffers gltf' (Just chunk) basePath
      
      let getValue = runGet (getScalar (fromIntegral <$> getUnsignedShort)) . fromStrict

      forM_ buffers $ \buffer -> do
        let value = getValue . unBuffer $ buffer
        value `shouldBe` ([1..4] :: Vector Integer)

  describe "loadImages" $ do
    it "Reads image URIs from GlTF" $ do
      images <- loadImages gltf basePath
      images `shouldBe` [ImageData "imagePayload"]

    it "Reads image BufferViews from GlTF" $ do
      let gltf' = gltf
            { GlTF.images = Just
                [ mkCodecImage { Image.uri = Nothing } ]
            }
      
      images <- loadImages gltf' basePath
      images `shouldBe` [ImageBufferView (BufferView.BufferViewIx 4)]

    it "Returns NoImage when no data specificed" $ do
      let gltf' = gltf
            { GlTF.images = Just
                [ mkCodecImage
                    { Image.uri = Nothing,
                      Image.bufferView = Nothing
                    }
                ]
            }
      
      images <- loadImages gltf' basePath
      images `shouldBe` [NoImageData]

    it "Handles malformed URIs" $ do
      let gltf' = gltf
            { GlTF.images = Just
                [mkCodecImage { Image.uri = Just (URI.URI "Uh oh!") }]
            }

      loadImages gltf' basePath `shouldThrow` anyException
  
  describe "vertexIndices" $ do
    it "Reads basic values from buffer" $ do
      vertexIndices gltf buffers' accessorIdIndices `shouldBe` [1, 2, 3, 4]

    it "Returns empty when accessor not defined" $ do
      let gltf' = gltf { GlTF.accessors = Nothing } 
      vertexIndices gltf' buffers' accessorIdIndices `shouldBe` []

    it "Returns empty when buffer not found" $ do
      let bufferView = mkCodecBufferViewIndices { BufferView.buffer = Buffer.BufferIx 99 }
          gltf' = gltf { GlTF.bufferViews = Just [bufferView] }
      vertexIndices gltf' buffers' accessorIdIndices `shouldBe` []

  describe "vertexPositions" $ do
    it "Reads basic values from buffer" $ do
      vertexPositions gltf buffers' accessorIdPositions `shouldBe`
        [ V3 1 1 1,
          V3 2 2 2,
          V3 3 3 3,
          V3 4 4 4
        ]

    it "Returns empty when accessor not defined" $ do
      let gltf' = gltf { GlTF.accessors = Nothing } 
      vertexPositions gltf' buffers' accessorIdPositions `shouldBe` []

    it "Returns empty when buffer not found" $ do
      let bufferView = mkCodecBufferViewIndices { BufferView.buffer = Buffer.BufferIx 99 }
          gltf' = gltf { GlTF.bufferViews = Just [bufferView] }
      vertexPositions gltf' buffers' accessorIdPositions `shouldBe` []

buffers' :: Vector GltfBuffer
buffers' = [bufferIndices, bufferPositions]

accessorIdIndices :: Accessor.AccessorIx
accessorIdIndices = Accessor.AccessorIx 0

accessorIdPositions :: Accessor.AccessorIx
accessorIdPositions = Accessor.AccessorIx 1

bufferIndices :: GltfBuffer
bufferIndices = GltfBuffer . toStrict . toLazyByteString $ putIndices
  where putIndices = foldr ((<>) . putWord16le) empty ([1, 2, 3, 4] :: [Word16])

bufferPositions :: GltfBuffer
bufferPositions = GltfBuffer . toStrict . runPut $ putPositions
  where putPositions = mapM_ (replicateM_ 3 . putFloatle) ([1..4] :: [Float])