packages feed

keid-render-basic-0.1.6.1: src/Render/Unlit/Sprite/Model.hs

{-# OPTIONS_GHC -fplugin Foreign.Storable.Generic.Plugin #-}

module Render.Unlit.Sprite.Model
  ( InstanceAttrs(..)
  , StorableAttrs
  , InstanceBuffer
  , fromTexture
  , fromAtlas

  , vkInstanceAttrs
  ) where

import RIO

import Foreign.Storable.Generic (GStorable)
import Geomancy (Vec2, Vec4, UVec2, withUVec2, vec2)
import Geomancy.Vec4 qualified as Vec4
import Render.Samplers qualified as Samplers
import Resource.Buffer qualified as Buffer
import Resource.Image.Atlas (Atlas)
import Resource.Image.Atlas qualified as Atlas
import RIO.Vector.Storable qualified as Storable
import Vulkan.Core10 qualified as Vk
import Vulkan.Zero (Zero(..))

type StorableAttrs = Storable.Vector InstanceAttrs

data InstanceAttrs = InstanceAttrs
  { vertRect :: Vec4
  , fragRect :: Vec4

  , tint    :: Vec4
  , outline :: Vec4

  , samplerId :: Int32
  , textureId :: Int32
  , textureSize :: UVec2
  }
  deriving (Show, Generic)

instance GStorable InstanceAttrs

instance Zero InstanceAttrs where
  zero = InstanceAttrs
    { vertRect = 0
    , fragRect = 0

    , tint    = 0
    , outline = 0

    , samplerId = 0
    , textureId = 0
    , textureSize = 0
    }

vkInstanceAttrs :: [Vk.Format]
vkInstanceAttrs =
  [ Vk.FORMAT_R32G32B32A32_SFLOAT -- Quad scale+offset
  , Vk.FORMAT_R32G32B32A32_SFLOAT -- UV scale+offset
  , Vk.FORMAT_R32G32B32A32_SFLOAT -- Tint
  , Vk.FORMAT_R32G32B32A32_SFLOAT -- Outline (when enabled)

  , Vk.FORMAT_R32G32_SINT -- Sampler + texture IDs
  , Vk.FORMAT_R32G32_UINT -- Texture size
  ]

type InstanceBuffer stage = Buffer.Allocated stage InstanceAttrs

fromTexture
  :: Int32 -- ^ Sampler index.
  -> Int32 -- ^ Texture index.
  -> Vec2  -- ^ Sprite size.
  -> Vec2  -- ^ Sprite position.
  -> InstanceAttrs
fromTexture sampler texture wh pos =
  InstanceAttrs
    { vertRect = Vec4.fromVec22 pos wh
    , fragRect = Vec4.fromVec22 0 1

    , tint    = 1
    , outline = 0

    , samplerId = sampler
    , textureId = texture
    , textureSize = 0
    }

fromAtlas
  :: Int32 -- ^ Texture ID.
  -> Atlas
  -> Vec2 -- ^ Sprite scale, wrt. to native tile size
  -> Vec2 -- ^ Tile position in atlas tiles. Can be fractional when using subgrids.
  -> Vec2 -- ^ Sprite position.
  -> InstanceAttrs
fromAtlas texture atlas scale atlasPos worldPos =
  InstanceAttrs
    { vertRect = Vec4.fromVec22 worldPos (tileSize * scale)
    , fragRect = Vec4.fromVec22 (atlasPos * uvScale) uvScale

    , tint    = 1
    , outline = 0

    , samplerId = Samplers.nearest Samplers.indices
    , textureId = texture
    , textureSize = Atlas.sizePx atlas
    }

  where
    uvScale = Atlas.uvScale atlas

    tileSize =
      withUVec2 (Atlas.tileSizePx atlas) \tw th ->
        vec2 (fromIntegral tw) (fromIntegral th)