spice 0.3.3.2 → 0.4.0.0
raw patch · 27 files changed
+1177/−1017 lines, 27 filesdep +JuicyPixelsdep +JuicyPixels-repadep +bytestringsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: JuicyPixels, JuicyPixels-repa, bytestring
API changes (from Hackage documentation)
- FRP.Spice.Graphics: fromElements :: [Element] -> Scene
- FRP.Spice.Graphics: renderScene :: Scene -> IO ()
+ FRP.Spice: Assets :: Map FilePath Sprite -> Assets
+ FRP.Spice: LoadSprite :: FilePath -> LoadAsset
+ FRP.Spice: appendSprite :: Assets -> FilePath -> Sprite -> Assets
+ FRP.Spice: data Assets
+ FRP.Spice: data LoadAsset
+ FRP.Spice: defaultAssets :: Assets
+ FRP.Spice: loadAssets :: Game a => a -> LoadAssets
+ FRP.Spice: loadSpriteAsset :: FilePath -> LoadAssets
+ FRP.Spice: performAssetLoads :: LoadAssets -> IO Assets
+ FRP.Spice: sprites :: Assets -> Map FilePath Sprite
+ FRP.Spice: type LoadAssets = DoList [LoadAsset]
+ FRP.Spice.Graphics: Sprite :: TextureObject -> Vector Float -> Sprite
+ FRP.Spice.Graphics: data Sprite
+ FRP.Spice.Graphics: fromgl :: GLfloat -> Float
+ FRP.Spice.Graphics: loadSprite :: FilePath -> IO Sprite
+ FRP.Spice.Graphics: renderLine :: Vector Float -> Vector Float -> Scene
+ FRP.Spice.Graphics: renderPolygon :: [Vector Float] -> Scene
+ FRP.Spice.Graphics: renderSprite :: Sprite -> Vector Float -> Scene
+ FRP.Spice.Graphics: renderTriangle :: Vector Float -> Vector Float -> Vector Float -> Scene
+ FRP.Spice.Graphics: spriteSize :: Sprite -> Vector Float
+ FRP.Spice.Graphics: spriteTex :: Sprite -> TextureObject
- FRP.Spice: render :: Game a => a -> Scene
+ FRP.Spice: render :: Game a => Assets -> a -> Scene
- FRP.Spice.Graphics: type Scene = DoList [Element]
+ FRP.Spice.Graphics: type Scene = IO ()
Files
- LICENSE +20/−20
- Setup.hs +2/−2
- spice.cabal +55/−51
- src/FRP/Spice.hs +24/−22
- src/FRP/Spice/Assets.hs +71/−0
- src/FRP/Spice/Config.hs +44/−44
- src/FRP/Spice/Engine.hs +90/−86
- src/FRP/Spice/Engine/Driver.hs +27/−26
- src/FRP/Spice/Engine/Network.hs +31/−31
- src/FRP/Spice/Engine/RenderWrapper.hs +31/−31
- src/FRP/Spice/Engine/RunInput.hs +31/−31
- src/FRP/Spice/Game.hs +29/−27
- src/FRP/Spice/Graphics.hs +17/−15
- src/FRP/Spice/Graphics/Color.hs +99/−92
- src/FRP/Spice/Graphics/Element.hs +0/−41
- src/FRP/Spice/Graphics/Geometry.hs +76/−74
- src/FRP/Spice/Graphics/Scene.hs +13/−24
- src/FRP/Spice/Graphics/Sprite.hs +111/−0
- src/FRP/Spice/Graphics/Utils.hs +24/−18
- src/FRP/Spice/Input.hs +26/−26
- src/FRP/Spice/Input/Backend.hs +97/−97
- src/FRP/Spice/Input/Keyboard.hs +40/−40
- src/FRP/Spice/Input/Mouse.hs +52/−52
- src/FRP/Spice/Input/MousePosition.hs +31/−31
- src/FRP/Spice/Math.hs +9/−9
- src/FRP/Spice/Math/Vector.hs +64/−64
- src/FRP/Spice/Utils/DoList.hs +63/−63
LICENSE view
@@ -1,20 +1,20 @@-Copyright (c) 2014 Cerek Hillen--Permission is hereby granted, free of charge, to any person obtaining-a copy of this software and associated documentation files (the-"Software"), to deal in the Software without restriction, including-without limitation the rights to use, copy, modify, merge, publish,-distribute, sublicense, and/or sell copies of the Software, and to-permit persons to whom the Software is furnished to do so, subject to-the following conditions:--The above copyright notice and this permission notice shall be included-in all copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+Copyright (c) 2014 Cerek Hillen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
spice.cabal view
@@ -1,51 +1,55 @@-name: spice-version: 0.3.3.2-synopsis: An FRP-based game engine written in Haskell.-description: An FRP-based game engine written in Haskell. - See the homepage for more information.-homepage: http://github.com/crockeo/spice-license: MIT-license-file: LICENSE-author: Cerek Hillen-maintainer: Cerek Hillen <cerekh@gmail.com>-copyright: (c) 2014, Cerek Hillen-category: FRP-build-type: Simple-cabal-version: >=1.10--library- ghc-options: -Wall -fno-warn-unused-do-bind-- exposed-modules: FRP.Spice- FRP.Spice.Graphics- FRP.Spice.Math-- other-modules: FRP.Spice.Config- FRP.Spice.Engine- FRP.Spice.Engine.Driver- FRP.Spice.Engine.Network- FRP.Spice.Engine.RenderWrapper- FRP.Spice.Engine.RunInput- FRP.Spice.Game- FRP.Spice.Graphics.Color- FRP.Spice.Graphics.Element- FRP.Spice.Graphics.Geometry- FRP.Spice.Graphics.Scene- FRP.Spice.Graphics.Utils- FRP.Spice.Input- FRP.Spice.Input.Backend- FRP.Spice.Input.Keyboard- FRP.Spice.Input.Mouse- FRP.Spice.Input.MousePosition- FRP.Spice.Math.Vector- FRP.Spice.Utils.DoList-- hs-source-dirs: src/-- build-depends: base == 4.7.*- , elerea == 2.7.*- , GLFW == 0.5.*- , containers == 0.5.*- , data-default == 0.5.*- , OpenGL-- default-language: Haskell2010+name: spice +version: 0.4.0.0 +synopsis: An FRP-based game engine written in Haskell. +description: An FRP-based game engine written in Haskell. - See the homepage for more information. +homepage: http://github.com/crockeo/spice +license: MIT +license-file: LICENSE +author: Cerek Hillen +maintainer: Cerek Hillen <cerekh@gmail.com> +copyright: (c) 2014, Cerek Hillen +category: FRP +build-type: Simple +cabal-version: >=1.10 + +library + ghc-options: -Wall -fno-warn-unused-do-bind + + exposed-modules: FRP.Spice + FRP.Spice.Graphics + FRP.Spice.Math + + other-modules: FRP.Spice.Assets + FRP.Spice.Config + FRP.Spice.Engine + FRP.Spice.Engine.Driver + FRP.Spice.Engine.Network + FRP.Spice.Engine.RenderWrapper + FRP.Spice.Engine.RunInput + FRP.Spice.Game + FRP.Spice.Graphics.Color + FRP.Spice.Graphics.Geometry + FRP.Spice.Graphics.Scene + FRP.Spice.Graphics.Sprite + FRP.Spice.Graphics.Utils + FRP.Spice.Input + FRP.Spice.Input.Backend + FRP.Spice.Input.Keyboard + FRP.Spice.Input.Mouse + FRP.Spice.Input.MousePosition + FRP.Spice.Math.Vector + FRP.Spice.Utils.DoList + + hs-source-dirs: src/ + + build-depends: base == 4.7.* + , elerea == 2.7.* + , GLFW == 0.5.* + , containers == 0.5.* + , data-default == 0.5.* + , bytestring == 0.10.* + , JuicyPixels == 3.1.* + , JuicyPixels-repa == 0.7.* + , OpenGL + + default-language: Haskell2010
src/FRP/Spice.hs view
@@ -1,22 +1,24 @@-{-|- This module re-exports some other modules in the spice library so that you- needn't import all of them explicitly.--}-module FRP.Spice ( module Graphics.UI.GLFW- , module Data.Map.Strict-- , module FRP.Spice.Config- , module FRP.Spice.Engine- , module FRP.Spice.Input- , module FRP.Spice.Game- ) where---------------------------- Rexported Imports ---import Graphics.UI.GLFW (Key (..), SpecialKey (..), MouseButton (..))-import Data.Map.Strict ((!))--import FRP.Spice.Config-import FRP.Spice.Engine-import FRP.Spice.Input-import FRP.Spice.Game+{-| + This module re-exports some other modules in the spice library so that you + needn't import all of them explicitly. +-} +module FRP.Spice ( module Graphics.UI.GLFW + , module Data.Map.Strict + + , module FRP.Spice.Assets + , module FRP.Spice.Config + , module FRP.Spice.Engine + , module FRP.Spice.Input + , module FRP.Spice.Game + ) where + +----------------------- +-- Rexported Imports -- +import Graphics.UI.GLFW (Key (..), SpecialKey (..), MouseButton (..)) +import Data.Map.Strict ((!)) + +import FRP.Spice.Assets +import FRP.Spice.Config +import FRP.Spice.Engine +import FRP.Spice.Input +import FRP.Spice.Game
+ src/FRP/Spice/Assets.hs view
@@ -0,0 +1,71 @@+{-| + This module provides a clean way to load assets that will later be used in + the spice-based program. +-} +module FRP.Spice.Assets where + +-------------------- +-- Global Imports -- +import qualified Data.Map.Strict as Map +import Control.Monad +import Data.Default + +------------------- +-- Local Imports -- +import FRP.Spice.Graphics.Sprite +import FRP.Spice.Utils.DoList + +---------- +-- Code -- + +{-| + A single call to load an asset. +-} +data LoadAsset = LoadSprite FilePath + +{-| + A list of @'LoadAssets'@ that may be composed in do-notation. +-} +type LoadAssets = DoList [LoadAsset] + +{-| + The data structure that contains the loaded assets. +-} +data Assets = Assets { sprites :: Map.Map FilePath Sprite } + +{-| + The default state for the @'Assets'@ data type. Used as the initial state for + @'performAssetLoads'@. +-} +defaultAssets :: Assets +defaultAssets = + Assets { sprites = Map.fromList [] } + +{-| + A synonym for @'defaultAssets'@ to fit within the data-default library. +-} +instance Default Assets where + def = defaultAssets + +{-| + Appending a @'Sprite'@ to an @'Assets'@. +-} +appendSprite :: Assets -> FilePath -> Sprite -> Assets +appendSprite assets path sprite = + assets { sprites = Map.insert path sprite $ sprites assets } + +{-| + Performing the actual loading upon a @'LoadAssets'@. +-} +performAssetLoads :: LoadAssets -> IO Assets +performAssetLoads la = + performAssetLoads' (values la) defaultAssets + where performAssetLoads' :: [LoadAsset] -> Assets -> IO Assets + performAssetLoads' [] assets = return assets + performAssetLoads' (LoadSprite path:xs) assets = (liftM (appendSprite assets path) $ loadSprite path) >>= performAssetLoads' xs + +{-| + Loading a @'Sprite'@ asset. +-} +loadSpriteAsset :: FilePath -> LoadAssets +loadSpriteAsset = fromValues . return . LoadSprite
src/FRP/Spice/Config.hs view
@@ -1,44 +1,44 @@-{-|- This module handles the configuration for the project.--}-module FRP.Spice.Config where------------------------- Global Imports ---import Data.Default--------------- Code ----{-|- A datatype to configure the window settings when creating an OpenGL context- using @'FRP.Spice.Engine.startEngine'@ in the engine.--}-data WindowConfig = WindowConfig { getWindowWidth :: Int- , getWindowHeight :: Int- , getWindowFullscreen :: Bool- , getWindowTitle :: String- }- deriving (Eq, Show, Read)--{-|- The default for @'WindowConfig'@-- > getWindowWidth = 640- > getWindowHeight = 480- > getWindowFullscreen = False- > getWindowTitle = "Spice Application"--}-defaultWindowConfig :: WindowConfig-defaultWindowConfig = WindowConfig { getWindowWidth = 640- , getWindowHeight = 480- , getWindowFullscreen = False- , getWindowTitle = "Spice Application"- }--{-|- A default instance for @'WindowConfig'@. Equivalent to calling- @'defaultWindowConfig'@.--}-instance Default WindowConfig where- def = defaultWindowConfig+{-| + This module handles the configuration for the project. +-} +module FRP.Spice.Config where + +-------------------- +-- Global Imports -- +import Data.Default + +---------- +-- Code -- + +{-| + A datatype to configure the window settings when creating an OpenGL context + using @'FRP.Spice.Engine.startEngine'@ in the engine. +-} +data WindowConfig = WindowConfig { getWindowWidth :: Int + , getWindowHeight :: Int + , getWindowFullscreen :: Bool + , getWindowTitle :: String + } + deriving (Eq, Show, Read) + +{-| + The default for @'WindowConfig'@ + + > getWindowWidth = 640 + > getWindowHeight = 480 + > getWindowFullscreen = False + > getWindowTitle = "Spice Application" +-} +defaultWindowConfig :: WindowConfig +defaultWindowConfig = WindowConfig { getWindowWidth = 640 + , getWindowHeight = 480 + , getWindowFullscreen = False + , getWindowTitle = "Spice Application" + } + +{-| + A default instance for @'WindowConfig'@. Equivalent to calling + @'defaultWindowConfig'@. +-} +instance Default WindowConfig where + def = defaultWindowConfig
src/FRP/Spice/Engine.hs view
@@ -1,86 +1,90 @@-{-|- This module handles starting the engine. This is done via the use of the- @'startEngine'@ function.--}-module FRP.Spice.Engine (startEngine) where------------------------- Global Imports ---import Graphics.Rendering.OpenGL-import Graphics.UI.GLFW as GLFW-import Data.IORef------------------------ Local Imports ---import FRP.Spice.Engine.RunInput-import FRP.Spice.Engine.Network-import FRP.Spice.Engine.Driver-import FRP.Spice.Input.Backend-import FRP.Spice.Config-import FRP.Spice.Game-import FRP.Spice.Math--------------- Code ------ Making the size from a WindowConfig-makeSize :: WindowConfig -> Size-makeSize wc = Size (fromIntegral $ getWindowWidth wc) (fromIntegral $ getWindowHeight wc)---- Making the displaybits from a WindowConfig-makeDisplayBits :: [DisplayBits]-makeDisplayBits = [DisplayRGBBits 8 8 8, DisplayAlphaBits 8, DisplayDepthBits 24]---- Making the window mode from a WindowConfig-makeWindowMode :: WindowConfig -> WindowMode-makeWindowMode wc =- if getWindowFullscreen wc- then FullScreen- else Window---- Resizing the window-resizeCallback :: IORef (Vector Int) -> WindowSizeCallback-resizeCallback wSize size@(Size w h) = do- writeIORef wSize $ Vector (fromIntegral w) (fromIntegral h)- viewport $= (Position 0 0, size)--{-|- Starting the spice engine with the parameters prescribed in the- @'WindowConfig'@. It updates and renders the @'Game'@ automatically so all- you need to to is set up the @'WindowConfig'@ and make a datatype with an- instance of @'Game'@.--}-startEngine :: Game a => WindowConfig -> a -> IO ()-startEngine wc game = do- -- Opening the window- initialize- openWindow (makeSize wc) makeDisplayBits (makeWindowMode wc)- windowTitle $= getWindowTitle wc-- -- Checking for the window being closed- closed <- newIORef False- windowCloseCallback $= do- writeIORef closed True- return True-- -- Function to run on window resize- wSizeRef <- newIORef $ Vector (getWindowWidth wc) (getWindowHeight wc)- windowSizeCallback $= resizeCallback wSizeRef-- -- Getting the input container- ic <- makeInputContainer-- -- Updating the input- mousePosCallback $= makeMousePositionCallback ic wSizeRef- keyCallback $= makeKeyboardCallback ic- mouseButtonCallback $= makeMouseCallback ic-- -- Creating the network- network <- makeNetwork (getInput ic) game-- -- Driving the network- GLFW.time $= 0- driveNetwork network $ runInput closed-- -- Closing the window, after all is said and done- closeWindow+{-| + This module handles starting the engine. This is done via the use of the + @'startEngine'@ function. +-} +module FRP.Spice.Engine (startEngine) where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Graphics.UI.GLFW as GLFW +import Data.IORef + +------------------- +-- Local Imports -- +import FRP.Spice.Engine.RunInput +import FRP.Spice.Engine.Network +import FRP.Spice.Engine.Driver +import FRP.Spice.Input.Backend +import FRP.Spice.Assets +import FRP.Spice.Config +import FRP.Spice.Game +import FRP.Spice.Math + +---------- +-- Code -- + +-- Making the size from a WindowConfig +makeSize :: WindowConfig -> Size +makeSize wc = Size (fromIntegral $ getWindowWidth wc) (fromIntegral $ getWindowHeight wc) + +-- Making the displaybits from a WindowConfig +makeDisplayBits :: [DisplayBits] +makeDisplayBits = [DisplayRGBBits 8 8 8, DisplayAlphaBits 8, DisplayDepthBits 24] + +-- Making the window mode from a WindowConfig +makeWindowMode :: WindowConfig -> WindowMode +makeWindowMode wc = + if getWindowFullscreen wc + then FullScreen + else Window + +-- Resizing the window +resizeCallback :: IORef (Vector Int) -> WindowSizeCallback +resizeCallback wSize size@(Size w h) = do + writeIORef wSize $ Vector (fromIntegral w) (fromIntegral h) + viewport $= (Position 0 0, size) + +{-| + Starting the spice engine with the parameters prescribed in the + @'WindowConfig'@. It updates and renders the @'Game'@ automatically so all + you need to to is set up the @'WindowConfig'@ and make a datatype with an + instance of @'Game'@. +-} +startEngine :: Game a => WindowConfig -> a -> IO () +startEngine wc game = do + -- Opening the window + initialize + openWindow (makeSize wc) makeDisplayBits (makeWindowMode wc) + windowTitle $= getWindowTitle wc + + -- Checking for the window being closed + closed <- newIORef False + windowCloseCallback $= do + writeIORef closed True + return True + + -- Function to run on window resize + wSizeRef <- newIORef $ Vector (getWindowWidth wc) (getWindowHeight wc) + windowSizeCallback $= resizeCallback wSizeRef + + -- Loading the assets + assets <- performAssetLoads $ loadAssets game + + -- Getting the input container + ic <- makeInputContainer + + -- Updating the input + mousePosCallback $= makeMousePositionCallback ic wSizeRef + keyCallback $= makeKeyboardCallback ic + mouseButtonCallback $= makeMouseCallback ic + + -- Creating the network + network <- makeNetwork (getInput ic) game + + -- Driving the network + GLFW.time $= 0 + driveNetwork assets network $ runInput closed + + -- Closing the window, after all is said and done + closeWindow
src/FRP/Spice/Engine/Driver.hs view
@@ -1,26 +1,27 @@-{-|- This module need not be used directly. Refer to @'FRP.Spice.Engine'@ instead.--}-module FRP.Spice.Engine.Driver where------------------------- Global Imports ---import FRP.Spice.Engine.RenderWrapper-import FRP.Spice.Game--------------- Code ----{-|- Driving a network created with the @'FRP.Spice.Engine.Network.makeNetwork'@- function and a function such as @'FRP.Spice.Engine.RunInput.runInput'@.--}-driveNetwork :: Game a => (Float -> IO a) -> IO (Maybe Float) -> IO ()-driveNetwork network iomdriver = do- mdriver <- iomdriver-- case mdriver of- Just driver -> do state <- network driver- renderWrapper $ render state- driveNetwork network iomdriver- Nothing -> return ()+{-| + This module need not be used directly. Refer to @'FRP.Spice.Engine'@ instead. +-} +module FRP.Spice.Engine.Driver where + +-------------------- +-- Global Imports -- +import FRP.Spice.Engine.RenderWrapper +import FRP.Spice.Assets +import FRP.Spice.Game + +---------- +-- Code -- + +{-| + Driving a network created with the @'FRP.Spice.Engine.Network.makeNetwork'@ + function and a function such as @'FRP.Spice.Engine.RunInput.runInput'@. +-} +driveNetwork :: Game a => Assets -> (Float -> IO a) -> IO (Maybe Float) -> IO () +driveNetwork assets network iomdriver = do + mdriver <- iomdriver + + case mdriver of + Just driver -> do state <- network driver + renderWrapper $ render assets state + driveNetwork assets network iomdriver + Nothing -> return ()
src/FRP/Spice/Engine/Network.hs view
@@ -1,31 +1,31 @@-{-|- This module need not be used directly. Refer to- @'FRP.Spice.Engine.startEngine'@ instead.--}-module FRP.Spice.Engine.Network where------------------------- Global Imports ---import FRP.Elerea.Param------------------------ Local Imports ---import FRP.Spice.Input-import FRP.Spice.Game--------------- Code ----{-|- The raw update function that @'makeNetwork'@ calls.--}-updateFn :: Game a => Float -> Input -> a -> a-updateFn = update--{-|- Creating a network to be used with the- @'FRP.Spice.Engine.Driver.driveNetwork'@ function.--}-makeNetwork :: Game a => Signal Input -> a -> IO (Float -> IO a)-makeNetwork inputSignal game =- start $ transfer game updateFn inputSignal+{-| + This module need not be used directly. Refer to + @'FRP.Spice.Engine.startEngine'@ instead. +-} +module FRP.Spice.Engine.Network where + +-------------------- +-- Global Imports -- +import FRP.Elerea.Param + +------------------- +-- Local Imports -- +import FRP.Spice.Input +import FRP.Spice.Game + +---------- +-- Code -- + +{-| + The raw update function that @'makeNetwork'@ calls. +-} +updateFn :: Game a => Float -> Input -> a -> a +updateFn = update + +{-| + Creating a network to be used with the + @'FRP.Spice.Engine.Driver.driveNetwork'@ function. +-} +makeNetwork :: Game a => Signal Input -> a -> IO (Float -> IO a) +makeNetwork inputSignal game = + start $ transfer game updateFn inputSignal
src/FRP/Spice/Engine/RenderWrapper.hs view
@@ -1,31 +1,31 @@-{-|- This module provides a wrapper to use with a given @'FRP.Spice.Game.Game'@'s- render function.--}-module FRP.Spice.Engine.RenderWrapper where------------------------- Global Imports ---import Graphics.Rendering.OpenGL-import Graphics.UI.GLFW------------------------- Global Imports ---import FRP.Spice.Graphics.Scene--------------- Code ----{-|- A function to be ran on a @'FRP.Spice.Game.Game'@'s render function to- provide a bit of a framework around it. It runs @'clear'@ before the render- function, and @'flush'@ / @'swapBuffers'@ afterwards.--}-renderWrapper :: Scene -> IO ()-renderWrapper scene = do- clear [ColorBuffer]-- renderScene scene-- flush- swapBuffers+{-| + This module provides a wrapper to use with a given @'FRP.Spice.Game.Game'@'s + render function. +-} +module FRP.Spice.Engine.RenderWrapper where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Graphics.UI.GLFW + +-------------------- +-- Global Imports -- +import FRP.Spice.Graphics.Scene + +---------- +-- Code -- + +{-| + A function to be ran on a @'FRP.Spice.Game.Game'@'s render function to + provide a bit of a framework around it. It runs @'clear'@ before the render + function, and @'flush'@ / @'swapBuffers'@ afterwards. +-} +renderWrapper :: Scene -> IO () +renderWrapper scene = do + clear [ColorBuffer] + + scene + + flush + swapBuffers
src/FRP/Spice/Engine/RunInput.hs view
@@ -1,31 +1,31 @@-{-|- This module need not be used directly. Refer to @'FRP.Spice.Engine'@ instead.--}-module FRP.Spice.Engine.RunInput where------------------------- Global Imports ---import Graphics.Rendering.OpenGL-import Graphics.UI.GLFW as GLFW-import Data.IORef-import GHC.Float--------------- Code ----{-|- Given an @'IORef'@ indicating whether or not the program should close, it- either returns the delta time since the last call, or a @'Nothing'@,- indicating that the program should close.--}-runInput :: IORef Bool -> IO (Maybe Float)-runInput closed = do- pollEvents- c <- readIORef closed-- if c- then return Nothing- else do- dt <- get GLFW.time- GLFW.time $= 0- return $ Just $ double2Float dt+{-| + This module need not be used directly. Refer to @'FRP.Spice.Engine'@ instead. +-} +module FRP.Spice.Engine.RunInput where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Graphics.UI.GLFW as GLFW +import Data.IORef +import GHC.Float + +---------- +-- Code -- + +{-| + Given an @'IORef'@ indicating whether or not the program should close, it + either returns the delta time since the last call, or a @'Nothing'@, + indicating that the program should close. +-} +runInput :: IORef Bool -> IO (Maybe Float) +runInput closed = do + pollEvents + c <- readIORef closed + + if c + then return Nothing + else do + dt <- get GLFW.time + GLFW.time $= 0 + return $ Just $ double2Float dt
src/FRP/Spice/Game.hs view
@@ -1,27 +1,29 @@-{-|- This module provides the API which people must use to define a game within- the scope of Spice.--}-module FRP.Spice.Game where------------------------ Local Imports ---import FRP.Spice.Graphics.Scene-import FRP.Spice.Input--------------- Code ----{-|- A synonym to make the update function more self-documenting.--}-type DeltaTime = Float--{-|- The class which is to be used in the @'FRP.Spice.Engine.startEngine'@- function. @'update'@ provides the API to update on every tick (purely), an- @'render'@ provides the API to render every frame.--}-class Game a where- update :: DeltaTime -> Input -> a -> a- render :: a -> Scene+{-| + This module provides the API which people must use to define a game within + the scope of Spice. +-} +module FRP.Spice.Game where + +------------------- +-- Local Imports -- +import FRP.Spice.Graphics.Scene +import FRP.Spice.Assets +import FRP.Spice.Input + +---------- +-- Code -- + +{-| + A synonym to make the update function more self-documenting. +-} +type DeltaTime = Float + +{-| + The class which is to be used in the @'FRP.Spice.Engine.startEngine'@ + function. @'update'@ provides the API to update on every tick (purely), an + @'render'@ provides the API to render every frame. +-} +class Game a where + update :: DeltaTime -> Input -> a -> a + render :: Assets -> a -> Scene + loadAssets :: a -> LoadAssets
src/FRP/Spice/Graphics.hs view
@@ -1,15 +1,17 @@-{-|- This module re-exports a number of modules having to do with graphics.--}-module FRP.Spice.Graphics ( module FRP.Spice.Graphics.Geometry- , module FRP.Spice.Graphics.Color- , module FRP.Spice.Graphics.Scene- , module FRP.Spice.Graphics.Utils- ) where---------------------------- Rexported Imports ---import FRP.Spice.Graphics.Geometry-import FRP.Spice.Graphics.Color-import FRP.Spice.Graphics.Scene-import FRP.Spice.Graphics.Utils+{-| + This module re-exports a number of modules having to do with graphics. +-} +module FRP.Spice.Graphics ( module FRP.Spice.Graphics.Geometry + , module FRP.Spice.Graphics.Sprite + , module FRP.Spice.Graphics.Color + , module FRP.Spice.Graphics.Scene + , module FRP.Spice.Graphics.Utils + ) where + +----------------------- +-- Rexported Imports -- +import FRP.Spice.Graphics.Geometry +import FRP.Spice.Graphics.Sprite +import FRP.Spice.Graphics.Color +import FRP.Spice.Graphics.Scene +import FRP.Spice.Graphics.Utils
src/FRP/Spice/Graphics/Color.hs view
@@ -1,92 +1,99 @@-{-|- This module provides an abstraction over the default representations of color- in the Haskell OpenGL bindings.--}-module FRP.Spice.Graphics.Color where------------------------ Local Imports ---import FRP.Spice.Graphics.Element-import FRP.Spice.Graphics.Scene--------------- Code ----{-|- Representing a Color using four @'Float'@ representing reg, green, blue, and- the alpha mask respectively. The @'Float'@s are in a range from 0-1,- representing @'Int'@s from 0-255.--}-data Color = Color { getRed :: Float- , getGreen :: Float- , getBlue :: Float- , getAlpha :: Float- }- deriving (Eq, Show, Read)--{-|- Converting a color to an action in a @'Scene'@.--}-bindColor :: Color -> Scene-bindColor (Color r g b a) = fromElements [SetColor r g b a]--{-|- A synonym for the @'Color'@ constructor.--}-color4f :: Float -> Float -> Float -> Float -> Color-color4f = Color--{-|- Constructing a @'Color'@ from 3 @'Float'@s, defaulting the alpha mask to 1.0.--}-color3f :: Float -> Float -> Float -> Color-color3f r g b = color4f r g b 1.0--{-|- Creating a @'Color'@ from 4 @'Int'@s. The ints, similarly to @'color4f'@- represent red, green, blue, and the alpha mask respectively. The ints should- be in the range of 0-255. (Note: @'color4i'@ is functionally equivalent (and- also equivalent in source code) to calling color4f with each of its arguments- divided by 255.)--}-color4i :: Int -> Int -> Int -> Int -> Color-color4i r g b a = color4f (fromIntegral r / 255)- (fromIntegral g / 255)- (fromIntegral b / 255)- (fromIntegral a / 255)--{-|- Constructing a @'Color'@ from 3 @'Int'@s, defaulting the alpha mask to 255.--}-color3i :: Int -> Int -> Int -> Color-color3i r g b = color4i r g b 255--{-|- The color black.--}-black :: Color-black = color3i 0 0 0--{-|- The color white.--}-white :: Color-white = color3i 255 255 255--{-|- The color red.--}-red :: Color-red = color3i 255 0 0--{-|- The color green.--}-green :: Color-green = color3i 0 255 0--{-|- The color blue.--}-blue :: Color-blue = color3i 0 0 255+{-| + This module provides an abstraction over the default representations of color + in the Haskell OpenGL bindings. +-} +module FRP.Spice.Graphics.Color where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL ( Color4 (..) + , color + ) + +------------------- +-- Local Imports -- +import FRP.Spice.Graphics.Scene +import FRP.Spice.Graphics.Utils + +---------- +-- Code -- + +{-| + Representing a Color using four @'Float'@ representing reg, green, blue, and + the alpha mask respectively. The @'Float'@s are in a range from 0-1, + representing @'Int'@s from 0-255. +-} +data Color = Color { getRed :: Float + , getGreen :: Float + , getBlue :: Float + , getAlpha :: Float + } + deriving (Eq, Show, Read) + +{-| + Converting a color to an action in a @'Scene'@. +-} +bindColor :: Color -> Scene +bindColor (Color r g b a) = + color $ Color4 (togl r) (togl g) (togl b) (togl a) + +{-| + A synonym for the @'Color'@ constructor. +-} +color4f :: Float -> Float -> Float -> Float -> Color +color4f = Color + +{-| + Constructing a @'Color'@ from 3 @'Float'@s, defaulting the alpha mask to 1.0. +-} +color3f :: Float -> Float -> Float -> Color +color3f r g b = color4f r g b 1.0 + +{-| + Creating a @'Color'@ from 4 @'Int'@s. The ints, similarly to @'color4f'@ + represent red, green, blue, and the alpha mask respectively. The ints should + be in the range of 0-255. (Note: @'color4i'@ is functionally equivalent (and + also equivalent in source code) to calling color4f with each of its arguments + divided by 255.) +-} +color4i :: Int -> Int -> Int -> Int -> Color +color4i r g b a = color4f (fromIntegral r / 255) + (fromIntegral g / 255) + (fromIntegral b / 255) + (fromIntegral a / 255) + +{-| + Constructing a @'Color'@ from 3 @'Int'@s, defaulting the alpha mask to 255. +-} +color3i :: Int -> Int -> Int -> Color +color3i r g b = color4i r g b 255 + +{-| + The color black. +-} +black :: Color +black = color3i 0 0 0 + +{-| + The color white. +-} +white :: Color +white = color3i 255 255 255 + +{-| + The color red. +-} +red :: Color +red = color3i 255 0 0 + +{-| + The color green. +-} +green :: Color +green = color3i 0 255 0 + +{-| + The color blue. +-} +blue :: Color +blue = color3i 0 0 255
− src/FRP/Spice/Graphics/Element.hs
@@ -1,41 +0,0 @@-{-|- This module provides a unified API for specifying every kind of rendering- datatype and how they get rendered. All of this -- of course -- is only in- the scope the spice project.--}-module FRP.Spice.Graphics.Element ( Element (..)- , renderElement- ) where------------------------- Global Imports ---import Graphics.Rendering.OpenGL-import Control.Monad------------------------ Local Imports ---import FRP.Spice.Graphics.Utils-import FRP.Spice.Math--------------- Code ----{-|- A type to purely contain the information for any kind of render call (that- has been implemented so far.)--}-data Element = RenderPrimitive PrimitiveMode [Vector Float]- | RenderSprite (Vector Float) (Vector Float) Float- | SetColor Float Float Float Float--{-|- Converting an @'Element'@ into its matching OpenGL call.--}-renderElement :: Element -> IO ()-renderElement (RenderPrimitive mode vertecies) =- renderPrimitive mode $- forM_ vertecies $ \(Vector x y) ->- vertex $ Vertex2 (togl x) (togl y)-renderElement (RenderSprite pos size rot) = return ()-renderElement (SetColor r g b a) =- color $ Color4 (togl r) (togl g) (togl b) (togl a)
src/FRP/Spice/Graphics/Geometry.hs view
@@ -1,74 +1,76 @@-{-|- This module provides a cleaner API to render a number of shapes in the- current OpenGL context.--}-module FRP.Spice.Graphics.Geometry ( renderPoint- , renderRectangle- , renderSquare- ) where------------------------- Global Imports ---import Graphics.Rendering.OpenGL------------------------ Local Imports ---import FRP.Spice.Graphics.Element-import FRP.Spice.Graphics.Scene-import FRP.Spice.Math.Vector--------------- Code ----{-|- Rendering a point.--}-renderPoint :: Vector Float -> Scene-renderPoint pos =- fromElements [RenderPrimitive Points [pos]]--{-|- Rendering a line between two points.--}-renderLine :: Vector Float -> Vector Float -> Scene-renderLine p1 p2 =- fromElements [ RenderPrimitive Lines [ p1- , p2- ]- ]--{-|- Rendering a rectangle.--}-renderRectangle :: Vector Float -> Vector Float -> Scene-renderRectangle (Vector x y) (Vector w h) = do- fromElements [ RenderPrimitive Quads [ Vector (x ) (y )- , Vector (x + w) (y )- , Vector (x + w) (y + h)- , Vector (x ) (y + h)- ]- ]--{-|- Rendering a square.--}-renderSquare :: Vector Float -> Float -> Scene-renderSquare pos size = renderRectangle pos $ Vector size size--{-|- Rendering a triangle.--}-renderTriangle :: Vector Float -> Vector Float -> Vector Float -> Scene-renderTriangle p1 p2 p3 =- fromElements [ RenderPrimitive Triangles [ p1- , p2- , p3- ]- ]--{-|- Rendering a polygon with 1-N vertecies.--}-renderPolygon :: [Vector Float] -> Scene-renderPolygon l =- fromElements [RenderPrimitive Polygon l]+{-| + This module provides a cleaner API to render a number of shapes in the + current OpenGL context. +-} +module FRP.Spice.Graphics.Geometry ( renderPoint + , renderLine + , renderRectangle + , renderSquare + , renderTriangle + , renderPolygon + ) where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Control.Monad + +------------------- +-- Local Imports -- +import FRP.Spice.Graphics.Scene +import FRP.Spice.Graphics.Utils +import FRP.Spice.Math.Vector + +---------- +-- Code -- + +{-| + A version of @'renderPrimitive'@ where it takes a list of @'Vector'@ + @'Float'@s instead of performing @'vertex'@ calls. +-} +renderPrimitive' :: PrimitiveMode -> [Vector Float] -> Scene +renderPrimitive' mode points = + renderPrimitive mode $ + forM_ points $ \(Vector x y) -> + vertex $ Vertex2 (togl x) (togl y) + +{-| + Rendering a point. +-} +renderPoint :: Vector Float -> Scene +renderPoint pos = renderPrimitive' Points [pos] + +{-| + Rendering a line between two points. +-} +renderLine :: Vector Float -> Vector Float -> Scene +renderLine p1 p2 = renderPrimitive' Lines [p1, p2] + +{-| + Rendering a rectangle. +-} +renderRectangle :: Vector Float -> Vector Float -> Scene +renderRectangle (Vector x y) (Vector w h) = + renderPrimitive' Quads [ Vector (x ) (y ) + , Vector (x + w) (y ) + , Vector (x + w) (y + h) + , Vector (x ) (y + h) + ] + +{-| + Rendering a square. +-} +renderSquare :: Vector Float -> Float -> Scene +renderSquare pos size = renderRectangle pos $ Vector size size + +{-| + Rendering a triangle. +-} +renderTriangle :: Vector Float -> Vector Float -> Vector Float -> Scene +renderTriangle p1 p2 p3 = renderPrimitive' Triangles [p1, p2, p3] + +{-| + Rendering a polygon with 1-N vertecies. +-} +renderPolygon :: [Vector Float] -> Scene +renderPolygon l = renderPrimitive' Polygon l
src/FRP/Spice/Graphics/Scene.hs view
@@ -1,24 +1,13 @@-{-|- This module provides an API to compose @'Scene'@s through do-notation.--}-module FRP.Spice.Graphics.Scene where------------------------ Local Imports ---import FRP.Spice.Graphics.Element-import FRP.Spice.Utils.DoList--------------- Code ----{-|- A DoList to compose a list of @'Element'@s to render using do-notation.--}-type Scene = DoList [Element]--fromElements :: [Element] -> Scene-fromElements = fromValues--renderScene :: Scene -> IO ()-renderScene scene =- mapM_ renderElement $ values scene+{-| + This module provides an API to compose @'Scene'@s through do-notation. +-} +module FRP.Spice.Graphics.Scene where + +---------- +-- Code -- + +{-| + A type synonym for a single IO () call to suggest that users should be + *rendering* in render calls, and not performing other IO. +-} +type Scene = IO ()
+ src/FRP/Spice/Graphics/Sprite.hs view
@@ -0,0 +1,111 @@+{-| + This module provides an API for loading and rendering textures in the form + of @'Sprite'@s. +-} +module FRP.Spice.Graphics.Sprite ( Sprite (..) + , renderSprite + , loadSprite + ) where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Data.ByteString.Unsafe +import Control.Applicative +import Codec.Picture.Repa +import Codec.Picture +import Control.Monad +import Foreign.Ptr + +------------------- +-- Local Imports -- +import FRP.Spice.Graphics.Color +import FRP.Spice.Graphics.Scene +import FRP.Spice.Graphics.Utils +import FRP.Spice.Math + +---------- +-- Code -- + +{-| + Getting the size from a @'DynamicImage'@. +-} +getInfo :: DynamicImage -> (Int, Int, PixelInternalFormat) +getInfo (ImageRGB8 (Image w h _)) = (w, h, RGB8) +getInfo (ImageRGB16 (Image w h _)) = (w, h, RGB16) +getInfo (ImageRGBA8 (Image w h _)) = (w, h, RGBA8) +getInfo (ImageRGBA16 (Image w h _)) = (w, h, RGBA16) + +{-| + Loading a @'TextureObject'@ and @'Size'@ from any RGB8, RGB16, RGBA8, or + RGBA16 images. +-} +loadTex :: FilePath -> IO (TextureObject, Size) +loadTex path = do + img <- either error id <$> readImageRGBA path + + let dynimg = imgToImage img + (w, h, format) = getInfo dynimg + glSize = TextureSize2D (fromIntegral w) (fromIntegral h) + bs = toByteString img + + ptr <- unsafeUseAsCString bs $ \cstr -> + return $ castPtr cstr + + [t] <- genObjectNames 1 + + textureBinding Texture2D $= Just t + texImage2D Texture2D NoProxy 0 format glSize 0 (PixelData ABGR UnsignedByte ptr) + + return (t, Size (fromIntegral w) (fromIntegral h)) + +{-| + A datatype to represent a @'TextureObject'@ through a reference to the + @'TextureObject'@ itself and its @'Size'@. +-} +data Sprite = Sprite { spriteTex :: TextureObject + , spriteSize :: Vector Float + } + +{-| + Performing an OpenGL call to render the @'Sprite'@. +-} +renderSprite :: Sprite -> Vector Float -> Scene +renderSprite sprite pos = do + textureWrapMode Texture2D S $= (Repeated, ClampToEdge) + textureWrapMode Texture2D T $= (Repeated, ClampToEdge) + textureFilter Texture2D $= ((Linear', Nothing), Linear') + textureFunction $= Replace + + texture Texture2D $= Enabled + textureBinding Texture2D $= (Just $ spriteTex sprite) + + renderPrimitive Quads $ + forM_ (generateCoords pos $ spriteSize sprite) $ \(Vector x y, Vector tx ty) -> do + texCoord $ TexCoord2 (togl tx) (togl ty) + vertex $ Vertex2 (togl x) (togl y) + + texture Texture2D $= Disabled + where generateCoords :: Vector Float -> Vector Float -> [(Vector Float, Vector Float)] + generateCoords (Vector x y) (Vector w h) = + [ (Vector (x ) (y ), Vector 0 0) + , (Vector (x + w) (y ), Vector 1 0) + , (Vector (x + w) (y + h), Vector 1 1) + , (Vector (x ) (y + h), Vector 0 1) + ] + +{-| + Creating a @'Sprite'@ from a @'TextureObject'@. +-} +makeSprite :: (TextureObject, Size) -> Sprite +makeSprite (to, (Size w h)) = + Sprite { spriteTex = to + , spriteSize = size + } + where size = Vector ((fromIntegral w) / 640) ((fromIntegral h) / 480) + +{-| + Loading a @'Sprite'@ from a file. +-} +loadSprite :: FilePath -> IO Sprite +loadSprite = liftM makeSprite . loadTex
src/FRP/Spice/Graphics/Utils.hs view
@@ -1,18 +1,24 @@-{-|- This module provides a set of utilities to ease the burden of writing OpenGL- rendering functions.--}-module FRP.Spice.Graphics.Utils where------------------------- Global Imports ---import Graphics.Rendering.OpenGL--------------- Code ----{-|- Converting a @'Float'@ to a @'GLfloat'@--}-togl :: Float -> GLfloat-togl = realToFrac+{-| + This module provides a set of utilities to ease the burden of writing OpenGL + rendering functions. +-} +module FRP.Spice.Graphics.Utils where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL + +---------- +-- Code -- + +{-| + Converting a @'Float'@ to a @'GLfloat'@ +-} +togl :: Float -> GLfloat +togl = realToFrac + +{-| + Converting a @'GLfloat'@ to a @'Float'@. +-} +fromgl :: GLfloat -> Float +fromgl = realToFrac
src/FRP/Spice/Input.hs view
@@ -1,26 +1,26 @@-{-|- This module provides everything relating to input in the scope of spice.--}-module FRP.Spice.Input where------------------------- Global Imports ---import Graphics.UI.GLFW-import Data.Map.Strict------------------------ Local Imports ---import FRP.Spice.Math--------------- Code ----{-|- A container for all of the states themselves. It is used as a @'Signal'@- @'Input'@ in the @'InputContainer'@ (which is necessary to use it within- Elerea's FRP network).--}-data Input = Input { mousePosition :: Vector Float- , keyboard :: Map Key Bool- , mouse :: Map MouseButton Bool- }+{-| + This module provides everything relating to input in the scope of spice. +-} +module FRP.Spice.Input where + +-------------------- +-- Global Imports -- +import Graphics.UI.GLFW +import Data.Map.Strict + +------------------- +-- Local Imports -- +import FRP.Spice.Math + +---------- +-- Code -- + +{-| + A container for all of the states themselves. It is used as a @'Signal'@ + @'Input'@ in the @'InputContainer'@ (which is necessary to use it within + Elerea's FRP network). +-} +data Input = Input { mousePosition :: Vector Float + , keyboard :: Map Key Bool + , mouse :: Map MouseButton Bool + }
src/FRP/Spice/Input/Backend.hs view
@@ -1,97 +1,97 @@-{-|- The backend to be used with the input.--}-module FRP.Spice.Input.Backend where------------------------- Global Imports ---import Graphics.Rendering.OpenGL-import Graphics.UI.GLFW as GLFW-import FRP.Elerea.Param-import Data.Map.Strict-import Data.IORef------------------------ Local Imports ---import qualified FRP.Spice.Input.MousePosition as MousePosition-import qualified FRP.Spice.Input.Keyboard as Keyboard-import qualified FRP.Spice.Input.Mouse as Mouse-import FRP.Spice.Input-import FRP.Spice.Math--------------- Code ----{-|- A wrapper around the sinks for the mouse position, the key states, and the- mouse button states.--}-data Sinks = Sinks { mousePositionSinks :: Vector Float -> IO ()- , keyboardSinks :: Map Key (Bool -> IO ())- , mouseSinks :: Map MouseButton (Bool -> IO ())- }----{-|- A container around @'Sinks'@ and @'Signal'@ @'Input'@ so that one needn't- pass around a tuple.--}-data InputContainer = InputContainer { getSinks :: Sinks- , getInput :: Signal Input- }--{-|- Making an @'InputContainer'@ filled with all necessary externals.--}-makeInputContainer :: IO InputContainer-makeInputContainer = do- mousePositionExternals <- MousePosition.externals- keyboardExternals <- Keyboard.externals- mouseExternals <- Mouse.externals-- let sinks = Sinks { mousePositionSinks = MousePosition.sinks mousePositionExternals- , keyboardSinks = Keyboard.sinks keyboardExternals- , mouseSinks = Mouse.sinks mouseExternals- }-- let input = do mps <- fst mousePositionExternals- kbs <- Keyboard.signals keyboardExternals- ms <- Mouse.signals mouseExternals-- return Input { mousePosition = mps- , keyboard = kbs- , mouse = ms- }-- return InputContainer { getSinks = sinks- , getInput = input- }--{-|- Creating a callback to update the mouse position's state.--}-makeMousePositionCallback :: InputContainer -> IORef (Vector Int) -> MousePosCallback-makeMousePositionCallback ic wSizeRef (Position x y) = do- (Vector w h) <- readIORef wSizeRef- mousePositionSinks (getSinks ic) $- Vector ( fromIntegral x / ((fromIntegral $ w) / 2) - 1)- ((-fromIntegral y) / ((fromIntegral $ h) / 2) + 1)--{-|- Creating a callback to update the keyboard's states.--}-makeKeyboardCallback :: InputContainer -> KeyCallback-makeKeyboardCallback ic key state =- keyboardSinks (getSinks ic) ! key $ case state of- Press -> True- Release -> False--{-|- Creating a callback to update the mouse buttons' states.--}-makeMouseCallback :: InputContainer -> MouseButtonCallback-makeMouseCallback ic button state =- mouseSinks (getSinks ic) ! button $ case state of- Press -> True- Release -> False+{-| + The backend to be used with the input. +-} +module FRP.Spice.Input.Backend where + +-------------------- +-- Global Imports -- +import Graphics.Rendering.OpenGL +import Graphics.UI.GLFW as GLFW +import FRP.Elerea.Param +import Data.Map.Strict +import Data.IORef + +------------------- +-- Local Imports -- +import qualified FRP.Spice.Input.MousePosition as MousePosition +import qualified FRP.Spice.Input.Keyboard as Keyboard +import qualified FRP.Spice.Input.Mouse as Mouse +import FRP.Spice.Input +import FRP.Spice.Math + +---------- +-- Code -- + +{-| + A wrapper around the sinks for the mouse position, the key states, and the + mouse button states. +-} +data Sinks = Sinks { mousePositionSinks :: Vector Float -> IO () + , keyboardSinks :: Map Key (Bool -> IO ()) + , mouseSinks :: Map MouseButton (Bool -> IO ()) + } + + + +{-| + A container around @'Sinks'@ and @'Signal'@ @'Input'@ so that one needn't + pass around a tuple. +-} +data InputContainer = InputContainer { getSinks :: Sinks + , getInput :: Signal Input + } + +{-| + Making an @'InputContainer'@ filled with all necessary externals. +-} +makeInputContainer :: IO InputContainer +makeInputContainer = do + mousePositionExternals <- MousePosition.externals + keyboardExternals <- Keyboard.externals + mouseExternals <- Mouse.externals + + let sinks = Sinks { mousePositionSinks = MousePosition.sinks mousePositionExternals + , keyboardSinks = Keyboard.sinks keyboardExternals + , mouseSinks = Mouse.sinks mouseExternals + } + + let input = do mps <- fst mousePositionExternals + kbs <- Keyboard.signals keyboardExternals + ms <- Mouse.signals mouseExternals + + return Input { mousePosition = mps + , keyboard = kbs + , mouse = ms + } + + return InputContainer { getSinks = sinks + , getInput = input + } + +{-| + Creating a callback to update the mouse position's state. +-} +makeMousePositionCallback :: InputContainer -> IORef (Vector Int) -> MousePosCallback +makeMousePositionCallback ic wSizeRef (Position x y) = do + (Vector w h) <- readIORef wSizeRef + mousePositionSinks (getSinks ic) $ + Vector ( fromIntegral x / ((fromIntegral $ w) / 2) - 1) + ((-fromIntegral y) / ((fromIntegral $ h) / 2) + 1) + +{-| + Creating a callback to update the keyboard's states. +-} +makeKeyboardCallback :: InputContainer -> KeyCallback +makeKeyboardCallback ic key state = + keyboardSinks (getSinks ic) ! key $ case state of + Press -> True + Release -> False + +{-| + Creating a callback to update the mouse buttons' states. +-} +makeMouseCallback :: InputContainer -> MouseButtonCallback +makeMouseCallback ic button state = + mouseSinks (getSinks ic) ! button $ case state of + Press -> True + Release -> False
src/FRP/Spice/Input/Keyboard.hs view
@@ -1,40 +1,40 @@-{-|- The keyboard specific section of input.--}-module FRP.Spice.Input.Keyboard where------------------------- Global Imports ---import qualified Data.Traversable as T-import Data.Map.Strict hiding (keys, map)-import Graphics.UI.GLFW as GLFW-import FRP.Elerea.Param-import Control.Monad--------------- Code ----{-|- A list of all possible keys used in the GLFW API.--}-keys :: [Key]-keys = map toEnum [0 .. 318]--{-|- A Map from @'Key'@ to the externals created for every single key.--}-externals :: IO (Map Key (Signal Bool, Bool -> IO ()))-externals = liftM fromList $ mapM (\k -> liftM ((,) k) $ external False) keys--{-|- Creating the @'Signal'@ of a @'Map'@ from @'Key'@ to @'Bool'@ from a @'Map'@- of @'externals'@.--}-signals :: Map Key (Signal Bool, Bool -> IO ()) -> Signal (Map Key Bool)-signals = T.sequence . fmap fst--{-|- Making a @'Map'@ from @'Key'@ to sink from a @'Map'@ of @'externals'@.--}-sinks :: Map Key (Signal Bool, Bool -> IO ()) -> Map Key (Bool -> IO ())-sinks = fmap snd+{-| + The keyboard specific section of input. +-} +module FRP.Spice.Input.Keyboard where + +-------------------- +-- Global Imports -- +import qualified Data.Traversable as T +import Data.Map.Strict hiding (keys, map) +import Graphics.UI.GLFW as GLFW +import FRP.Elerea.Param +import Control.Monad + +---------- +-- Code -- + +{-| + A list of all possible keys used in the GLFW API. +-} +keys :: [Key] +keys = map toEnum [0 .. 318] + +{-| + A Map from @'Key'@ to the externals created for every single key. +-} +externals :: IO (Map Key (Signal Bool, Bool -> IO ())) +externals = liftM fromList $ mapM (\k -> liftM ((,) k) $ external False) keys + +{-| + Creating the @'Signal'@ of a @'Map'@ from @'Key'@ to @'Bool'@ from a @'Map'@ + of @'externals'@. +-} +signals :: Map Key (Signal Bool, Bool -> IO ()) -> Signal (Map Key Bool) +signals = T.sequence . fmap fst + +{-| + Making a @'Map'@ from @'Key'@ to sink from a @'Map'@ of @'externals'@. +-} +sinks :: Map Key (Signal Bool, Bool -> IO ()) -> Map Key (Bool -> IO ()) +sinks = fmap snd
src/FRP/Spice/Input/Mouse.hs view
@@ -1,52 +1,52 @@-{-|- The mouse (button) specific section of input.--}-module FRP.Spice.Input.Mouse where------------------------- Global Imports ---import qualified Data.Traversable as T (sequence)-import Data.Map.Strict hiding (keys, map)-import Graphics.UI.GLFW as GLFW-import FRP.Elerea.Param-import Control.Monad--------------- Code ----{-|- Adding an instance of Ord to MouseButton so it can be used with the map.--}-instance Ord MouseButton where- mb1 <= mb2 = fromEnum mb1 < fromEnum mb2--{-|- A list of all mouse buttons available via the GLFW api.--}-buttons :: [MouseButton]-buttons = [ ButtonLeft- , ButtonRight- , ButtonMiddle- ]- ++- map ButtonNo [0 .. 7]--{-|- A map from @'MouseButton'@ to externals created for every button.--}-externals :: IO (Map MouseButton (Signal Bool, Bool -> IO ()))-externals = liftM fromList $ mapM (\b -> liftM ((,) b) $ external False) buttons--{-|- Creating the @'Signal'@ of a @'Map'@ from @'MouseButton'@ to @'Bool'@ from a- @'Map'@ of @'externals'@.--}-signals :: Map MouseButton (Signal Bool, Bool -> IO ()) -> Signal (Map MouseButton Bool)-signals = T.sequence . fmap fst--{-|- Making a @'Map'@ from @'MouseButton'@ to sink from a @'Map'@ of- @'externals'@.--}-sinks :: Map MouseButton (Signal Bool, Bool -> IO ()) -> Map MouseButton (Bool -> IO ())-sinks = fmap snd+{-| + The mouse (button) specific section of input. +-} +module FRP.Spice.Input.Mouse where + +-------------------- +-- Global Imports -- +import qualified Data.Traversable as T (sequence) +import Data.Map.Strict hiding (keys, map) +import Graphics.UI.GLFW as GLFW +import FRP.Elerea.Param +import Control.Monad + +---------- +-- Code -- + +{-| + Adding an instance of Ord to MouseButton so it can be used with the map. +-} +instance Ord MouseButton where + mb1 <= mb2 = fromEnum mb1 < fromEnum mb2 + +{-| + A list of all mouse buttons available via the GLFW api. +-} +buttons :: [MouseButton] +buttons = [ ButtonLeft + , ButtonRight + , ButtonMiddle + ] + ++ + map ButtonNo [0 .. 7] + +{-| + A map from @'MouseButton'@ to externals created for every button. +-} +externals :: IO (Map MouseButton (Signal Bool, Bool -> IO ())) +externals = liftM fromList $ mapM (\b -> liftM ((,) b) $ external False) buttons + +{-| + Creating the @'Signal'@ of a @'Map'@ from @'MouseButton'@ to @'Bool'@ from a + @'Map'@ of @'externals'@. +-} +signals :: Map MouseButton (Signal Bool, Bool -> IO ()) -> Signal (Map MouseButton Bool) +signals = T.sequence . fmap fst + +{-| + Making a @'Map'@ from @'MouseButton'@ to sink from a @'Map'@ of + @'externals'@. +-} +sinks :: Map MouseButton (Signal Bool, Bool -> IO ()) -> Map MouseButton (Bool -> IO ()) +sinks = fmap snd
src/FRP/Spice/Input/MousePosition.hs view
@@ -1,31 +1,31 @@-{-|- The mouse (position) specific section of input.--}-module FRP.Spice.Input.MousePosition where------------------------- Global Imports ---import FRP.Elerea.Param-import Data.Default------------------------ Local Imports ---import FRP.Spice.Math.Vector--{-|- Creating the external for the mouse position.--}-externals :: IO (Signal (Vector Float), Vector Float -> IO ())-externals = external def--{-|- Getting the signal from the mouse position external.--}-signals :: (Signal (Vector Float), Vector Float -> IO ()) -> Signal (Vector Float)-signals = fst--{-|- Getting the sink from the mouse position external.--}-sinks :: (Signal (Vector Float), Vector Float -> IO ()) -> Vector Float -> IO ()-sinks = snd+{-| + The mouse (position) specific section of input. +-} +module FRP.Spice.Input.MousePosition where + +-------------------- +-- Global Imports -- +import FRP.Elerea.Param +import Data.Default + +------------------- +-- Local Imports -- +import FRP.Spice.Math.Vector + +{-| + Creating the external for the mouse position. +-} +externals :: IO (Signal (Vector Float), Vector Float -> IO ()) +externals = external def + +{-| + Getting the signal from the mouse position external. +-} +signals :: (Signal (Vector Float), Vector Float -> IO ()) -> Signal (Vector Float) +signals = fst + +{-| + Getting the sink from the mouse position external. +-} +sinks :: (Signal (Vector Float), Vector Float -> IO ()) -> Vector Float -> IO () +sinks = snd
src/FRP/Spice/Math.hs view
@@ -1,9 +1,9 @@-{-|- This module re-exports a number of modules having to do with math.--}-module FRP.Spice.Math ( module FRP.Spice.Math.Vector- ) where---------------------------- Rexported Imports ---import FRP.Spice.Math.Vector+{-| + This module re-exports a number of modules having to do with math. +-} +module FRP.Spice.Math ( module FRP.Spice.Math.Vector + ) where + +----------------------- +-- Rexported Imports -- +import FRP.Spice.Math.Vector
src/FRP/Spice/Math/Vector.hs view
@@ -1,64 +1,64 @@-{-|- This module provides a @'Vector'@ datatype as described below.--}-module FRP.Spice.Math.Vector where------------------------- Global Imports ---import Control.Applicative-import Data.Default-import Data.Monoid--------------- Code ----{-|- A datatype that houses two values of a given type. It is provided with a- @'Num'@ instance so that, when used with number types it can function- similarly (though not exactly) to a mathematical vector.--}-data Vector a = Vector a a--{-|- The default for the @'Vector'@.--}-instance Default a => Default (Vector a) where- def = Vector def def--{-|- Displaying the @'Vector'@.--}-instance Show a => Show (Vector a) where- show (Vector x y) = mconcat ["Vector ", show x, " ", show y]--{-|- Performs operations on the matching fields of the other @'Vector'@.--}-instance Num a => Num (Vector a) where- (Vector x1 y1) + (Vector x2 y2) = Vector (x1 + x2) (y1 + y2)- (Vector x1 y1) * (Vector x2 y2) = Vector (x1 * x2) (y1 * y2)-- fromInteger n = Vector (fromInteger n) (fromInteger n)-- abs (Vector x y) = Vector (abs x) (abs y)- signum (Vector x y) = Vector (signum x) (signum y)- negate (Vector x y) = Vector (negate x) (negate y)--{-|- Maps over both values in the @'Vector'@.--}-instance Functor Vector where- fmap fn (Vector x y) = Vector (fn x) (fn y)--{-|- Applicative instance for @'Vector'@.--}-instance Applicative Vector where- pure a = Vector a a- (Vector fn _) <*> (Vector x y) = Vector (fn x) (fn y)--{-|- Multiplying @'Vector'@ by a scalar value.--}-scalar :: Num a => Vector a -> a -> Vector a-scalar (Vector x y) n = Vector (x * n) (y * n)+{-| + This module provides a @'Vector'@ datatype as described below. +-} +module FRP.Spice.Math.Vector where + +-------------------- +-- Global Imports -- +import Control.Applicative +import Data.Default +import Data.Monoid + +---------- +-- Code -- + +{-| + A datatype that houses two values of a given type. It is provided with a + @'Num'@ instance so that, when used with number types it can function + similarly (though not exactly) to a mathematical vector. +-} +data Vector a = Vector a a + +{-| + The default for the @'Vector'@. +-} +instance Default a => Default (Vector a) where + def = Vector def def + +{-| + Displaying the @'Vector'@. +-} +instance Show a => Show (Vector a) where + show (Vector x y) = mconcat ["Vector ", show x, " ", show y] + +{-| + Performs operations on the matching fields of the other @'Vector'@. +-} +instance Num a => Num (Vector a) where + (Vector x1 y1) + (Vector x2 y2) = Vector (x1 + x2) (y1 + y2) + (Vector x1 y1) * (Vector x2 y2) = Vector (x1 * x2) (y1 * y2) + + fromInteger n = Vector (fromInteger n) (fromInteger n) + + abs (Vector x y) = Vector (abs x) (abs y) + signum (Vector x y) = Vector (signum x) (signum y) + negate (Vector x y) = Vector (negate x) (negate y) + +{-| + Maps over both values in the @'Vector'@. +-} +instance Functor Vector where + fmap fn (Vector x y) = Vector (fn x) (fn y) + +{-| + Applicative instance for @'Vector'@. +-} +instance Applicative Vector where + pure a = Vector a a + (Vector fn _) <*> (Vector x y) = Vector (fn x) (fn y) + +{-| + Multiplying @'Vector'@ by a scalar value. +-} +scalar :: Num a => Vector a -> a -> Vector a +scalar (Vector x y) n = Vector (x * n) (y * n)
src/FRP/Spice/Utils/DoList.hs view
@@ -1,63 +1,63 @@-{-|- This module provides a list that can be composed and concatenated through- do-notation.----}-module FRP.Spice.Utils.DoList where------------------------- Global Imports ---import Control.Applicative-import Data.Monoid--------------- Code ----{-|- The datastructure itself. Almost equivalent to a tuple of length 2 without- and more context.--}-data DoListT a b = DoListT a b--{-|- A more commonly used synonym for DoListT--}-type DoList a = DoListT a ()--{-|- Getting the elements from a @'DoListT'@.--}-values :: DoListT a b -> a-values (DoListT a _) = a--{-|- Constructing a do-list from a @'Monoid'@.--}-fromValues :: Monoid a => a -> DoListT a ()-fromValues a = DoListT a ()--{-|- A functor instance to satisfy @'Applicative'@'s requirements.--}-instance Functor (DoListT a) where- fmap fn (DoListT a b) =- DoListT a $ fn b--{-|- An applicative instance to satisfy @'Monad'@'s requirements.--}-instance Monoid a => Applicative (DoListT a) where- pure b = DoListT mempty b- (DoListT _ fn) <*> dl = fmap fn dl--{-|- A monad instance to perform the actual composition of the list. List is a- loose word seeing as the monad instance simply needs an instance of- @'Monoid'@.--}-instance Monoid a => Monad (DoListT a) where- return = pure- (DoListT a b) >>= fn =- let (DoListT a' b') = fn b in- DoListT (mappend a a') b'+{-| + This module provides a list that can be composed and concatenated through + do-notation. + + +-} +module FRP.Spice.Utils.DoList where + +-------------------- +-- Global Imports -- +import Control.Applicative +import Data.Monoid + +---------- +-- Code -- + +{-| + The datastructure itself. Almost equivalent to a tuple of length 2 without + and more context. +-} +data DoListT a b = DoListT a b + +{-| + A more commonly used synonym for DoListT +-} +type DoList a = DoListT a () + +{-| + Getting the elements from a @'DoListT'@. +-} +values :: DoListT a b -> a +values (DoListT a _) = a + +{-| + Constructing a do-list from a @'Monoid'@. +-} +fromValues :: Monoid a => a -> DoListT a () +fromValues a = DoListT a () + +{-| + A functor instance to satisfy @'Applicative'@'s requirements. +-} +instance Functor (DoListT a) where + fmap fn (DoListT a b) = + DoListT a $ fn b + +{-| + An applicative instance to satisfy @'Monad'@'s requirements. +-} +instance Monoid a => Applicative (DoListT a) where + pure b = DoListT mempty b + (DoListT _ fn) <*> dl = fmap fn dl + +{-| + A monad instance to perform the actual composition of the list. List is a + loose word seeing as the monad instance simply needs an instance of + @'Monoid'@. +-} +instance Monoid a => Monad (DoListT a) where + return = pure + (DoListT a b) >>= fn = + let (DoListT a' b') = fn b in + DoListT (mappend a a') b'