packages feed

shapes-demo-0.1.0.0: src/Physics/Demo/IOWorld.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RecordWildCards #-}

module Physics.Demo.IOWorld where

import Control.Monad
import Control.Monad.IO.Class
import Control.Lens ((^.), (.~), (%~), (&), makeLenses)
import Data.Proxy


import Linear.V2
import Linear.Matrix (M33)

import EasySDL.Draw
import qualified SDL.Event as E
import qualified SDL.Time as T
import qualified SDL.Video.Renderer as R
import qualified SDL.Input.Keyboard as K
import qualified SDL.Input.Keyboard.Codes as KC
import GameLoop hiding (testStep)

import Physics.Draw
import Physics.Draw.Canonical
import Physics.Engine.Class
import Utils.Utils

import Physics.Scenes.Scene
import Physics.Demo.Scenes

class (PhysicsEngine e, PEExternalObj e ~ (), MonadIO (DemoM e)) => Demo e where
  type DemoM e :: * -> *
  runDemo :: Proxy e -> Scene e -> DemoM e a -> IO a
  resetEngine :: Proxy e -> Scene e -> DemoM e ()
  drawWorld :: Proxy e -> R.Renderer -> M33 Double -> DemoM e ()
  demoWorld :: Proxy e -> DemoM e (PEWorld' e)
  worldContacts :: Proxy e -> DemoM e [Contact]
  worldAabbs :: Proxy e -> DemoM e [Aabb]
  debugEngineState :: Proxy e -> DemoM e String
  updateWorld :: Proxy e -> DemoM e ()

data DemoState =
  DemoState { _demoFinished :: Bool
            , _demoSceneIndex :: Int
            , _demoDrawDebug :: Bool
            , _demoPrintDebug :: Bool
            , _demoViewTransform :: M33 Double
            }
makeLenses ''DemoState

getViewTransform :: V2 Double -> V2 Double -> M33 Double
getViewTransform window scale = fst $ viewTransform window scale (V2 0 0)

initialState :: Int -> M33 Double -> DemoState
initialState i = DemoState False i True False

nextInitialState :: (Demo e) => Proxy e -> DemoState -> Int -> DemoM e DemoState
nextInitialState p DemoState{..} i = do
  resetEngine p (scenes p !! i)
  return $ initialState i _demoViewTransform
    & demoDrawDebug .~ _demoDrawDebug
    & demoPrintDebug .~ _demoPrintDebug

timeStep :: Num a => a
timeStep = 10

renderWorld :: (Demo e) => Proxy e -> R.Renderer -> DemoState -> DemoM e ()
renderWorld p r DemoState{..} = do
  liftIO $ setColor r black
  drawWorld p r _demoViewTransform

renderContacts :: (Demo e) => Proxy e -> R.Renderer -> DemoState -> DemoM e ()
renderContacts p r DemoState{..} = do
  liftIO $ setColor r pink
  contacts <- worldContacts p
  liftIO $ mapM_ (drawContact r . transform _demoViewTransform) contacts

renderAabbs :: (Demo e) => Proxy e -> R.Renderer -> DemoState -> DemoM e ()
renderAabbs p r DemoState{..} = do
  liftIO $ setColor r silver
  aabbs <- worldAabbs p
  liftIO $ mapM_ (drawAabb r . transform _demoViewTransform) aabbs

demoStep :: (Demo e) => Proxy e -> R.Renderer -> DemoState -> DemoM e DemoState
demoStep p r s0@DemoState{..} = do
  events <- liftIO E.pollEvents
  liftIO $ clearScreen r
  renderWorld p r s0
  when (s0 ^. demoDrawDebug) $ do
    renderContacts p r s0
    renderAabbs p r s0
  when (s0 ^. demoPrintDebug) $ do
    debug <- debugEngineState p
    liftIO $ print debug
  s1 <- foldM (handleEvent p) s0 events
  updateWorld p
  liftIO $ R.present r
  return s1

handleEvent :: (Demo e) => Proxy e -> DemoState -> E.Event -> DemoM e DemoState
handleEvent _ s0 (E.Event _ E.QuitEvent) =
  return s0 { _demoFinished = True }
handleEvent p s0 (E.Event _ (E.KeyboardEvent (E.KeyboardEventData _ motion _ key)))
  | motion == E.Pressed =
    handleKeypress p s0 (K.keysymScancode key) (K.keysymModifier key)
  | otherwise = return s0
handleEvent _ s0 _ = return s0

handleKeypress :: (Demo e)
               => Proxy e
               -> DemoState
               -> K.Scancode
               -> K.KeyModifier
               -> DemoM e DemoState
handleKeypress p state KC.ScancodeR _ =
  nextInitialState p state (state ^. demoSceneIndex)
handleKeypress p state KC.ScancodeN km
  | K.keyModifierLeftShift km || K.keyModifierRightShift km =
    nextInitialState p state $
    (state ^. demoSceneIndex - 1)
    `posMod` sceneCount
  | otherwise =
    nextInitialState p state $
    (state ^. demoSceneIndex + 1)
    `mod` sceneCount
  where sceneCount = length $ scenes p
handleKeypress _ state KC.ScancodeD _ =
  return $ state & demoDrawDebug %~ not
handleKeypress _ state KC.ScancodeP _ =
  return $ state & demoPrintDebug %~ not
handleKeypress _ state _ _ = return state

demoMain :: (Demo e) => Proxy e -> V2 Double -> V2 Double -> R.Renderer -> IO ()
demoMain p window scale r = do
  t0 <- T.ticks
  let demo = timedRunUntil t0 timeStep (initialState 0 $ getViewTransform window scale) _demoFinished (\s _ -> demoStep p r s)
  runDemo p (head $ scenes p) demo