@menu * Kernel:: The fundamental components. * Input:: Keyboard, mouse, and controller input. * Math:: Linear algebra and more. * Graphics:: Eye candy. * Audio:: Sound effects and music. @end menu @node Kernel @section Kernel At the very core of Chickadee, in the @code{(chickadee)} module, lies an event loop. This loop, or ``kernel'', is responsible for creating and managing the game window, dispatching input events, ensuring that the game is updated at the desired interval, and rendering graphics. The kernel implements what is known as a ``fixed timestep'' game loop, meaning that the game simulation will be advanced by a fixed interval of time and will never vary from frame to frame, unlike some other styles of game loops. The appropriately named @code{run-game} and @code{abort-game} procedures are the entry and exit points to the Chickadee kernel. On its own, the kernel does not do very much at all. In order to actually respond to input events, update game state, or draw something to the game window, a hacker with a penchant for game development must latch onto extension points built into the kernel, called ``hooks'', and specify what action ought to be taken for any given event. For example, the @code{key-press-hook} can be used to respond to the @code{a} key being pressed by swinging the player's mighty sword. There are many hooks available, so read on to learn about all of them. For information about using Guile's hook API, see @xref{Hooks,,, guile, GNU Guile Reference Manual}. @deffn {Scheme Procedure} run-game [#:window-title "Chickadee!"] @ [#:window-width 640] [#:window-height 480] [#:window-fullscreen? #f] @ [#:update-hz 60] Start the event loop. This procedure will not return until @code{abort-game} is called. The @code{update-hook} will be run @var{update-hz} times per second. A new graphical window will be opened with @var{window-width} x @var{window-height} as its dimensions, @var{window-title} as its title, and in fullscreen mode if @var{window-fullscreen?} is @code{#t}. @end deffn @deffn {Scheme Procedure} abort-game Stop the currently running Chickadee event loop. @end deffn @deffn {Scheme Procedure} time Return the current game time in milliseconds. @end deffn @defvr {Scheme Variable} load-hook A hook that is run once when the event loop boots, before any other hook is run. This hook is run with zero arguments. @example (add-hook! load-hook (lambda () (display "hello!\n"))) @end example @end defvr @defvr {Scheme Variable} update-hook A hook that is run every time the game simulation should be advanced. This hook is run with a single argument @var{dt}, the fixed timestep that was configured when the event loop was started, in milliseconds. @example (add-hook! update-hook (lambda (dt) (display "tick!\n"))) @end example @end defvr @defvr {Scheme Variable} before-draw-hook A hook that is run before a frame is rendered. This hook is run with zero arguments. @example (add-hook! before-draw-hook (lambda () (display "about to draw!\n"))) @end example @end defvr @defvr {Scheme Variable} after-draw-hook A hook that is run after a frame is rendered. This hook is run with zero arguments. @example (add-hook! after-draw-hook (lambda () (display "done drawing!\n"))) @end example Combined with @code{before-draw-hook}, one can perform a frames per second calculation to monitor game performance and stability. @end defvr @defvr {Scheme Variable} draw-hook A hook that is run each time a frame should be rendered. This hook is run with a single argument @var{alpha}, a value in the range [0, 1] which represents how much time has past since the last game state update relative to the upcoming game state update, as a percentage. Because the game state is updated independent of rendering, it is often the case that rendering is occuring between two updates. If the game is rendered as it was during the last update, a strange side-effect will occur that makes animation appear rough or ``choppy''. To counter this, the @var{alpha} value can be used to perfrom a linear interpolation of a moving object between its current position and its previous position. This odd trick has the pleasing result of making the animation look smooth again, but requires keeping track of previous state. @c TODO: Add example of linear interpolation @example (add-hook! draw-hook (lambda (alpha) (display "<(._.<) \n"))) @end example @end defvr @defvr {Scheme Variable} quit-hook A hook that is run when the user clicks the close button on the game window. This hook is run with zero arguments. @example (add-hook! quit-hook (lambda () (display "bye!\n"))) @end example @end defvr @defvr {Scheme Variable} key-press-hook A hook that is run when a key is pressed on the keyboard. This hook is run with four arguments: @enumerate @item @var{key}: The symbolic name of the ``virtual'' key that was pressed. For example: @code{backspace}. It's called a virtual key because the operating system may map a physical keyboard key to another key entirely, such as how the author binds the ``caps lock'' key to mean ``control''. @item @var{scancode}: The symbolic name of the physical key that was pressed. @item @var{modifiers}: A list of the symbolic names of modifier keys that were being held down when the key was pressed. Possible values include @code{ctrl}, @code{alt}, and @code{shift}. @item @var{repeat?}: @code{#t} if this is a repeated press of the same key. @end enumerate @example (add-hook! key-press-hook (lambda (key scancode modifiers repeat?) (display "pressed key: ") (display key) (newline))) @end example @end defvr @defvr {Scheme Variable} key-release-hook A hook that is run when a key is released on the keyboard. This hook is run with three arguments: @enumerate @item @var{key}: The symbolic name of the ``virtual'' key that was released. @item @var{scancode}: The symbolic name of the physical key that was released. @item @var{modifiers}: A list of the symbolic names of modifier keys that were being held down when the key was released. @end enumerate @end defvr @defvr {Scheme Variable} text-input-hook A hook that is run when printable text is typed on the keyboard. This hook is run with a single argument, @var{text}, a string containing the text that was entered. @end defvr @defvr {Scheme Variable} mouse-press-hook A hook that is run when a mouse button is pressed. This hook is run with four arguments: @enumerate @item @var{button}: The symbolic name of the button that was pressed, such as @code{left}, @code{middle}, or @code{right}. @item @var{clicks}: The number of times the button has been clicked in a row. @item @var{x}: The x coordinate of the mouse cursor. @item @var{y}: The y coordinate of the mouse cursor. @end enumerate @end defvr @defvr {Scheme Variable} mouse-release-hook A hook that is run when a mouse button is released. This hook is run with three arguments: @enumerate @item @var{button}: The symbolic name of the button that was released. @item @var{x}: The x coordinate of the mouse cursor. @item @var{y}: The y coordinate of the mouse cursor. @end enumerate @end defvr @defvr {Scheme Variable} mouse-move-hook A hook that is run when the mouse is moved. This hook is run with five arguments: @enumerate @item @var{x}: The x coordinate of the mouse cursor. @item @var{y}: The y coordinate of the mouse cursor. @item @var{dx}: The amount the mouse has moved along the x axis since the last mouse move event. @item @var{dy}: The amount the mouse has moved along the y axis since the last mouse move event. @item @var{buttons}: A list of the buttons that were pressed down when the mouse was moved. @end enumerate @end defvr @defvr {Scheme Variable} controller-add-hook A hook that is run when a game controller is connected. This hook is run with a single argument, @var{controller}, the controller that was connected. @end defvr @defvr {Scheme Variable} controller-remove-hook A hook that is run when a game controller is disconnected. This hook is run with a single argument, @var{controller}, the controller that was disconnected. @end defvr @defvr {Scheme Variable} controller-press-hook A hook that is run when a button on a game controller is pressed. This hook is run with two arguments: @enumerate @item @var{controller}: The controller that triggered the event. @item @var{button}: The symbolic name of the button that was pressed. Possible buttons are: @itemize @item @code{a} @item @code{b} @item @code{x} @item @code{y} @item @code{back} @item @code{guide} @item @code{start} @item @code{left-stick} @item @code{right-stick} @item @code{left-shoulder} @item @code{right-shoulder} @item @code{dpad-up} @item @code{dpad-down} @item @code{dpad-left} @item @code{dpad-right} @end itemize @end enumerate @end defvr @defvr {Scheme Variable} controller-release-hook A hook that is run when a button on a game controller is released. This hook is run with two arguments: @enumerate @item @var{controller}: The controller that triggered the event. @item @var{button}: The symbolic name of the button that was released. @end enumerate @end defvr @defvr {Scheme Variable} controller-move-hook A hook that is run when an analog stick or trigger on a game controller is moved. This hook is run with three arguments @enumerate @item @var{controller}: The controller that triggered the event. @item @var{axis}: The symbolic name of the axis that was moved. Possible values are: @itemize @item @code{left-x} @item @code{left-y} @item @code{right-x} @item @code{right-y} @item @code{trigger-left} @item @code{trigger-right} @end itemize @end enumerate @end defvr @node Input @section Input @node Math @section Math Chickadee contains data types and procedures for performing the most common computations in video game simulations such as linear algebra with vectors and matrices and axis-aligned bounding box collision detection. @menu * Vectors:: Euclidean vectors. * Matrices:: Transformation matrices. * Rectangles:: Axis-aligned bounding boxes. @end menu @node Vectors @subsection Vectors @node Matrices @subsection Matrices @node Rectangles @subsection Rectangles @node Graphics @section Graphics Chickadee aims to make hardware-accelerated graphics rendering as simple and efficient as possible by providing high-level APIs that interact with the low-level OpenGL API under the hood. Anyone that has worked with OpenGL directly knows that it has a steep learning curve and a lot of effort is needed to render even a single triangle. The Chickadee rendering engine attempts to make it easy to do common tasks like rendering a sprite while also providing all of the building blocks to implement additional rendering techniques. @menu * Rendering Engine:: Rendering state management. * Textures:: 2D images. * Sprites:: Draw 2D images. * Lines and Shapes:: Draw line segments and polygons. * Fonts:: Drawing text. * Blending and Depth Testing:: Control how pixels are combined. * Vertex Arrays:: Create 2D/3D models. * Shaders:: Create custom GPU programs. * Framebuffers:: Render to texture. * Viewports:: Restrict rendering to @end menu @node Rendering Engine @subsection Rendering Engine Chickadee defines rendering using a metaphor familiar to Scheme programmers: procedure application. A shader (@pxref{Shaders}) is like a procedure for the GPU to apply. Shaders are passed arguments: A vertex array containing the geometry to render (@pxref{Vertex Arrays}) and zero or more keyword arguments that the shader understands. Similar to how Scheme has @code{apply} for calling procedures, Chickadee provides @code{gpu-apply} for calling shaders. Additionally, there is some dynamic state that effects how @code{gpu-apply} will behave. Things like the current viewport, framebuffer, and blend mode are stored as dynamic state because it would be tedious to have to have to specify them each time @code{gpu-apply} is called. The following procedures and syntax can be found in the @code{(chickadee render)} module. @deffn {Scheme Syntax} gpu-apply @var{shader} @var{vertex-array} @ [#:uniform-key @var{uniform-value} ...] @deffnx {Scheme Syntax} gpu-apply* @var{shader} @var{vertex-array} @ @var{count} [#:uniform-key @var{uniform-value} ...] Render @var{vertex-array} using @var{shader} with the uniform values specified in the following keyword arguments. While @code{gpu-apply} will draw every vertex in @var{vertex-array}, @code{gpu-apply*} will only draw @var{count} vertices. @end deffn @deffn {Scheme Procedure} current-viewport Return the currently bound viewport. @xref{Viewports} for more details about using viewports. @end deffn @deffn {Scheme Procedure} current-framebuffer Return the currently bound framebuffer. @xref{Framebuffers} for more details about using framebuffers. @end deffn @deffn {Scheme Procedure} current-blend-mode Return the currently bound blend mode. @xref{Blending and Depth Testing} for more details about using blend modes. @end deffn @deffn {Scheme Procedure} current-depth-test Return @code{#t} if depth testing is currently enabled. @xref{Blending and Depth Testing} for more details about using the depth test. @end deffn @deffn {Scheme Procedure} current-texture Return the currently bound texture. @xref{Textures} for more details about using textures. @end deffn @deffn {Scheme Procedure} current-projection Return the currently bound projection matrix. @xref{Matrices} for more details about matrices. @end deffn @deffn {Scheme Syntax} with-viewport @var{viewport} @var{body} ... Evaluate @var{body} with the current viewport bound to @var{viewport}. @end deffn @deffn {Scheme Syntax} with-framebuffer @var{framebuffer} @var{body} ... Evaluate @var{body} with the current framebuffer bound to @var{framebuffer}. @end deffn @deffn {Scheme Syntax} with-blend-mode @var{blend-mode} @var{body} ... Evaluate @var{body} with the current blend mode bound to @var{blend-mode}. @end deffn @deffn {Scheme Syntax} with-depth-test @var{depth-test?} @var{body} ... Evaluate @var{body} with the depth-test disabled if @var{depth-test?} is @code{#f}, or enabled otherwise. @end deffn @deffn {Scheme Syntax} with-texture @var{texture} @var{body} ... Evaluate @var{body} with the current texture bound to @var{texture}. @end deffn @deffn {Scheme Syntax} with-projection @var{projection} @var{body} ... Evaluate @var{body} with the current projection matrix bound to @var{projection}. @end deffn @node Textures @subsection Textures @deffn {Scheme Procedure} load-image @var{file} [#:min-filter nearest] @ [#:mag-filter nearest] [#:wrap-s repeat] [#:wrap-t repeat] Load the image data from @var{file} and return a new texture object. @var{min-filter} and @var{mag-filter} describe the method that should be used for minification and magnification when rendering, respectively. Possible values are @code{nearest} and @code{linear}. @var{wrap-s} and @var{wrap-t} describe how to interpret texture coordinates that are greater than @code{1.0}. Possible values are @code{repeat}, @code{clamp}, @code{clamp-to-border}, and @code{clamp-to-edge}. @end deffn @node Sprites @subsection Sprites For those who are new to this game, a sprite is a 2D rectangular bitmap that is rendered to the screen. For 2D games, sprites are the most essential graphical abstraction. They are used for drawing maps, players, NPCs, items, particles, text, etc. In Chickadee, bitmaps are stored in textures (@pxref{Textures}) and can be used to draw sprites via the @code{draw-sprite} procedure. @deffn {Scheme Procedure} draw-sprite @var{texture} @var{region} @ [#:scale] [#:rotation] [#:blend-mode alpha] [#:texture-region] @ [#:shader] @end deffn It's not uncommon to need to draw hundreds or thousands of sprites each frame. However, GPUs (graphics processing units) are tricky beasts that prefer to be sent few, large chunks of data to render rather than many, small chunks. Using @code{draw-sprite} on its own will involve at least one GPU call @emph{per sprite}, which will quickly lead to poor performance. To deal with this, a technique known as ``sprite batching'' can be used. Instead of drawing each sprite immediately, the sprite batch will build up a large of buffer of sprites to draw and defer rendering until the last possible moment. Batching isn't a panacea, though. Batching only works if the sprites being drawn share as much in common as possible. Every time you draw a sprite with a different texture or blend mode, the batch will be sent off to the GPU. Therefore, batching is most useful if you minimize such changes. A good strategy for reducing texture changes is to stuff many bitmaps into a single image file and create a ``texture atlas'' (@pxref{Textures}) to access the sub-images within. Taking advantage of sprite batching in Chickadee is easy, just wrap the code that is calling @code{draw-sprite} a lot in the @code{with-batched-sprites} form. @deffn {Scheme Syntax} with-batched-sprites @var{body} @dots{} Use batched rendering for all @code{draw-sprite} calls within @var{body}. @end deffn With a basic sprite abstraction in place, it's possible to build other abstractions on top of it. One such example is the ``nine patch''. A nine patch is a sprite that can be rendered at various sizes without becoming distorted. This is achieved by diving up the sprite into nine regions: @itemize @item the center, which can be scaled horizontally and vertically @item the four corners, which can never be scaled @item the left and right sides, which can be scaled vertically @item the top and bottom sides, which can be scaled horizontally @end itemize The one caveat is that the bitmap regions must be designed in such a way so that they are not distorted when stretched along the affected axes. For example, that means that the top and bottom sides could have varying colored pixels vertically, but not horizontally. The most common application of this technique is for graphical user interface widgets like buttons and dialog boxes. By using a nine patch, they can be rendered at any size without unappealing scaling artifacts. @deffn {Scheme Procedure} draw-nine-patch @var{texture} @var{region} @ [#:margin 0] [#:top-margin margin] [#:bottom-margin margin] @ [#:left-margin margin] [#:right-margin margin] @ [#:texture-region] [#:scale] [#:rotation] [#:blend-mode alpha] @ [#:shader] Draw a nine patch sprite. A nine patch sprite renders @var{texture} as a @var{width} x @var{height} rectangle whose stretchable areas are defined by the given margin measurements @var{top-margin}, @var{bottom-margin}, @var{left-margin}, and @var{right-margin}. The @var{margin} argument may be used to configure all four margins at once. Refer to @code{draw-sprite} (@pxref{Sprites}) for information about the other arguments. @end deffn @node Lines and Shapes @subsection Lines and Shapes Sprites are fun, but sometimes simple, untextured lines and polygons are desired. That's where the @code{(chickadee render shapes)} module comes in! @deffn {Scheme Procedure} draw-line @var{start} @var{end} @ [#:thickness 0.5] [#:feather 1.0] [#:cap round] [#:color] @ [#:shader] Draw a line segment from @var{start} to @var{end}. The line will be @var{thickness} pixels thick with an antialiased border @var{feather} pixels wide. The line will be colored @var{color}. @var{cap} specifies the type of end cap that should be used to terminate the lines, either @code{none}, @code{butt}, @code{square}, @code{round}, @code{triangle-in}, or @code{triangle-out}. Advanced users may use the @var{shader} argument to override the built-in line segment shader. @end deffn @node Fonts @subsection Fonts Unlike the traditional TrueType font format that many are accustomed to, Chickadee loads and renders bitmap fonts in the @url{http://www.angelcode.com/products/bmfont/doc/file_format.html, Angel Code format}. But why use this seemingly obscure format? It's easy to find TTFs but not easy to find FNTs (the canonical file extension used for Angel Code fonts) and bitmap fonts don't scale well. The reason is efficiency. If all of the glyphs of a font are pre-rendered and packed into an image file then it becomes possible to use a texture atlas (@pxref{Textures}) and a sprite batch (@pxref{Sprites}) when rendering, which is a more efficient way to render fonts than using, say, @url{https://www.libsdl.org/projects/SDL_ttf/, SDL_ttf} or other solutions that involve using the FreeType library directly. Now what about scaling? In libraries that use TTF fonts, one must choose the size that the glyphs will be rasterized at up front. To use @code{n} sizes of the same font, one must load @code{n} variants of that font. If the size of the text is dynamic, some kind of texture scaling algorithm must be used and the text will inevitably look blurry. At first glance, using bitmap fonts seem to have an even worse issue. Instead of just loading the same font @code{n} times at different sizes, one would need to generate @code{n} image files for each font size needed. This is where the ``signed distance field'' rendering technique comes in. Introduced by @url{http://www.valvesoftware.com/.../2007/SIGGRAPH2007_AlphaTestedMagnification.pdf, Valve} in 2007, signed distance field fonts can be efficiently stored in a bitmap and be rendered at arbitrary scale factors with good results. Chickadee can render both traditional bitmap fonts and signed distance field fonts. While Chickadee does not yet offer a tool for converting TTF fonts into FNT fonts, tools such as @url{https://github.com/libgdx/libgdx/wiki/Hiero, Hiero} may be used in the meantime. The following procedures can be found in the @code{(chickadee render font)} module. @deffn {Scheme Procedure} load-font @var{file} Load the Angel Code formatted XML document in @var{file} and return a new font object. @end deffn @deffn {Scheme Procedure} font? @var{obj} Return @code{#t} if @var{obj} is a font object. @end deffn @deffn {Scheme Procedure} font-face @var{font} Return the name of @var{font}. @end deffn @deffn {Scheme Procedure} font-line-height @var{font} Return the line height of @var{font}. @end deffn @deffn {Scheme Procedure} font-line-height @var{font} Return the line height of @var{font}. @end deffn @deffn {Scheme Procedure} font-bold? @var{font} Return @code{#t} if @var{font} is a bold font. @end deffn @deffn {Scheme Procedure} font-italic? @var{font} Return @code{#t} if @var{font} is an italicized font. @end deffn @deffn {Scheme Procedure} draw-text @var{font} @var{text} @var{position} [#:scale] [#:rotation] [#:blend-mode] Draw the string @var{text} with the first character starting at @var{position} using @var{font}. @example (draw-text font "Hello, world!" (vec2 128.0 128.0)) @end example Refer to @code{draw-sprite} (@pxref{Sprites}) for information about the other arguments. @end deffn @node Blending and Depth Testing @subsection Blending and Depth Testing @node Vertex Arrays @subsection Vertex Arrays @node Shaders @subsection Shaders Shaders are programs for the GPU to evaluate. They are written in the OpenGL Shading Language, or GLSL. Chickadee does not currently provide a Scheme-like domain specific language for writing shaders. Since shaders must be written in GLSL and not Scheme, they are considered an advanced feature. @node Framebuffers @subsection Framebuffers @node Viewports @subsection Viewports @node Audio @section Audio Chickadee has two data types for audio: samples and music. Samples are for short sound effects like explosions. Music is for, well, uh@dots{}, music. Supported file formats include WAV and OGG. @deffn {Scheme Procedure} load-sample @var{file} Load audio sample from @var{file}. @end deffn @deffn {Scheme Procedure} set-sample-volume! @var{volume} Set the volume that all samples are played at to @var{volume}, an integer value between 0 and 128. @end deffn @deffn {Scheme Procedure} play-sample @var{sample} Play @var{sample}. Pretty straightforward! @end deffn @deffn {Scheme Procedure} load-music @var{file} Load music from @var{file}. @end deffn @deffn {Scheme Procedure} music-volume Return the volume level for music, an integer value between 0 and 128. @end deffn @deffn {Scheme Procedure} set-music-volume! @var{volume} Set the volume that music is played at to @var{volume}, an integer value between 0 and 128. @end deffn @deffn {Scheme Procedure} play-music @var{music} [@var{loop?}] Play @var{music}. If @var{loop?}, play it over and over and over and over and@dots{} @end deffn @deffn {Scheme Procedure} pause-music Pause the current music track. @end deffn @deffn {Scheme Procedure} resume-music Resume the current music track. @end deffn @deffn {Scheme Procedure} rewind-music estart the current music track from the beginning. @end deffn @deffn {Scheme Procedure} stop-music Stop playing the current music track. @end deffn @deffn {Scheme Procedure} music-playing? Return @code{#t} if music is currently playing. @end deffn @deffn {Scheme Procedure} music-paused? Return @code{#t} if music is currently paused. @end deffn