-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Type-safe, type-level and stateless graphics framework -- -- This package exposes several modules to work with GPUs in a -- stateless and type-safe way. Currently, it uses OpenGL as backend -- hardware technology but others will be added later on, such as Vulkan. -- -- One very important point is the fact that luminance has nothing to do -- with 3D engines or scene development kits. Don’t expect -- to find materials, lights or mesh loaders. It’s -- just a graphics framework initiated to fix the design choices of -- OpenGL. It won’t change in any other way. However, as it grows, the -- plan is to make luminance a good graphics framework following the -- Haskell philosophy. In the end, it should be used as-is, not as an -- OpenGL abstraction. -- -- luminance is a small yet powerful graphics API. It was designed so -- that people can quickly get their feet wet and start having fun with -- graphics in Haskell. The main idea is to unleash the graphical -- and visual properties of GPUs in a stateless and type-safe way. -- -- This library doesn’t expose any architectural patterns or designs. -- It’s up to you to design your program as you want and following your -- own plans. -- -- In the end, as many people has asked me since the beginning of the -- development, the OpenGL version – current is 4.5 – will be lowered. -- You’ll be able to choose the backend to use through compilation flags. @package luminance @version 0.8 module Graphics.Luminance.Shader.Stage -- | A shader Stage. data Stage -- | A shader Stage type. data StageType TessControlShader :: StageType TessEvaluationShader :: StageType VertexShader :: StageType GeometryShader :: StageType FragmentShader :: StageType stageID :: Stage -> GLuint -- | Create a shader stage from a String representation of its -- source code and its type. -- -- Note: on some hardware and backends, tessellation shaders -- aren’t available. That function throws UnsupportedStage error -- in such cases. createStage :: (HasStageError e, MonadError e m, MonadIO m, MonadResource m) => StageType -> String -> m Stage -- | Error type of shaders. -- -- 'CompilationFailed reason' occurs when a shader fails to compile, and -- the String reason contains a description of the -- failure. -- -- 'UnsupportedStage stage' occurs when you try to create a shader which -- type is not supported on the current hardware. data StageError CompilationFailed :: String -> StageError UnsupportedStage :: StageType -> StageError -- | Types that can handle StageError. class HasStageError a fromStageError :: HasStageError a => StageError -> a module Graphics.Luminance.RW -- | Readable typeclass, for types that admit reads. class Readable r -- | Read-only type. data R -- | Writable typeclass, for types that admit writes. class Writable w -- | Write-only type. data W -- | Read-write type. data RW module Graphics.Luminance.Query -- | Get the OpenGL vendor String. getGLVendor :: (MonadIO m) => m String -- | Get the OpenGL renderer String. getGLRenderer :: (MonadIO m) => m String -- | Get the OpenGL version String. getGLVersion :: (MonadIO m) => m String -- | Get the GLSL version String. getGLSLVersion :: (MonadIO m) => m String -- | Retrieve all the supported OpenGL extensions as Strings. getGLExtensions :: (MonadIO m) => m [String] module Graphics.Luminance.Pixel class ChannelSize c -- | A 8-bit channel. data C8 C8 :: C8 -- | A 16-bit channel. data C16 C16 :: C16 -- | A 32-bit channel. data C32 C32 :: C32 class ChannelType t -- | Channels are integral values. data CInts CInts :: CInts -- | Channels are unsigned integral values. data CUInts CUInts :: CUInts -- | Channels are floating values. data CFloats CFloats :: CFloats -- | A red channel only. data CR a CR :: CR a -- | Rd and green channels. data CRG a b CRG :: CRG a b -- | Red, green and blue channels. data CRGB a b c CRGB :: CRGB a b c -- | Red, green, blue and alpha channels. data CRGBA a b c d CRGBA :: CRGBA a b c d -- | A depth channel. data CDepth a CDepth :: CDepth a -- | A pixel format. data Format t c Format :: Format t c class Pixel f type RGB8UI = Format CUInts (CRGB C8 C8 C8) type RGBA8UI = Format CUInts (CRGBA C8 C8 C8 C8) type RGBA8F = Format CFloats (CRGBA C8 C8 C8 C8) type RGB32F = Format CFloats (CRGB C32 C32 C32) type RGBA32F = Format CFloats (CRGBA C32 C32 C32 C32) type Depth32F = Format CFloats (CDepth C32) class (Pixel p) => ColorPixel p module Graphics.Luminance.Texture -- | Class of all textures. class Texture t where type family TextureSize t :: * type family TextureOffset t :: * -- | 'createTexture w h levels sampling' a new 'w'*'h' texture with -- levels levels. The format is set through the type. createTexture :: (MonadIO m, MonadResource m, Texture t) => TextureSize t -> Natural -> Sampling -> m t -- | A sampling configuration type. data Sampling Sampling :: Wrap -> Wrap -> Wrap -> Filter -> Filter -> Maybe CompareFunc -> Sampling [samplingWrapS] :: Sampling -> Wrap [samplingWrapT] :: Sampling -> Wrap [samplingWrapR] :: Sampling -> Wrap [samplingMinFilter] :: Sampling -> Filter [samplingMagFilter] :: Sampling -> Filter [samplingCompareFunction] :: Sampling -> Maybe CompareFunc -- | Default Sampling for convenience. -- --
--   defaultSampling = Sampling {
--       samplingWrapS           = ClampToEdge
--     , samplingWrapT           = ClampToEdge
--     , samplingWrapR           = ClampToEdge
--     , samplingMinFilter       = Linear
--     , samplingMagFilter       = Linear
--     , samplingCompareFunction = Nothing
--     }
--   
defaultSampling :: Sampling -- | Sampling filter. Nearest will sample the nearest texel at the -- sampling coordinates whilst Linear will perform linear -- interpolation with the texels nearby. data Filter Nearest :: Filter Linear :: Filter -- | Wrap texture parameter. Such an object is used to tell how to sampling -- is performed when going out of the texture coordinates. -- -- ClampToEdge will clamp the texture coordinates between in -- '[0,1]'. If you pass '1.1' or '31.456', in both cases you’ll end up -- with '1'. Same thing for negative values clamped to '0'. -- -- Repeat will clamp the texture in '[0,1]' after applying a -- fract on the value, yielding a a repeated '[0,1]' pattern. data Wrap ClampToEdge :: Wrap -- | ClampToBorder Repeat :: Wrap MirroredRepeat :: Wrap -- | For textures that might require depth comparison, that type defines -- all the possible cases for comparison. data CompareFunc Never :: CompareFunc Less :: CompareFunc Equal :: CompareFunc LessOrEqual :: CompareFunc Greater :: CompareFunc GreaterOrEqual :: CompareFunc NotEqual :: CompareFunc Always :: CompareFunc -- | uploadSub tex offset size autolvl texels uploads data -- to a subpart of the texture’s storage. The offset is given with origin -- at upper-left corner, and size is the size of the area to -- upload to. autolvl is a Bool that can be used to -- automatically generate mipmaps. uploadSub :: (MonadIO m, Storable a, Texture t) => t -> TextureOffset t -> TextureSize t -> Bool -> Vector a -> m () -- | Fill a subpart of the texture’s storage with a given value. fillSub :: (MonadIO m, Storable a, Texture t) => t -> TextureOffset t -> TextureSize t -> Bool -> Vector a -> m () -- | A 1D texture. data Texture1D f texture1DW :: Texture1D f -> Natural -- | A 1D texture array. data Texture1DArray (n :: Nat) (f :: *) texture1DArrayW :: Texture1DArray n f -> Natural -- | A 2D texture. data Texture2D f texture2DW :: Texture2D f -> Natural texture2DH :: Texture2D f -> Natural -- | A 2D texture array. data Texture2DArray (n :: Nat) (f :: *) texture2DArrayW :: Texture2DArray n f -> Natural -- | A 3D texture. data Texture3D f texture3DW :: Texture3D f -> Natural texture3DH :: Texture3D f -> Natural texture3DD :: Texture3D f -> Natural -- | A cubemap. data Cubemap f -- | Face of a Cubemap. data CubeFace PositiveX :: CubeFace NegativeX :: CubeFace PositiveY :: CubeFace NegativeY :: CubeFace PositiveZ :: CubeFace NegativeZ :: CubeFace cubemapSize :: Cubemap f -> Natural -- | A cubemap array. data CubemapArray (n :: Nat) (f :: *) cubemapArraySize :: CubemapArray n f -> Natural module Graphics.Luminance.Core.Tuple -- | A tuple of types, right-associated. -- -- The Storable instance is used for foreign packing on 32-bit. data (:.) a b (:.) :: a -> b -> (:.) a b instance GHC.Generics.Constructor Graphics.Luminance.Core.Tuple.C1_0:. instance GHC.Generics.Datatype Graphics.Luminance.Core.Tuple.D1:. instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (a Graphics.Luminance.Core.Tuple.:. b) instance (GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (a Graphics.Luminance.Core.Tuple.:. b) instance GHC.Generics.Generic (a Graphics.Luminance.Core.Tuple.:. b) instance GHC.Base.Functor ((Graphics.Luminance.Core.Tuple.:.) a) instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (a Graphics.Luminance.Core.Tuple.:. b) instance (Foreign.Storable.Storable a, Foreign.Storable.Storable b) => Foreign.Storable.Storable (a Graphics.Luminance.Core.Tuple.:. b) module Graphics.Luminance.Framebuffer -- | A Framebuffer represents two buffers: a color buffer and -- depth buffer. You can select which one you want and specify the -- formats to use by providing Pixel types. If you want to mute a -- buffer, use '()'. data Framebuffer rw c d -- | A Framebuffer with the depth buffer muted. type ColorFramebuffer rw c = Framebuffer rw c () -- | A Framebuffer with the color buffer muted. Can be used -- to implement fast pre-passes. type DepthFramebuffer rw d = Framebuffer rw () d framebufferID :: Framebuffer rw c d -> GLuint framebufferOutput :: Framebuffer rw c d -> Output c d -- | createFramebuffer w h mipmaps creates a new -- Framebuffer with dimension w * h and allocating spaces -- for mipmaps level of textures. The textures are created by -- providing a correct type. -- -- For the color part, you can pass either: -- -- -- -- For the depth part, you can pass either: -- -- -- -- Finally, the rw parameter can be set to R, W or -- RW to specify which kind of framebuffer access you’ll need. createFramebuffer :: (HasFramebufferError e, MonadError e m, MonadIO m, MonadResource m, FramebufferColorAttachment c, FramebufferColorRW rw, FramebufferDepthAttachment d, FramebufferTarget rw) => Natural -> Natural -> Natural -> m (Framebuffer rw c d) -- | Typeclass of possible framebuffer color attachments. class FramebufferColorAttachment c -- | Typeclass of possible framebuffer depth attachments. class FramebufferDepthAttachment d -- | Typeclass used to implement read/write operation per color attachment. class FramebufferColorRW rw -- | Framebuffer representation of R, W and RW. class FramebufferTarget rw -- | Given a Format, inject it into a Texture2D to handle it. -- | Framebuffer output. data Output c d Output :: (TexturizeFormat c) -> (TexturizeFormat d) -> Output c d -- | Mask for framebuffer blit operation. data FramebufferBlitMask BlitColor :: FramebufferBlitMask BlitDepth :: FramebufferBlitMask BlitBoth :: FramebufferBlitMask -- | The default Framebuffer represents the screen (back buffer with -- double buffering). defaultFramebuffer :: Framebuffer RW () () newtype FramebufferError IncompleteFramebuffer :: String -> FramebufferError class HasFramebufferError a fromFramebufferError :: HasFramebufferError a => FramebufferError -> a module Graphics.Luminance.RenderCmd -- | A GPU render command. That type exists to implement a stateless -- way to issue draw commands to the GPU. You can set several -- hints for a given draw command: -- -- -- -- Finally, a RenderCmd holds a value. That value will be consumed -- later by other functions. In general, it’ll be Geometry. data RenderCmd rw c d u a -- | renderCmd blending depthTest uniformInterface u a -- constructs a new RenderCmd. renderCmd :: Maybe (BlendingMode, BlendingFactor, BlendingFactor) -> Bool -> U u -> u -> a -> RenderCmd rw c d u a -- | A standard RenderCmd builder. -- -- stdRenderCmd :: U u -> u -> a -> RenderCmd rw c d u a -- | A standard RenderCmd builder with no uniform interface. -- -- stdRenderCmd_ :: a -> RenderCmd rw c d () a module Graphics.Luminance.Shader.Program -- | Shader program. data Program programID :: Program -> GLuint -- | Create a new shader Program. -- -- That function takes a list of Stages and a uniform interface -- builder function and yields a Program and the interface. -- -- The builder function takes a function you can use to retrieve -- uniforms. You can pass values of type UniformName to identify -- the uniform you want to retrieve. If the uniform can’t be retrieved, -- throws InactiveUniform. -- -- In the end, you get the new Program and a polymorphic value you -- can choose the type of in the function you pass as argument. You can -- use that value to gather uniforms for instance. createProgram :: (HasProgramError e, MonadError e m, MonadIO m, MonadResource m) => [Stage] -> ((forall a. UniformName a -> UniformInterface m (U a)) -> UniformInterface m i) -> m (Program, i) -- | A simpler version of createProgram. That function assumes you -- don’t need a uniform interface and then just returns the -- Program. createProgram_ :: (HasProgramError e, MonadError e m, MonadIO m, MonadResource m) => [Stage] -> m Program -- | Class of types that can be sent down to shaders. That class is closed -- because shaders cannot handle a lot of uniform types. However, you -- should have a look at the U documentation for further -- information about how to augment the scope of the types you can send -- down to shaders. class Uniform a -- | A shader uniform. U a doesn’t hold any value. It’s -- more like a mapping between the host code and the shader the uniform -- was retrieved from. -- -- U is contravariant in its argument. That means that you can use -- contramap to build more interesting uniform types. It’s also a -- divisible contravariant functor, then you can divide structures to -- take advantage of divisible contravariant properties and then glue -- several U with different types. That can be useful to build a -- uniform type by gluing its fields. -- -- Another interesting part is the fact that U is also monoidal. -- You can accumulate several of them with '(<>)' if they have the -- same type. That means that you can join them so that when you pass an -- actual value, it gets shared inside the resulting value. -- -- The '()' instance doesn’t do anything and doesn’t even use its -- argument ('()'). data U a data UniformInterface m a class UniformBlock a where isStruct _ = True alignmentSTD140 _ = galignmentSTD140 (Proxy :: Proxy (Rep a)) sizeOfSTD140 _ = gsizeOfSTD140 (Proxy :: Proxy (Rep a)) peekSTD140 p o = liftIO $ fmap to (gpeekSTD140 p o) pokeSTD140 p o a = liftIO $ gpokeSTD140 p o (from a) newtype UB a UB :: a -> UB a [unUB] :: UB a -> a -- | Shader program error. -- -- 'LinkFailed reason' happens when a program fails to link. -- reason contains the error message. -- -- 'InactiveUniform uni' happens at linking when a uniform is inactive in -- the program; that is, unused or semantically set to a negative value. data ProgramError LinkFailed :: String -> ProgramError InactiveUniform :: SomeUniformName -> ProgramError -- | Types that can handle ProgramError – read as, “have”. class HasProgramError a fromProgramError :: HasProgramError a => ProgramError -> a module Graphics.Luminance.Shader module Graphics.Luminance.Vertex data V (n :: k) a :: k -> * -> * -- | Create a new V 2. vec2 :: a -> a -> V 2 a -- | Create a new V 3. vec3 :: a -> a -> a -> V 3 a -- | Create a new V 4. vec4 :: a -> a -> a -> a -> V 4 a -- | A vertex might have several attributes. The types of those attributes -- have to implement the VertexAttribute typeclass in order to be -- used as vertex attributes. class VertexAttribute a -- | A vertex has to implement Vertex in order to be used as-is. -- That typeclass is closed, so you cannot add anymore instances. -- However, you shouldn’t need to since you can use the already provided -- types to build up your vertex type. class Vertex v module Graphics.Luminance.Geometry -- | A Geometry represents a GPU version of a mesh; that is, -- vertices attached with indices and a geometry mode. You can have -- Geometry in two flavours: -- -- data Geometry -- | The GeometryMode is used to specify how vertices should be -- connected between each other. -- -- A Point mode won’t connect vertices at all and will leave them -- as a vertices cloud. -- -- A Line mode will connect vertices two-by-two. You then have to -- provide pairs of indices to correctly connect vertices and form lines. -- -- A Triangle mode will connect vertices three-by-three. You then -- have to provide triplets of indices to correctly connect vertices and -- form triangles. data GeometryMode Point :: GeometryMode Line :: GeometryMode Triangle :: GeometryMode -- | This function is the single one to create Geometry. It takes a -- Foldable type of vertices used to provide the Geometry -- with vertices and might take a Foldable of indices -- (Word32). If you don’t pass indices (Nothing), you end -- up with a direct geometry. Otherwise, you get an indexed -- geometry. You also have to provide a GeometryMode to state -- how you want the vertices to be connected with each other. createGeometry :: (Foldable f, MonadIO m, MonadResource m, Storable v, Vertex v) => f v -> Maybe (f Word32) -> GeometryMode -> m Geometry -- | O (n log n) -- -- Turn direct geometry data into indirect data. This -- function removes duplicate vertices from the data you pass in and -- registers indices in consequence. nubDirect :: (Foldable f, Ord a, Integral i) => f a -> ([a], [i]) module Graphics.Luminance.Cmd -- | Command type. Used to accumulate GPU commands. Use runCmd to -- execute the whole chain of commands. data Cmd a runCmd :: (MonadIO m) => Cmd a -> m a -- | Draw a framebuffer batch and return the framebuffer’s output. draw :: FBBatch rw c d -> Cmd (Output c d) -- | Blit a framebuffer batch onto a framebuffer and return the -- framebuffer’s output of the write framebuffer. blit :: (Readable r, Writable w) => Framebuffer r c0 d0 -> Framebuffer w c1 d1 -> Int -> Int -> Natural -> Natural -> Int -> Int -> Natural -> Natural -> FramebufferBlitMask -> Filter -> Cmd (Output c1 d1) module Graphics.Luminance.Buffer -- | A Buffer is an opaque and untyped region of abstract GPU -- memory. You cannot do much with it and you might even not see the type -- in the user interface as it’s not really needed. It’s shown for -- informational purposes only. data Buffer bufferID :: Buffer -> GLuint -- | Create a new Buffer and expose Regions. Through the -- BuildRegion type, you can yield new regions and embed them in -- the type of your choice. The function returns that type. createBuffer :: (BufferRW rw, MonadIO m, MonadResource m) => BuildRegion rw a -> m a createBuffer_ :: (BufferRW rw, MonadIO m, MonadResource m) => BuildRegion rw a -> m (a, Buffer) -- | Buffer’s Regions can have reads and writes. That -- typeclass makes implements all possible cases. class BufferRW rw -- | A Region is a GPU typed memory area. It can be pictured as a -- GPU array. data Region rw a -- | Convenient type to build Regions. data BuildRegion rw a -- | Create a new Region by providing the number of wished elements. newRegion :: (Storable a) => Word32 -> BuildRegion rw (Region rw a) -- | Read a whole Region. readWhole :: (MonadIO m, Readable r, Storable a) => Region r a -> m [a] -- | Write the whole Region. If value are missing, only the provided -- values will replace the existing ones. If there are more values than -- the size of the Region, they are ignored. writeWhole :: (Foldable f, MonadIO m, Storable a, Writable w) => Region w a -> f a -> m () -- | Fill a Region with a value. fill :: (MonadIO m, Storable a, Writable w) => Region w a -> a -> m () -- | Index getter. Bounds checking is performed and returns Nothing -- if out of bounds. (@?) :: (MonadIO m, Storable a, Readable r) => Region r a -> Word32 -> m (Maybe a) -- | Index getter. Unsafe version of '(@?)'. (@!) :: (MonadIO m, Storable a, Readable r) => Region r a -> Word32 -> m a -- | Index setter. Bounds checking is performed and nothing is done if out -- of bounds. writeAt :: (MonadIO m, Storable a, Writable w) => Region w a -> Word32 -> a -> m () -- | Index setter. Unsafe version of writeAt'. writeAt' :: (MonadIO m, Storable a, Writable w) => Region w a -> Word32 -> a -> m () -- | That module exports blending-related types and functions. -- -- Given two pixels src and dst – source and -- destination, we associate each pixel a blending factor – -- respectively, srcK and dstK. src is the -- pixel being computed, and dst is the pixel that is already -- stored in the framebuffer. -- -- The pixels can be blended in several ways. See the documentation of -- BlendingMode for further details. -- -- The factors are encoded with BlendingFactor. module Graphics.Luminance.Blending -- | All different blending modes. -- -- Additive represents the following blending equation: -- --
--   blended = src * srcK + dst * dstK
--   
-- -- Subtract represents the following blending equation: -- --
--   blended = src * srcK - dst * dstK
--   
-- -- Because subtracting is not commutating, ReverseSubtract -- represents the following additional blending equation: -- --
--   blended = dst * dstK - src * srcK
--   
-- -- Min represents the following blending equation: -- --
--   blended = min src dst
--   
-- -- Max represents the following blending equation: -- --
--   blended = max src dst
--   
data BlendingMode Additive :: BlendingMode Subtract :: BlendingMode ReverseSubtract :: BlendingMode Min :: BlendingMode Max :: BlendingMode -- | Blending factors. data BlendingFactor -- | 1 * color = factor One :: BlendingFactor -- | 0 * color = 0 Zero :: BlendingFactor -- | src * color SrcColor :: BlendingFactor -- | (1 - src) * color NegativeSrcColor :: BlendingFactor -- | dst * color DestColor :: BlendingFactor -- | (1 - dst) * color NegativeDestColor :: BlendingFactor -- | srcA * color SrcAlpha :: BlendingFactor -- | (1 - src) * color NegativeSrcAlpha :: BlendingFactor -- | dstA * color DstAlpha :: BlendingFactor -- | (1 - dstA) * color NegativeDstAlpha :: BlendingFactor SrcAlphaSaturate :: BlendingFactor module Graphics.Luminance.Batch -- | Framebuffer batch. -- -- A FBBatch is used to expose a Framebuffer and share it -- between several shader program batches. data FBBatch rw c d -- | Share a Framebuffer between several shader program batches. framebufferBatch :: Framebuffer rw c d -> [AnySPBatch rw c d] -> FBBatch rw c d -- | Shader Program batch. -- -- Such a batch is used to share a Program between several -- RenderCmd. It also gathers a uniform U u and a -- u value to send to the uniform. -- -- The u type can be used to send uniforms for the whole batch. -- It can be useful for cold values – that won’t change very often for a -- given frame – like the resolution of the screen, the mouse cursor -- coordinates, the time, and so on and so forth. -- -- The v type variable is used to add uniforms -- per-RenderCmd. data SPBatch rw c d u v -- | Abstract SPBatch over uniform interface. data AnySPBatch rw c d -- | Abstract SPBatch. anySPBatch :: SPBatch rw c d u v -> AnySPBatch rw c d -- | Create a new SPBatch. shaderProgramBatch :: Program -> U u -> u -> [RenderCmd rw c d v Geometry] -> SPBatch rw c d u v -- | Create a new SPBatch with no uniform interface. shaderProgramBatch_ :: Program -> [RenderCmd rw c d v Geometry] -> SPBatch rw c d () v -- |

What is luminance?

-- -- luminance is a small yet powerful graphics API. It was designed -- so that people can quickly get their feet wet and start having fun -- with graphics in Haskell. The main idea is to unleash the -- graphical and visual properties of GPUs in a stateless and -- type-safe way. -- -- This library doesn’t expose any architectural patterns or designs. -- It’s up to you to design your program as you want and following your -- own plans. Because it’s a graphics and rendering API, you won’t find -- several common things you find in animations, games or simulations. If -- you need those you’ll have to look for dedicated libraries instead. -- -- One of the most important thing you have to keep in mind is the fact -- that luminance won’t provide you with anything else than working with -- the GPU. That is, it won’t even provide functions to open -- windows. That’s actually a good thing, because then you’ll be able to -- use it with any kind of windowing and system library you want -- to! -- -- The drawback is about safety. If you screw up setting up the OpenGL -- context, there’s no way luminance will work. If users feel the need, a -- few dedicated packages will be uploaded, like luminance-glfw to -- add GLFW-b support for instance. -- --

Getting started

-- --

Setting up the window and OpenGL context

-- -- The first thing to do is to create a window. Here’s a typical -- GLFW-b snippet to create such a window. -- --
--   initialized <- init
--   when initialized $ do
--   
-- -- In the first place, we initialize the GLFW-b library and make -- sure everything ran smoothly. -- --
--   windowHint (WindowHint'Resizable False)
--   windowHint (WindowHint'ContextVersionMajor 4)
--   windowHint (WindowHint'ContextVersionMinor 5)
--   windowHint (WindowHint'OpenGLForwardCompat False)
--   windowHint (WindowHint'OpenGLProfile OpenGLProfile'Core)
--   window <- createWindow 800 600 "luminance application" Nothing Nothing
--   
-- -- That part just setup the window’s OpenGL hints so that we create a -- compatible context for luminance. luminance will work with OpenGL -- 4.5 only, don’t even try to make it work with a lower -- implementation. We also disable the forward compatibility -- because we don’t need it and ask to stick to a core profile. -- --
--   case window of
--     Just window' -> do
--       makeContextCurrent window
--       swapInterval 1
--       -- we’re good to go!
--       destroyWindow window'
--     Nothing -> hPutStrLn stderr "unable to create window; please check your hardware support OpenGL4.5"
--   
-- -- We then test window. If we have successfully opened the -- window, we go on by making the OpenGL context of the window the -- current one for the current thread, set the swap interval (that’s not -- important for the purpose of that tutorial) and we’re good to go. -- Otherwise, we just display an error message and quit. -- --
--   terminate
--   
-- -- We finally close the GLFW-b context to cleanup everything. -- --

Preparing the environment for luminance

-- -- A lot of the functions you’ll use work in special types. For instance, -- a lot of create* functions will require (MonadIO -- m,MonadResource m,MonadError e m) => m or -- so. For that reason, we’ll be using a type of our own and will unwrap -- it so that we end up in IO in the end. -- -- Here’s the type: -- --
--   type App = ExceptT AppError (ResourceT IO)
--   
--   newtype AppError = AppError String deriving (Eq,Show)
--   
-- -- And we’ll unwrap from our type to IO with: -- --
--   runResourceT . runExcepT
--   
-- --

Getting something to the screen

-- --

About the screen

-- -- luminance generalizes OpenGL concepts so that they’re made safer. In -- order to render something onto the screen, you have to understand what -- the screen truly is. It’s actually… a back buffer – assuming we have -- double buffering enabled, which the case with GLFW-b by -- default. So rendering to the screen is the same thing than rendering -- to the back buffer and ask GLFW-b to swap the back buffer with -- the front buffer. -- -- And guess what. luminance wraps the back buffer into a -- Framebuffer object. You can access it through -- defaultFramebuffer. That value will always represent the back -- buffer. -- --

About batched rendering

-- -- In most graphics frameworks, rendering is the act of taking an object -- and getting it rendered. luminance follows a different path. Because -- of real world needs and, well, real applications, you cannot do that -- in luminance. Because, what serious application will render only -- one object? None. If so, then it’s an exception. We shouldn’t -- design our libraries and interface for the exceptions. We should build -- them for the most used case, which is, having a lot of objects in a -- scene. -- -- That’s why luminance exposes the concept of batched rendering. -- The idea is that you have to gather you objects in batches and -- render them all at once. That enables a correct sharing of resources – -- for instance, framebuffers or textures – and is very straight-forward -- to reason about. -- -- luminance has several types of batches, each for the type of shared -- information. You can – up to now – shared two information between the -- rendered objects: -- -- -- -- The idea is that the SPBatches are stored in FBBatches. -- That creates a structure similar to an AST luminance knows how to -- dispatch to the GPU. -- --

About shader stages

-- -- luminance supports five kinds of shader stage: -- -- -- -- Additionnaly, you can create compute shaders but they’re not -- usable up to now. -- -- When creating a new shader, you have to pass a String -- representing the source code. This will change in the end. An EDSL is -- planned to make things easier and safer, but in the waiting, you are -- stuck with String, I’m sorry. -- -- You have to write GLSL450-conformant code. -- --

About uniforms

-- -- Shaders are customized through uniforms. Those are very handy and very -- simple to use in luminance. You have the possibility to get them when -- creating shader Programs. The createProgram function -- expects two parameters: a list of shader Stages and a uniform -- interface builder function. That function takes another function as -- parameter you can use to retrieve a uniform U by passing -- Either a String for the name of the uniform or a -- Natural for its explicit semantic. Be careful when using -- explicit semantics though; they’re not tested. -- -- Here’s an exemple of such a use: -- --
--   (program,uniformInterface) <- createProgram shaderStages $ \uni -> do
--     resolutionU <- uni $ Left "resolution"
--     timeU <- uni $ Left "time"
--     pure $ divided resolutionU timeU
--   
-- -- In that example, uniformInterface has type U -- ((Float,Float),Float), (Float,Float being the type of -- the resolutionU part and Float being the part for -- timeU. divided is a method of Divisible – -- the typeclass of divisible contravariant functors – which is defined -- in the "contravariant" package. -- -- If you don’t need uniform interface, you can build a dummy object, -- like (), or simply use the appropriate createProgram_ -- function. -- --

About RenderCmd and Geometry

-- -- RenderCmd is a very simple type yet powerful one. It’s a way to -- add stateless support to OpenGL render commands – draw commands, -- actually. It gathers several information you can set when performing a -- draw command. A RenderCmd can hold any type of object, but the -- most useful version of it holds Geometry. -- -- A Geometry is a GPU version of a mesh. It’s composed of -- vertices, indices and a primitive mode used to know how -- to link vertices between each others. Sometimes, Geometry -- doesn’t have indices. That’s called direct geometry, -- because the vertices are supposed to be directly used when -- creating primitives. If you use indices, then you have an -- indexed geometry and the vertices can linked by looking -- at the indices you’ve fed in. -- -- A Geometry is created with the createGeometry function -- and a RenderCmd is created with renderCmd. You’re -- supposed to create a Geometry once – while loading your -- resources for example – and the RenderCmd can be created on the -- fly – it doesn’t require IO. -- --

Putting all together

-- -- Let’s draw a triangle on the screen! First, we need the vertices! -- --
--   vertices :: [V 2 Float]
--   vertices =
--     [
--       vec2 (-0.5) (-0.5)
--     , vec2 0 0.5
--     , vec2 0.5 (-0.5)
--     ]
--   
-- -- V 2 is a cool type used to represent vertex -- attributes. You’ll need DataKinds to be able to use it. -- -- Then, we don’t need indices because we can directly issue a -- draw. Let’s then have the GPU version of those vertices: -- --
--   triangle <- createGeometry vertices Nothing Triangle
--   
-- -- Then, we need a shader! Let’s write the vertex shader first: -- --
--   in vec2 co;
--   out vec4 vertexColor;
--    
--   vec4 color[3] = vec4[](
--       vec4(1., 0., 0., 1.)
--     , vec4(0., 1., 0., 1.)
--     , vec4(0., 0., 1., 1.)
--     );
--    
--   void main() {
--     gl_Position = vec4(co, 0., 1.);
--     vertexColor = color[gl_VertexID];
--   }
--   
-- -- Nothing fancy, except that we pass vertexColor to the next -- stage so that we can blend between vertices. -- -- Now, a fragment shader: -- --
--   in vec4 vertexColor;
--   out vec4 frag;
--    
--   void main() {
--     frag = vertexColor;
--   }
--   
-- -- Now, let’s create the shader Stages and the shader -- Program: -- --
--   program <- sequenceA [createVertexShader vsSrc,createFragmentShader fsSrc] >>= createProgram_
--   
-- -- Once again, that’s pretty straight-forward. -- -- Finally, we need the batches. We’ll need one FBBatch and one -- SPBatch. -- --
--   let spb = shaderProgramBatch_ program [stdRenderCmd_ triangle]
--       fbb = framebufferBatch defaultFramebuffer [anySPBatch spb]
--   
-- -- Ok, so let’s explain all of this. shaderProgramBatch_ is a -- shorter version of shaderProgramBatch you can use to build -- SPBatch. The extra underscore means you don’t want no uniform -- interface. We pass our program and a singleton list -- containing a RenderCmd we create with the stdRenderCmd_. -- Once again, the extra underscore stands for no uniform interface. We -- then just pass our triangle. Notice that both -- stdRenderCmd and stdRenderCmd_ disable color blending -- and enable depth test so that you don’t have to pass those information -- around. -- -- Then, we create the FBBatch. That is done via the -- framebufferBatch function. It takes the Framebuffer to -- render into – in our case, the defaultFramebuffer, which is the -- back buffer. We also pass a singleton list of the universally -- quantified SPBatch with the anySPBatch function. -- -- We just need to issue a command to the GPU to render our -- triangle. That is done with a constrained type, Cmd. -- --
--   void . runCmd $ draw fbb
--   
-- -- We don’t need the result of runCmd in our case so we discard it -- with void. runCmd runs in MonadIO. -- -- We just need to swap the buffers with swapBuffers window – -- see GLFW-b for further details – and we’re good! -- --

Dealing with Texture2D

-- -- Up to now, luminance only supports 2D-textures. More texture types -- will be added as luminance gets mature. The interface might change a -- lot, because it might be very inefficient, especially when converting -- from containers to others. module Graphics.Luminance