From 594e76d1df5a2148387fced2730f3ec2d89a7814 Mon Sep 17 00:00:00 2001 From: Jeanne-Kamikaze Date: Fri, 22 Feb 2013 14:01:28 +0100 Subject: Merged Setup and Game --- Spear.cabal | 2 +- Spear/App/Application.hs | 38 +++----- Spear/App/Input.hs | 10 ++- Spear/Assets/Image.hsc | 32 ++----- Spear/Assets/Model.hsc | 51 ++--------- Spear/GLSL.hs | 195 ++++++++++-------------------------------- Spear/Game.hs | 72 +++++++++++++--- Spear/Math/Camera.hs | 4 +- Spear/Render/AnimatedModel.hs | 45 +++------- Spear/Render/Model.hsc | 9 +- Spear/Render/StaticModel.hs | 39 +++------ Spear/Scene/Loader.hs | 116 +++++++------------------ Spear/Setup.hs | 59 ------------- 13 files changed, 190 insertions(+), 482 deletions(-) delete mode 100644 Spear/Setup.hs diff --git a/Spear.cabal b/Spear.cabal index f7d0536..2f21fad 100644 --- a/Spear.cabal +++ b/Spear.cabal @@ -29,7 +29,7 @@ library Spear.Render.Material Spear.Render.Model Spear.Render.Program Spear.Render.StaticModel Spear.Scene.Graph Spear.Scene.Light Spear.Scene.Loader Spear.Scene.Scene Spear.Scene.SceneResources - Spear.Setup Spear.Sys.Timer Spear.Sys.Store Spear.Sys.Store.ID + Spear.Sys.Timer Spear.Sys.Store Spear.Sys.Store.ID Spear.Math.Quad Spear.Math.Ray Spear.Math.Segment Spear.Math.Utils Spear.Math.Spatial2 Spear.Math.Spatial3 diff --git a/Spear/App/Application.hs b/Spear/App/Application.hs index 33400b8..82bfde0 100644 --- a/Spear/App/Application.hs +++ b/Spear/App/Application.hs @@ -8,12 +8,10 @@ module Spear.App.Application , Size(..) , DisplayBits(..) , WindowMode(..) -, Opened(..) , WindowSizeCallback -- * Setup , setup , quit -, releaseWindow -- * Main loop , run , runCapped @@ -23,9 +21,7 @@ module Spear.App.Application ) where - import Spear.Game -import Spear.Setup import Spear.Sys.Timer as Timer import Control.Applicative @@ -37,25 +33,24 @@ import Graphics.Rendering.OpenGL as GL import System.Exit import Unsafe.Coerce - -- | Window dimensions. type Dimensions = (Int, Int) -- | A tuple specifying the desired OpenGL context, of the form (Major, Minor). type Context = (Int, Int) - -- | Represents a window. newtype SpearWindow = SpearWindow { rkey :: Resource } +instance ResourceClass SpearWindow where + getResource = rkey -- | Set up an application 'SpearWindow'. setup :: Dimensions -> [DisplayBits] -> WindowMode -> Context - -> WindowSizeCallback -> Setup SpearWindow + -> WindowSizeCallback -> Game s SpearWindow setup (w, h) displayBits windowMode (major, minor) onResize' = do glfwInit - - setupIO $ do + gameIO $ do openWindowHint OpenGLVersionMajor major openWindowHint OpenGLVersionMinor minor disableSpecial AutoPollEvent @@ -73,45 +68,35 @@ setup (w, h) displayBits windowMode (major, minor) onResize' = do rkey <- register quit return $ SpearWindow rkey - --- | Release the given 'SpearWindow'. -releaseWindow :: SpearWindow -> Setup () -releaseWindow = release . rkey - - -glfwInit :: Setup () +glfwInit :: Game s () glfwInit = do - result <- setupIO GLFW.initialize + result <- gameIO GLFW.initialize case result of - False -> setupError "GLFW.initialize failed" + False -> gameError "GLFW.initialize failed" True -> return () - -- | Close the application's window. quit :: IO () quit = GLFW.terminate - -- | Return true if the application should continue running, false otherwise. type Update s = Float -> Game s (Bool) - -- | Run the application's main loop. run :: Update s -> Game s () run update = do timer <- gameIO $ start newTimer run' timer update - run' :: Timer -> Update s -> Game s () run' timer update = do timer' <- gameIO $ tick timer continue <- update $ getDelta timer' - case continue of + opened <- gameIO $ getParam Opened + case continue && opened of False -> return () True -> run' timer' update - -- | Run the application's main loop, with a limit on the frame rate. runCapped :: Int -> Update s -> Game s () runCapped maxFPS update = do @@ -119,12 +104,12 @@ runCapped maxFPS update = do timer <- gameIO $ start newTimer runCapped' ddt timer update - runCapped' :: Float -> Timer -> Update s -> Game s () runCapped' ddt timer update = do timer' <- gameIO $ tick timer continue <- update $ getDelta timer' - case continue of + opened <- gameIO $ getParam Opened + case continue && opened of False -> return () True -> do t'' <- gameIO $ tick timer' @@ -132,7 +117,6 @@ runCapped' ddt timer update = do when (dt < ddt) $ gameIO $ Timer.sleep (ddt - dt) runCapped' ddt timer' update - onResize :: WindowSizeCallback -> Size -> IO () onResize callback s@(Size w h) = do GL.viewport $= (Position 0 0, Size (fromIntegral w) (fromIntegral h)) diff --git a/Spear/App/Input.hs b/Spear/App/Input.hs index 44b94a9..0207147 100644 --- a/Spear/App/Input.hs +++ b/Spear/App/Input.hs @@ -14,6 +14,7 @@ module Spear.App.Input , getKeyboard , newMouse , getMouse +, newInput , getInput , pollInput -- * Toggled input @@ -68,7 +69,7 @@ data Input = Input } --- | Return a dummy keyboard. +-- | Return a new dummy keyboard. -- -- This function should be called to get an initial keyboard. -- @@ -90,7 +91,7 @@ getKeyboard = >>= return . keyboard' --- | Return a dummy mouse. +-- | Return a new dummy mouse. -- -- This function should be called to get an initial mouse. -- @@ -133,6 +134,11 @@ getMouse oldMouse = } +-- | Return a new dummy input. +newInput :: Input +newInput = Input newKeyboard newMouse + + -- | Get input devices. getInput :: Input -> IO Input getInput (Input _ oldMouse) = do diff --git a/Spear/Assets/Image.hsc b/Spear/Assets/Image.hsc index 2b5c482..0efbca6 100644 --- a/Spear/Assets/Image.hsc +++ b/Spear/Assets/Image.hsc @@ -6,7 +6,6 @@ module Spear.Assets.Image Image -- * Loading and unloading , loadImage -, releaseImage -- * Accessors , width , height @@ -15,8 +14,7 @@ module Spear.Assets.Image ) where - -import Spear.Setup +import Spear.Game import Foreign.Ptr import Foreign.Storable import Foreign.C.Types @@ -26,11 +24,9 @@ import Foreign.Marshal.Alloc (alloca) import Data.List (splitAt, elemIndex) import Data.Char (toLower) - #include "Image.h" #include "BMP/BMP_load.h" - data ImageErrorCode = ImageSuccess | ImageReadError @@ -40,7 +36,6 @@ data ImageErrorCode | ImageNoSuitableLoader deriving (Eq, Enum, Show) - data CImage = CImage { cwidth :: CInt , cheight :: CInt @@ -48,7 +43,6 @@ data CImage = CImage , cpixels :: Ptr CUChar } - instance Storable CImage where sizeOf _ = #{size Image} alignment _ = alignment (undefined :: CInt) @@ -66,36 +60,34 @@ instance Storable CImage where #{poke Image, bpp} ptr bpp #{poke Image, pixels} ptr pixels - -- | Represents an image 'Resource'. data Image = Image { imageData :: CImage , rkey :: Resource } +instance ResourceClass Image where + getResource = rkey foreign import ccall "Image.h image_free" image_free :: Ptr CImage -> IO () - foreign import ccall "BMP_load.h BMP_load" bmp_load' :: Ptr CChar -> Ptr CImage -> IO Int - bmp_load :: Ptr CChar -> Ptr CImage -> IO ImageErrorCode bmp_load file image = bmp_load' file image >>= \code -> return . toEnum $ code - -- | Load the image specified by the given file. -loadImage :: FilePath -> Setup Image +loadImage :: FilePath -> Game s Image loadImage file = do dotPos <- case elemIndex '.' file of - Nothing -> setupError $ "file name has no extension: " ++ file + Nothing -> gameError $ "file name has no extension: " ++ file Just p -> return p let ext = map toLower . tail . snd $ splitAt dotPos file - result <- setupIO . alloca $ \ptr -> do + result <- gameIO . alloca $ \ptr -> do status <- withCString file $ \fileCstr -> do case ext of "bmp" -> bmp_load fileCstr ptr @@ -111,34 +103,24 @@ loadImage file = do case result of Right image -> register (freeImage image) >>= return . Image image - Left err -> setupError $ "loadImage: " ++ err - - --- | Release the given 'Image'. -releaseImage :: Image -> Setup () -releaseImage = release . rkey - + Left err -> gameError $ "loadImage: " ++ err -- | Free the given 'CImage'. freeImage :: CImage -> IO () freeImage image = Foreign.with image image_free - -- | Return the given image's width. width :: Image -> Int width = fromIntegral . cwidth . imageData - -- | Return the given image's height. height :: Image -> Int height = fromIntegral . cheight . imageData - -- | Return the given image's bits per pixel. bpp :: Image -> Int bpp = fromIntegral . cbpp . imageData - -- | Return the given image's pixels. pixels :: Image -> Ptr CUChar pixels = cpixels . imageData diff --git a/Spear/Assets/Model.hsc b/Spear/Assets/Model.hsc index 6c4cfe5..5e6e756 100644 --- a/Spear/Assets/Model.hsc +++ b/Spear/Assets/Model.hsc @@ -27,9 +27,7 @@ module Spear.Assets.Model ) where - -import Spear.Setup - +import Spear.Game import qualified Data.ByteString.Char8 as B import Data.Char (toLower) @@ -45,12 +43,10 @@ import Foreign.Marshal.Alloc (alloca, allocaBytes) import Foreign.Marshal.Array (allocaArray, copyArray, peekArray) import Unsafe.Coerce (unsafeCoerce) - #include "Model.h" #include "MD2/MD2_load.h" #include "OBJ/OBJ_load.h" - data ModelErrorCode = ModelSuccess | ModelReadError @@ -60,15 +56,12 @@ data ModelErrorCode | ModelNoSuitableLoader deriving (Eq, Enum, Show) - sizeFloat = #{size float} sizePtr = #{size int*} - -- | A 2D vector. data Vec2 = Vec2 {-# UNPACK #-} !Float {-# UNPACK #-} !Float - instance Storable Vec2 where sizeOf _ = 2*sizeFloat alignment _ = alignment (undefined :: CFloat) @@ -82,11 +75,9 @@ instance Storable Vec2 where pokeByteOff ptr 0 f0 pokeByteOff ptr sizeFloat f1 - -- | A 3D vector. data Vec3 = Vec3 {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float - instance Storable Vec3 where sizeOf _ = 3*sizeFloat alignment _ = alignment (undefined :: CFloat) @@ -102,11 +93,9 @@ instance Storable Vec3 where pokeByteOff ptr sizeFloat f1 pokeByteOff ptr (2*sizeFloat) f2 - -- | A 2D texture coordinate. data TexCoord = TexCoord {-# UNPACK #-} !Float {-# UNPACK #-} !Float - instance Storable TexCoord where sizeOf _ = 2*sizeFloat alignment _ = alignment (undefined :: CFloat) @@ -120,7 +109,6 @@ instance Storable TexCoord where pokeByteOff ptr 0 f0 pokeByteOff ptr sizeFloat f1 - -- | A raw triangle holding vertex/normal and texture indices. data CTriangle = CTriangle { vertexIndex0 :: {-# UNPACK #-} !CUShort @@ -131,7 +119,6 @@ data CTriangle = CTriangle , textureIndex3 :: {-# UNPACK #-} !CUShort } - instance Storable CTriangle where sizeOf _ = #{size triangle} alignment _ = alignment (undefined :: CUShort) @@ -156,11 +143,9 @@ instance Storable CTriangle where #{poke triangle, textureIndices[1]} ptr t1 #{poke triangle, textureIndices[2]} ptr t2 - -- | A 3D axis-aligned bounding box. data Box = Box {-# UNPACK #-} !Vec3 {-# UNPACK #-} !Vec3 - instance Storable Box where sizeOf _ = 6 * sizeFloat alignment _ = alignment (undefined :: CFloat) @@ -182,11 +167,9 @@ instance Storable Box where pokeByteOff ptr (4*sizeFloat) ymax pokeByteOff ptr (5*sizeFloat) zmax - -- | A model skin. newtype Skin = Skin { skinName :: B.ByteString } - instance Storable Skin where sizeOf (Skin s) = 64 alignment _ = 1 @@ -198,7 +181,6 @@ instance Storable Skin where poke ptr (Skin s) = do B.useAsCStringLen s $ \(sptr, len) -> copyArray (unsafeCoerce ptr) sptr len - -- | A model animation. -- -- See also: 'animation', 'animationByName', 'numAnimations'. @@ -208,7 +190,6 @@ data Animation = Animation , end :: Int } - instance Storable Animation where sizeOf _ = #{size animation} alignment _ = alignment (undefined :: CUInt) @@ -224,7 +205,6 @@ instance Storable Animation where #{poke animation, start} ptr start #{poke animation, end} ptr end - -- | A 3D model. data Model = Model { vertices :: S.Vector Vec3 -- ^ Array of 'numFrames' * 'numVerts' vertices. @@ -241,7 +221,6 @@ data Model = Model , numAnimations :: Int -- ^ Number of animations. } - instance Storable Model where sizeOf _ = #{size Model} alignment _ = alignment (undefined :: CUInt) @@ -291,7 +270,6 @@ instance Storable Model where #{poke Model, numSkins} ptr numSkins #{poke Model, numAnimations} ptr numAnimations - -- | A model triangle. -- -- See also: 'triangles''. @@ -307,7 +285,6 @@ data Triangle = Triangle , t2 :: TexCoord } - instance Storable Triangle where sizeOf _ = #{size model_triangle} alignment _ = alignment (undefined :: Float) @@ -335,39 +312,33 @@ instance Storable Triangle where #{poke model_triangle, t1} ptr t1 #{poke model_triangle, t2} ptr t2 - foreign import ccall "Model.h model_free" model_free :: Ptr Model -> IO () - foreign import ccall "MD2_load.h MD2_load" md2_load' :: Ptr CChar -> CChar -> CChar -> Ptr Model -> IO Int - foreign import ccall "OBJ_load.h OBJ_load" obj_load' :: Ptr CChar -> CChar -> CChar -> Ptr Model -> IO Int - md2_load :: Ptr CChar -> CChar -> CChar -> Ptr Model -> IO ModelErrorCode md2_load file clockwise leftHanded model = md2_load' file clockwise leftHanded model >>= \code -> return . toEnum $ code - obj_load :: Ptr CChar -> CChar -> CChar -> Ptr Model -> IO ModelErrorCode obj_load file clockwise leftHanded model = obj_load' file clockwise leftHanded model >>= \code -> return . toEnum $ code - -- | Load the model specified by the given file. -loadModel :: FilePath -> Setup Model +loadModel :: FilePath -> Game s Model loadModel file = do dotPos <- case elemIndex '.' file of - Nothing -> setupError $ "file name has no extension: " ++ file + Nothing -> gameError $ "file name has no extension: " ++ file Just p -> return p let ext = map toLower . tail . snd $ splitAt dotPos file - result <- setupIO . alloca $ \ptr -> do + result <- gameIO . alloca $ \ptr -> do status <- withCString file $ \fileCstr -> do case ext of "md2" -> md2_load fileCstr 0 0 ptr @@ -387,25 +358,21 @@ loadModel file = do case result of Right model -> return model - Left err -> setupError $ "loadModel: " ++ err - + Left err -> gameError $ "loadModel: " ++ err -- | Return 'True' if the model is animated, 'False' otherwise. animated :: Model -> Bool animated = (>1) . numFrames - -- | Return the model's ith animation. animation :: Model -> Int -> Animation animation model i = animations model S.! i - -- | Return the animation specified by the given string. animationByName :: Model -> String -> Maybe Animation animationByName model anim = let anim' = B.pack anim in S.find ((==) anim' . name) $ animations model - -- | Return a copy of the model's triangles. triangles' :: Model -> IO [Triangle] triangles' model = @@ -416,11 +383,9 @@ triangles' model = tris <- peekArray n arrayPtr return tris - foreign import ccall "Model.h model_copy_triangles" model_copy_triangles :: Ptr Model -> Ptr Triangle -> IO () - -- | Transform the model's vertices. transformVerts :: Model -> (Vec3 -> Vec3) -> Model transformVerts model f = model { vertices = vertices' } @@ -429,7 +394,6 @@ transformVerts model f = model { vertices = vertices' } vertices' = S.generate n f' f' i = f $ vertices model S.! i - -- | Transform the model's normals. transformNormals :: Model -> (Vec3 -> Vec3) -> Model transformNormals model f = model { normals = normals' } @@ -438,7 +402,6 @@ transformNormals model f = model { normals = normals' } normals' = S.generate n f' f' i = f $ normals model S.! i - -- | Translate the model such that its lowest point has y = 0. toGround :: Model -> IO Model toGround model = @@ -447,11 +410,9 @@ toGround model = in with model' model_to_ground >> return model' - foreign import ccall "Model.h model_to_ground" model_to_ground :: Ptr Model -> IO () - -- | Get the model's 3D bounding boxes. modelBoxes :: Model -> IO (V.Vector Box) modelBoxes model = @@ -474,8 +435,6 @@ modelBoxes model = box = Box pmin pmax peekBoxes ptr n (cur+1) (off + 6*sizeFloat) $ fmap (box:) l fmap (V.fromList . reverse) getBoxes - - foreign import ccall "Model.h model_compute_boxes" model_compute_boxes :: Ptr Model -> Ptr Vec2 -> IO () diff --git a/Spear/GLSL.hs b/Spear/GLSL.hs index 2947515..8541e1f 100644 --- a/Spear/GLSL.hs +++ b/Spear/GLSL.hs @@ -6,7 +6,6 @@ module Spear.GLSL , ShaderType(..) -- ** Programs , newProgram -, releaseProgram , linkProgram , useProgram , withGLSLProgram @@ -15,7 +14,6 @@ module Spear.GLSL , detachShader , loadShader , newShader -, releaseShader -- *** Source loading , loadSource , shaderSource @@ -36,12 +34,10 @@ module Spear.GLSL -- ** Helper functions , ($=) , Data.StateVar.get - -- * VAOs , VAO -- ** Creation and destruction , newVAO -, releaseVAO -- ** Manipulation , bindVAO , enableVAOAttrib @@ -49,20 +45,17 @@ module Spear.GLSL -- ** Rendering , drawArrays , drawElements - -- * Buffers , GLBuffer , TargetBuffer(..) , BufferUsage(..) -- ** Creation and destruction , newBuffer -, releaseBuffer -- ** Manipulation , bindBuffer , bufferData , bufferDatal , withGLBuffer - -- * Textures , Texture , SettableStateVar @@ -70,14 +63,12 @@ module Spear.GLSL -- ** Creation and destruction , newTexture , loadTextureImage -, releaseTexture -- ** Manipulation , bindTexture , loadTextureData , texParami , texParamf , activeTexture - -- * Error Handling , getGLError , printGLError @@ -89,12 +80,11 @@ module Spear.GLSL ) where - import Spear.Assets.Image +import Spear.Game import Spear.Math.Matrix3 (Matrix3) import Spear.Math.Matrix4 (Matrix4) import Spear.Math.Vector -import Spear.Setup import Control.Monad import Control.Monad.Trans.Class @@ -114,47 +104,45 @@ import System.Directory (doesFileExist, getCurrentDirectory, setCurrentDirectory import System.IO (hPutStrLn, stderr) import Unsafe.Coerce - -- -- MANAGEMENT -- - -- | A GLSL shader handle. data GLSLShader = GLSLShader { getShader :: GLuint , getShaderKey :: Resource } +instance ResourceClass GLSLShader where + getResource = getShaderKey -- | A GLSL program handle. data GLSLProgram = GLSLProgram { getProgram :: GLuint , getProgramKey :: Resource } - - + +instance ResourceClass GLSLProgram where + getResource = getProgramKey + -- | Supported shader types. data ShaderType = VertexShader | FragmentShader deriving (Eq, Show) - toGLShader :: ShaderType -> GLenum toGLShader VertexShader = gl_VERTEX_SHADER toGLShader FragmentShader = gl_FRAGMENT_SHADER - -- | Apply the given function to the program's id. withGLSLProgram :: GLSLProgram -> (GLuint -> a) -> a withGLSLProgram prog f = f $ getProgram prog - -- | Get the location of the given uniform variable within the given program. uniformLocation :: GLSLProgram -> String -> GettableStateVar GLint uniformLocation prog var = makeGettableStateVar get where get = withCString var $ \str -> glGetUniformLocation (getProgram prog) (unsafeCoerce str) - -- | Get or set the location of the given variable to a fragment shader colour number. fragLocation :: GLSLProgram -> String -> StateVar GLint fragLocation prog var = makeStateVar get set @@ -163,7 +151,6 @@ fragLocation prog var = makeStateVar get set set idx = withCString var $ \str -> glBindFragDataLocation (getProgram prog) (unsafeCoerce idx) (unsafeCoerce str) - -- | Get or set the location of the given attribute within the given program. attribLocation :: GLSLProgram -> String -> StateVar GLint attribLocation prog var = makeStateVar get set @@ -172,26 +159,19 @@ attribLocation prog var = makeStateVar get set set idx = withCString var $ \str -> glBindAttribLocation (getProgram prog) (unsafeCoerce idx) (unsafeCoerce str) - -- | Create a new program. -newProgram :: [GLSLShader] -> Setup GLSLProgram +newProgram :: [GLSLShader] -> Game s GLSLProgram newProgram shaders = do - h <- setupIO glCreateProgram - when (h == 0) $ setupError "glCreateProgram failed" + h <- gameIO glCreateProgram + when (h == 0) $ gameError "glCreateProgram failed" rkey <- register $ deleteProgram h let program = GLSLProgram h rkey - mapM_ (setupIO . attachShader program) shaders + mapM_ (gameIO . attachShader program) shaders linkProgram program return program - --- | Release the program. -releaseProgram :: GLSLProgram -> Setup () -releaseProgram = release . getProgramKey - - -- | Delete the program. deleteProgram :: GLuint -> IO () --deleteProgram = glDeleteProgram @@ -199,12 +179,11 @@ deleteProgram prog = do putStrLn $ "Deleting shader program " ++ show prog glDeleteProgram prog - -- | Link the program. -linkProgram :: GLSLProgram -> Setup () +linkProgram :: GLSLProgram -> Game s () linkProgram prog = do let h = getProgram prog - err <- setupIO $ do + err <- gameIO $ do glLinkProgram h alloca $ \statptr -> do glGetProgramiv h gl_LINK_STATUS statptr @@ -215,52 +194,41 @@ linkProgram prog = do case length err of 0 -> return () - _ -> setupError err - + _ -> gameError err -- | Use the program. useProgram :: GLSLProgram -> IO () useProgram prog = glUseProgram $ getProgram prog - -- | Attach the given shader to the given program. attachShader :: GLSLProgram -> GLSLShader -> IO () attachShader prog shader = glAttachShader (getProgram prog) (getShader shader) - -- | Detach the given GLSL from the given program. detachShader :: GLSLProgram -> GLSLShader -> IO () detachShader prog shader = glDetachShader (getProgram prog) (getShader shader) - -- | Load a shader from the file specified by the given string. -- -- This function creates a new shader. To load source code into an existing shader, -- see 'loadSource', 'shaderSource' and 'readSource'. -loadShader :: FilePath -> ShaderType -> Setup GLSLShader +loadShader :: FilePath -> ShaderType -> Game s GLSLShader loadShader file shaderType = do shader <- newShader shaderType loadSource file shader compile file shader return shader - -- | Create a new shader. -newShader :: ShaderType -> Setup GLSLShader +newShader :: ShaderType -> Game s GLSLShader newShader shaderType = do - h <- setupIO $ glCreateShader (toGLShader shaderType) + h <- gameIO $ glCreateShader (toGLShader shaderType) case h of - 0 -> setupError "glCreateShader failed" + 0 -> gameError "glCreateShader failed" _ -> do rkey <- register $ deleteShader h return $ GLSLShader h rkey - --- | Release the shader. -releaseShader :: GLSLShader -> Setup () -releaseShader = release . getShaderKey - - -- | Free the shader. deleteShader :: GLuint -> IO () --deleteShader = glDeleteShader @@ -268,36 +236,33 @@ deleteShader shader = do putStrLn $ "Deleting shader " ++ show shader glDeleteShader shader - -- | Load a shader source from the file specified by the given string -- into the shader. -loadSource :: FilePath -> GLSLShader -> Setup () +loadSource :: FilePath -> GLSLShader -> Game s () loadSource file h = do - exists <- setupIO $ doesFileExist file + exists <- gameIO $ doesFileExist file case exists of - False -> setupError "the specified shader file does not exist" - True -> setupIO $ do + False -> gameError "the specified shader file does not exist" + True -> gameIO $ do code <- readSource file withCString code $ shaderSource h - -- | Load the given shader source into the shader. shaderSource :: GLSLShader -> CString -> IO () shaderSource shader str = let ptr = unsafeCoerce str in withArray [ptr] $ flip (glShaderSource (getShader shader) 1) nullPtr - -- | Compile the shader. -compile :: FilePath -> GLSLShader -> Setup () +compile :: FilePath -> GLSLShader -> Game s () compile file shader = do let h = getShader shader -- Compile - setupIO $ glCompileShader h + gameIO $ glCompileShader h -- Verify status - err <- setupIO $ alloca $ \statusPtr -> do + err <- gameIO $ alloca $ \statusPtr -> do glGetShaderiv h gl_COMPILE_STATUS statusPtr result <- peek statusPtr case result of @@ -306,13 +271,11 @@ compile file shader = do case length err of 0 -> return () - _ -> setupError $ "Unable to compile shader " ++ file ++ ":\n" ++ err - + _ -> gameError $ "Unable to compile shader " ++ file ++ ":\n" ++ err type StatusCall = GLuint -> GLenum -> Ptr GLint -> IO () type LogCall = GLuint -> GLsizei -> Ptr GLsizei -> Ptr GLchar -> IO () - getStatus :: StatusCall -> LogCall -> GLuint -> IO String getStatus getStatus getLog h = do alloca $ \lenPtr -> do @@ -322,14 +285,12 @@ getStatus getStatus getLog h = do 0 -> return "" _ -> withCString (replicate (unsafeCoerce len) '\0') $ getErrorString getLog h (unsafeCoerce len) - getErrorString :: LogCall -> GLuint -> GLsizei -> CString -> IO String getErrorString getLog h len str = do let ptr = unsafeCoerce str getLog h len nullPtr ptr peekCString str - -- | Load the shader source specified by the given file. -- -- This function implements an #include mechanism, so the given file can @@ -337,7 +298,6 @@ getErrorString getLog h len str = do readSource :: FilePath -> IO String readSource = fmap B.unpack . readSource' - readSource' :: FilePath -> IO B.ByteString readSource' file = do let includeB = B.pack "#include" @@ -365,14 +325,12 @@ readSource' file = do return code - -- | Load a 2D vector. uniformVec2 :: GLint -> Vector2 -> IO () uniformVec2 loc v = glUniform2f loc x' y' where x' = unsafeCoerce $ x v y' = unsafeCoerce $ y v - -- | Load a 3D vector. uniformVec3 :: GLint -> Vector3 -> IO () uniformVec3 loc v = glUniform3f loc x' y' z' @@ -380,7 +338,6 @@ uniformVec3 loc v = glUniform3f loc x' y' z' y' = unsafeCoerce $ y v z' = unsafeCoerce $ z v - -- | Load a 4D vector. uniformVec4 :: GLint -> Vector4 -> IO () uniformVec4 loc v = glUniform4f loc x' y' z' w' @@ -389,21 +346,18 @@ uniformVec4 loc v = glUniform4f loc x' y' z' w' z' = unsafeCoerce $ z v w' = unsafeCoerce $ w v - -- | Load a 3x3 matrix. uniformMat3 :: GLint -> Matrix3 -> IO () uniformMat3 loc mat = with mat $ \ptrMat -> glUniformMatrix3fv loc 1 (toEnum 0) (unsafeCoerce ptrMat) - -- | Load a 4x4 matrix. uniformMat4 :: GLint -> Matrix4 -> IO () uniformMat4 loc mat = with mat $ \ptrMat -> glUniformMatrix4fv loc 1 (toEnum 0) (unsafeCoerce ptrMat) - -- | Load a list of floats. uniformfl :: GLint -> [GLfloat] -> IO () uniformfl loc vals = withArray vals $ \ptr -> @@ -413,7 +367,6 @@ uniformfl loc vals = withArray vals $ \ptr -> 3 -> glUniform3fv loc 1 ptr 4 -> glUniform4fv loc 1 ptr - -- | Load a list of integers. uniformil :: GLint -> [GLint] -> IO () uniformil loc vals = withArray vals $ \ptr -> @@ -423,65 +376,50 @@ uniformil loc vals = withArray vals $ \ptr -> 3 -> glUniform3iv loc 1 ptr 4 -> glUniform4iv loc 1 ptr - - - - - -- -- VAOs -- - -- | A vertex array object. data VAO = VAO { getVAO :: GLuint , vaoKey :: Resource } +instance ResourceClass VAO where + getResource = vaoKey instance Eq VAO where vao1 == vao2 = getVAO vao1 == getVAO vao2 - instance Ord VAO where vao1 < vao2 = getVAO vao1 < getVAO vao2 - -- | Create a new vao. -newVAO :: Setup VAO +newVAO :: Game s VAO newVAO = do - h <- setupIO . alloca $ \ptr -> do + h <- gameIO . alloca $ \ptr -> do glGenVertexArrays 1 ptr peek ptr rkey <- register $ deleteVAO h return $ VAO h rkey - --- | Release the vao. -releaseVAO :: VAO -> Setup () -releaseVAO = release . vaoKey - - -- | Delete the vao. deleteVAO :: GLuint -> IO () deleteVAO vao = Foreign.with vao $ glDeleteVertexArrays 1 - -- | Bind the vao. bindVAO :: VAO -> IO () bindVAO = glBindVertexArray . getVAO - -- | Enable the given vertex attribute of the bound vao. --- +-- -- See also 'bindVAO'. enableVAOAttrib :: GLuint -- ^ Attribute index. -> IO () enableVAOAttrib = glEnableVertexAttribArray - -- | Bind the bound buffer to the given point. attribVAOPointer :: GLuint -- ^ The index of the generic vertex attribute to be modified. @@ -494,7 +432,6 @@ attribVAOPointer attribVAOPointer idx ncomp dattype normalise stride off = glVertexAttribPointer idx ncomp dattype (unsafeCoerce normalise) stride (unsafeCoerce off) - -- | Draw the bound vao. drawArrays :: GLenum -- ^ The kind of primitives to render. @@ -503,7 +440,6 @@ drawArrays -> IO () drawArrays mode first count = glDrawArrays mode (unsafeCoerce first) (unsafeCoerce count) - -- | Draw the bound vao, indexed mode. drawElements :: GLenum -- ^ The kind of primitives to render. @@ -513,22 +449,18 @@ drawElements -> IO () drawElements mode count t idxs = glDrawElements mode (unsafeCoerce count) t idxs - - - - - -- -- BUFFER -- - -- | An OpenGL buffer. data GLBuffer = GLBuffer { getBuffer :: GLuint , rkey :: Resource } +instance ResourceClass GLBuffer where + getResource = rkey -- | The type of target buffer. data TargetBuffer @@ -538,14 +470,12 @@ data TargetBuffer | PixelUnpackBuffer deriving (Eq, Show) - fromTarget :: TargetBuffer -> GLenum fromTarget ArrayBuffer = gl_ARRAY_BUFFER fromTarget ElementArrayBuffer = gl_ELEMENT_ARRAY_BUFFER fromTarget PixelPackBuffer = gl_PIXEL_PACK_BUFFER fromTarget PixelUnpackBuffer = gl_PIXEL_UNPACK_BUFFER - -- | A buffer usage. data BufferUsage = StreamDraw @@ -559,7 +489,6 @@ data BufferUsage | DynamicCopy deriving (Eq, Show) - fromUsage :: BufferUsage -> GLenum fromUsage StreamDraw = gl_STREAM_DRAW fromUsage StreamRead = gl_STREAM_READ @@ -571,33 +500,24 @@ fromUsage DynamicDraw = gl_DYNAMIC_DRAW fromUsage DynamicRead = gl_DYNAMIC_READ fromUsage DynamicCopy = gl_DYNAMIC_COPY - -- | Create a new buffer. -newBuffer :: Setup GLBuffer +newBuffer :: Game s GLBuffer newBuffer = do - h <- setupIO . alloca $ \ptr -> do + h <- gameIO . alloca $ \ptr -> do glGenBuffers 1 ptr peek ptr rkey <- register $ deleteBuffer h return $ GLBuffer h rkey - --- | Release the buffer. -releaseBuffer :: GLBuffer -> Setup () -releaseBuffer = release . rkey - - -- | Delete the buffer. deleteBuffer :: GLuint -> IO () deleteBuffer buf = Foreign.with buf $ glDeleteBuffers 1 - -- | Bind the buffer. bindBuffer :: GLBuffer -> TargetBuffer -> IO () bindBuffer buf target = glBindBuffer (fromTarget target) $ getBuffer buf - -- | Set the buffer's data. bufferData :: TargetBuffer -> Int -- ^ Buffer size in bytes. @@ -606,7 +526,6 @@ bufferData :: TargetBuffer -> IO () bufferData target n bufData usage = glBufferData (fromTarget target) (unsafeCoerce n) bufData (fromUsage usage) - -- | Set the buffer's data. bufferDatal :: Storable a => TargetBuffer @@ -617,16 +536,10 @@ bufferDatal :: Storable a bufferDatal target n bufData usage = withArray bufData $ \ptr -> bufferData target (n * length bufData) ptr usage - -- | Apply the given function the buffer's id. withGLBuffer :: GLBuffer -> (GLuint -> a) -> a withGLBuffer buf f = f $ getBuffer buf - - - - - -- -- TEXTURE -- @@ -637,31 +550,25 @@ data Texture = Texture , texKey :: Resource } - instance Eq Texture where t1 == t2 = getTex t1 == getTex t2 - instance Ord Texture where t1 < t2 = getTex t1 < getTex t2 +instance ResourceClass Texture where + getResource = texKey -- | Create a new texture. -newTexture :: Setup Texture +newTexture :: Game s Texture newTexture = do - tex <- setupIO . alloca $ \ptr -> do + tex <- gameIO . alloca $ \ptr -> do glGenTextures 1 ptr peek ptr rkey <- register $ deleteTexture tex return $ Texture tex rkey - --- | Release the texture. -releaseTexture :: Texture -> Setup () -releaseTexture = release . texKey - - -- | Delete the texture. deleteTexture :: GLuint -> IO () --deleteTexture tex = with tex $ glDeleteTextures 1 @@ -669,16 +576,15 @@ deleteTexture tex = do putStrLn $ "Releasing texture " ++ show tex with tex $ glDeleteTextures 1 - -- | Load the 'Texture' specified by the given file. loadTextureImage :: FilePath -> GLenum -- ^ Texture's min filter. -> GLenum -- ^ Texture's mag filter. - -> Setup Texture + -> Game s Texture loadTextureImage file minFilter magFilter = do image <- loadImage file tex <- newTexture - setupIO $ do + gameIO $ do let w = width image h = height image pix = pixels image @@ -691,12 +597,10 @@ loadTextureImage file minFilter magFilter = do return tex - -- | Bind the texture. bindTexture :: Texture -> IO () bindTexture = glBindTexture gl_TEXTURE_2D . getTex - -- | Load data onto the bound texture. -- -- See also 'bindTexture'. @@ -721,31 +625,22 @@ loadTextureData target level internalFormat width height border format texType t texType texData - -- | Set the bound texture's parameter to the given value. texParami :: GLenum -> GLenum -> SettableStateVar GLenum texParami target param = makeSettableStateVar $ \val -> glTexParameteri target param $ fromIntegral . fromEnum $ val - -- | Set the bound texture's parameter to the given value. texParamf :: GLenum -> GLenum -> SettableStateVar Float texParamf target param = makeSettableStateVar $ \val -> glTexParameterf target param (unsafeCoerce val) - -- | Set the active texture unit. activeTexture :: SettableStateVar GLenum activeTexture = makeSettableStateVar glActiveTexture - - - - - -- -- ERROR -- - -- | Get the last OpenGL error. getGLError :: IO (Maybe String) getGLError = fmap translate glGetError @@ -758,22 +653,20 @@ getGLError = fmap translate glGetError | err == gl_OUT_OF_MEMORY = Just "Out of memory" | otherwise = Just "Unknown error" - -- | Print the last OpenGL error. printGLError :: IO () printGLError = getGLError >>= \err -> case err of Nothing -> return () Just str -> hPutStrLn stderr str - -- | Run the given setup action and check for OpenGL errors. -- -- If an OpenGL error is produced, an exception is thrown containing -- the given string appended to the string describing the error. -assertGL :: Setup a -> String -> Setup a +assertGL :: Game s a -> String -> Game s a assertGL action err = do result <- action - status <- setupIO getGLError + status <- gameIO getGLError case status of - Just str -> setupError $ "OpenGL error raised: " ++ err ++ "; " ++ str + Just str -> gameError $ "OpenGL error raised: " ++ err ++ "; " ++ str Nothing -> return result diff --git a/Spear/Game.hs b/Spear/Game.hs index 08fc460..6bb1fa6 100644 --- a/Spear/Game.hs +++ b/Spear/Game.hs @@ -1,42 +1,88 @@ module Spear.Game ( Game -, gameIO +, Resource +, ResourceClass(..) + -- * Game State , getGameState , saveGameState , modifyGameState + -- * Game Resources +, register +, unregister +, gameError +, assertMaybe + -- * Running and IO , runGame +, runGame' +, evalSubGame +, execSubGame +, gameIO ) where - import Control.Monad.Trans.Class (lift) import Control.Monad.State.Strict +import Control.Monad.Error +import qualified Control.Monad.Trans.Resource as R +type Resource = R.ReleaseKey +type Game s = StateT s (R.ResourceT (ErrorT String IO)) -type Game s = StateT s IO - - --- | Perform the given IO action in the 'Game' monad. -gameIO :: IO a -> Game s a -gameIO = lift - +class ResourceClass a where + getResource :: a -> Resource + + release :: a -> Game s () + release = unregister . getResource + + clean :: a -> IO () + clean = R.release . getResource -- | Retrieve the game state. getGameState :: Game s s getGameState = get - -- | Save the game state. saveGameState :: s -> Game s () saveGameState = put - -- | Modify the game state. modifyGameState :: (s -> s) -> Game s () modifyGameState = modify +-- | Register the given cleaner. +register :: IO () -> Game s Resource +register = lift . R.register + +-- | Release the given 'Resource'. +unregister :: Resource -> Game s () +unregister = lift . R.release + +-- | Throw an error from the 'Game' monad. +gameError :: String -> Game s a +gameError = lift . lift . throwError + +-- | Throw the given error string if given 'Nothing'. +assertMaybe :: Maybe a -> String -> Game s a +assertMaybe Nothing err = gameError err +assertMaybe (Just x) _ = return x + +-- | Run the given game. +runGame :: Game s a -> s -> IO (Either String (a,s)) +runGame game state = runErrorT . R.runResourceT . runStateT game $ state -- | Run the given game. -runGame :: Game s a -> s -> IO () -runGame game state = runStateT game state >> return () +runGame' :: Game s a -> s -> IO () +runGame' game state = runGame game state >> return () + +-- | Run the given game and return its result. +evalSubGame :: Game s a -> s -> Game t a +evalSubGame g s = lift $ evalStateT g s + +-- | Run the given game and return its state. +execSubGame :: Game s a -> s -> Game t s +execSubGame g s = lift $ execStateT g s + +-- | Perform the given IO action in the 'Game' monad. +gameIO :: IO a -> Game s a +gameIO = lift . lift . lift diff --git a/Spear/Math/Camera.hs b/Spear/Math/Camera.hs index e22f3c2..a86d5f5 100644 --- a/Spear/Math/Camera.hs +++ b/Spear/Math/Camera.hs @@ -27,7 +27,7 @@ perspective :: Float -- ^ Fovy - Vertical field of view angle in degrees. perspective fovy r n f right up fwd pos = Camera { projection = M.perspective fovy r n f - , transform = M.transform right up fwd pos + , transform = M.transform right up (neg fwd) pos } @@ -47,7 +47,7 @@ ortho :: Float -- ^ Left. ortho l r b t n f right up fwd pos = Camera { projection = M.ortho l r b t n f - , transform = M.transform right up fwd pos + , transform = M.transform right up (neg fwd) pos } diff --git a/Spear/Render/AnimatedModel.hs b/Spear/Render/AnimatedModel.hs index dfaadfd..e554272 100644 --- a/Spear/Render/AnimatedModel.hs +++ b/Spear/Render/AnimatedModel.hs @@ -7,7 +7,6 @@ module Spear.Render.AnimatedModel -- * Construction and destruction , animatedModelResource , animatedModelRenderer -, Spear.Render.AnimatedModel.release -- * Accessors , animationSpeed , box @@ -28,9 +27,9 @@ module Spear.Render.AnimatedModel ) where - import Spear.Assets.Model import Spear.Collision +import Spear.Game import Spear.GLSL import Spear.Math.AABB import Spear.Math.Matrix4 (Matrix4) @@ -38,17 +37,14 @@ import Spear.Math.Vector import Spear.Render.Material import Spear.Render.Model import Spear.Render.Program -import Spear.Setup as Setup import Control.Applicative ((<$>), (<*>)) import qualified Data.Vector as V import Graphics.Rendering.OpenGL.Raw.Core31 import Unsafe.Coerce (unsafeCoerce) - type AnimationSpeed = Float - -- | An animated model resource. -- -- Contains model data necessary to render an animated model. @@ -63,14 +59,14 @@ data AnimatedModelResource = AnimatedModelResource , rkey :: Resource } - instance Eq AnimatedModelResource where m1 == m2 = vao m1 == vao m2 - instance Ord AnimatedModelResource where m1 < m2 = vao m1 < vao m2 +instance ResourceClass AnimatedModelResource where + getResource = rkey -- | An animated model renderer. -- @@ -92,31 +88,28 @@ data AnimatedModelRenderer = AnimatedModelRenderer , animationSpeed :: Float -- ^ Get the renderer's animation speed. } - instance Eq AnimatedModelRenderer where m1 == m2 = modelResource m1 == modelResource m2 - instance Ord AnimatedModelRenderer where m1 < m2 = modelResource m1 < modelResource m2 - -- | Create an model resource from the given model. animatedModelResource :: AnimatedProgramChannels -> Material -> Texture -> Model - -> Setup AnimatedModelResource + -> Game s AnimatedModelResource animatedModelResource (AnimatedProgramChannels vertChan1 vertChan2 normChan1 normChan2 texChan) material texture model = do - RenderModel elements numFrames numVertices <- setupIO . renderModelFromModel $ model + RenderModel elements numFrames numVertices <- gameIO . renderModelFromModel $ model elementBuf <- newBuffer vao <- newVAO - boxes <- setupIO $ modelBoxes model + boxes <- gameIO $ modelBoxes model - setupIO $ do + gameIO $ do let elemSize = 56 elemSize' = fromIntegral elemSize @@ -139,27 +132,20 @@ animatedModelResource enableVAOAttrib normChan2 enableVAOAttrib texChan - rkey <- register . runSetup_ $ do - setupIO $ putStrLn "Releasing animated model resource" - releaseVAO vao - releaseBuffer elementBuf + rkey <- register $ do + putStrLn "Releasing animated model resource" + clean vao + clean elementBuf return $ AnimatedModelResource model vao (unsafeCoerce numFrames) (unsafeCoerce numVertices) material texture boxes rkey - --- | Release the given model resource. -release :: AnimatedModelResource -> Setup () -release = Setup.release . rkey - - -- | Create a renderer from the given model resource. animatedModelRenderer :: AnimationSpeed -> AnimatedModelResource -> AnimatedModelRenderer animatedModelRenderer animSpeed modelResource = AnimatedModelRenderer modelResource 0 0 0 0 0 animSpeed - -- | Update the renderer. update dt (AnimatedModelRenderer model curAnim startFrame endFrame curFrame fp s) = AnimatedModelRenderer model curAnim startFrame endFrame curFrame' fp' s @@ -171,22 +157,18 @@ update dt (AnimatedModelRenderer model curAnim startFrame endFrame curFrame fp s in if x > endFrame then startFrame else x else curFrame - -- | Get the model's ith bounding box. box :: Int -> AnimatedModelResource -> Box box i model = boxes model V.! i - -- | Get the renderer's current animation. currentAnimation :: Enum a => AnimatedModelRenderer -> a currentAnimation = toEnum . currentAnim - -- | Get the renderer's model resource. modelRes :: AnimatedModelRenderer -> AnimatedModelResource modelRes = modelResource - -- | Get the renderer's next frame. nextFrame :: AnimatedModelRenderer -> Int nextFrame rend = @@ -196,7 +178,6 @@ nextFrame rend = then frameStart rend else curFrame + 1 - -- | Set the active animation to the given one. setAnimation :: Enum a => a -> AnimatedModelRenderer -> AnimatedModelRenderer setAnimation anim modelRend = @@ -205,12 +186,10 @@ setAnimation anim modelRend = in modelRend { currentAnim = anim', frameStart = f1, frameEnd = f2, currentFrame = f1 } - -- | Set the renderer's animation speed. setAnimationSpeed :: AnimationSpeed -> AnimatedModelRenderer -> AnimatedModelRenderer setAnimationSpeed s r = r { animationSpeed = s } - -- | Bind the given renderer to prepare it for rendering. bind :: AnimatedProgramUniforms -> AnimatedModelRenderer -> IO () bind (AnimatedProgramUniforms kaLoc kdLoc ksLoc shiLoc texLoc _ _ _ _) modelRend = @@ -221,7 +200,6 @@ bind (AnimatedProgramUniforms kaLoc kdLoc ksLoc shiLoc texLoc _ _ _ _) modelRend activeTexture $= gl_TEXTURE0 glUniform1i texLoc 0 - -- | Render the model described by the given renderer. render :: AnimatedProgramUniforms -> AnimatedModelRenderer -> IO () render uniforms (AnimatedModelRenderer model _ _ _ curFrame fp _) = @@ -235,7 +213,6 @@ render uniforms (AnimatedModelRenderer model _ _ _ curFrame fp _) = glUniform1f (fpLoc uniforms) (unsafeCoerce fp) drawArrays gl_TRIANGLES (n*curFrame) n - -- | Compute AABB collisioners in view space from the given model. mkColsFromAnimated :: Int -- ^ Source frame diff --git a/Spear/Render/Model.hsc b/Spear/Render/Model.hsc index b6c561b..d7dbdfe 100644 --- a/Spear/Render/Model.hsc +++ b/Spear/Render/Model.hsc @@ -7,9 +7,8 @@ module Spear.Render.Model ) where - import qualified Spear.Assets.Model as Assets -import Spear.Setup +import Spear.Game import Foreign.Ptr import Foreign.C.Types @@ -18,22 +17,18 @@ import Foreign.Marshal.Array import Foreign.Marshal.Utils (with) import Foreign.Storable - #include "RenderModel.h" - data Vec3 = Vec3 !CFloat !CFloat !CFloat data TexCoord = TexCoord !CFloat !CFloat - data RenderModel = RenderModel { elements :: Ptr CChar , numFrames :: CUInt , numVertices :: CUInt -- ^ Number of vertices per frame. } - instance Storable RenderModel where sizeOf _ = #{size RenderModel} alignment _ = alignment (undefined :: CUInt) @@ -49,11 +44,9 @@ instance Storable RenderModel where #{poke RenderModel, numFrames} ptr numFrames #{poke RenderModel, numVertices} ptr numVertices - foreign import ccall "RenderModel.h render_model_from_model_asset" render_model_from_model_asset :: Ptr Assets.Model -> Ptr RenderModel -> IO Int - -- | Convert the given 'Model' to a 'ModelData' instance. renderModelFromModel :: Assets.Model -> IO RenderModel renderModelFromModel m = with m $ \mPtr -> alloca $ \mdPtr -> do diff --git a/Spear/Render/StaticModel.hs b/Spear/Render/StaticModel.hs index ed8d065..fc7006e 100644 --- a/Spear/Render/StaticModel.hs +++ b/Spear/Render/StaticModel.hs @@ -6,7 +6,6 @@ module Spear.Render.StaticModel -- * Construction and destruction , staticModelResource , staticModelRenderer -, Spear.Render.StaticModel.release -- * Manipulation , box , modelRes @@ -18,9 +17,9 @@ module Spear.Render.StaticModel ) where - import Spear.Assets.Model import Spear.Collision +import Spear.Game import Spear.GLSL import Spear.Math.AABB import Spear.Math.Matrix4 (Matrix4) @@ -28,13 +27,11 @@ import Spear.Math.Vector import Spear.Render.Material import Spear.Render.Model import Spear.Render.Program -import Spear.Setup as Setup import qualified Data.Vector as V import Graphics.Rendering.OpenGL.Raw.Core31 import Unsafe.Coerce (unsafeCoerce) - data StaticModelResource = StaticModelResource { vao :: VAO , nVertices :: Int @@ -44,40 +41,37 @@ data StaticModelResource = StaticModelResource , rkey :: Resource } - instance Eq StaticModelResource where m1 == m2 = vao m1 == vao m2 - instance Ord StaticModelResource where m1 < m2 = vao m1 < vao m2 +instance ResourceClass StaticModelResource where + getResource = rkey data StaticModelRenderer = StaticModelRenderer { model :: StaticModelResource } - instance Eq StaticModelRenderer where m1 == m2 = model m1 == model m2 - instance Ord StaticModelRenderer where m1 < m2 = model m1 < model m2 - -- | Create a model resource from the given model. staticModelResource :: StaticProgramChannels -> Material -> Texture -> Model - -> Setup StaticModelResource + -> Game s StaticModelResource staticModelResource (StaticProgramChannels vertChan normChan texChan) material texture model = do - RenderModel elements _ numVertices <- setupIO . renderModelFromModel $ model + RenderModel elements _ numVertices <- gameIO . renderModelFromModel $ model elementBuf <- newBuffer vao <- newVAO - boxes <- setupIO $ modelBoxes model + boxes <- gameIO $ modelBoxes model - setupIO $ do + gameIO $ do let elemSize = 32 elemSize' = fromIntegral elemSize @@ -96,35 +90,26 @@ staticModelResource (StaticProgramChannels vertChan normChan texChan) material t enableVAOAttrib normChan enableVAOAttrib texChan - rkey <- register . runSetup_ $ do - setupIO $ putStrLn "Releasing static model resource" - releaseVAO vao - releaseBuffer elementBuf + rkey <- register $ do + putStrLn "Releasing static model resource" + clean vao + clean elementBuf return $ StaticModelResource vao (unsafeCoerce numVertices) material texture boxes rkey - --- | Release the given model resource. -release :: StaticModelResource -> Setup () -release = Setup.release . rkey - - -- | Create a renderer from the given model resource. staticModelRenderer :: StaticModelResource -> StaticModelRenderer staticModelRenderer = StaticModelRenderer - -- | Get the model's ith bounding box. box :: Int -> StaticModelResource -> Box box i model = boxes model V.! i - -- | Get the renderer's model resource. modelRes :: StaticModelRenderer -> StaticModelResource modelRes = model - -- | Bind the given renderer to prepare it for rendering. bind :: StaticProgramUniforms -> StaticModelRenderer -> IO () bind (StaticProgramUniforms kaLoc kdLoc ksLoc shiLoc texLoc _ _ _) (StaticModelRenderer model) = @@ -135,7 +120,6 @@ bind (StaticProgramUniforms kaLoc kdLoc ksLoc shiLoc texLoc _ _ _) (StaticModelR activeTexture $= gl_TEXTURE0 glUniform1i texLoc 0 - -- | Render the given renderer. render :: StaticProgramUniforms -> StaticModelRenderer -> IO () render uniforms (StaticModelRenderer model) = @@ -147,7 +131,6 @@ render uniforms (StaticModelRenderer model) = glUniform1f (shiLoc uniforms) $ unsafeCoerce shi drawArrays gl_TRIANGLES 0 $ nVertices model - -- | Compute AABB collisioners in view space from the given model. mkColsFromStatic :: Matrix4 -- ^ Modelview matrix diff --git a/Spear/Scene/Loader.hs b/Spear/Scene/Loader.hs index 07d4f05..09d69eb 100644 --- a/Spear/Scene/Loader.hs +++ b/Spear/Scene/Loader.hs @@ -17,9 +17,9 @@ module Spear.Scene.Loader ) where - import Spear.Assets.Model as Model import Spear.Collision +import Spear.Game import qualified Spear.GLSL as GLSL import Spear.Math.Matrix3 as M3 import Spear.Math.Matrix4 as M4 @@ -33,7 +33,6 @@ import Spear.Scene.GameObject as GO import Spear.Scene.Graph import Spear.Scene.Light import Spear.Scene.SceneResources -import Spear.Setup import Control.Monad.State.Strict import Control.Monad.Trans (lift) @@ -43,37 +42,27 @@ import qualified Data.StateVar as SV (get) import Graphics.Rendering.OpenGL.Raw.Core31 import Text.Printf (printf) - -type Loader = StateT SceneResources Setup - - -loaderSetup = lift -loaderIO = loaderSetup . setupIO -loaderError = loaderSetup . setupError - +type Loader = Game SceneResources -- | Load the scene specified by the given file. -loadScene :: FilePath -> Setup (SceneResources, SceneGraph) +loadScene :: FilePath -> Game s (SceneResources, SceneGraph) loadScene file = do - result <- setupIO $ loadSceneGraphFromFile file + result <- gameIO $ loadSceneGraphFromFile file case result of - Left err -> setupError $ show err + Left err -> gameError $ show err Right g -> case validate g of Nothing -> do sceneRes <- resourceMap g return (sceneRes, g) - Just err -> setupError err - + Just err -> gameError err -- | Validate the given SceneGraph. validate :: SceneGraph -> Maybe String validate _ = Nothing - -- | Load the scene described by the given 'SceneGraph'. -resourceMap :: SceneGraph -> Setup SceneResources -resourceMap g = execStateT (resourceMap' g) emptySceneResources - +resourceMap :: SceneGraph -> Game s SceneResources +resourceMap g = execSubGame (resourceMap' g) emptySceneResources resourceMap' :: SceneGraph -> Loader () resourceMap' node@(SceneLeaf nid props) = do @@ -86,63 +75,51 @@ resourceMap' node@(SceneLeaf nid props) = do resourceMap' node@(SceneNode nid props children) = do mapM_ resourceMap' children - -- Lookup the given resource in the data pool. Load it if it is not present, otherwise return it. loadResource :: String -- ^ Resource name. -> (SceneResources -> Map String a) -- ^ Map getter. -> (String -> a -> Loader ()) -- ^ Function to modify resources. - -> Setup a -- ^ Resource loader. + -> Loader a -- ^ Resource loader. -> Loader a loadResource key field modifyResources load = do sceneData <- get case M.lookup key $ field sceneData of Just val -> return val Nothing -> do - loaderIO $ printf "Loading %s..." key - resource <- loaderSetup load - loaderIO $ printf "done\n" + gameIO $ printf "Loading %s..." key + resource <- load + gameIO $ printf "done\n" modifyResources key resource return resource - addShader name shader = modify $ \sceneData -> sceneData { shaders = M.insert name shader $ shaders sceneData } - addCustomProgram name prog = modify $ \sceneData -> sceneData { customPrograms = M.insert name prog $ customPrograms sceneData } - addStaticProgram name prog = modify $ \sceneData -> sceneData { staticPrograms = M.insert name prog $ staticPrograms sceneData } - addAnimatedProgram name prog = modify $ \sceneData -> sceneData { animatedPrograms = M.insert name prog $ animatedPrograms sceneData } - addTexture name tex = modify $ \sceneData -> sceneData { textures = M.insert name tex $ textures sceneData } - addStaticModel name model = modify $ \sceneData -> sceneData { staticModels = M.insert name model $ staticModels sceneData } - addAnimatedModel name model = modify $ \sceneData -> sceneData { animatedModels = M.insert name model $ animatedModels sceneData } - -- Get the given resource from the data pool. getResource :: (SceneResources -> Map String a) -> String -> Loader a getResource field key = do sceneData <- get case M.lookup key $ field sceneData of Just val -> return val - Nothing -> loaderSetup . setupError $ "Oops, the given resource has not been loaded: " ++ key - - - + Nothing -> gameError $ "Oops, the given resource has not been loaded: " ++ key ---------------------- -- Resource Loading -- @@ -163,9 +140,9 @@ newModel (SceneLeaf _ props) = do let rotation = asRotation $ value "rotation" props scale = asVec3 $ value "scale" props - loaderIO $ printf "Loading model %s..." name - model <- loaderSetup $ loadModel' file rotation scale - loaderIO . putStrLn $ "done" + gameIO $ printf "Loading model %s..." name + model <- loadModel' file rotation scale + gameIO . putStrLn $ "done" texture <- loadTexture tex sceneRes <- get @@ -174,25 +151,24 @@ newModel (SceneLeaf _ props) = do case animated model of False -> case M.lookup prog $ staticPrograms sceneRes of - Nothing -> (loaderError $ "Static shader program " ++ prog ++ " does not exist") >> return () + Nothing -> (gameError $ "Static shader program " ++ prog ++ " does not exist") >> return () Just p -> let StaticProgram _ channels _ = p in do - model' <- loaderSetup $ staticModelResource channels material texture model + model' <- staticModelResource channels material texture model loadResource name staticModels addStaticModel (return model') return () True -> case M.lookup prog $ animatedPrograms sceneRes of - Nothing -> (loaderError $ "Animated shader program " ++ prog ++ " does not exist") >> return () + Nothing -> (gameError $ "Animated shader program " ++ prog ++ " does not exist") >> return () Just p -> let AnimatedProgram _ channels _ = p in do - model' <- loaderSetup $ animatedModelResource channels material texture model + model' <- animatedModelResource channels material texture model loadResource name animatedModels addAnimatedModel (return model') return () - -loadModel' :: FilePath -> Maybe Rotation -> Maybe Vector3 -> Setup Model +loadModel' :: FilePath -> Maybe Rotation -> Maybe Vector3 -> Game s Model loadModel' file rotation scale = do let transform = (case rotation of @@ -204,8 +180,7 @@ loadModel' file rotation scale = do Just s -> flip Model.transformVerts $ \(Vec3 x' y' z') -> Vec3 (x s * x') (y s * y') (z s * z')) - (fmap transform $ Model.loadModel file) >>= setupIO . toGround - + (fmap transform $ Model.loadModel file) >>= gameIO . toGround rotateModel :: Rotation -> Model -> Model rotateModel (Rotation ax ay az order) model = @@ -226,22 +201,20 @@ rotateModel (Rotation ax ay az order) model = in flip Model.transformVerts vTransform . flip Model.transformNormals nTransform $ model - loadTexture :: FilePath -> Loader GLSL.Texture loadTexture file = loadResource file textures addTexture $ GLSL.loadTextureImage file gl_LINEAR gl_LINEAR - newShaderProgram :: SceneGraph -> Loader () newShaderProgram (SceneLeaf _ props) = do (vsName, vertShader) <- Spear.Scene.Loader.loadShader GLSL.VertexShader props (fsName, fragShader) <- Spear.Scene.Loader.loadShader GLSL.FragmentShader props name <- asString $ mandatory' "name" props stype <- asString $ mandatory' "type" props - prog <- loaderSetup $ GLSL.newProgram [vertShader, fragShader] + prog <- GLSL.newProgram [vertShader, fragShader] - let getUniformLoc name = loaderSetup $ (setupIO . SV.get $ GLSL.uniformLocation prog name) `GLSL.assertGL` name + let getUniformLoc name = (gameIO . SV.get $ GLSL.uniformLocation prog name) `GLSL.assertGL` name case stype of "static" -> do @@ -312,12 +285,8 @@ newShaderProgram (SceneLeaf _ props) = do loadResource name customPrograms addCustomProgram $ return prog return () - - - - loadShader :: GLSL.ShaderType -> [Property] -> Loader (String, GLSL.GLSLShader) -loadShader _ [] = loaderSetup . setupError $ "Loader::vertexShader: empty list" +loadShader _ [] = gameError $ "Loader::vertexShader: empty list" loadShader shaderType ((stype, file):xs) = if shaderType == GLSL.VertexShader && stype == "vertex-shader" || shaderType == GLSL.FragmentShader && stype == "fragment-shader" @@ -325,22 +294,17 @@ loadShader shaderType ((stype, file):xs) = in loadShader' f shaderType >>= \shader -> return (f, shader) else Spear.Scene.Loader.loadShader shaderType xs - loadShader' :: String -> GLSL.ShaderType -> Loader GLSL.GLSLShader loadShader' file shaderType = loadResource file shaders addShader $ GLSL.loadShader file shaderType - newLight :: SceneGraph -> Loader () newLight _ = return () - - - -------------------- -- Object Loading -- -------------------- -loadGO :: GameStyle -> SceneResources -> [Property] -> Matrix3 -> Setup GameObject +loadGO :: GameStyle -> SceneResources -> [Property] -> Matrix3 -> Game s GameObject loadGO style sceneRes props transf = do modelName <- asString . mandatory "model" $ props axis <- asVec3 . mandatory "axis" $ props @@ -353,12 +317,11 @@ loadGO style sceneRes props transf = do Just model -> return $ goNew style (Left model) [] transf axis Nothing -> - setupError $ "model " ++ modelName ++ " not found" + gameError $ "model " ++ modelName ++ " not found" return $ case animSpeed of Nothing -> go Just s -> GO.setAnimationSpeed s go - type CreateGameObject m a = String -- ^ The object's name. -> SceneResources @@ -366,7 +329,6 @@ type CreateGameObject m a -> Matrix3 -- ^ The object's transform. -> m a - -- | Load objects from the given 'SceneGraph'. loadObjects :: Monad m => CreateGameObject m a -> SceneResources -> SceneGraph -> m [a] loadObjects newGO sceneRes g = @@ -374,7 +336,6 @@ loadObjects newGO sceneRes g = Nothing -> return [] Just n -> sequence . concat . fmap (newObject newGO sceneRes) $ children n - -- to-do: use a strict accumulator and make loadObjects tail recursive. newObject :: Monad m => CreateGameObject m a -> SceneResources -> SceneGraph -> [m a] newObject newGO sceneRes (SceneNode nid props children) = @@ -383,7 +344,6 @@ newObject newGO sceneRes (SceneNode nid props children) = newObject newGO sceneRes (SceneLeaf nid props) = [newObject' newGO sceneRes nid props] - newObject' :: Monad m => CreateGameObject m a -> SceneResources -> String -> [Property] -> m a newObject' newGO sceneRes nid props = do -- Optional properties. @@ -399,15 +359,11 @@ newObject' newGO sceneRes nid props = do newGO goType sceneRes props (M3.transform right up position) - vectors :: Maybe Vector2 -> (Vector2, Vector2) vectors up = case up of Nothing -> (unitx2, unity2) Just u -> (perp u, u) - - - ---------------------- -- Helper functions -- ---------------------- @@ -418,53 +374,43 @@ value name props = case L.find ((==) name . fst) props of Nothing -> Nothing Just prop -> Just . snd $ prop - unspecified :: Maybe a -> a -> a unspecified (Just x) _ = x unspecified Nothing x = x - -mandatory :: String -> [Property] -> Setup [String] +mandatory :: String -> [Property] -> Game s [String] mandatory name props = case value name props of - Nothing -> setupError $ "Loader::mandatory: key not found: " ++ name + Nothing -> gameError $ "Loader::mandatory: key not found: " ++ name Just x -> return x - mandatory' :: String -> [Property] -> Loader [String] -mandatory' name props = loaderSetup $ mandatory name props - +mandatory' name props = mandatory name props asString :: Functor f => f [String] -> f String asString = fmap concat - asFloat :: Functor f => f [String] -> f Float asFloat = fmap (read . concat) - asVec2 :: Functor f => f [String] -> f Vector2 asVec2 val = fmap toVec2 val where toVec2 (x:y:_) = vec2 (read x) (read y) toVec2 (x:[]) = let x' = read x in vec2 x' x' - asVec3 :: Functor f => f [String] -> f Vector3 asVec3 val = fmap toVec3 val where toVec3 (x:y:z:_) = vec3 (read x) (read y) (read z) toVec3 (x:[]) = let x' = read x in vec3 x' x' x' - asVec4 :: Functor f => f [String] -> f Vector4 asVec4 val = fmap toVec4 val where toVec4 (x:y:z:w:_) = vec4 (read x) (read y) (read z) (read w) toVec4 (x:[]) = let x' = read x in vec4 x' x' x' x' - asRotation :: Functor f => f [String] -> f Rotation asRotation val = fmap parseRotation val where parseRotation (ax:ay:az:order:_) = Rotation (read ax) (read ay) (read az) (readOrder order) - data Rotation = Rotation { ax :: Float , ay :: Float @@ -472,10 +418,8 @@ data Rotation = Rotation , order :: RotationOrder } - data RotationOrder = XYZ | XZY | YXZ | YZX | ZXY | ZYX deriving Eq - readOrder :: String -> RotationOrder readOrder "xyz" = XYZ readOrder "xzy" = XZY diff --git a/Spear/Setup.hs b/Spear/Setup.hs deleted file mode 100644 index 0326c4b..0000000 --- a/Spear/Setup.hs +++ /dev/null @@ -1,59 +0,0 @@ -module Spear.Setup -( - Setup -, Resource -, register -, release -, runSetup -, runSetup_ -, setupError -, setupIO -, assertMaybe -) -where - - -import Control.Monad.Error -import qualified Control.Monad.Trans.Resource as R -import qualified Control.Monad.Trans.Class as MT (lift) - - -type Setup = R.ResourceT (ErrorT String IO) - -type Resource = R.ReleaseKey - - --- | Register the given cleaner. -register :: IO () -> Setup Resource -register = R.register - - --- | Release the given 'Resource'. -release :: Resource -> Setup () -release = R.release - - --- | Run the given 'Setup', freeing all of its allocated resources. -runSetup :: Setup a -> IO (Either String a) -runSetup = runErrorT . R.runResourceT - - --- | Run the given 'Setup', freeing all of its allocated resources. -runSetup_ :: Setup a -> IO () -runSetup_ s = (runErrorT . R.runResourceT) s >> return () - - --- | Throw an error from the 'Setup' monad. -setupError :: String -> Setup a -setupError = MT.lift . throwError - - --- | Lift the given IO action into the 'Setup' monad. -setupIO :: IO a -> Setup a -setupIO = MT.lift . MT.lift - - --- | Throw the given error string if given 'Nothing'. -assertMaybe :: Maybe a -> String -> Setup a -assertMaybe Nothing err = setupError err -assertMaybe (Just x) _ = return x -- cgit v1.2.3