packages feed

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

module Mischief.Materials.Sprite where

import Mischief.Materials
import Mischief.Materials.Time (TimeMaterial, getTime)
import Mischief.Render.Camera (CameraMatrices (CameraMatrices), Matrices (..))
import Mischief.Render.Core (DefaultSampler (..))
import Mischief.Render.Image
import Mischief.Render.Sprite.Common
import Prelude ()

data SpriteData f = SpriteData
  { coords :: Field f (Vec 3 Float),
    size :: Field f (Vec 2 Float),
    slice :: Field f (Vec 4 Float),
    flip :: Field f (Vec 2 Int),
    rotation :: Field f (Mat 3 3)
  }
  deriving stock (Generic)
  deriving anyclass (Bufferable)

newtype VertexInput f = VertexInput (BuiltIn f "vertex_index" Uint)
  deriving stock (Generic)
  deriving anyclass (ShaderParam)

data VertexOutput f = VertexOutput
  { pos :: BuiltIn f "position" (Vec 4 Float),
    uv :: Location f 0 (Vec 2 Float)
  }
  deriving (Generic, ShaderParam)

data Bindings f = Bindings
  { matrices :: Uniform f (Matrices f),
    sprite :: Uniform f (SpriteData f),
    texture :: Binding f Texture,
    sampler :: Binding f Sampler
  }
  deriving (Generic, Bindable)

data SpriteMaterial

instance Material SpriteMaterial where
  type VI SpriteMaterial = VertexInput
  type VO SpriteMaterial = VertexOutput
  type B SpriteMaterial = Bindings

  vertex :: VertexInput GPU -> Shader SpriteMaterial (VertexOutput GPU)
  vertex (VertexInput index) = do
    b <- bindings

    positions <- var $ array @(Vec2 Float) [vec2 (-1) (-1), vec2 1 (-1), vec2 1 1, vec2 (-1) (-1), vec2 1 1, vec2 (-1) 1]
    uvs <- var $ array @(Vec2 Float) [vec2 0 1, vec2 1 1, vec2 1 0, vec2 0 1, vec2 1 0, vec2 0 0]

    pos <- var $ (positions !! index) * b.sprite.size * diag 0.5
    pos <- var $ b.sprite.rotation !*^ (pos +: 0)
    pos <- var $ pos + b.sprite.coords
    pos <- var $ b.matrices.projection !*^ (b.matrices.view !*^ (pos +: 1))

    pure
      VertexOutput
        { pos = vec3 pos.x pos.y pos.z +: 1,
          uv = uvs !! index
        }

  fragment :: VertexOutput GPU -> Shader SpriteMaterial (Vec 4 Float)
  fragment VertexOutput {uv} = sampleSprite uv

  fetch =
    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})

sampleSprite :: Vec 2 Float -> Shader SpriteMaterial (Vec 4 Float)
sampleSprite uv = do
  b <- bindings
  let uv_flipped :: Vec 2 Float = abs $ cast b.sprite.flip - uv
  let uv = uv_flipped * vec2 b.sprite.slice.z b.sprite.slice.w + b.sprite.slice.xy
  pure $ sample b.texture b.sampler uv

newtype SomeColor f = SomeColor {color :: Field f (Vec 3 Float)}
  deriving stock (Generic)
  deriving anyclass (Bufferable)

newtype CustomBindings f = CustomBindings (Uniform f (SomeColor f))
  deriving stock (Generic)
  deriving anyclass (Bindable)

data CustomMat

instance Material CustomMat where
  type VI CustomMat = VI SpriteMaterial
  type VO CustomMat = VO SpriteMaterial

  type B CustomMat = CustomBindings

  type Extends CustomMat = '[SpriteMaterial, TimeMaterial]

  vertex = liftExt . vertex @SpriteMaterial

  fragment :: VO CustomMat GPU -> Shader CustomMat (Vec 4 Float)
  fragment vo = do
    color <- liftExt $ fragment @SpriteMaterial vo
    time <- liftExt getTime
    (CustomBindings (SomeColor c)) <- bindings
    pure $ color * (c ^* cos time +: 1)

  fetch =
    FetchBindings
      { camera = const $ pure (),
        mesh = \sprite -> qget sprite [qd|(Buffer SomeColor)|],
        result = const CustomBindings
      }