@menu * Kernel:: The fundamental components. * Math:: Vectors, matrices, bounding boxes, and more. * Graphics:: 2D and 3D rendering. * Audio:: Make some noise. * Scripting:: Bringing the game world to life. * Data Structures:: Queues, heaps, spatial partitioning, and more. @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. @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. If you are using @command{chickadee play} to launch your game, then calling @code{run-game} is already taken care of for you. @deffn {Procedure} run-game [#:window-title "Chickadee!"] @ [#:window-width 640] [#:window-height 480] @ [#:window-fullscreen? @code{#f}] @ [#:window-resizable? @code{#f}] @ [#:update-hz 60] @ [#:clear-color] @ [#: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. The screen color will be set to @var{clear-color}, or a pleasant light blue, by default. @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{mouse-wheel}: Called with two arguments when the mouse wheel is scrolled: @enumerate @item @var{x}: The scroll amount along the X axis. @item @var{y}: The scroll amount along the Y axis. @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 two arguments when an error occurs: @enumerate @item @var{exception}: The exception object. @item @var{stack}: The call stack at the point of the exception. @end enumerate If no error handler is specified, exceptions will simply be re-raised. @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* [#:init] [#: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{init}: Called just before game loop is started. Use it to perform game initialization. @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 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. @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 tau 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 @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 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 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= m1 m2 Return @code{#t} if @var{m1} is the same matrix as @var{m2}. @end deffn @deffn {Procedure} matrix3-copy matrix Return a new 3x3 matrix that is a copy of @var{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 @deffn {Procedure} matrix3-inverse matrix Return the inverse of @var{matrix}. @end deffn The following procedures perform in-place, destructive updates to 3x3 matrix objects: @deffn {Procedure} matrix3-copy! src dest Copy the contents of matrix @var{src} to @var{dest}. @end deffn @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 @deffn {Procedure} matrix3-inverse! matrix target Compute the inverse of @var{matrix} and store the results in @var{target}. @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= m1 m2 Return @code{#t} if @var{m1} is the same matrix as @var{m2}. @end deffn @deffn {Procedure} matrix4-copy matrix Return a new 4x4 matrix that is a copy of @var{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} matrix4-inverse matrix Return the inverse of @var{matrix}. A matrix multiplied by its inverse is the identity matrix, thought not always exactly due to the nature of floating point numbers. @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 The following procedures perform in-place, destructive updates to 4x4 matrix objects: @deffn {Procedure} matrix4-copy! src dest Copy the contents of matrix @var{src} to @var{dest}. @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-inverse! matrix target Compute the inverse of @var{matrix} and store the result in @var{target}. @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] [#:shear] 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{shear}, 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 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, tile atlases, cube maps, etc. * Sprites:: Draw 2D images. * Fonts:: Drawing text. * Vector Paths:: Draw filled and stroked paths. * Particles:: Pretty little flying pieces! * Tile Maps:: Draw 2D tile maps. * Models:: Load 3D models from disk. * Lights:: Dynamic 3D lighting. * Skyboxes:: Backgrounds for 3D environments. * Meshes:: 3D meshes and materials. * Buffers:: Send data to the GPU. * Shaders:: Create custom GPU programs. * Framebuffers:: Render to texture. * Viewports:: Restrict rendering to a particular area. * Render Settings:: Depth tests, stencil tests, polygon modes, etc. * Rendering Engine:: Rendering state management. @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. However, this is a great simplification. Textures can be used to store any kind of data that can be encoded into color channels. 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{mirrored-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 @deffn {Procedure} current-texture-0 Return the current texture associated with texture unit 0 on the GPU. @end deffn @deffn {Procedure} current-texture-1 Return the current texture associated with texture unit 1 on the GPU. @end deffn @deffn {Procedure} current-texture-2 Return the current texture associated with texture unit 2 on the GPU. @end deffn @deffn {Procedure} current-texture-3 Return the current texture associated with texture unit 3 on the GPU. @end deffn @deffn {Procedure} current-texture-4 Return the current texture associated with texture unit 4 on the GPU. @end deffn @defvar g:texture-0 Render state for texture unit 0 (@pxref{Rendering Engine}.) @end defvar @defvar g:texture-1 Render state for texture unit 1 (@pxref{Rendering Engine}.) @end defvar @defvar g:texture-2 Render state for texture unit 2 (@pxref{Rendering Engine}.) @end defvar @defvar g:texture-3 Render state for texture unit 3 (@pxref{Rendering Engine}.) @end defvar @defvar g:texture-4 Render state for texture unit 4 (@pxref{Rendering Engine}.) @end defvar @subsubsection Tile Atlases 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 @subsubsection Cube Maps A cube map is a special type of texture composed of 6 images that can be thought of as forming the 6 faces of a cube. @xref{Skyboxes} for a practical use of cube maps. @deffn {Procedure} load-cube-map #:right #:left #:top #:bottom #:front #:back @ [#:min-filter linear-mipmap-linear] [#:mag-filter linear] Return a new cube map that uses the image data in the files @var{right}, @var{left}, @var{top}, @var{bottom}, @var{front}, and @var{back} for the 6 faces of the cube. @end deffn @deffn {Procedure} cube-map? obj Return @code{#t} if @var{obj} is a cube map. @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 @ [#:blend-mode] [#:origin] [#:rect] [#:rotation] [#:scale] @ [#:shear] [#:tint white] 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. @var{shear} specifies the shearing 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 @subsubsection Sprite Batches 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 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] [:rotation] [#:scale] [#:shear] @ [#:texture-region] [#:tint @code{white}] 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 @subsubsection 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. The @code{(chickadee graphics 9-patch)} module provides this functionality. @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} 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 text)} 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.} 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 @deffn {Procedure} arc-to c1 c2 radius Draw a circular arc with radius @var{radius} that is tangential to the line segment formed by the current pen position and @var{c1}, as well as the line segment formed by @var{c1} and @var{c2}. The result is a smooth corner. @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} bezier-path p1 c1 c2 p2 . points Return a path that draws a series of bezier points starting at @var{p1}, moving to @var{p2} using control points @var{c1} and @var{c2}, and proceeding to draw additional bezier curves as defined by @var{points}. Each additional curve requires 3 additional arguments: two control points and and an ending point. @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 {Syntax} with-style ((style-name value) ...) painter Apply all the given style settings to @var{painter}. Possible style attributes are: @itemize @item @code{blend-mode} @item @code{fill-color} @item @code{stroke-color} @item @code{stroke-width} @item @code{stroke-feather} @item @code{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 Fill colors may be either solid colors (@pxref{Colors}) or gradients. For gradient fills, there are two styles: linear or radial. @deffn {Procedure} linear-gradient [#:origin (vec2 0 0)] [#:start-color white] @ [#:end-color black] [#:rotation 0] [#:offset 0] [#:length 100] Return a new linear gradient that transitions from @var{start-color} on the left to @var{end-color} on the right over @var{length} pixels. @var{offset} may be used to push the gradient start point to the right, creating a solid block of @var{start-color} on the right hand side of the painter. The line's direction may be changed by specifying a @var{rotation} value. The starting point for the gradient is determined by @var{origin} and defaults to the lower left corner of the painter's bounding box. @end deffn @deffn {Procedure} radial-gradient [#:origin (vec2 0 0)] [#:start-color white] @ [#:end-color black] [#:radius 50.0] [#:radius-x] [#:radius-y] @ [#:rotation 0] [#:offset 0] Return a new radial gradient that transitions from @var{start-color} at the point @var{origin} to @var{end-color} at @var{radius} pixels away. @var{offset} specifies the distance from the origin where @var{start-color} begins to transition. The default is to immediately start transitioning. The default shape of this type of gradient is a circle, but it can also be made elliptical by specifying different @var{radius-x} and @var{radius-y} values. When the gradient shape is elliptical, @var{rotation} can be used to rotate it. @end deffn @deffn {Procedure} gradient? obj Return @code{#t} when @var{obj} is a gradient object. @end deffn Painters can also be transformed and combined to form new painters. @deffn {Procedure} transform matrix painter Apply @var{matrix}, a 3x3 transformation matrix (@pxref{Matrices}), 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} horizontal-flip painter Flip @var{painter} horizontally. @end deffn @deffn {Procedure} vertical-flip painter Flip @var{painter} vertically. @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 The next batch of procedures is taken straight out of the picture language described in @url{https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-15.html#%_sec_2.2.4, Structure and Interpretation of Computer Programs} section 2.2.4. @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 {Procedure} right-split painter n Subdivide @var{painter} into 3 sections, recursively, like so: @verbatim *----------------*----------------* | | | | | right-split | | | n - 1 | | | | | painter *----------------* | | | | | right-split | | | n - 1 | | | | *----------------*----------------* @end verbatim @end deffn @deffn {Procedure} up-split painter n Subdivide @var{painter} into 3 sections, recursively, like so: @verbatim *----------------*----------------* | | | | up-split | up-split | | n - 1 | n - 1 | | | | *----------------*----------------* | | | painter | | | | | *----------------*----------------* @end verbatim @end deffn @deffn {Procedure} corner-split painter n Subdivide @var{painter} into 4 sections, recursively, like so: @verbatim *----------------*----------------* | | | | up-split | corner-split | | n - 1 | n - 1 | | | | *----------------*----------------* | | | | painter | right-split | | | n - 1 | | | | *----------------*----------------* @end verbatim @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{Render Settings} 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 [#:chunk-size] 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] 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 Models @subsection 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!} The @code{(chickadee graphics model)} module provides procedures to 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 graphics light) (chickadee graphics model) (chickadee graphics skybox)) (define model (load-gltf "Suzanne.gltf")) (define camera-position (vec3 0.0 0.0 3.0)) (define world (make-identity-matrix4)) (define view (look-at camera-position (vec3 0.0 0.0 0.0) (vec3 0.0 1.0 0.0))) (define projection (perspective-projection (/ pi 3.0) (/ 4.0 3.0) 0.1 5.0)) (define (draw alpha) (with-projection projection (draw-model model world view camera-position @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] @ [#:camera-position (vec3 0 0 0)] [#:skybox] [#:lights '()] Render @var{model} with the transformation matrices @var{model-matrix} and @var{view-matrix} applied. @var{camera-position} is the world position of the camera, for correct specular lighting calculations. @var{skybox} is used to apply ambient lighting to the model. @var{lights} contains all of the dynamic lights (@pxref{Lights}) that should have an effect on the model. @end deffn Models are composed of simpler data types: meshes, primitives, and materials. Let's start with materials. A material controls the appearance of a 3D object. Is the object a rough stone? Or maybe a smooth metal? Materials control all of this and more. There are two types of materials in Chickadee: Phong and PBR. @node Lights @subsection Lights The @code{(chickadee graphics light)} module provides a data type for dynamic lights that can be used to light 3D objects (such as @ref{Models} or @ref{Meshes}.) There are 3 types of dynamic lights: @itemize @item point light: Emits light in all directions from a specific point in space, like a light bulb. @item directional light: Emits light of a constant intensity in a direction, like sunlight. @item spot light: Emits a cone of light in a direction, like a flashlight. @end itemize @deffn {Procedure} make-point-light [#:position (vec3 0 0 0)] [#:color black] @ [#:intensity 1] Return a new point light located at @var{position} that emits @var{color} light. @end deffn @deffn {Procedure} make-directional-light [#:direction (vec3 0 -1 0)] @ [#:color black] [#:intensity 1] Return a new directional light that emits @var{color} light in @var{direction}. @end deffn @deffn {Procedure} make-spot-light [#:position (vec3 0 0 0)] @ [#:direction (vec3 0 -1 0)] [#:color black] [#:cut-off @math{Ï€/4}] @ [#:intensity 1] Return a new spot light located at @var{position} that emits @var{color} light in @var{direction} in a cone that cuts off at @var{cut-off} radians. @end deffn @deffn {Procedure} point-light? obj Return @code{#t} if @var{obj} is a point light. @end deffn @deffn {Procedure} directional-light? obj Return @code{#t} if @var{obj} is a directional light. @end deffn @deffn {Procedure} spot-light? obj Return @code{#t} if @var{obj} is a spot light. @end deffn @deffn {Procedure} light-type light Return the type of @var{light}, one of: @itemize @item point @item directional @item spot @end itemize @end deffn @deffn {Procedure} light-color light Return the color of @var{light}. @end deffn @deffn {Procedure} light-intensity light Return the intensity of @var{light}. @end deffn @deffn {Procedure} light-position light Return the position of @var{light}. The value is irrelevant for directional lights. @end deffn @deffn {Procedure} light-direction light Return the direction of @var{light}. The value is irrelevant for point lights. @end deffn @deffn {Procedure} light-cut-off light Return the cosine of the cut off angle of @var{light}. The value is only relevant for spot lights. @end deffn @deffn {Procedure} set-light-color! light color Set the color of @var{light} to @var{color}. @end deffn @deffn {Procedure} set-light-position! light position Set the position of @var{light} to @var{position}. @end deffn @deffn {Procedure} set-light-direction! light direction Set the direction of @var{light} to @var{direction}. @end deffn @deffn {Procedure} set-light-cut-off! light cut-off Set the cut off angle of @var{light} to @var{cut-off}. @end deffn @node Skyboxes @subsection Skyboxes Skyboxes are used as backgrounds in 3D environments, as well as a source of ambient lighting data when rendering 3D objects. The @code{(chickadee graphics skybox)} module provides an API for making skyboxes. @deffn {Procedure} make-skybox cube-map Return a new skybox that uses @var{cube-map} for a texture. @end deffn @deffn {Procedure} skybox? obj Return @code{#t} if @var{obj} is a skybox. @end deffn @deffn {Procedure} skybox-cube-map sky-box Return the cube map of @var{skybox}. @end deffn @deffn {Procedure} draw-skybox skybox view Render @var{skybox} to the screen using the view matrix @var{view}. @end deffn @node Meshes @subsection Meshes The @code{(chickadee graphics mesh)} modules provides procedures for programmatically creating 3D objects (to load 3D models from a file on disk, @pxref{Meshes}.) @subsubsection Materials Before we talk about meshes themselves, let's discuss ``materials.'' Materials control the appearance of 3D objects. Whether an object appears like a rough rock, a smooth and shiny metal, etc. depends on the material that is applied to it. Different types of materials use different lighting models. Chickadee supports two lighting models: The classic Blinn-Phong model and a more modern physically based rendering (PBR) model. All materials share some common data: @deffn {Procedure} material? obj Return @code{#t} if @var{obj} is a material. @end deffn @deffn {Procedure} material-name material Return the name of @var{material}. @end deffn @deffn {Procedure} material-shader material Return the shader of @var{material}. @end deffn @deffn {Procedure} material-blend-mode material Return the blend mode of @var{material}. @end deffn @deffn {Procedure} material-polygon-mode material Return the polygon mode of @var{material}. @end deffn @deffn {Procedure} material-cull-face-mode material Return the cull face mode of @var{material}. @end deffn @deffn {Procedure} material-depth-test material Return the depth test of @var{material}. @end deffn @deffn {Procedure} material-stencil-test material Return the stencil test of @var{material}. @end deffn @deffn {Procedure} material-multisample? material Return @code{#t} if @var{material} uses multisampling. @end deffn Materials support 5 textures. What each texture is used for depends on the lighting model. @deffn {Procedure} material-texture-0 material Return the first texture of @var{material}. @end deffn @deffn {Procedure} material-texture-1 material Return the second texture of @var{material}. @end deffn @deffn {Procedure} material-texture-2 material Return the third texture of @var{material}. @end deffn @deffn {Procedure} material-texture-3 material Return the fourth texture of @var{material}. @end deffn @deffn {Procedure} material-texture-4 material Return the fifth texture of @var{material}. @end deffn For all data that is specific to the lighting model, materials have a ``properties'' field. @deffn {Procedure} material-properties material Return the lighting model specific properties of @var{material}. @end deffn @subsubsection Blinn-Phong Materials The @code{(chickadee graphics phong)} module has the Blinn-Phong lighting model: @deffn {Procedure} make-phong-material [#:name "anonymous"] [#:blend-mode] @ [#:polygon-mode] [#:cull-face-mode] [#:depth-test] [#:stencil-test] @ [#:multisample? #f] [#:ambient-factor (vec3 1 1 1)] @ [#:diffuse-factor (vec3 1 1 1)] [#:specular-factor (vec3 1 1 1)] @ [#:shininess 32] [#:ambient-texture] [#:diffuse-texture] @ [#:specular-texture] [#:normal-texture] Return a new Blinn-Phong material. @end deffn @deffn {Procedure} make-phong-properties [#:ambient] [#:diffuse] [#:specular] @ [#:shininess] Return a new Blinn-Phong properties object. @end deffn @deffn {Procedure} phong-properties? obj Return @code{#t} if @var{obj} is a Blinn-Phong properties object. @end deffn @deffn {Procedure} phong-properties-ambient properties Return the ambient factor of @var{properties}. @end deffn @deffn {Procedure} phong-properties-diffuse properties Return the diffuse factor of @var{properties}. @end deffn @deffn {Procedure} phong-properties-specular properties Return the specular factor of @var{properties}. @end deffn @deffn {Procedure} phong-properties-shininess properties Return the shininess factor of @var{properties}. @end deffn @subsubsection PBR Materials The @code{(chickadee graphics pbr)} module has the PBR lighting model: @deffn {Procedure} make-pbr-material [#:name "anonymous"] [#:blend-mode] @ [#:polygon-mode] [#:cull-face-mode] [#:depth-test] [#:stencil-test] @ [#:multisample? #f] [#:base-color-factor (vec3 1 1 1)] @ [#:base-color-texcoord 0] [#:metallic-factor 1.0] @ [#:roughness-factor 1.0] [#:metallic-roughness-texcoord 0] @ [#:normal-texcoord 0] [#:occlusion-texcoord 0] @ [#:emissive-factor (vec3 1 1 1)] [#:emissive-texcoord 0] @ [#:alpha-mode opaque] [#:alpha-cutoff 0.5] @ [#:base-color-texture] [#:metallic-roughness-texture] @ [#:normal-texture] [#:occlusion-texture] [#:emissive-texture] Return a new PBR material. @end deffn @deffn {Procedure} make-pbr-properties [#:base-color-factor] @ [#:base-color-texcoord] [#:metallic-factor] [#:roughness-factor] @ [#:metallic-roughness-texcoord] [#:normal-texcoord] @ [#:occlusion-texcoord] [#:emissive-factor] [#:emissive-texcoord] @ [#:alpha-mode] [#:alpha-cutoff] Return a new PBR properties object. @end deffn @deffn {Procedure} pbr-properties? obj Return @code{#t} if @var{obj} is a PBR properties object. @end deffn @deffn {Procedure} pbr-properties-base-color-factor properties Return the base color factor of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-base-color-texcoord properties Return the base color texture coordinate attribute index of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-metallic-factor properties Return the metallic factor of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-roughness properties Return the roughness factor of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-metallic-roughness-texcoord properties Return the metallic-roughness texture coordinate attribute index of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-normal-texcoord properties Return the normal texture coordinate attribute index of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-occlusion-texcoord properties Return the ambient occlusion texture coordinate attribute index of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-emissive-factor properties Return the emissive factor of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-emissive-texcoord properties Return the emissive texture coordinate attribute index of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-alpha-mode properties Return the alpha mode of @var{properties}. @end deffn @deffn {Procedure} pbr-properties-alpha-cutoff properties Return the alpha cutoff threshold of @var{properties}. @end deffn @subsubsection Primitives and Meshes A mesh is a collection of ``primitives,'' so we should discuss those next. A primitive contains vertex data and a material. @deffn {Procedure} make-primitive name vertex-array material Return a new primitive named @var{name} that renders @var{vertex-array} (@pxref{Buffers}) using @var{material}. @end deffn @deffn {Procedure} primitive? obj Return @code{#t} if @var{obj} is a primitive. @end deffn @deffn {Procedure} primitive-name primitive Return the name of @var{primitive}. @end deffn @deffn {Procedure} primitive-vertex-array primitive Return the vertex array of @var{primitive}. @end deffn @deffn {Procedure} primitive-material primitive Return the material of @var{primitive}. @end deffn Okay, now we can talk about meshes, which are just a glorified list of primitive objects. @deffn {Procedure} make-mesh name primitives Return a new mesh named @var{name} that is composed of the list @var{primitives}. @end deffn @deffn {Procedure} mesh? obj Return @code{#t} if @var{obj} is a mesh. @end deffn @deffn {Procedure} mesh-name mesh Return the name of @var{mesh}. @end deffn @deffn {Procedure} mesh-primitives mesh Return the list of primitives for @var{mesh}. @end deffn The mesh module also conveniently provides procedures to build several basic 3D shapes. @deffn {Procedure} make-plane length width material Return a new mesh that forms a flat plane on the XZ axis that is @var{width} units long along the X axis and @var{length} units long along the Z axis. @end deffn @deffn {Procedure} make-tesselated-plane length width resolution material Return a new mesh that forms a tesselated plane on the XZ axis that is @var{width} units long along the X axis and @var{length} units long along the Z axis. A regular plane is a single rectangle, but a tesselated plane is subdivided into many smaller rectangles by @var{resolution}. This allows for transforming the vertices in a shader to achieve effects such as waves in water or mountainous terrain. @end deffn @deffn {Procedure} make-cube size material Return a new mesh that forms a cube that is @var{size} units big. @end deffn @deffn {Procedure} make-sphere radius material [#:quality 2] Return a new mesh that forms a sphere that has a radius of @var{radius} units. Since 3D models are composed of triangles, the quality of a sphere is entirely dependent upon how many triangles are used to appromixate the shape. The higher the value of @var{quality}, the better the appromixation, but the more time it will take to generate and the more expensive it will be to draw. The number of triangles in the resulting sphere increases exponentially with each increment to @var{quality}. @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 define vertex attributes, the @code{make-vertex-attribute} procedure is needed: @example (define vertices (make-vertex-attribute #:buffer buffer #:type 'vec2 #:component-type 'float #:length 4)) (define texcoords (make-vertex-attribute #: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-vertex-attribute #: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 vertex attributes have been created, all that's left is to bind them all together in a vertex array. Vertex arrays associate each vertex attribute 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. @deffn {Procedure} current-buffer Return the current buffer. @end deffn @defvar g:buffer Render state for buffers (@pxref{Rendering Engine}.) @end defvar This form is useful for streaming buffers that need to update their contents dynamically, such as a sprite batch. @end deffn @deffn {Procedure} make-vertex-attribute #:buffer #:type @ #:component-type #:length [#:offset @code{0}] [#:divisor @code{1}] @ [#:name @code{"anonymous"}] Return a new vertex attribute 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} vertex-attribute? obj Return @code{#t} if @var{obj} is a vertex attribute. @end deffn @deffn {Procedure} vertex-attribute->buffer vertex-attribute Return the buffer that @var{vertex-attribute} is using. @end deffn @deffn {Procedure} vertex-attribute-name vertex-attribute Return the name of @var{vertex-attribute}. @end deffn @deffn {Procedure} vertex-attribute-offset vertex-attribute Return the byte offset of @var{vertex-attribute}. @end deffn @deffn {Procedure} vertex-attribute-type vertex-attribute Return the data type of @var{vertex-attribute}. @end deffn @deffn {Procedure} vertex-attribute-component-type vertex-attribute Return the component data type of @var{vertex-attribute} @end deffn @deffn {Procedure} vertex-attribute-divisor vertex-attribute Return the instance divisor for @var{vertex-attribute}. @end deffn @deffn {Syntax} with-mapped-vertex-attribute vertex-attribute body @dots{} Evaluate @var{body} in the context of @var{vertex-attribute} 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 vertex attributes @var{indices} and the vertex attribute data within @var{attributes}. @var{attributes} is an alist mapping shader attribute indices to vertex attributes: @example `((1 . ,vertex-attribute-a) (2 . ,vertex-attribute-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 @deffn {Procedure} current-vertex-array Return the current vertex array. @end deffn @defvar g:vertex-array Render state for vertex arrays (@pxref{Rendering Engine}.) @end defvar @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 Light { bool enabled; int type; vec3 position; vec3 direction; vec4 color; float intensity; float cutOff; }; uniform Light light; @end verbatim @end example While @code{light} is declared as a single uniform in the shader code, OpenGL translates this into @emph{seven} uniforms in this case: One uniform each member of the @code{Light} 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{Light} struct: @example (define-shader-type make-light light? (bool enabled light-enabled?) (int type light-type) (float-vec3 position light-position) (float-vec3 direction light-direction) (float-vec4 color light-color) (float intensity light-intensity) (float cut-off light-cut-off)) @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 (@pxref{Colors}) or rectangle (@pxref{Rectangles}.) @end defvar @defvar mat3 A 3x3 matrix (@pxref{Matrices}.) @end defvar @defvar mat4 A 4x4 matrix (@pxref{Matrices}.) @end defvar @defvar sampler-2d A texture (@pxref{Textures}.) @end defvar @defvar sampler-cube A cube map (@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 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 @deffn {Procedure} current-framebuffer Return the current framebuffer. @end deffn @defvar g:framebuffer Render state for framebuffers (@pxref{Rendering Engine}.) @end defvar @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. 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 @deffn {Procedure} current-viewport Return the current viewport. @end deffn @defvar g:viewport Render state for viewports (@pxref{Rendering Engine}.) @end defvar @node Render Settings @subsection Render Settings @subsubsection 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. The @code{(chickadee graphics blend)} module provides a data type for blending modes. 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 @subsubsection Polygon Modes and Culling The @code{(chickadee graphics polygon)} module provides access to the @code{g:polygon-mode} and @code{g:cull-face-mode} render states. @defvar fill-polygon-mode Completely fill in the polygon. This is the default mode. @end defvar @defvar line-polygon-mode Render only the edges of the polygon. Produces a wireframe. @end defvar @defvar point-polygon-mode Render only the vertex positions as points. @end defvar @deffn {Procedure} make-polygon-mode front back Return a new polygon mode that uses the method @var{front} for the front face and @var{back} for the back face. The valid modes are @code{fill}, @code{line}, and @var{point}. @end deffn @deffn {Procedure} polygon-mode? obj Return @code{#t} if @var{obj} is a polygon mode. @end deffn @deffn {Procedure} current-polygon-mode Return the current polygon mode. @end deffn @defvar g:polygon-mode Render state for polygon modes (@pxref{Rendering Engine}.) @end defvar @defvar no-cull-face-mode Don't cull any faces. @end defvar @defvar back-cull-face-mode Cull only back faces. @end defvar @defvar front-cull-face-mode Cull only front faces. @end defvar @defvar front-and-back-cull-face-mode Cull both front and back faces. @end defvar @deffn {Procedure} cull-face-mode? obj Return @code{#t} if @var{obj} is a cull face mode. @end deffn @deffn {Procedure} cull-face-mode-front? cull-face-mode Return @code{#t} if @var{cull-face-mode} culls front faces. @end deffn @deffn {Procedure} cull-face-mode-back? cull-face-mode Return @code{#t} if @var{cull-face-mode} culls back faces. @end deffn @deffn {Procedure} current-cull-face-mode Return the current cull face mode. @end deffn @defvar g:cull-face-mode Render state for cull face modes (@pxref{Rendering Engine}.) @end defvar @subsubsection Depth Testing The @code{(chickadee graphics depth)} module provides access to the @code{g:depth-test} render state. @deffn {Procedure} make-depth-test [#:write? #t] [#:function 'less-than] @ [#:near 0.0] [#:far 1.0] Return a new depth test object. If @var{write} is @code{#t}, the depth buffer will be written to during a draw call. @var{near} and @var{far} define the min/max Z values for which depth testing may pass. @var{function} specifies how the depth value of pixel being drawn compares to the depth value that is already in the depth buffer. When this comparison is true, the depth test passes and the pixel is drawn. When it fails, the pixel is discarded. The possible values of @var{function} are: @itemize @item @code{always} @item @code{never} @item @code{equal} @item @code{not-equal} @item @code{less-than} @item @code{less-than-or-equal} @item @code{greater-than} @item @code{greater-than-or-equal} @end itemize @end deffn @deffn {Procedure} depth-test? obj Return @code{#t} when @var{obj} is a depth test object. @end deffn @deffn {Procedure} depth-test-write? depth-test Return @code{#t} when @var{depth-test} will write to the depth buffer. @end deffn @deffn {Procedure} depth-test-function depth-test Return the comparison function of @var{depth-test}. @end deffn @deffn {Procedure} depth-test-near depth-test Return the near Z value of @var{depth-test}. @end deffn @deffn {Procedure} depth-test-far depth-test Return the far Z value of @var{depth-test}. @end deffn @deffn {Procedure} current-depth-test Return the current depth test. @end deffn @defvar g:depth-test Render state for depth tests (@pxref{Rendering Engine}.) @end defvar @subsubsection Stencil Testing The @code{(chickadee graphics stencil)} module provides access to the @code{g:stencil-test} render state. @defvar default-stencil-test A stencil test that always passes. @end defvar @deffn {Procedure} make-stencil-test [#:mask #xFF] [#:function always] @ [#:function-mask #xFF] [#:reference 0] [#:on-fail keep] @ [#:on-depth-fail keep] [#:on-pass keep] [#:mask-front mask] @ [#:mask-back mask] [#:function-mask-front function-mask] @ [#:function-mask-back function-mask] [#:refrence-front reference] @ [#:reference-back reference] [#:on-fail-front on-fail] @ [#:on-fail-back on-fail] [#:on-depth-fail-front on-depth-fail] @ [#:on-depth-fail-back on-depth-fail] [#:on-pass-front on-pass] @ [#:on-pass-back on-pass] Return a new stencil test object. Different configurations can be used for the front and back faces by using the arguments that end in ``front'' or ``back''. Valid values for @var{on-pass}, @var{on-fail}, and @var{on-depth-fail} are: @itemize @item @code{zero} @item @code{keep} @item @code{replace} @item @code{increment} @item @code{increment-wrap} @item @code{decrement} @item @code{decrement-wrap} @item @code{invert} @end itemize Valid values for @var{function} are: @itemize @item @code{always} @item @code{never} @item @code{equal} @item @code{not-equal} @item @code{less-than} @item @code{less-than-or-equal} @item @code{greater-than} @item @code{greater-than-or-equal} @end itemize @end deffn @deffn {Procedure} stencil-test? obj Return @code{#t} when @var{obj} is a stencil test object. @end deffn @deffn {Procedure} stencil-test-mask-front stencil-test Return the front mask of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-mask-back stencil-test Return the back mask of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-function-front stencil-test Return the front function of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-function-back stencil-test Return the back function of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-function-mask-front stencil-test Return the front function-mask of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-function-mask-back stencil-test Return the back function-mask of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-reference-front stencil-test Return the front reference value of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-reference-back stencil-test Return the back reference value of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-fail-front stencil-test Return the front failure action of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-fail-back stencil-test Return the back failure action of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-depth-fail-front stencil-test Return the front depth test failure action of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-depth-fail-back stencil-test Return the back depth test failure action of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-pass-front stencil-test Return the front pass action of @var{stencil-test}. @end deffn @deffn {Procedure} stencil-test-on-pass-back stencil-test Return the back pass action of @var{stencil-test}. @end deffn @deffn {Procedure} current-stencil-test Return the current stencil test. @end deffn @defvar g:stencil-test Render state for stencil testing (@pxref{Rendering Engine}.) @end defvar @subsubsection Multisample Antialiasing Multisample antialiasing is a feature supported by many, but not all, graphics cards. It is a nice easy way to improve the final frame that the user sees, particularly in 3D scenes. The @code{(chickadee graphics multisample)} module provides access to the @code{g:multisample?} render state. @deffn {Procedure} current-multisample Return @code{#t} if multisampling is enabled. @end deffn @defvar g:multisample? Render state for multisampling (@pxref{Rendering Engine}.) @end defvar @node Rendering Engine @subsection Rendering Engine The @code{(chickadee graphics engine)} module provides a Scheme abstraction to the state of the GPU driver. When the Chickadee game loop launches, it takes care to initialize the engine. All draw calls and state changes happen within the context of this engine. Performing a custom draw call could look something like this: @example (with-graphics-state ((g:blend-mode blend:alpha) (g:texture-0 my-texture)) (shader-apply my-shader #:foo 1)) @end example @subsubsection Render States Render states represent individual state values on the GPU. For example, the current shader. As a naming convention, Chickadee prefixes variables containing render states with @code{g:}. Render states can be manipulated using the @code{with-graphics-state} macro. @deffn {Syntax} with-graphics-state ((name value) @dots{}) body @dots{} Evaluate @var{body} with render states defined by @var{name} changed to @var{value}. The render states are restored to their previous values afterwards. @end deffn One additional piece of state that the rendering engine has, that is not part of the GPU state, is the current projection matrix: @deffn {Procedure} current-projection Return the currently bound projection matrix (@pxref{Matrices}). @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 @subsubsection Rendering Chickadee likens a GPU draw call to a Scheme procedure call. A shader (@pxref{Shaders}) is like a procedure for the GPU to apply. Shaders are passed arguments: The positional arguments are vertex array attributes (@pxref{Buffers}) and the keyword arguments correspond to the shader's uniform variables. Scheme uses @code{apply} to call a procedure, so Chickadee uses @code{shader-apply} to call a shader. @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 @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 (define sample (load-audio "neat-sound-effect.wav")) (audio-play sample) @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{