@menu * Kernel:: The fundamental components. * Math:: Linear algebra, spatial partitioning, and more. * Graphics:: Eye candy. * Audio:: Make some noise. * Scripting:: Bringing the game world to life. @end menu @node Kernel @section Kernel This section of the manual covers the foundation of Chickadee: The game loop and desktop environment interaction. @menu * The Game Loop:: The core event loop. * Input Devices:: Mouse, keyboard, controller input. * Window Manipulation:: Inspect and modify the graphical window. * Live Coding:: Tips for building games from the REPL. @end menu @node The Game Loop @subsection The Game Loop At the very core of Chickadee there is an event loop. This loop, or ``kernel'', is responsible for ensuring that the game is updated at the desired interval, handling input devices, rendering the current state of the game world, and handling errors if they occur. 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 game loop. @deffn {Procedure} run-game [#:window-title "Chickadee!"] @ [#:window-width 640] [#:window-height 480] @ [#:window-fullscreen? @code{#f}] @ [#:window-resizable? @code{#f}] @ [#:update-hz 60] @ [#:load] [#:update] [#:draw] [#:quit] @ [#:key-press] [#:key-release] [#:text-input] @ [#:mouse-press] [#:mouse-release] [#:mouse-move] @ [#:controller-add] [#:controller-remove] [#:controller-press] @ [#:controller-release] [#:controller-move] [#:error] Run the Chickadee game loop. 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}. If @var{window-resizable?} is @code{#t} then the window can be resized by the user. @itemize @item @var{load}: Called with zero arguments when the game window has opened but before the game loop has started. Can be used to perform initialization that requires an open window and OpenGL context such as loading textures. @item @var{update}: Called @var{update-hz} times per second with one argument: The amount of time to advance the game simulation. @item @var{draw}: Called each time a frame should be rendered with a single argument known as the @code{alpha} value. See the documentation for @code{run-game*} for an explanation of this value. @item @var{quit}: Called with zero arguments when the user tries to close the game window. The default behavior is to exit the game. @item @var{key-press}: Called with four arguments when a key is pressed on the keyboard: @enumerate @item @var{key}: The symbolic name of the key that was pressed. For example: @code{backspace}. @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 @item @var{key-release}: Called with three arguments when a key is released on the keyboard: @enumerate @item @var{key}: The symbolic name of the 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 @item @var{text-input}: Called with a single argument, a string of text, when printable text is typed on the keyboard. @item @var{mouse-press}: Called with four arguments when a mouse button is pressed: @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 @item @var{mouse-release}: Called with three arguments when a mouse button is released: @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 @item @var{mouse-move}: Called with five arguments when the mouse is moved: @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 @item @var{controller-add}: Called with a single argument, an SDL game controller object, when a game controller is connected. @item @var{controller-remove}: Called with a single argument, an SDL game controller object, when a game controller is disconnected. @item @var{controller-press}: Called with two arguments when a button on a game controller is pressed: @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 @item @var{controller-release}: Called with two arguments when a button on a game controller is released: @enumerate @item @var{controller}: The controller that triggered the event. @item @var{button}: The symbolic name of the button that was released. @end enumerate @item @var{controller-move}: Called with three arguments when an analog stick or trigger on a game controller is moved: @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 @item @var{error}: Called with three arguments when an error occurs: @enumerate @item @var{stack}: The call stack at the point of error. @item @var{key}: The exception key. @item @var{args}: The arguments thrown with the exception. @end enumerate The default behavior is to re-throw the error. @end itemize @end deffn To stop the game loop, simply call @code{abort-game}. @deffn {Procedure} abort-game Stop the currently running Chickadee game loop. @end deffn The above explanation of the game loop was partially a lie. It's true that there is a game loop at the center of Chickadee, but @code{run-game} is not it's true entry point. There exists an even lower level procedure, @code{run-game*}, in the @code{(chickadee game-loop)} module that @code{run-game} uses under the hood. On its own, @code{run-game*} does not do very much at all. In order to actually respond to input events, update game state, or render output, the developer must provide an engine. @code{run-game} is such an engine, and it's likely all a developer will need. However, what if a developer wanted to use all of the useful Chickadee features to make a terminal roguelike game instead? Chickadee doesn't come with a terminal rendering engine, but the developer could write one without having to write their own core game loop. @deffn {Procedure} run-game* [#:update] [#:render] [#:time] [#:error] @ [#:update-hz 60] Start the game loop. This procedure will not return until @code{abort-game} is called. The core game loop is generic and requires four additional procedures to operate: @itemize @item @var{update}: Called @var{update-hz} times per second to advance the game simulation. This procedure is called with a single argument: The amount of time that has passed since the last update, in milliseconds. @item @var{render}: Called each iteration of the loop to render the game to the desired output device. This procedure is called with a single argument: 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. @item @var{time}: Called to get the current time in seconds. This procedure is called with no arguments. @item @var{error}: Called when an error from the @var{update} or @var{render} procedures reaches the game loop. This procedure is called with three arguments: The call stack, the error key, and the error arguments. If no error handler is provided, the default behavior is to simply re-throw the error. @end itemize @end deffn @deffn {Procedure} elapsed-time Return the current value of the system timer in seconds. @end deffn @node Input Devices @subsection Input Devices While @code{run-game} provides hooks for mouse/keyboard/controller input events, it is often necessary to query input devices for their current state. For example, it could be desirable to query the state of the arrow keys every time the update hook is called to determine which direction the player should move that frame. @deffn {Procedure} key-pressed? key Return @code{#t} if @var{key} is currently being pressed. @end deffn @deffn {Procedure} key-released? key Return @code{#t} if @var{key} is @emph{not} currently being pressed. @end deffn @deffn {Procedure} mouse-x Return the current X coordinate of the mouse cursor. @end deffn @deffn {Procedure} mouse-y Return the current Y coordinate of the mouse cursor. @end deffn @deffn {Procedure} mouse-button-pressed? button Return @code{#t} if @var{button} is currently being pressed. @end deffn @deffn {Procedure} mouse-button-released? button Return @code{#t} if @var{button} is @emph{not} currently being pressed. @end deffn @deffn {Procedure} controller-axis controller axis Return a floating point value in the range [-1, 1] corresponding to how much @var{axis} (an analog stick or trigger) is being pushed on @var{controller}. 0 is returned if @var{axis} is not being pushed at all. @end deffn @deffn {Procedure} controller-name controller Return the name of @var{controller}. @end deffn @deffn {Procedure} controller-button-pressed? controller button Return @code{#t} if @var{button} on @var{controller} is currently being pressed. @end deffn @deffn {Procedure} controller-button-released? controller button Return @code{#t} if @var{button} on @var{controller} is @emph{not} currently being pressed. @end deffn @node Window Manipulation @subsection Window Manipulation @deffn {Procedure} current-window Return the currently active game window. @end deffn @deffn {Procedure} window? obj Return @code{#t} if @var{obj} is a window object. @end deffn @deffn {Procedure} window-title window Return the title of @var{window}. @end deffn @deffn {Procedure} window-width window Return the width of @var{window} in pixels. @end deffn @deffn {Procedure} window-height window Return the height of @var{window} in pixels. @end deffn @deffn {Procedure} window-x window Retun the X coordinate of the upper-left corner of @var{window}. @end deffn @deffn {Procedure} window-y window Return the Y coordinate of the upper-left corner of @var{window}. @end deffn @deffn {Procedure} hide-window! window Hide @var{window}. @end deffn @deffn {Procedure} show-window! window Show @var{window}. @end deffn @deffn {Procedure} maximize-window! window Maximize @var{window}. @end deffn @deffn {Procedure} minimize-window! window Minimize @var{window}. @end deffn @deffn {Procedure} raise-window! window Make @var{window} visible over all other windows. @end deffn @deffn {Procedure} restore-window! window Restore the size and position of a minimized or maximized @var{window}. @end deffn @deffn {Procedure} set-window-border! window border? Enable/disable the border around @var{window}. If @var{border?} is @code{#f}, the border is disabled, otherwise it is enabled. @end deffn @deffn {Procedure} set-window-title! window title Change the title of @var{window} to @var{title}. @end deffn @deffn {Procedure} set-window-size! window width height Change the dimensions of @var{window} to @var{width} x @var{height} pixels. @end deffn @deffn {Procedure} set-window-position! window x y Move the upper-left corner of @var{window} to pixel coordinates (@var{x}, @var{y}). @end deffn @deffn {Procedure} set-window-fullscreen! window fullscreen? Enable or disable fullscreen mode for @var{window}. If @var{fullscreen?} is @code{#f}, fullscreen mode is disabled, otherwise it is enabled. @end deffn @node Live Coding @subsection Live Coding One of the biggest appeals of any Lisp dialect is the ability to use the ``read-eval-print loop'' (REPL for short) to build programs iteratively and interactively while the program is running. However, programs that run in an event loop and respond to user input (such as a video game) require special care for this workflow to be pleasant. Chickadee provides no built-in support for live coding, but it's fairly easy to hook up a special kind of REPL yourself. First, create a cooperative REPL server (It's important to use Guile's cooperative REPL server instead of the standard REPL server in @code{(system repl server)} to avoid thread synchronization issues). Then, in the game loop's update procedure, call @code{poll-coop-repl-server} and pass the REPL object. Here is a template to follow: @example (use-modules (chickadee) (system repl coop-server)) (define repl (spawn-coop-repl-server)) (define (update dt) (poll-coop-repl-server repl) ...) (run-game #:update update ...) @end example To use the REPL, connect to it via port 37146. Telnet will do the trick, but using the @uref{https://www.nongnu.org/geiser/, Geiser} extension for Emacs is by far the best way to develop at the REPL with Guile. Use @code{M-x connect-to-guile} to connect to the REPL server. @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 * Basics:: Commonly used, miscellaneous things. * Vectors:: Euclidean vectors. * Rectangles:: Axis-aligned bounding boxes. * Matrices:: Transformation matrices. * Quaternions:: Rotations about an arbitrary axis. * Easings:: Easing functions for interesting animations. * Bezier Curves:: Cubic Bezier curves and paths in 2D space. * Path Finding:: Generic A* path finding. * Grid:: Spatial partitioning for bounding boxes. @end menu @node Basics @subsection Basics @defvar pi An essential constant for all trigonometry. Pi is the ratio of a circle's circumferences to its diameter. Since pi is an irrational number, the @var{pi} in Chickadee is a mere floating point approximation that is ``good enough.'' @end defvar @defvar pi/2 Half of @var{pi}. @end defvar @defvar 2pi Twice @var{pi}. @end defvar @deffn {Procedure} cotan z Return the cotangent of @var{z}. @end deffn @deffn {Procedure} clamp min max x Restrict @var{x} to the inclusive range defined by @var{min} and @var{max}. This procedure assumes that @var{min} is actually less than @var{max}. @end deffn @deffn {Procedure} lerp start end alpha Linearly interpolate the numbers @var{start} and @var{end} using the factor @var{alpha}, a number in the range [0, 1]. @end deffn @deffn {Procedure} degrees->radians degrees Convert @var{degrees} to radians. @end deffn @deffn {Procedure} radians->degrees radians Convert @var{radians} to degrees. @end deffn @node Vectors @subsection Vectors Unlike Scheme's vector data type, which is a sequence of arbitrary Scheme objects, Chickadee's @code{(chickadee math vector)} module provides vectors in the linear algebra sense: Sequences of numbers specialized for particular coordinate spaces. As of now, Chickadee provides 2D and 3D vectors, with 4D vector support coming in a future release. Here's a quick example of adding two vectors: @example (define v (vec2+ (vec2 1 2) (vec2 3 4))) @end example Since vectors are used so frequently, the reader macro @code{#v} is used to cut down on typing: @example (define v (vec2+ #v(1 2) #v(3 4))) @end example @emph{A Note About Performance} A lot of time has been spent making Chickadee's vector operations perform relatively efficiently in critical code paths where excessive garbage generation will cause major performance issues. The general rule is that procedures ending with @code{!} perform an in-place modification of one of the arguments in order to avoid allocating a new vector. These procedures are also inlined by Guile's compiler in order to take advantage of optimizations relating to floating point math operations. The downside is that since these are not pure functions, they do not compose well and create more verbose code. @subsubsection 2D Vectors @deffn {Procedure} vec2 x y Return a new 2D vector with coordinates (@var{x}, @var{y}). @end deffn @deffn {Procedure} vec2/polar r theta Return a new 2D vector containing the Cartesian representation of the polar coordinate (@var{r}, @var{theta}). The angle @var{theta} is measured in radians. @end deffn @deffn {Procedure} vec2? obj Return @code{#t} if @var{obj} is a 2D vector. @end deffn @deffn {Procedure} vec2-x v Return the X coordinate of the 2D vector @var{v}. @end deffn @deffn {Procedure} vec2-y v Return the Y coordinate of the 2D vector @var{v}. @end deffn @deffn {Procedure} vec2-copy v Return a fresh copy of the 2D vector @var{v}. @end deffn @deffn {Procedure} vec2-magnitude v Return the magnitude of the 2D vector @var{v}. @end deffn @deffn {Procedure} vec2-dot-product v1 v2 Return the dot product of the 2D vectors @var{v1} and @var{v2}. @end deffn @deffn {Procedure} vec2-normalize v Return the normalized form of the 2D vector @var{v}. @end deffn @deffn {Procedure} vec2+ v x Add @var{x}, either a 2D vector or a scalar (i.e. a real number), to the 2D vector @var{v} and return a new vector containing the sum. @end deffn @deffn {Procedure} vec2- v x Subtract @var{x}, either a 2D vector or a scalar, from the 2D vector @var{v} and return a new vector containing the difference. @end deffn @deffn {Procedure} vec2* v x Multiply the 2D vector @var{v} by @var{x}, a 2D vector or a scalar, and return a new vector containing the product. @end deffn @deffn {Procedure} set-vec2-x! v x Set the X coordinate of the 2D vector @var{v} to @var{x}. @end deffn @deffn {Procedure} set-vec2-y! v y Set the Y coordinate of the 2D vector @var{v} to @var{y}. @end deffn @deffn {Procedure} set-vec2! v x y Set the X and Y coordinates of the 2D vector @var{v} to @var{x} and @var{y}, respectively. @end deffn @deffn {Procedure} vec2-copy! source target Copy the 2D vector @var{source} into the 2D vector @var{target}. @end deffn @deffn {Procedure} vec2-add! v x Perform an in-place modification of the 2D vector @var{v} by adding @var{x}, a 2D vector or a scalar. @end deffn @deffn {Procedure} vec2-sub! v x Perform an in-place modification of the 2D vector @var{v} by subtracting @var{x}, a 2D vector or a scalar. @end deffn @deffn {Procedure} vec2-mult! v x Perform an in-place modification of the 2D vector @var{v} by multiplying it by @var{x}, a 2D vector or a scalar. @end deffn @subsubsection 3D Vectors @deffn {Procedure} vec3 x y Return a new 2D vector with coordinates (@var{x}, @var{y}). @end deffn @deffn {Procedure} vec3? obj Return @code{#t} if @var{obj} is a 3D vector. @end deffn @deffn {Procedure} vec3-x v Return the X coordinate of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3-y v Return the Y coordinate of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3-z v Return the Z coordinate of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3-copy v Return a fresh copy of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3-magnitude v Return the magnitude of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3-dot-product v1 v2 Return the dot product of the 3D vectors @var{v1} and @var{v2}. @end deffn @deffn {Procedure} vec3-cross v1 v2 Return a new 3D vector containing the cross product of @var{v1} and @var{v2}. @end deffn @deffn {Procedure} vec3-normalize v Return the normalized form of the 3D vector @var{v}. @end deffn @deffn {Procedure} vec3+ v x Add @var{x}, either a 3D vector or a scalar (i.e. a real number), to the 3D vector @var{v} and return a new vector containing the sum. @end deffn @deffn {Procedure} vec3- v x Subtract @var{x}, either a 3D vector or a scalar, from the 3D vector @var{v} and return a new vector containing the difference. @end deffn @deffn {Procedure} vec3* v x Multiply the 3D vector @var{v} by @var{x}, a 3D vector or a scalar, and return a new vector containing the product. @end deffn @deffn {Procedure} set-vec3-x! v x Set the X coordinate of the 3D vector @var{v} to @var{x}. @end deffn @deffn {Procedure} set-vec3-y! v y Set the Y coordinate of the 3D vector @var{v} to @var{y}. @end deffn @deffn {Procedure} set-vec3-z! v z Set the Z coordinate of the 3D vector @var{v} to @var{z}. @end deffn @deffn {Procedure} set-vec3! v x y z Set the X, Y, and Z coordinates of the 3D vector @var{v} to @var{x}, @var{y}, and @var{z}, respectively. @end deffn @deffn {Procedure} vec3-copy! source target Copy the 3D vector @var{source} into the 3D vector @var{target}. @end deffn @deffn {Procedure} vec3-add! v x Perform an in-place modification of the 3D vector @var{v} by adding @var{x}, a 3D vector or a scalar. @end deffn @deffn {Procedure} vec3-sub! v x Perform an in-place modification of the 3D vector @var{v} by subtracting @var{x}, a 3D vector or a scalar. @end deffn @deffn {Procedure} vec3-mult! v x Perform an in-place modification of the 3D vector @var{v} by multiplying it by @var{x}, a 3D vector or a scalar. @end deffn @deffn {Procedure} vec3-cross! dest v1 v2 Compute the cross product of the 3D vectors @var{v1} and @var{v2} and store the result in @var{dest}. @end deffn @node Rectangles @subsection Rectangles The @code{(chickadee math rect)} module provides an API for manipulating axis-aligned bounding boxes (AABBs). AABBs are often used for collision detection in games. Common use-cases are defining ``hitboxes'' in platformers or using them for the ``broad phase'' of a collision detection algorithm that uses a more complex (and thus slower) method of determining the actual collisions. Like some of the other math modules, there exists a collection of functions that do in-place modification of rectangles for use in performance critical code paths. @deffn {Procedure} rect x y width height @deffnx {Procedure} make-rect @var{x} @var{y} @var{width} @var{height} Create a new rectangle that is @var{width} by @var{height} in size and whose bottom-left corner is located at (@var{x}, @var{y}). @end deffn @deffn {Procedure} rect? obj Return @code{#t} if @var{obj} is a rectangle. @end deffn @deffn {Procedure} rect-within? rect1 rect2 Return @code{#t} if @var{rect2} is completely within @var{rect1}. @end deffn @deffn {Procedure} rect-intersects? rect1 rect2 Return @code{#t} if @var{rect2} overlaps @var{rect1}. @end deffn @deffn {Procedure} rect-contains? rect x y Return @code{#t} if the coordinates (@var{x}, @var{y}) are within @var{rect}. @end deffn @deffn {Procedure} rect-contains-vec2? rect v Return @code{#t} if the 2D vector @var{v} is within the bounds of @var{rect}. @end deffn @deffn {Procedure} rect-x rect Return the X coordinate of the lower-left corner of @var{rect}. @end deffn @deffn {Procedure} rect-y rect Return the Y coordinate of the lower-left corner of @var{rect}. @end deffn @deffn {Procedure} rect-left rect Return the left-most X coordinate of @var{rect}. @end deffn @deffn {Procedure} rect-right rect Return the right-most X coordinate of @var{rect}. @end deffn @deffn {Procedure} rect-bottom rect Return the bottom-most Y coordinate of @var{rect}. @end deffn @deffn {Procedure} rect-top rect Return the top-most Y coordinate of @var{rect}. @end deffn @deffn {Procedure} rect-center-x rect Return the X coordinate of the center of @var{rect}. @end deffn @deffn {Procedure} rect-center-y rect Return the Y coordinate of the center of @var{rect}. @end deffn @deffn {Procedure} rect-width rect Return the width of @var{rect}. @end deffn @deffn {Procedure} rect-height rect Return the height of @var{rect}. @end deffn @deffn {Procedure} rect-area rect Return the surface area covered by @var{rect}. @end deffn @deffn {Procedure} rect-clamp-x rect x Restrict @var{x} to the portion of the X axis covered by @var{rect}. @end deffn @deffn {Procedure} rect-clamp-y rect y Restrict @var{y} to the portion of the Y axis covered by @var{rect}. @end deffn @deffn {Procedure} rect-clamp rect1 rect2 Return a new rect that adjusts the location of @var{rect1} so that it is completely within @var{rect2}. An exception is thrown in the case that @var{rect1} cannot fit completely within @var{rect2}. @end deffn @deffn {Procedure} rect-move rect x y Return a new rectangle based on @var{rect} but moved to the coordinates (@var{x}, @var{y}). @end deffn @deffn {Procedure} rect-move-vec2 rect v Return a new rectangle based on @var{rect} but moved to the coordinates in the 2D vector @var{v}. @end deffn @deffn {Procedure} rect-move-by rect x y Return a new rectangle based on @var{rect} but moved by (@var{x}, @var{y}) units relative to its current location. @end deffn @deffn {Procedure} rect-move-by-vec2 rect v Return a new rectangle based on @var{rect} but moved by the 2D vector @var{v} relative to its current location. @end deffn @deffn {Procedure} rect-inflate rect width height Return a new rectangle based on @var{rect}, but expanded by @var{width} units on the X axis and @var{height} units on the Y axis, while keeping the rectangle centered on the same point. @end deffn @deffn {Procedure} rect-union rect1 rect2 Return a new rectangle that completely covers the area of @var{rect1} and @var{rect2}. @end deffn @deffn {Procedure} rect-clip rect1 rect2 Return a new rectangle that is the overlapping region of @var{rect1} and @var{rect2}. If the two rectangles do not overlap, a rectangle of 0 width and 0 height is returned. @end deffn @deffn {Procedure} set-rect-x! rect x Set the left X coordinate of @var{rect} to @var{x}. @end deffn @deffn {Procedure} set-rect-y! rect y Set the bottom Y coordinate of @var{rect} to @var{y}. @end deffn @deffn {Procedure} set-rect-width! rect width Set the width of @var{rect} to @var{width}. @end deffn @deffn {Procedure} set-rect-height! rect height Set the height of @var{rect} to @var{height}. @end deffn @deffn {Procedure} rect-move! rect x y Move @var{rect} to (@var{x}, @var{y}) in-place. @end deffn @deffn {Procedure} rect-move-vec2! rect v Move @var{rect} to the 2D vector @var{v} in-place. @end deffn @deffn {Procedure} rect-move-by! rect x y Move @var{rect} by (@var{x}, @var{y}) in-place. @end deffn @deffn {Procedure} rect-move-by-vec2! rect v Move @var{rect} by the 2D vector @var{v} in-place. @end deffn @deffn {Procedure} rect-inflate! rect width height Expand @var{rect} by @var{width} and @var{height} in-place. @end deffn @deffn {Procedure} rect-union! rect1 rect2 Modify @var{rect1} in-place to completely cover the area of both @var{rect1} and @var{rect2}. @end deffn @deffn {Procedure} rect-clip! rect1 rect2 Modify @var{rect1} in-place to be the overlapping region of @var{rect1} and @var{rect2}. @end deffn @deffn {Procedure} rect-clamp! rect1 rect2 Adjust the location of @var{rect1} in-place so that its bounds are completely within @var{rect2}. An exception is thrown in the case that @var{rect1} cannot fit completely within @var{rect2}. @end deffn @deffn {Procedure} vec2-clamp-to-rect! v rect Restrict the coordinates of the 2D vector @var{v} so that they are within the bounds of @var{rect}. @var{v} is modified in-place. @end deffn @node Matrices @subsection Matrices The @code{(chickadee math matrix)} module provides an interface for working with the most common type of matrices in game development: 4x4 transformation matrices. @emph{Another Note About Performance} Much like the vector API, the matrix API is commonly used in performance critical code paths. In order to reduce the amount of garbage generated and improve matrix multiplication performance, there are many procedures that perform in-place modifications of matrix objects. @subsubsection 3x3 Matrices @deffn {Procedure} make-matrix3 aa ab ac ba bb bc ca cb cc Return a new 3x3 initialized with the given 9 values in column-major format. @end deffn @deffn {Procedure} make-null-matrix3 Return a new 3x3 matrix with all values initialized to 0. @end deffn @deffn {Procedure} make-identity-matrix3 Return a new 3x3 identity matrix. Any matrix multiplied by the identity matrix yields the original matrix. This procedure is equivalent to the following code: @example (make-matrix3 1 0 0 0 1 0 0 0 1) @end example @end deffn @deffn {Procedure} matrix3? obj Return @code{#t} if @var{obj} is a 3x3 matrix. @end deffn @deffn {Procedure} matrix3* . matrices Return a new 3x3 matrix containing the product of multiplying all of the given @var{matrices}. Note: Remember that matrix multiplication is @strong{not} commutative! @end deffn @deffn {Procedure} matrix3-translate v Return a new 3x3 matrix that represents a translation by @var{v}, a 2D vector. @end deffn @deffn {Procedure} matrix3-scale s Return a new 3x3 matrix that represents a scaling along the x and y axes by the scaling factor @var{s}, a number or 2D vector. @end deffn @deffn {Procedure} matrix3-rotate angle Return a new 3x3 matrix that represents a rotation by @var{angle} radians. @end deffn @deffn {Procedure} matrix3-transform matrix v Return a new 2D vector that is @var{v} as transformed by the 3x3 matrix @var{matrix}. @end deffn The following procedures perform in-place, destructive updates to 3x3 matrix objects: @deffn {Procedure} matrix3-identity! matrix Modify @var{matrix} in-place to contain the identity matrix. @end deffn @deffn {Procedure} matrix3-mult! dest a b Multiply the 3x3 matrix @var{a} by the 3x3 matrix @var{b} and store the result in the 3x3 matrix @var{dest}. @end deffn @deffn {Procedure} matrix3-translate! matrix v Modify @var{matrix} in-place to contain a translation by @var{v}, a 2D vector. @end deffn @deffn {Procedure} matrix3-scale! matrix s Modify @var{matrix} in-place to contain a scaling along the x and y axes by the scaling factor @var{s}, a number or 2D vector. @end deffn @deffn {Procedure} matrix3-rotate! matrix angle Modify @var{matrix} in-place to contain a rotation by @var{angle} radians. @end deffn @deffn {Procedure} matrix3-transform! matrix v Modify the 2D vector @var{v} in-place to contain @var{v} as transformed by the 3x3 matrix @var{matrix}. @end deffn @subsubsection 4x4 Matrices @deffn {Procedure} make-matrix4 aa ab ac ad @ ba bb bc bd @ ca cb cc cd @ da db dc dd Return a new 4x4 matrix initialized with the given 16 values in column-major format. @end deffn @deffn {Procedure} make-null-matrix4 Return a new 4x4 matrix with all values initialized to 0. @end deffn @deffn {Procedure} make-identity-matrix4 Return a new 4x4 identity matrix. Any matrix multiplied by the identity matrix yields the original matrix. This procedure is equivalent to the following code: @example (make-matrix4 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1) @end example @end deffn @deffn {Procedure} matrix4? obj Return @code{#t} if @var{obj} is a 4x4 matrix. @end deffn @deffn {Procedure} matrix4* . matrices Return a new 4x4 matrix containing the product of multiplying all of the given @var{matrices}. Note: Remember that matrix multiplication is @strong{not} commutative! @end deffn @deffn {Procedure} orthographic-projection left right top bottom near far Return a new 4x4 matrix that represents an orthographic (2D) projection for the horizontal clipping plane @var{top} and @var{bottom}, the vertical clipping plane @var{top} and @var{bottom}, and the depth clipping plane @var{near} and @var{far}. @end deffn @deffn {Procedure} perspective-projection fov aspect-ratio near far Return a new 4x4 matrix that represents a perspective (3D) projection with a field of vision of @var{fov} radians, an aspect ratio of @var{aspect-ratio}, and a depth clipping plane defined by @var{near} and @var{far}. @end deffn @deffn {Procedure} matrix4-translate x Return a new 4x4 matrix that represents a translation by @var{x}, a 2D vector, a 3D vector, or a rectangle (in which case the bottom-left corner of the rectangle is used). @end deffn @deffn {Procedure} matrix4-scale s Return a new 4x4 matrix that represents a scaling along the X, Y, and Z axes by the scaling factor @var{s}, a real number. @end deffn @deffn {Procedure} matrix4-rotate q Return a new 4x4 matrix that represents a rotation about an arbitrary axis defined by the quaternion @var{q}. @end deffn @deffn {Procedure} matrix4-rotate-z theta Return a new 4x4 matrix that represents a rotation about the Z axis by @var{theta} radians. @end deffn @deffn {Procedure} matrix4-identity! matrix Modify @var{matrix} in-place to contain the identity matrix. @end deffn @deffn {Procedure} matrix4-mult! dest a b Multiply the 4x4 matrix @var{a} by the 4x4 matrix @var{b} and store the result in the 4x4 matrix @var{dest}. @end deffn @deffn {Procedure} matrix4-translate! matrix x Modify @var{matrix} in-place to contain a translation by @var{x}, a 2D vector, a 3D vector, or a rectangle (in which case the bottom-left corner of the rectangle is used). @end deffn @deffn {Procedure} matrix4-scale! matrix s Modify @var{matrix} in-place to contain a scaling along the X, Y, and Z axes by the scaling factor @var{s}, a real number. @end deffn @deffn {Procedure} matrix4-rotate! matrix q Modify @var{matrix} in-place to contain a rotation about an arbitrary axis defined by the quaternion @var{q}. @end deffn @deffn {Procedure} matrix4-rotate-z! matrix theta Modify @var{matrix} in-place to contain a rotation about the Z axis by @var{theta} radians. @end deffn @deffn {Procedure} matrix4-2d-transform! matrix [#:origin] @ [#:position] [#:rotation] @ [#:scale] [#:skew] Modify @var{matrix} in-place to contain the transformation described by @var{position}, a 2D vector or rectangle, @var{rotation}, a scalar representing a rotation about the Z axis, @var{scale}, a 2D vector, and @var{skew}, a 2D vector. The transformation happens with respect to @var{origin}, a 2D vector. If an argument is not provided, that particular transformation will not be included in the result. @end deffn @deffn {Procedure} matrix4-transform! matrix v Modify the 2D vector @var{v} in-place by multiplying it by the 4x4 matrix @var{matrix}. @end deffn @node Quaternions @subsection Quaternions In game development, the quaternion is most often used to represent rotations. Why not use a matrix for that, you may ask. Unlike matrices, quaternions can be interpolated (animated) and produce a meaningful result. When interpolating two quaternions, there is a smooth transition from one rotation to another, whereas interpolating two matrices would yield garbage. @deffn {Procedure} quaternion x y z w Return a new quaternion with values @var{x}, @var{y}, @var{z}, and @var{w}. @end deffn @deffn {Procedure} quaternion? obj Return @code{#t} if @var{obj} is a quaternion. @end deffn @deffn {Procedure} quaternion-w q Return the W component of the quaternion @var{q}. @end deffn @deffn {Procedure} quaternion-x q Return the X component of the quaternion @var{q}. @end deffn @deffn {Procedure} quaternion-y q Return the Y component of the quaternion @var{q}. @end deffn @deffn {Procedure} quaternion-z q Return the Z component of the quaternion @var{q}. @end deffn @deffn {Procedure} make-identity-quaternion Return the identity quaternion. @end deffn @node Easings @subsection Easings Easing functions are essential for animation. Each easing function provides a different path to go from an initial value to a final value. These functions make an excellent companion to the @code{tween} procedure (@pxref{Tweening}). Experiment with them to figure out which function makes an animation look the best. Pro tip: @code{smoothstep} provides nice results most of the time and creates smoother animation than using @code{linear}. @deffn {Procedure} linear t @end deffn @deffn {Procedure} smoothstep t @end deffn @deffn {Procedure} ease-in-quad t @end deffn @deffn {Procedure} ease-out-quad t @end deffn @deffn {Procedure} ease-in-out-quad t @end deffn @deffn {Procedure} ease-in-cubic t @end deffn @deffn {Procedure} ease-out-cubic t @end deffn @deffn {Procedure} ease-in-out-cubic t @end deffn @deffn {Procedure} ease-in-quart t @end deffn @deffn {Procedure} ease-out-quart t @end deffn @deffn {Procedure} ease-in-out-quart t @end deffn @deffn {Procedure} ease-in-quint t @end deffn @deffn {Procedure} ease-out-quint t @end deffn @deffn {Procedure} ease-in-out-quint t @end deffn @deffn {Procedure} ease-in-sine t @end deffn @deffn {Procedure} ease-out-sine t @end deffn @deffn {Procedure} ease-in-out-sine t @end deffn @node Bezier Curves @subsection Bezier Curves The @code{(chickadee math bezier)} module provides an API for describing cubic Bezier curves in 2D space. These curves are notably used in font description, vector graphics programs, and when it comes to games: path building. With Bezier curves, it's somewhat easy to create a smooth looking path for an enemy to move along, for example. Bezier curves become particularly interesting when they are chained together to form a Bezier ``path'', where the end point of one curve becomes the starting point of the next. @deffn {Procedure} make-bezier-curve p0 p1 p2 p3 Return a new Bezier curve object whose starting point is @var{p0}, ending point is @var{p3}, and control points are @var{p1} and @var{p2}. All points are 2D vectors. @end deffn @deffn {Procedure} bezier-curve? obj Return @code{#t} if @var{obj} is a Bezier curve. @end deffn @deffn {Procedure} bezier-curve-p0 bezier Return the starting point of @var{bezier}. @end deffn @deffn {Procedure} bezier-curve-p1 bezier Return the first control point of @var{bezier}. @end deffn @deffn {Procedure} bezier-curve-p2 bezier Return the second control point of @var{bezier}. @end deffn @deffn {Procedure} bezier-curve-p3 bezier Return the end point of @var{bezier}. @end deffn @deffn {Procedure} bezier-path . control-points Return a list of connected bezier curves defined by @var{control-points}. The first curve is defined by the first 4 arguments and every additional curve thereafter requires 3 additional arguments. @end deffn @deffn {Procedure} bezier-curve-point-at bezier t Return the coordinates for @var{bezier} at @var{t} (a value in the range [0, 1] representing how far from the start of the curve to check) as a 2D vector. @end deffn @deffn {Procedure} bezier-curve-point-at! dest bezier t Modify the 2D vector @var{dest} in-place to contain the coordinates for @var{bezier} at @var{t}. @end deffn @node Path Finding @subsection Path Finding Most game worlds have maps. Often, these games have a need to move non-player characters around in an unscripted fashion. For example, in a real-time strategy game, the player may command one of their units to attack something in the enemy base. To do so, the unit must calculate the shortest route to get there. It wouldn't be a very fun game if units didn't know how to transport themselves efficiently. This is where path finding algorithms come in handy. The @code{(chickadee math path-finding)} module provides a generic implementation of the popular A* path finding algorithm. Just add a map implementation! The example below defines a very simple town map and finds the quickest way to get from the town common to the school. @example (define world-map '((town-common . (town-hall library)) (town-hall . (town-common school)) (library . (town-common cafe)) (school . (town-hall cafe)) (cafe . (library school)))) (define (neighbors building) (assq-ref town-map building)) (define (cost a b) 1) (define (distance a b) 1) (define pf (make-path-finder)) (a* pf 'town-common 'school neighbors cost distance) @end example In this case, the @code{a*} procedure will return the list @code{(town-common town-hall school)}, which is indeed the shortest route. (The other possible route is @code{(town-common library cafe school)}.) The @code{a*} procedure does not know anything about about any kind of map and therefore must be told how to look up neighboring nodes, which is what the @code{neighbors} procedure in the example does. To simulate different types of terrain, a cost procedure is used. In this example, it is just as easy to move between any two nodes because @code{cost} always returns 1. In a real game, perhaps moving from from a field to a rocky hill would cost a lot more than moving from one field to another. Finally, a heuristic is used to calculate an approximate distance between two nodes on the map. In this simple association list based graph it is tough to calculate a distance between nodes, so the @code{distance} procedure isn't helpful and always returns 1. In a real game with a tile-based map, for example, the heuristic could be a quick Manhattan distance calculation based on the coordinates of the two map tiles. Choose an appropriate heuristic for optimal path finding! @deffn {Procedure} make-path-finder Return a new path finder object. @end deffn @deffn {Procedure} path-finder? obj Return @code{#t} if @var{obj} is a path finder. @end deffn @deffn {Procedure} a* path-finder start goal neighbors cost distance Return a list of nodes forming a path from @var{start} to @var{goal} using @var{path-finder} to hold state. @var{neighbors} is a procedure that accepts a node and returns a list of nodes that neighbor it. @var{cost} is a procedure that accepts two neighboring nodes and returns the cost of moving from the first to the second as a real number. @var{distance} is a procedure that accepts two nodes and returns an approximate distance between them. @end deffn @node Grid @subsection Grid The @code{(chickadee math grid)} module provides a simple spatial partitioning system for axis-aligned bounding boxes (@pxref{Rectangles}) in 2D space. The grid divides the world into tiles and keeps track of which rectangles occupy which tiles. When there are lots of moving objects in the game world that need collision detection, the grid greatly speeds up the process. Instead of checking collisions of each object against every other object (an O(n^2) operation), the grid quickly narrows down which objects could possibly be colliding and only performs collision testing against a small set of objects. In addition to checking for collisions, the grid also handles the resolution of collisions. Exactly how each collision is resolved is user-defined. A player bumping into a wall may slide against it. An enemy colliding with a projectile shot by the player may get pushed back in the opposite direction. Two players colliding may not need resolution at all and will just pass through each other. The way this works is that each time an object (A) is moved within the grid, the grid looks for an object (B) that may possibly be colliding with A. A user-defined procedure known as a ``filter'' is then called with both A and B. If the filter returns @code{#f}, it means that even if A and B are colliding, no collision resolution is needed. In this case the grid won't waste time checking if they really do collide because it doesn't matter. If A and B are collidable, then the filter returns a procedure that implements the resolution technique. The grid will then perform a collision test. If A and B are colliding, the resolver procedure is called. It's the resolvers job to adjust the objects such that they are no longer colliding. The grid module comes with a very simple resolution procedure, @code{slide}, that adjusts object A by the smallest amount so that it no longer overlaps with B. By using this filtering technique, a game can resolve collisions between different objects in different ways. @deffn {Procedure} make-grid [cell-size 64] Return a new grid partitioned into @var{cell-size} tiles. @end deffn @deffn {Procedure} grid? obj Return @code{#t} if @var{obj} is a grid. @end deffn @deffn {Procedure} cell? obj Return @code{#t} if @var{obj} is a grid cell. @end deffn @deffn {Procedure} cell-count cell Return the number of items in @var{cell}. @end deffn @deffn {Procedure} grid-cell-size grid Return the cell size of @var{grid}. @end deffn @deffn {Procedure} grid-cell-count grid Return the number of cells currently in @var{grid}. @end deffn @deffn {Procedure} grid-item-count grid Return the number of items in @var{grid}. @end deffn @deffn {Procedure} grid-add grid item x y @ width height Add @var{item} to @var{grid} represented by the axis-aligned bounding box whose lower-left corner is at (@var{x}, @var{y}) and is @var{width} x @var{height} in size. @end deffn @deffn {Procedure} grid-remove grid item Return @var{item} from @var{grid}. @end deffn @deffn {Procedure} grid-clear grid Remove all items from @var{grid}. @end deffn @deffn {Procedure} grid-move grid item position filter Attempt to move @var{item} in @var{grid} to @var{position} (a 2D vector) and check for collisions. For each collision, @var{filter} will be called with two arguments: @var{item} and the item it collided with. If a collision occurs, @var{position} may be modified to resolve the colliding objects. @end deffn @deffn {Procedure} for-each-cell proc grid [rect] Call @var{proc} with each cell in @var{grid} that intersects @var{rect}, or every cell if @var{rect} is @code{#f}. @end deffn @deffn {Procedure} for-each-item proc grid Call @var{proc} for each item in @var{grid}. @end deffn @deffn {Procedure} slide item item-rect other other-rect goal Resolve the collision that occurs between @var{item} and @var{other} when moving @var{item-rect} to @var{goal} by sliding @var{item-rect} the minimum amount needed to make it no longer overlap @var{other-rect}. @end deffn @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 * Colors:: Such pretty colors... * Textures:: 2D images. * Sprites:: Draw 2D images. * 9-Patches:: Scalable bitmap boxes. * Fonts:: Drawing text. * Vector Paths:: Draw filled and stroked paths. * Particles:: Pretty little flying pieces! * Tile Maps:: Draw 2D tile maps. * 3D Models:: Spinning teapots everywhere. * Blending:: Control how pixels are combined. * Framebuffers:: Render to texture. * Viewports:: Restrict rendering to a particular area. * Rendering Engine:: Rendering state management. * Buffers:: Send data to the GPU. * Shaders:: Create custom GPU programs. @end menu @node Colors @subsection Colors Merriam-Webster defines color as ``a phenomenon of light (such as red, brown, pink, or gray) or visual perception that enables one to differentiate otherwise identical objects.'' In this essay, I will@dots{} Okay, okay. We all know what colors are. Chickadee provides a data type to represent color and some convenient procedures to work with them in the @code{(chickadee graphics color)} module. Colors are made up of four components, or channels: red, green, blue, and alpha (transparency.) Each of these values is expressed as a uniform floating point value in the range [0, 1]. 0 means that color channel is unrepresented in the resulting color, whereas 1 means that color channel is at full intensity. Making a color object is easy, and there's a few ways to do it depending on what's most convenient. The first is @code{make-color}, where you specify each channel exactly as described above. This is fully opaque magenta: @example (make-color 1.0 0.0 1.0 1.0) @end example Many people are used to representing colors as 6 or 8 digit hexadecimal numbers, so Chickadee also allows that. Here's magenta, again: @example (rgba #xFF00FFFF) (rgb #xFF00FF) ; equivalent to the above @end example @deffn {Procedure} make-color r g b a Return a new color object with a red value of @var{r}, a green value of @var{g}, a blue value of @var{b}, and an alpha (transparency) value of @var{a}. All values are clamped to the range [0, 1]. @end deffn @deffn {Procedure} rgba color-code Return a new color object using the values of the first 32 bits of @var{color-code}. Each channel occupies 8 bits. Starting from the most significant bit, red is first, followed by green, then blue, then alpha. Color codes are often represented as 6 or 8 digit hexadecimal numbers in various other programs. @end deffn @deffn {Procedure} rgb color-code Like @code{rgba}, but @var{color-code} is a 24 bit code with no alpha channel. @end deffn @deffn {Procedure} color? obj Return @code{#t} if @var{obj} is a color object. @end deffn @deffn {Procedure} color-r color Return the red channel of @var{color}. @end deffn @deffn {Procedure} color-g color Return the green channel of @var{color}. @end deffn @deffn {Procedure} color-b color Return the blue channel of @var{color}. @end deffn @deffn {Procedure} color-a color Return the alpha channel of @var{color}. @end deffn @deffn {Procedure} transparency alpha Return a new color that is white (RGB channels set to 1) with an alpha channel value of @var{alpha}. This can be useful for creating a color that can be multiplied against another color to make it more transparent. @end deffn @deffn {Procedure} string->color s Convert the hexadecimal color code in the string @var{s} to a color object. The following string formats are supported: @example (string->color "#FF00FFFF") (string->color "FF00FFFF") (string->color "#FF00FF") (string->color "FF00FF") @end example @end deffn @deffn {Procedure} color* a b Multiply the color @var{a} with the color or number @var{b} and return a new color with the result. @end deffn @deffn {Procedure} color+ a b Add the color @var{a} to the color @var{b} and return a new color with the result. @end deffn @deffn {Procedure} color- a b Subtract the color @var{b} from the color @var{a} and return a new color with the result. @end deffn @deffn {Procedure} color-inverse color Invert the red, green, and blue channels of @var{color} and return a new color with the result. @end deffn @deffn {Procedure} color-lerp start end alpha Linearly interpolate the colors @var{start} and @var{end} using the factor @var{alpha}, a number in the range [0, 1]. @end deffn @subsubsection Stock Colors For convenience, Chickadee comes with some basic colors predefined: @defvar white @end defvar @defvar black @end defvar @defvar red @end defvar @defvar green @end defvar @defvar blue @end defvar @defvar yellow @end defvar @defvar magenta @end defvar @defvar cyan @end defvar For fun, there are also predefined colors for the classic @url{https://en.wikipedia.org/wiki/Tango_Desktop_Project#Palette, Tango color palette}. @defvar tango-light-butter @end defvar @defvar tango-butter @end defvar @defvar tango-dark-butter @end defvar @defvar tango-light-orange @end defvar @defvar tango-orange @end defvar @defvar tango-dark-orange @end defvar @defvar tango-light-chocolate @end defvar @defvar tango-chocolate @end defvar @defvar tango-dark-chocolate @end defvar @defvar tango-light-chameleon @end defvar @defvar tango-chameleon @end defvar @defvar tango-dark-chameleon @end defvar @defvar tango-light-sky-blue @end defvar @defvar tango-sky-blue @end defvar @defvar tango-dark-sky-blue @end defvar @defvar tango-light-plum @end defvar @defvar tango-plum @end defvar @defvar tango-dark-plum @end defvar @defvar tango-light-scarlet-red @end defvar @defvar tango-scarlet-red @end defvar @defvar tango-dark-scarlet-red @end defvar @defvar tango-aluminium-1 @end defvar @defvar tango-aluminium-2 @end defvar @defvar tango-aluminium-3 @end defvar @defvar tango-aluminium-4 @end defvar @defvar tango-aluminium-5 @end defvar @defvar tango-aluminium-6 @end defvar @node Textures @subsection Textures Textures are essentially images: a 2D grid of color values. The @code{(chickadee graphics texture)} module provides an interface for working with texture objects. @deffn {Procedure} load-image file [#:min-filter nearest] @ [#:mag-filter nearest] [#:wrap-s repeat] [#:wrap-t repeat] @ [#:transparent-color] 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}. For color-keyed images (images where a specific color should be made transparent), specify the appropriate @var{transparent-color}. @end deffn @deffn {Procedure} texture? obj Return @code{#t} if @var{obj} is a texture. @end deffn @deffn {Procedure} texture-region? obj Return @code{#t} if @var{obj} is a texture region. @end deffn @deffn {Procedure} texture-parent texture If @var{texture} is a texture region, return the full texture that it is based upon. Otherwise, return @code{#f}. @end deffn @deffn {Procedure} texture-min-filter texture Return the minification filter for @var{texture}, either @code{nearest} or @code{linear}. @end deffn @deffn {Procedure} texture-mag-filter texture Return the magnification filter for @var{texture}, either @code{nearest} or @code{linear}. @end deffn @deffn {Procedure} texture-wrap-s texture Return the method used for wrapping texture coordinates along the X axis for @var{texture}. Possible wrapping methods: @itemize @item @code{repeat} @item @code{clamp} @item @code{clamp-to-border} @item @code{clamp-to-edge} @end itemize @end deffn @deffn {Procedure} texture-wrap-t texture Return the method used for wrapping texture coordinates along the Y axis for @var{texture}. @end deffn @deffn {Procedure} texture-width texture Return the width of @var{texture} in pixels. @end deffn @deffn {Procedure} texture-height texture Return the height of @var{texture} in pixels. @end deffn It is common practice to combine multiple bitmap images into a single texture, known as a ``tile atlas'' or ``tile set'', because it is more efficient to render many regions of a large texture than it is to render a bunch of small textures. Chickadee provides a tile atlas data type for collecting texture regions into a single vector. @deffn {Procedure} split-texture texture tile-width tile-height @ [#:margin 0] [#:spacing 0] Return a new texture atlas that splits @var{texture} into a grid of @var{tile-width} by @var{tile-height} rectangles. Optionally, each tile may have @var{spacing} pixels of horizontal and vertical space between surrounding tiles and the entire image may have @var{margin} pixels of empty space around its border. This type of texture atlas layout is very common for 2D tile maps. @xref{Tile Maps} for more information. @end deffn @deffn {Procedure} load-tileset file-name tile-width tile-height @ [#:margin 0] [#:spacing 0] @ [#:min-filter nearest] @ [#:mag-filter nearest] [#:wrap-s repeat] [#:wrap-t repeat] @ [#:transparent-color] Return a new texture atlas that splits the texture loaded from the file @var{file-name} into a grid of @var{tile-width} by @var{tile-height} rectangles. See @code{load-image} and @code{split-texture} for information about all keyword arguments. @end deffn @deffn {Procedure} texture-atlas? obj Return @code{#t} if @var{obj} is a texture atlas. @end deffn @deffn {Procedure} texture-atlas-texture atlas Return the texture that all texture regions in @var{atlas} have been created from. @end deffn @deffn {Procedure} texture-atlas-ref atlas index Return the texture region in @var{atlas} at @var{index}. @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, the @code{(chickadee graphics sprite)} module provides the interface for working with sprites. Bitmaps are stored in textures (@pxref{Textures}) and can be used to draw sprites via the @code{draw-sprite} procedure. @deffn {Procedure} draw-sprite texture position @ [#:tint white] [#:origin] [#:scale] [#:rotation] [#:blend-mode] @ [#:rect] Draw @var{texture} at @var{position}. Optionally, other transformations may be applied to the sprite. @var{rotation} specifies the angle to rotate the sprite, in radians. @var{scale} specifies the scaling factor as a 2D vector. All transformations are applied relative to @var{origin}, a 2D vector, which defaults to the lower-left corner. @var{tint} specifies the color to multiply against all the sprite's pixels. By default white is used, which does no tinting at all. Alpha blending is used by default but the blending method can be changed by specifying @var{blend-mode}. The area drawn to is as big as the texture, by default. To draw to an arbitrary section of the screen, specify @var{rect}. @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}. This is fine for rendering a few dozen sprites, but will become a serious bottleneck when rendering hundreds or thousands of sprites. To deal with this, a technique known as ``sprite batching'' is used. Instead of drawing each sprite immediately, the sprite batch will build up a large of buffer of sprites to draw and send them to the GPU all at once. There is one caveat, however. Batching only works if the sprites being drawn share a common texture. A good strategy for reducing the number of different textures is to stuff many bitmaps into a single image file and create a ``texture atlas'' (@pxref{Textures}) to access the sub-images within. @deffn {Procedure} make-sprite-batch texture [#:capacity 256] Create a new sprite batch for @var{texture} with initial space for @var{capacity} sprites. Sprite batches automatically resize when they are full to accomodate as many sprites as necessary. @end deffn @deffn {Procedure} sprite-batch? obj Return @code{#t} if @var{obj} is a sprite batch. @end deffn @deffn {Procedure} sprite-batch-texture batch Return the texture for @var{batch}. @end deffn @deffn {Procedure} set-sprite-batch-texture! batch texture Set texture for @var{batch} to @var{texture}. @end deffn @deffn {Procedure} sprite-batch-add! batch position @ [#:origin] [#:scale] [:rotation] @ [#:tint @code{white}] [#:texture-region] Add sprite located at @var{position} to @var{batch}. To render a subsection of the batch's texture, a texture object whose parent is the batch texture may be specified as @var{texture-region}. See @code{draw-sprite} for information about the other arguments. @end deffn @deffn {Procedure} sprite-batch-clear! batch Reset size of @var{batch} to 0. @end deffn @deffn {Procedure} draw-sprite-batch batch [#:blend-mode] Render @var{batch} using @var{blend-mode}. Alpha blending is used by default. @end deffn @node 9-Patches @subsection 9-Patches A 9-patch is a method of rendering a texture so that it can be stretched to cover an area of any size without becoming distorted. This is achieved by dividing up the sprite into nine regions: @itemize @item the center, which can be stretched or tiled horizontally and vertically @item the four corners, which are never stretched or tiled @item the left and right sides, which can be stretched or tiled vertically @item the top and bottom sides, which can be stretched or tiled horizontally @end itemize The most common application of this technique is for graphical user interface widgets like buttons and dialog boxes which are often dynamically resizable. By using a 9-patch, they can be rendered at any size without scaling artifacts. @deffn {Procedure} draw-9-patch texture rect @ [#:margin 0] [#:top-margin margin] [#:bottom-margin margin] @ [#:left-margin margin] [#:right-margin margin] [#:mode stretch] @ [#:origin] [#:scale] [#:rotation] [#:blend-mode] @ [#:tint white] Draw a 9-patch over the area @var{rect} using @var{texture} whose stretchable/tileable patches are defined by the given margin measurements. The corners are never stretched/tiled, the left and right edges will be stretched/tiled vertically, the top and bottom edges may be stretched/tiled horizontally, and the center may be stretched/tiled in both directions. @var{mode} may be either @code{stretch} (the default) or @code{tile}. @var{margin} specifies the margin size for all sides of the 9-patch. To make margins of differing sizes, the @var{top-margin}, @var{bottom-margin}, @var{left-margin}, and @var{right-margin} arguments may be used. Refer to @code{draw-sprite} (@pxref{Sprites}) for information about the other arguments as they are the same. @end deffn @node Fonts @subsection Fonts Printing text to the screen is quite easy: @example (draw-text "Hello, world" (vec2 100.0 100.0)) @end example Chickadee supports OpenType/TrueType fonts (via the FreeType library), bitmap fonts in Angel Code bmfont format, and simple sprite sheet bitmap fonts. A default font named Inconsolata is used for all text rendering operations where a font is not specified, as is the case in the above example. The following procedures can be found in the @code{(chickadee graphics font)} module: @deffn {Procedure} load-font file-name point-size [#:char-set] Load the scalable (OpenType, TrueType, etc.) font in the file @var{file-name} and display it at the given @var{point-size}. By default, all the characters in the ASCII character set are loaded. This can be changed by passing a different character set (@pxref{Character Sets,,, guile, GNU Guile Reference Manual}) using the @var{char-set} keyword argument. @end deffn @deffn {Procedure} load-bitmap-font file Load the Angel Code font (in either XML or FNT format) in @var{file} and return a new font object. @end deffn @deffn {Procedure} font? obj Return @code{#t} if @var{obj} is a font object. @end deffn @deffn {Procedure} font-face font Return the name of @var{font}. @end deffn @deffn {Procedure} font-line-height font Return the line height of @var{font}. @end deffn @deffn {Procedure} font-line-height font Return the line height of @var{font}. @end deffn @deffn {Procedure} font-bold? font Return @code{#t} if @var{font} is a bold font. @end deffn @deffn {Procedure} font-italic? font Return @code{#t} if @var{font} is an italicized font. @end deffn @deffn {Procedure} draw-text text position [#:font] [#:color] [#:origin] [#:scale] [#:rotation] [#:blend-mode] [#:start 0] [#:end @code{(string-length text)}] Draw the string @var{text} with the first character starting at @var{position} using @var{font}. If @var{font} is not specified, a built-in font is used. @example (draw-text "Hello, world!" (vec2 128.0 128.0)) @end example To render a substring of @var{text}, use the @var{start} and @var{end} arguments. Refer to @code{draw-sprite} (@pxref{Sprites}) for information about the other arguments. @end deffn @node Vector Paths @subsection Vector Paths The @code{(chickadee graphics path)} module can be used to draw lines, curves, circles, rectangles, and more in a scalable, resolution independent manner. It is @emph{not} an SVG compliant renderer, nor does it intend to be. However, those familiar with SVG and/or the HTML5 Canvas API should find lots of similarities. @emph{This API is considered to be experimental and may change substantially in future releases of Chickadee. There are many missing features such as gradient fills and dashed strokes.} The first step to rendering vector graphics is to create a @emph{path}: A series of commands that can be thought of as moving a pen around a piece of paper. A path can be either open or closed. A closed path draws a straight line from the last point in the path to the first. @deffn {Procedure} path . commands Return a new path that follows @var{commands}. @example (path (move-to (vec2 50.0 50.0)) (line-to (vec2 500.0 50.0)) (line-to (vec2 400.0 200.0)) (bezier-to (vec2 500.0 250.0) (vec2 380.0 300.0) (vec2 400.0 400.0)) (line-to (vec2 300.0 400.0)) (close-path)) @end example @end deffn Available drawing commands: @deffn {Procedure} move-to point Pick up the pen and move it to @var{point}. @end deffn @deffn {Procedure} line-to point Draw a line from the current pen position to @var{point}. @end deffn @deffn {Procedure} bezier-to control1 control2 point Draw a cubic bezier curve from the current pen position to @var{point}. The shape of the curve is determined by the two control points: @var{control1} and @var{control2}. @end deffn @deffn {Procedure} close-path Draw a straight line back to the first point drawn in the path. @end deffn @deffn {Procedure} arc center rx ry angle-start angle-end Draw an elliptical arc spanning the angle range [@var{angle-start}, @var{angle-end}], centered at @var{center} with radii @var{rx} and @var{ry} (set both to the same value for a circular arc.) @end deffn Included are some helpful procedures for generating common types of paths: @deffn {Procedure} line start end Return a path that draws a straight line from @var{start} to @var{end}. @end deffn @deffn {Procedure} polyline . points Return a path that draws a series of lines connecting @var{points}. @end deffn @deffn {Procedure} rectangle bottom-left width height Return a path that draws a rectangle whose bottom-left corner is at @var{bottom-left} and whose size is defined by @var{width} and @var{height}. @end deffn @deffn {Procedure} square bottom-left size Return a path draws a square whose bottom-left corner is at @var{bottom-left} and whose size is defined by @var{size}. @end deffn @deffn {Procedure} rounded-rectangle bottom-left width height @ [#:radius 4.0] [#:radius-bottom-left] @ [#:radius-bottom-right] [#:radius-top-left] @ [#:radius-top-right] Return a path that draws a rectangle with rounded corners whose bottom-left corner is at @var{bottom-left} and whose size is defined by @var{width} and @var{height}. The argument @var{radius} is used to define the corner radius for all corners. To use a different radius value for a corner, use @var{radius-bottom-left}, @var{radius-bottom-right}, @var{radius-top-left}, and/or @var{radius-top-right}. @end deffn @deffn {Procedure} regular-polygon center num-sides radius Return a path that draws a regular polygon with @var{num-sides} sides centered on the point @var{center} with each vertex @var{radius} units away from the center. @end deffn @deffn {Procedure} ellipse center rx ry Return a path that draws an ellipsed centered on the point @var{center} with radii @var{rx} and @var{ry}. @end deffn @deffn {Procedure} circle center r Return a path that draws a circle centered on the point @var{center} with radius @var{r}. @end deffn With one or more paths created, a @emph{painter} is needed to give the path its style and placement in the final picture. Painters can be combined together to form arbitrarily complex pictures. @deffn {Procedure} stroke . paths Apply a stroked drawing style to @var{paths}. @end deffn @deffn {Procedure} fill . paths Apply a filled drawing style to @var{paths}. @end deffn @deffn {Procedure} fill-and-stroke . paths Apply a filled and stroked drawing style to @var{paths}. @end deffn @deffn {Procedure} transform matrix painter Apply @var{matrix}, a 3x3 transformation matrix, to @var{painter}. @end deffn @deffn {Procedure} translate v painter Translate @var{painter} by the 2D vector @var{v}. @end deffn @deffn {Procedure} rotate angle painter Rotate @var{painter} by @var{angle} radians. @end deffn @deffn {Procedure} scale x painter Scale @var{painter} by the scalar @var{x}. @end deffn @deffn {Procedure} pad pad-x pad-y painter Add @var{pad-x} and @var{pad-y} amount of empty space around @var{painter}. @end deffn @deffn {Procedure} superimpose . painters Stack @var{painters} on top of each other. @end deffn @deffn {Procedure} beside . painters Place @var{painters} next to each other in a row. @end deffn @deffn {Procedure} below . painters Place @var{painters} next to each other in a column. @end deffn @deffn {Syntax} with-style ((style-name value) ...) painter Apply all the given style settings to @var{painter}. Possible style attributes are: @itemize @item blend-mode @item fill-color @item stroke-color @item stroke-width @item stroke-feather @item stroke-cap @end itemize @example (with-style ((stroke-color green) (stroke-width 4.0)) (stroke (circle (vec2 100.0 100.0) 50.0))) @end example @end deffn As in real life, a painter cannot paint anything without a canvas. Once a painter has been associated with a canvas, it can finally be rendered to the screen. @deffn {Procedure} make-canvas painter [#:matrix] Return a new canvas that will @var{painter} will draw on. Optionally, a 3x3 @var{matrix} may be specified to apply an arbitrary transformation to the resulting image. @end deffn @deffn {Procedure} make-empty-canvas [#:matrix] Return a new canvas that no painter is using. Optionally, a 3x3 @var{matrix} may be specified to apply an arbitrary transformation to the image, should a painter later be associated with this canvas. @end deffn @deffn {Procedure} canvas? obj Return @code{#t} is @var{obj} is a canvas. @end deffn @deffn {Procedure} set-canvas-painter! canvas painter Associate @var{painter} with @var{canvas}. @end deffn @deffn {Procedure} set-canvas-matrix! canvas matrix Set the 3x3 transformation matrix of @var{canvas} to @var{matrix}. @end deffn @deffn {Procedure} draw-canvas canvas Render @var{canvas} to the screen. @end deffn @node Particles @subsection Particles Effects like smoke, fire, sparks, etc. are often achieved by animating lots of little, short-lived sprites known as ``particles''. In fact, all of these effects, and more, can be accomplished by turning a few configuration knobs in a ``particle system''. A particle system takes care of managing the many miniscule moving morsels so the developer can quickly produce an effect and move on with their life. The @code{(chickadee graphics particles)} module provides an API for manipulating particle systems. Below is an example of a very simple particle system that utilizes nearly all of the default configuration settings: @example (use-modules (chickadee graphics particles)) (define texture (load-image "particle.png")) (define particles (make-particles 2000 #:texture texture)) @end example In order to put particles into a particle system, a particle ``emitter'' is needed. Emitters know where to spawn new particles, how many of them to spawn, and for how long they should do it. Below is an example of an emitter that spawns 16 particles per frame at the coordinates @code{(320, 240)}: @example (use-modules (chickadee math rect)) (define emitter (make-particle-emitter (make-rect 0.0 0.0 320.0 240.0) 16)) (add-particle-emitter particles emitter) @end example To see all of the tweakable knobs and switches, read on! @deffn {Procedure} make-particles capacity [#:blend-mode] @ [#:color white] [#:end-color transparent] [#:texture] @ [#:animation-rows 1] [#:animation-columns 1] [#:width] [#:height] @ [#:speed-range (vec2 0.1 1.0)] [#:acceleration-range (vec2 0.0 0.1)] @ [#:direction-range (vec2 0 (* 2 pi))] [#:lifetime 30] [#:sort] Return a new particle system that may contain up to @var{capacity} particles. Achieving the desired particle effect involves tweaking the following keyword arguments as needed: - @var{blend-mode}: Pixel blending mode. Alpha blending is used by default. (@pxref{Blending} for more about blend modes). - @var{start-color}: The tint color of the particle at the beginning of its life. White by default. - @var{end-color}: The tint color of the particle at the end of of its life. Completely transparent by default for a fade-out effect. The color in the middle of a particle's life will be an interpolation of @var{start-color} and @var{end-color}. - @var{texture}: The texture applied to the particles. The texture may be subdivided into many animation frames. - @var{animation-rows}: How many animation frame rows there are in the texture. Default is 1. - @var{animation-columns}: How many animation frame columns there are in the texture. Default is 1. - @var{width}: The width of each particle. By default, the width of an animation frame (in pixels) is used. - @var{height}: The height of each particle. By default, the height of an animation frame (in pixels) is used. - @var{speed-range}: A 2D vector containing the min and max particle speed. Each particle will have a speed chosen at random from this range. By default, speed ranges from 0.1 to 1.0. - @var{acceleration-range}: A 2D vector containing the min and max particle acceleration. Each particle will have an acceleration chosen at random from this range. By default, acceleration ranges from 0.0 to 0.1. - @var{direction-range}: A 2D vector containing the min and max particle direction as an angle in radians. Each particle will have a direction chosen at random from this range. By default, the range covers all possible angles. - @var{lifetime}: How long each particle lives, measured in updates. 30 by default. - @var{sort}: @code{youngest} if youngest particle should be drawn last or @code{oldest} for the reverse. By default, no sorting is applied at all. @end deffn @deffn {Procedure} particles? obj Return @code{#t} if @var{obj} is a particle system. @end deffn @deffn {Procedure} update-particles particles Advance the simulation of @var{particles}. @end deffn @deffn {Procedure} draw-particles particles Render @var{particles}. @end deffn @deffn {Procedure} draw-particles* particles matrix Render @var{particles} with @var{matrix} applied. @end deffn @deffn {Procedure} make-particle-emitter spawn-area rate [duration] Return a new particle emitter that spawns @var{rate} particles per frame within @var{spawn-area} (a rectangle or 2D vector) for @var{duration} frames. If @var{duration} is not specified, the emitter will spawn particles indefinitely. @end deffn @deffn {Procedure} particle-emitter? obj Return @code{#t} if @var{obj} is a particle emitter. @end deffn @deffn {Procedure} particle-emitter-spawn-area emitter Return the spawn area for @var{emitter}. @end deffn @deffn {Procedure} particle-emitter-rate emitter Return the number of particles that @var{emitter} will spawn per frame. @end deffn @deffn {Procedure} particle-emitter-life emitter Return the number of frames remaining in @var{emitter}'s lifespan. @end deffn @deffn {Procedure} particle-emitter-done? emitter Return @code{#t} if @var{emitter} has finished spawning particlces. @end deffn @deffn {Procedure} add-particle-emitter particles emitter Add @var{emitter} to @var{particles}. @end deffn @deffn {Procedure} remove-particle-emitter particles emitter Remove @var{emitter} to @var{particles} @end deffn @node Tile Maps @subsection Tile Maps A tile map is a scene created by composing lots of small sprites, called ``tiles'', into a larger image. One program for editing such maps is called @url{http://mapeditor.org,Tiled}. Chickadee has native support for loading and rendering Tiled maps in the @code{(chickadee graphics tile-map)} module. @deffn {Procedure} load-tile-map file-name Load the Tiled formatted map in @var{file-name} and return a new tile map object. @end deffn @deffn {Procedure} tile-map? obj Return @code{#t} if @var{obj} is a tile map. @end deffn @deffn {Procedure} tile-map-orientation tile-map Return the orientation of @var{tile-map}. @end deffn @deffn {Procedure} tile-map-width tile-map Return the width of @var{tile-map} in tiles. @end deffn @deffn {Procedure} tile-map-height tile-map Return the height of @var{tile-map} in tiles. @end deffn @deffn {Procedure} tile-map-tile-width tile-map Return the width of tiles in @var{tile-map}. @end deffn @deffn {Procedure} tile-map-tile-height tile-map Return the height of tiles in @var{tile-map}. @end deffn @deffn {Procedure} tile-map-tilesets tile-map Return the tilesets for @var{tile-map}. @end deffn @deffn {Procedure} tile-map-layers tile-map Return the layers of @var{tile-map}. @end deffn @deffn {Procedure} tile-map-properties tile-map Return the custom properties of @var{tile-map}. @end deffn @deffn {Procedure} point->tile tile-map x y Translate the pixel coordinates (@var{x}, @var{y}) into tile coordinates. @end deffn @deffn {Procedure} draw-tile-map tile-map [#:layers] [#:camera] @ [#:origin] [#:position] [#:scale] [#:rotation] [#:blend-mode] @ [#:tint] [#:time] [#:chunk-size] Draw the layers of @var{tile-map} as viewed from @var{camera}, a 2D vector offset. By default, all layers are drawn. To draw a subset of the available layers, pass a list of layer ids using the @var{layers} keyword argument. @end deffn @deffn {Procedure} tileset? obj Return @code{#t} if @var{obj} is a tileset. @end deffn @deffn {Procedure} tileset-name tileset Return the name of @var{tileset}. @end deffn @deffn {Procedure} tileset-first-gid tileset Return the starting GID of @var{tileset}. @end deffn @deffn {Procedure} tileset-size tileset Return the number of tiles in @var{tileset}. @end deffn @deffn {Procedure} tileset-tile-width tileset Return the width of tiles in @var{tileset}. @end deffn @deffn {Procedure} tileset-tile-height tileset Return the height of tiles in @var{tileset}. @end deffn @deffn {Procedure} tileset-atlas tileset Return the texture atlas for @var{tileset}. @end deffn @deffn {Procedure} tileset-tiles tileset Return the tiles in @var{tileset}. @end deffn @deffn {Procedure} tileset-rows tileset Return the number of rows in @var{tileset}. @end deffn @deffn {Procedure} tileset-columns tileset Return the number of columns in @var{tileset}. @end deffn @deffn {Procedure} tileset-properties tileset Return the custom properties of @var{tileset}. @end deffn @deffn {Procedure} tile? obj Return @code{#t} if @var{obj} is a tile. @end deffn @deffn {Procedure} tile-id tile Return the ID of @var{tile}. @end deffn @deffn {Procedure} tile-animation tile Return the animation for @var{tile}. @end deffn @deffn {Procedure} tile-properties tile Return the custom properties of @var{tile}. @end deffn @deffn {Procedure} animation? obj Return @code{#t} if @var{obj} is an animation. @end deffn @deffn {Procedure} animation-frames animation Return a vector of frames in @var{animation}. @end deffn @deffn {Procedure} animation-duration animation Return the duration of @var{animation}. @end deffn @deffn {Procedure} animation-frame? obj Return @code{#t} if @var{obj} is an animation frame. @end deffn @deffn {Procedure} animation-frame-tile frame Return the tile for @var{frame}. @end deffn @deffn {Procedure} animation-frame-duration frame Return the duration of @var{frame}. @end deffn @deffn {Procedure} tile-layer? obj Return @code{#t} if @var{obj} is a tile layer. @end deffn @deffn {Procedure} tile-layer-name layer Return the name of @var{layer}. @end deffn @deffn {Procedure} tile-layer-width layer Return the width in tiles of @var{layer}. @end deffn @deffn {Procedure} tile-layer-height layer Return the height in tiles of @var{layer}. @end deffn @deffn {Procedure} tile-layer-tiles layer Return the tile data for @var{layer}. @end deffn @deffn {Procedure} tile-layer-properties layer Return the custom properties of @var{layer}. @end deffn @deffn {Procedure} object-layer? obj Return @code{#t} if @var{obj} is an object layer. @end deffn @deffn {Procedure} object-layer-name layer Return the name of @var{layer}. @end deffn @deffn {Procedure} object-layer-objects layer Return the objects for @var{layer}. @end deffn @deffn {Procedure} object-layer-properties layer Return the custom properties of @var{layer}. @end deffn @deffn {Procedure} map-object? obj Return @code{#t} if @var{obj} is a map object. @end deffn @deffn {Procedure} map-object-id obj Return the ID of @var{obj}. @end deffn @deffn {Procedure} map-object-name obj Return the name of @var{obj}. @end deffn @deffn {Procedure} map-object-type obj Return the type of @var{obj}. @end deffn @deffn {Procedure} map-object-shape obj Return the shape of @var{obj}. @end deffn @deffn {Procedure} map-object-properties obj Return the custom properties of @var{obj}. @end deffn @deffn {Procedure} polygon? obj Return @code{#t} if @var{obj} is a polygon. @end deffn @deffn {Procedure} polygon-points polygon Return the list of points that form @var{polygon}. @end deffn @node 3D Models @subsection 3D Models @emph{Disclaimer: Chickadee is alpha software, but 3D model support is even more alpha than that. There are many missing features in both the model loading and rendering components, so set your expectations accordingly!} Chickadee can load and render 3D models in the classic OBJ and more modern glTF 2.0 formats. Here's some basic boilerplate to render a 3D model: @example (use-modules (chickadee) (chickadee math) (chickadee math matrix) (chickadee graphics model)) (define model #f) (define projection-matrix (perspective-projection (/ pi 3.0) (/ 4.0 3.0) 0.1 500.0)) ;; Adjust these 2 matrices so that you can actually see the model. (define view-matrix (make-identity-matrix4)) (define model-matrix (make-identity-matrix4)) (define (load) (set! model (load-obj "model.obj")) (define (draw alpha) (with-projection projection-matrix (with-depth-test #t (draw-model model model-matrix view-matrix)))) (run-game #:load load #:draw draw) @end example @deffn {Procedure} load-obj file-name Load the OBJ formatted model in @var{file-name} and return a 3D model object. OBJ models are rendered using a Phong lighting model, which is a work-in-progress. @end deffn @deffn {Procedure} load-gltf file-name Load the glTF 2.0 formatted model in @var{file-name} and return a 3D model object. glTF models are rendered using a physically based lighting model, which is currently a stub to be implemented later. @end deffn @deffn {Procedure} model? obj Return @code{#t} if @var{obj} is a 3D model. @end deffn @deffn {Procedure} draw-model model model-matrix view-matrix Render @var{model} with the transformation matrices @var{model-matrix} and @var{view-matrix} applied. @end deffn @node Blending @subsection Blending Rendering a scene often involves drawing layers of objects that overlap each other. Blending determines how two overlapping pixels are combined in the final image that is rendered to the screen. Chickadee provides the following blend modes: @defvar blend:alpha Blend pixels according to the values of their alpha channels. This is the most commonly used blend mode. @end defvar @defvar blend:replace Overwrite the output pixel color with the color being drawn. @end defvar @defvar blend:add Add all pixel color values together. The more colors blended together, the more white the final color becomes. @end defvar @defvar blend:subtract Subtract all pixel color values. The more colors blended together, the more black the final color becomes. @end defvar @defvar blend:multiply @end defvar @defvar blend:darken @end defvar @defvar blend:lighten @end defvar @defvar blend:screen @end defvar Custom blend modes can be created using the @code{make-blend-mode} procedure: @deffn {Procedure} make-blend-mode equation source-function destination-function Return a new custom blend mode that applies @var{source-function} to the source color, @var{destination-function} to the destination color, and finally applies @var{equation} to the transformed source/destination color values. These arguments are @emph{not} procedures, but symbolic representations of the functions that OpenGL supports. Valid values for @var{equation} are: @itemize @item @code{add} @item @code{subtract} @item @code{reverse-subtract} @item @code{min} @item @code{max} @item @code{alpha-min} @item @code{alpha-max} @end itemize Valid values for @var{source-function} are: @itemize @item @code{zero} @item @code{one} @item @code{destination-color} @item @code{one-minus-destination-color} @item @code{source-alpha-saturate} @item @code{source-alpha} @item @code{one-minus-source-alpha} @item @code{destination-alpha} @item @code{one-minus-destination-alpha} @item @code{constant-color} @item @code{one-minus-constant-color} @item @code{constant-alpha} @item @code{one-minus-constant-alpha} @end itemize Valid values for @var{destination-function} are: @itemize @item @code{zero} @item @code{one} @item @code{source-color} @item @code{one-minus-source-color} @item @code{source-alpha} @item @code{one-minus-source-alpha} @item @code{destination-alpha} @item @code{one-minus-destination-alpha} @item @code{constant-color} @item @code{one-minus-constant-color} @item @code{constant-alpha} @item @code{one-minus-constant-alpha} @end itemize @end deffn @node Framebuffers @subsection Framebuffers A framebuffer is a chunk of memory that the GPU can render things onto. By default, the framebuffer that is used for rendering is the one belonging to the game window, but custom framebuffers can be used as well. A common use-case for custom framebuffers is applying post-processing effects: The entire scene is rendered to a framebuffer, and then the contents of that framebuffer are applied to a post-processing shader and rendered to the game window. The post-processing shader could do any number of things: scaling, antialiasing, motion blur, etc. @deffn {Procedure} make-framebuffer width height @ [#:min-filter @code{linear}] @ [#:mag-filter @code{linear}] @ [#:wrap-s @code{repeat}] @ [#:wrap-t @code{repeat}] Create a new framebuffer that is @var{width} pixels wide and @var{height} pixels high. @var{min-filter} and @var{mag-filter} determine the scaling algorithm applied to the framebuffer when rendering. By default, linear scaling is used in both cases. To perform no smoothing at all, use @code{nearest} for simple nearest neighbor scaling. This is typically the best choice for pixel art games. @end deffn @deffn {Procedure} framebuffer? obj Return @code{#t} if @var{obj} is a framebuffer. @end deffn @deffn {Procedure} framebuffer-texture fb Return the texture backing the framebuffer @var{fb}. @end deffn @deffn {Procedure} framebuffer-viewport fb Return the default viewport (@pxref{Viewports}) used by the framebuffer @var{fb}. @end deffn @deffn {Procedure} null-framebuffer The default framebuffer. @end deffn @node Viewports @subsection Viewports A viewport represents a subset of the screen (or framebuffer). When rendering a frame, the resulting image will only appear within that viewport. These aren't often needed, and Chickadee's default viewport occupies the entire screen, but there are certain situations where they are useful. For example, a split-screen multiplayer game may render to two different viewports, each occupying a different half of the screen. For information about how to set the current viewport, see @code{with-viewport} in @ref{Rendering Engine}. The @code{(chickadee graphics viewport)} module provides the following API: @deffn {Procedure} make-viewport x y width height @ [#:clear-color] [#:clear-flags] Create a viewport that covers an area of the window starting from coordinates (@var{x}, @var{y}) and spanning @var{width} @code{x} @var{height} pixels. Fill the viewport with @var{clear-color} when clearing the screen. Clear the buffers denoted by the list of symbols in @var{clear-flags}. Possible values for @var{clear-flags} are @var{color-buffer}, @var{depth-buffer}, @var{accum-buffer}, and @var{stencil-buffer}. @end deffn @deffn {Procedure} viewport? obj Return @code{#t} if @var{obj} is a viewport. @end deffn @deffn {Procedure} viewport-x viewport Return the left edge of @var{viewport}. @end deffn @deffn {Procedure} viewport-y viewport Return the bottom edge of @var{viewport}. @end deffn @deffn {Procedure} viewport-width viewport Return the width of @var{viewport}. @end deffn @deffn {Procedure} viewport-height viewport Return the height of @var{viewport}. @end deffn @deffn {Procedure} viewport-clear-color viewport Return the clear color for @var{viewport}. @end deffn @deffn {Procedure} viewport-clear-flags viewport Return the list of clear flags for @var{viewport}. @end deffn @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{Buffers}) and zero or more keyword arguments that the shader understands. Similar to how Scheme has @code{apply} for calling procedures, Chickadee provides @code{shader-apply} for calling shaders. Additionally, there is some dynamic state that effects how @code{shader-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{shader-apply} is called. The following procedures and syntax can be found in the @code{(chickadee graphics)} module. @deffn {Syntax} shader-apply shader vertex-array @ [#:uniform-key uniform-value @dots{}] @deffnx {Syntax} shader-apply* shader vertex-array count @ [#:uniform-key uniform-value @dots{}] Render @var{vertex-array} using @var{shader} with the uniform values specified in the following keyword arguments. While @code{shader-apply} will draw every vertex in @var{vertex-array}, @code{shader-apply*} will only draw @var{count} vertices. @end deffn @deffn {Syntax} shader-apply/instanced shader vertex-array @ n [#:uniform-key uniform-value @dots{}] @deffnx {Syntax} shader-apply/instanced shader vertex-array @ count n [#:uniform-key uniform-value @dots{}] Render @var{vertex-array} @var{n} times using @var{shader} with the uniform values specified in the following keyword arguments. Instanced rendering is very beneficial for rendering the same object many times with only small differences for each one. For example, the particle effects described in @ref{Particles} use instanced rendering. While @code{shader-apply/instanced} will draw every vertex in @var{vertex-array}, @code{shader-apply*} will only draw @var{count} vertices. @end deffn @deffn {Procedure} current-viewport Return the currently bound viewport (@pxref{Viewports}). @end deffn @deffn {Procedure} current-framebuffer Return the currently bound framebuffer (@pxref{Framebuffers}). @end deffn @deffn {Procedure} current-blend-mode Return the currently bound blend mode (@pxref{Blending}). @end deffn @deffn {Procedure} current-depth-test Return @code{#t} if depth testing is currently enabled (@pxref{Blending}). @end deffn @deffn {Procedure} current-texture Return the currently bound texture (@pxref{Textures}). @end deffn @deffn {Procedure} current-projection Return the currently bound projection matrix (@pxref{Matrices}). @end deffn @deffn {Syntax} with-viewport viewport body @dots{} Evaluate @var{body} with the current viewport bound to @var{viewport} (@pxref{Viewports}). @end deffn @deffn {Syntax} with-framebuffer framebuffer body @dots{} Evaluate @var{body} with the current framebuffer bound to @var{framebuffer} (@pxref{Framebuffers}). @end deffn @deffn {Syntax} with-blend-mode blend-mode body @dots{} Evaluate @var{body} with the current blend mode bound to @var{blend-mode} (@pxref{Blending}). @end deffn @deffn {Syntax} with-depth-test depth-test? body @dots{} Evaluate @var{body} with the depth-test disabled if @var{depth-test?} is @code{#f}, or enabled otherwise (@pxref{Blending}). @end deffn @deffn {Syntax} with-texture texture body @dots{} Evaluate @var{body} with the current texture bound to @var{texture} (@pxref{Textures}). @end deffn @deffn {Syntax} with-projection projection body @dots{} Evaluate @var{body} with the current projection matrix bound to @var{projection} (@pxref{Matrices}). @end deffn @node Buffers @subsection Buffers Alright, let's brush aside all of those pretty high level abstractions and discuss what is going on under the hood. The GPU exists as a discrete piece of hardware separate from the CPU. In order to make it draw things, we must ship lots of data out of our memory space and into the GPU. The @code{(chickadee graphics buffer}) module provides an API for manipulating GPU buffers. In OpenGL terminology, a chunk of data allocated on the GPU is a ``vertex buffer object'' or VBO. For example, here is a bytevector that could be transformed into a GPU buffer that packs together vertex position and texture coordinates: @example (use-modules (chickadee graphics buffer) (srfi srfi-4)) (define data (f32vector -8.0 -8.0 ; 2D vertex 0.0 0.0 ; 2D texture coordinate 8.0 -8.0 ; 2D vertex 1.0 0.0 ; 2D texture coordinate 8.0 8.0 ; 2D vertex 1.0 1.0 ; 2D texture coordinate -8.0 8.0 ; 2D vertex 0.0 1.0)) ; 2D texture coordinate @end example This data represents a textured 16x16 square centered on the origin. To send this data to the GPU, the @code{make-buffer} procedure is needed: @example (define buffer (make-buffer data #:stride 16)) @end example The @code{#:stride} keyword argument indicates how many bytes make up each element of the buffer. In this case, there are 4 floats per element: 2 for the vertex, and 2 for the texture coordinate. A 32-bit float is 4 bytes in length, so the buffer's stride is 16. Within a VBO, one or more ``attributes'', as OpenGL calls them, may be present. Attributes are subregions within the buffer that have a particular data type. In this case, there are two attributes packed into the buffer. To provided a typed view into a buffer, the @code{make-buffer-view} procedure is needed: @example (define vertices (make-buffer-view #:buffer buffer #:type 'vec2 #:component-type 'float #:length 4)) (define texcoords (make-buffer-view #:buffer buffer #:type 'vec2 #:component-type 'float #:length 4 #:offset 8)) @end example To render a square, the GPU needs to draw two triangles, which means we need 6 vertices in total. However, the above buffer only contains data for 4 vertices. This is because there are only 4 unique vertices for a square, but 2 of them must be repeated for each triangle. To work with deduplicated vertex data, an ``index buffer'' must be created. @example (define index-buffer (make-buffer (u32vector 0 3 2 0 2 1) #:target 'index) (define indices (make-buffer-view #:type 'scalar #:component-type 'unsigned-int #:buffer index-buffer)) @end example Note the use of the @code{#:target} keyword argument. It is required because the GPU treats index data in a special way and must be told which data is index data. Now that the buffer views representing each attribute have been created, all that's left is to bind them all together in a ``vertex array object'', or VAO. Vertex arrays associate each buffer view with an attribute index on the GPU. The indices that are chosen must correspond with the indices that the shader (@pxref{Shaders}) expects for each attribute. @example (define vertex-array (make-vertex-array #:indices indices #:attributes `((0 . ,vertices) (1 . ,texcoords)))) @end example With the vertex array created, the GPU is now fully aware of how to interpret the data that it has been given in the original buffer. Actually rendering this square is left as an exercise to the reader. See the @ref{Shaders} section and the @code{shader-apply} procedure in @ref{Rendering Engine} for the remaining pieces of a successful draw call. Additionally, consider reading the source code for sprites, shapes, or particles to see GPU buffers in action. Without further ado, the API reference: @deffn {Procedure} make-buffer data [#:name "anonymous"] @ [#:length] [#:offset 0] [#:stride 0] [#:target @code{vertex}] @ [#:usage @code{static}] Upload @var{data}, a bytevector, to the GPU. By default, the entire bytevector is uploaded. A subset of the data may be uploaded by specifying the @var{offset}, the index of the first byte to be uploaded, and @var{length}, the number of bytes to upload. If @var{data} is @code{#f}, allocate @var{length} bytes of fresh GPU memory instead. @var{target} and @var{usage} are hints that tell the GPU how the buffer is intended to be used. @var{target} may be: @itemize @item @code{vertex} Vertex attribute data. @item @code{index} Index buffer data. @end itemize @var{usage} may be: @itemize @item @code{static} The buffer data will not be modified after creation. @item @code{stream} The buffer data will be modified frequently. @end itemize @var{name} is simply an arbitrary string for debugging purposes that is never sent to the GPU. @end deffn @deffn {Procedure} buffer? obj Return @code{#t} if @var{obj} is a GPU buffer. @end deffn @deffn {Procedure} index-buffer? buffer Return @code{#t} if @var{buffer} is an index buffer. @end deffn @defvar null-buffer Represents the absence of a buffer. @end defvar @deffn {Procedure} buffer-name buffer Return the name of @var{buffer}. @end deffn @deffn {Procedure} buffer-length buffer Return the length of @var{buffer}. @end deffn @deffn {Procedure} buffer-stride buffer Return the amount of space, in bytes, between each element in @var{buffer}. @end deffn @deffn {Procedure} buffer-target buffer Return the the intended usage of @var{buffer}, either @code{vertex} or @code{index}. @end deffn @deffn {Procedure} buffer-usage buffer Return the intended usage of @var{buffer}, either @code{static} for buffer data that will not change once sent to the GPU, or @code{stream} for buffer data that will be frequently updated from the client-side. @end deffn @deffn {Procedure} buffer-data buffer Return a bytevector containing all the data within @var{buffer}. If @var{buffer} has not been mapped (see @code{with-mapped-buffer}) then this procedure will return @code{#f}. @end deffn @deffn {Syntax} with-mapped-buffer buffer body @dots{} Evaluate @var{body} in the context of @var{buffer} having its data synced from GPU memory to RAM. In this context, @code{buffer-data} will return a bytevector of all the data stored in @var{buffer}. When program execution exits this form, the data (including any modifications) is synced back to the GPU. This form is useful for streaming buffers that need to update their contents dynamically, such as a sprite batch. @end deffn @deffn {Procedure} make-buffer-view #:buffer #:type @ #:component-type #:length [#:offset @code{0}] [#:divisor @code{1}] @ [#:name @code{"anonymous"}] Return a new buffer view for @var{buffer} starting at byte index @var{offset} of @var{length} elements, where each element is of @var{type} and composed of @var{component-type} values. Valid values for @var{type} are: @itemize @item @code{scalar} single number @item @code{vec2} 2D vector @item @code{vec3} 3D vector @item @code{vec4} 4D vector @item @code{mat2} 2x2 matrix @item @code{mat3} 3x3 matrix @item @code{mat4} 4x4 matrix @end itemize Valid values for @var{component-type} are: @itemize @item @code{byte} @item @code{unsigned-byte} @item @code{short} @item @code{unsigned-short} @item @code{int} @item @code{unsigned-int} @item @code{float} @item @code{double} @end itemize @var{divisor} is only needed for instanced rendering applications (see @code{shader-apply/instanced} in @ref{Rendering Engine}) and represents how many instances each vertex element applies to. A divisor of 0 means that a single element is used for every instance and is used for the data being instanced. A divisor of 1 means that each element is used for 1 instance. A divisor of 2 means that each element is used for 2 instances, and so on. @end deffn @deffn {Procedure} buffer-view? obj Return @code{#t} if @var{obj} is a buffer view. @end deffn @deffn {Procedure} buffer-view->buffer buffer-view Return the buffer that @var{buffer-view} is using. @end deffn @deffn {Procedure} buffer-view-name buffer-view Return the name of @var{buffer-view}. @end deffn @deffn {Procedure} buffer-view-offset buffer-view Return the byte offset of @var{buffer-view}. @end deffn @deffn {Procedure} buffer-view-type buffer-view Return the data type of @var{buffer-view}. @end deffn @deffn {Procedure} buffer-view-component-type buffer-view Return the component data type of @var{buffer-view} @end deffn @deffn {Procedure} buffer-view-divisor buffer-view Return the instance divisor for @var{buffer-view}. @end deffn @deffn {Syntax} with-mapped-buffer-view buffer-view body @dots{} Evaluate @var{body} in the context of @var{buffer-view} having its data synced from GPU memory to RAM. See @code{with-mapped-buffer} for more information. @end deffn @deffn {Procedure} make-vertex-array #:indices #:attributes @ [#:mode @code{triangles}] Return a new vertex array using the index data within the buffer view @var{indices} and the vertex attribute data within @var{attributes}. @var{attributes} is an alist mapping shader attribute indices to typed buffers containing vertex data: @example `((1 . ,buffer-view-a) (2 . ,buffer-view-b) @dots{}) @end example By default, the vertex array is interpreted as containing a series of triangles. If another primtive type is desired, the @var{mode} keyword argument may be overridden. The following values are supported: @itemize @item @code{points} @item @code{lines} @item @code{line-loop} @item @code{line-strip} @item @code{triangles} @item @code{triangle-strip} @item @code{triangle-fan} @end itemize @end deffn @defvar null-vertex-array Represents the absence of a vertex array. @end defvar @deffn {Procedure} vertex-array? obj Return @code{#t} if @var{obj} is a vertex array. @end deffn @deffn {Procedure} vertex-array-indices vertex-array Return the buffer view containing index data for @var{vertex-array}. @end deffn @deffn {Procedure} vertex-array-attributes vertex-array Return the attribute index -> buffer view mapping of vertex attribute data for @var{vertex-array}. @end deffn @deffn {Procedure} vertex-array-mode vertex-array Return the primitive rendering mode for @var{vertex-array}. @end deffn @node Shaders @subsection Shaders Shaders are programs that the GPU can evaluate that allow the programmer to completely customized the final output of a GPU draw call. The @code{(chickadee graphics shader)} module provides an API for building custom shaders. Shaders are written in the OpenGL Shading Language, or GLSL for short. Chickadee aspires to provide a domain specific language for writing shaders in Scheme, but we are not there yet. Shader programs consist of two components: A vertex shader and a fragment shader. A vertex shader receives vertex data (position coordinates, texture coordinates, normals, etc.) and transforms them as desired, whereas a fragment shader controls the color of each pixel. Sample vertex shader: @example @verbatim #version 130 in vec2 position; in vec2 tex; out vec2 fragTex; uniform mat4 mvp; void main(void) { fragTex = tex; gl_Position = mvp * vec4(position.xy, 0.0, 1.0); } @end verbatim @end example Sample fragment shader: @example @verbatim #version 130 in vec2 fragTex; uniform sampler2D colorTexture; void main (void) { gl_FragColor = texture2D(colorTexture, fragTex); } @end verbatim @end example This manual will not cover GLSL features and syntax as there is lots of information already available about this topic. One way to think about rendering with shaders, and the metaphor Chickadee uses, is to think about it as a function call: The shader is a function, and it is applied to some ``attributes'' (positional arguments), and some ``uniforms'' (keyword arguments). @example (define my-shader (load-shader "vert.glsl" "frag.glsl")) (define vertices (make-vertex-array @dots{})) (shader-apply my-shader vertices #:color red) @end example @xref{Rendering Engine} for more details about the @code{shader-apply} procedure. Shaders are incredibly powerful tools, and there's more information about them than we could ever fit into this manual, so we highly recommend searching the web for more information and examples. What we can say, though, is how to use our API: @deffn {Procedure} strings->shader vertex-source fragment-source Compile @var{vertex-source}, the GLSL code for the vertex shader, and @var{fragment-source}, the GLSL code for the fragment shader, into a GPU shader program. @end deffn @deffn {Procedure} load-shader vertex-source-file fragment-source-file Compile the GLSL source code within @var{vertex-source-file} and @var{fragment-source-file} into a GPU shader program. @end deffn @deffn {Procedure} make-shader vertex-port fragment-port Read GLSL source from @var{vertex-port} and @var{fragment-port} and compile them into a GPU shader program. @end deffn @deffn {Procedure} shader? obj Return @code{#t} if @var{obj} is a shader. @end deffn @defvar null-shader Represents the absence shader program. @end defvar @deffn {Procedure} shader-uniform shader name Return the metadata for the uniform @var{name} in @var{shader}. @end deffn @deffn {Procedure} shader-uniforms shader Return a hash table of uniforms for @var{shader}. @end deffn @deffn {Procedure} shader-attributes shader Return a hash table of attributes for @var{shader}. @end deffn @deffn {Procedure} shader-uniform-set! shader uniform value @end deffn @subsubsection Attributes @deffn {Procedure} attribute? obj Return @code{#t} if @var{obj} is an attribute. @end deffn @deffn {Procedure} attribute-name attribute Return the variable name of @var{attribute}. @end deffn @deffn {Procedure} attribute-location attribute Return the binding location of @var{attribute}. @end deffn @deffn {Procedure} attribute-type attribute Return the data type of @var{attribute}. @end deffn @subsubsection Uniforms @deffn {Procedure} uniform? obj Return @code{#t} if @var{obj} is a uniform. @end deffn @deffn {Procedure} uniform-name uniform Return the variable name of @var{uniform}. @end deffn @deffn {Procedure} uniform-type uniform Return the data type of @var{uniform}. @end deffn @deffn {Procedure} uniform-value uniform Return the current value of @var{uniform}. @end deffn @subsubsection User-Defined Shader Types The shader examples in this manual thus far have only shown uniforms defined using primitive types. However, GLSL shaders support user-defined compound structs, such as this one: @example @verbatim struct DirectionalLight { vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular; }; uniform DirectionalLight light; @end verbatim @end example While @code{light} is declared as a single uniform in the shader code, OpenGL translates this into @emph{four} uniforms in this case: One uniform each member of the @code{DirectionalLight} struct. This poses a problem for sending Scheme data to the GPU. How can compound Scheme data translate into compound uniform data on the GPU? The answer is with shader types. Shader types are a special kind of Guile struct that provide a one-to-one mapping between a Scheme data structure and a shader struct. Some example code will explain this concept best. Here is the Scheme equivalent of the @code{DirectionalLight} struct: @example (define-shader-type make-directional-light directional-light? (float-vec3 direction directional-light-direction) (float-vec3 ambient directional-light-ambient) (float-vec3 diffuse directional-light-diffuse) (float-vec3 specular directional-light-specular) (float shininess directional-light-shininess)) @end example The macro @code{define-shader-type} closely resembles the familiar @code{define-record-type} from SRFI-9, but with one notable difference: Each struct field contains type information. The type must be one of several primitive types (documented below) or another shader type in the case of a nested structure. It is important to note that the names of the shader type fields @emph{must} match the names of the struct members in the GLSL code, otherwise Chickadee will be unable to perform the proper translation. As of this writing, this interface is new and experimental. It remains to be seen if this model is robust enough for all use-cases. Primitive data types: @defvar bool Either @code{#t} or @code{#f}. @end defvar @defvar int An integer. @end defvar @defvar unsigned-int An unsigned integer. @end defvar @defvar float A floating point number. @end defvar @defvar float-vec2 A 2D vector (@pxref{Vectors}.) @end defvar @defvar float-vec3 A 3D vector (@pxref{Vectors}.) @end defvar @defvar float-vec4 A color. @end defvar @defvar mat4 A matrix (@pxref{Matrices}.) @end defvar @defvar sampler-2d A texture (@pxref{Textures}.) @end defvar @defvar local-field A special type that means that the data is for the client-side (Scheme-side) only and should not be sent to the GPU. Any object may be stored in a local field. @end defvar @deffn {Syntax} define-shader-type constructor predicate @ (field-type field-name [field-getter] [field-setter]) @dots{} Define a new shader data type called @var{}. Instances of this data type are created by calling the @var{constructor} procedure. This procedure maps each field to a keyword argument. A shader data type with the fields @code{foo}, @code{bar}, and @code{baz} would have a constructor that accepts the keyword arguments @code{#:foo}, @code{#:bar}, and @code{#:baz}. A procedure named @var{predicate} will test if an object is a @var{} shader data type. Fields follow the format @code{(field-type field-name [field-getter] [field-setter])}. @var{field-type} and @var{field-name} are required for each field, but @var{field-getter} and @var{field-setter} are optional. @end deffn @deffn {Procedure} shader-data-type? obj Return @code{#t} if @var{obj} is a shader data type object. @end deffn @node Audio @section Audio A game isn't complete without sound. Most games play some cool background music to set the mood and have many sound effects to play when events happen. The @code{(chickadee audio)} module provides a robust audio API backed by the OpenAL 3D audio system. @menu * Audio Files:: Not audiophiles. * Sources:: Audio emitters. * The Listener:: The player's ears. * The Environment:: Global sound model settings. @end menu The basics of playing audio are very simple. Just load an audio file in the load hook (or anywhere else once the game loop is running) and play it! @example (use-modules (chickadee audio)) (define audio #f) (define (load) (set! audio (load-audio "neat-sound-effect.wav")) (audio-play audio)) (run-game #:load load) @end example For more advanced usage, check out the full API reference in the following sections. @node Audio Files @subsection Audio Files Sound data is represented by a special @code{