packages feed

mischief-render-0.1.0.0: src/Mischief/Render/Sprite.hs

{-# OPTIONS_GHC -Wno-partial-fields #-}

module Mischief.Render.Sprite where

import Data.Text qualified as T
import Linear.V4
import Mischief.ECS
import Mischief.Materials.Sprite (Bindings (..), SpriteData (..))
import Mischief.Math hiding (qd)
import Mischief.Math.Quat qualified as Quat
import Mischief.Math.Transform
import Mischief.Render.Buffer
import Mischief.Render.Camera
import Mischief.Render.Core
import Mischief.Render.Image
import Mischief.Render.Material.Definition
import Mischief.Render.Sprite.Common
import Mischief.Render.Texture

data SpritePlugin

instance Plugin SpritePlugin where
  init = do
    systems updateSpriteData
      & schedule @RenderPrepare
  deps = [dep @ImageUploadingPlugin]

updateSpriteData :: System ()
updateSpriteData = query_ $ do
  (sprite, Sprite {image}, GlobalTransform spriteT, slice', SpriteFlip {x = flipX, y = flipY}, buffer, Anchor anchor) <-
    [q|Entity, Sprite, GlobalTransform, Maybe SpriteSlice, SpriteFlip, Maybe (Buffer SpriteData), Anchor
      / Without (Buffer SpriteData) || Changed GlobalTransform || Changed Sprite || Changed SpriteSlice || Changed SpriteFlip || Changed Anchor|]

  ImageTexture Texture {desc} <- qget image [qd|ImageTexture|]

  queue <- qres
  device <- qres

  qlift $ do
    buffer <- case buffer of
      Just b -> pure b
      Nothing -> do
        buffer <- createBuffer @SpriteData device
        insert buffer sprite
        pure buffer

    let scale = V2 spriteT.scale.x spriteT.scale.y

    let size = case slice' of
          Just SpriteSlice {size = V2 x y} -> V2 (fromIntegral x) (fromIntegral y) * scale
          Nothing -> V2 (fromIntegral desc.width) (fromIntegral desc.height) * scale

    let slice = case slice' of
          Just SpriteSlice {start, size} ->
            V4
              (fromIntegral start.x / fromIntegral desc.width)
              (fromIntegral start.y / fromIntegral desc.height)
              (fromIntegral size.x / fromIntegral desc.width)
              (fromIntegral size.y / fromIntegral desc.height)
          Nothing -> V4 0 0 1 1

    let calcFlip x = if x then 1 else 0
    let flip = V2 (calcFlip flipY) (calcFlip flipX)

    let anchorOffset = (anchor - V2 0.5 0.5) * size
    let rotation = transpose $ Quat.toMat3 spriteT.rotation

    uploadBuffer queue buffer (SpriteData {coords = spriteT.translation + V3 anchorOffset.x anchorOffset.y 0, size, slice, flip, rotation})

fetchSpriteBindings :: FetchBindings Bindings
fetchSpriteBindings =
  FetchBindings
    ( \camera -> do
        (DefaultSampler sampler) <- qres @DefaultSampler
        CameraMatrices matrices <- qget camera [qd|CameraMatrices|]
        pure (matrices, sampler)
    )
    ( \sprite -> do
        (Sprite {image}, buffer) <- qget sprite [qd|Sprite, (Buffer SpriteData)|]
        ImageTextureView texture <- qget image [qd|ImageTextureView|]
        pure (buffer, texture)
    )
    (\(matrices, sampler) (buffer, texture) -> Bindings {matrices, sampler, sprite = buffer, texture})

-- newtype SpriteBuffer = SpriteBuffer (Buffer SpriteData) deriving anyclass (Component)