-- 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:
--
--
-- - '()': that will mute the color buffer of the framebuffer;
-- - Format t c: that will create a single texture with
-- the wished color format;
-- - or a :. b: that will create a chain of textures;
-- a and b cannot be '()'.
--
--
-- For the depth part, you can pass either:
--
--
-- - '()': that will mute the depth buffer of the framebuffer;
-- - Format t c: that will create a single texture with
-- the wished depth format.
--
--
-- 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:
--
--
-- - blending: the blending mode is represented by
-- Maybe
-- (BlendingMode,BlendingFactor,BlendingFactor).
-- If you pass Nothing, blending is disabled for that draw
-- command. If you want to enable it, you have to pass Just
-- (mode,srcK,dstK), where mode is the BlendingMode
-- and srcK and dstK are both BlendingFactor
-- representing the source and destination factors.
-- - depth test: the depth test can be enabled by passing
-- True and disabled with False.
-- - a per draw command uniform interface and uniform value:
-- that is the way you can customize the draw calls. You just have to
-- pass the uniform interface and the value to send down to the
-- shader.
--
--
-- 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.
--
--
-- - no blending
-- - depth test enabled
--
stdRenderCmd :: U u -> u -> a -> RenderCmd rw c d u a
-- | A standard RenderCmd builder with no uniform interface.
--
--
-- - no blending
-- - depth test enabled
--
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:
--
--
-- - direct geometry: doesn’t require any indices as all
-- vertices are unique and in the right order to connect vertices between
-- each other ;
-- - indexed geometry: requires indices to know how to connect
-- and share vertices between each other.
--
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:
--
--
-- - framebuffer: that means you can create a FBBatch
-- that will gather several values under the same
-- Framebuffer;
-- - or shaders: that means you can create a SPBatch that
-- will gather several values under the same shader Program.
--
--
-- 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:
--
--
-- - tessellation evaluation shader
-- - tessellation control shader
-- - vertex shader
-- - geometry shader
-- - fragment shader
--
--
-- 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.
--
--
--
-- 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!
--
--
--
-- 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