7.8

top ← prev up next →

mode-lambda: the best 2D graphics of the 90s, today!

Jay McCarthy

The mode-lambda package provides a set of libraries for creating high-performance 2D graphics. Try running racket -l mode-lambda/examples/one to see what it can do.

1 mode-lambda drawing model

mode-lambda renders rectangular bitmaps (called sprites) in a pixel-perfect manner on a rendering surface. The size of the rendering surface is fixed ahead-of-time when the backend is initialized. When drawn, the rendering surface is then scaled to fit the display environment’s drawing surface. mode-lambda follows an ahead-of-time, staged rendering regime whereby all color data must be "compiled", using compile-sprite-db, before any drawing can occur.

mode-lambda draws sprites on to one of the layers. The larger layers are on-top, with transparent pixels seeping through. mode-lambda makes no guarantees about the layering and transparency behavior of sprites within a single layer. Each layer may have an independent center point, width, scaling coefficients, rotation, wrapping behavior, and Mode-7 coefficients.

Varying the center point (cx & cy) over time creates a scrolling effect given stationary sprites on the layer. When the scrolling effect is combined with a larger or smaller frame (hw & hh) that the rendering surface, this produces a clipping effect. The scaling coefficients (mx & my) allow the entire layer to be zoomed in and out. (A scaling change over time is used in many SNES RPGs to initiate combat.) The rotation coefficient (theta) rotates the entire layer when it is drawn. The wrapping behavior (wrap-x? & wrap-y?) allows sprites that overlap with the vertical (or horizontal) edge of the screen to be partly drawn on the opposite side. Any of these changes could be implemented in software by the mode-lambda client, but the layer configuration provides a more efficient implementation.

Finally, the Mode-7 coefficients create a perspective effect whereby the horizon is defined to be a particular Y value ( horiz ) and then sprites are gradually scaled towards the viewer who has a particular field of view ( fov ). Specifically:

When mode7-coeff is 0, then there is no effect.

When mode7-coeff is 1, then the horizon defines a "ceiling".

When mode7-coeff is 2, then the horizon defines a "floor". This is the most common usage of Mode-7 and is used in Mario Kart for the track and many RPGs for the world map.

When mode7-coeff is 3, then the horizon defines the middle of "cylinder".

It is easiest to understand this effect by looking at some screenshots of SNES games. (Although, mode-lambda does not support as powerful of a Mode-7 effect, because the parameters cannot be changed per Y line, but all common uses are supported.)

When mode-lambda draws a sprite, that sprite instance is associated with a palette of 16 colors. If the palette is 0, then the sprite bitmap color data is directly consulted for the color to render. Otherwise, only the green value of the sprite’s color data is used and it is used after scaling it down by 14 as an offset into the given palette.

Each sprite instance has many drawing paramters. The most important is its center point (cx & cy) and which sprite is to be drawn (spr-idx). Next, the layer (layer) and palette (pal-idx) can be specified as mentioned previously. The color of the pixels can be further influenced by a color tint. The given alpha value (a) is multiplied with the sprite/palette color, while the diffuse colors (r, g, and b) are added. Finally, each sprite may be independently scaled in the X (mx) or Y (my) direction and may be independently rotated (theta).

Each rendering pass of mode-lambda receives two trees of sprite instances. The first is called the "static" sprites and the second are the "dynamic" sprites. The static sprites are always drawn first on a given layer. Aside from this, they are treated equally, but mode-lambda will guarantee that if a tree is eq? to the tree from the last frame, the GPU’s data will not be refreshed. This guarantees that static level geometry is uploaded once, if it never changes, so mode-lambda programs should factor their graphics into static and dynamic pieces for performance.

mode-lambda is extremely memory efficient. Each sprite instance consumes 32 bytes on the CPU and the GPU. If a GPU supported one gigabyte per second memory transfer, then about 550,000 sprites would be supported at 60 FPS. Given that most GPUs support many gigabytes per second memory transfer rates, this means that performance is almost always dominated by the mode-lambda client’s preparation of the sprite trees and mode-lambda rendering can be treated as free.

2 mode-lambda static: sprite libraries

Use this module to construct a database of sprites that your program will display. This can be run offline and the result saved persistently with save-csd!.

Returns a sprite database.

make-sprite-db Identifies values returned by

n ) is a symbol? pal ) is either #f or a symbol? w ) and fouth ( h ) are 16-bit exact-nonnegative-integer? bs ) is a bytes? bs must be ( * 4 w h ) long. If pal is not false, then the palette pal is used to interpret the pixel values into palette offsets. Identifies a vector with 5 elements. The first () is a, the second () is eitheror a, the third () and fouth () are 16-bit, and the fifth () is adescribing the ARGB pixels of the sprite.must belong. Ifis not false, then the paletteis used to interpret the pixel values into palette offsets.

Adds the sprite returned by load-spr to db .

Adds the sprite returned by load-bm to db using the name n . If pal is not false, then the palette named is consulted to interpret the bitmap pixel values into palette offsets.

Adds the sprite inside file to db using the name n . If pal is not false, then the palette named is consulted to interpret the bitmap pixel values into palette offsets.

Adds the sprite val to db using the name n . If pal is not false, then the palette named is consulted to interpret the bitmap pixel values into palette offsets.

Adds the palette cs to db with the name n .

file , which is expected to be a 1x PALETTE-DEPTH db with the name n . Adds the palette inside, which is expected to be a 1xbitmap towith the name

Saves the compiled database db into the directory path with the name "csd.rktd.gz" . If debug? is #t , then the sprite atlas and palette is saved in the directory as PNG files.

Compiles the database db . A border of padding pixels is added to each sprite to prevent color data bleeding across sprites in the atlas texture.

3 mode-lambda edition: amazing graphics

Use this module to construct scenes for one of the backends. Connect it to mode-lambda/static with load-csd.

16 . The number of colors in one palette supported by mode-lambda . Currently

compile-sprite-db Identifies values returned by

Loads the compiled database from the bytes bs .

Loads the compiled database from the directory path .

Looks up the index for the sprite n in cdb .

Looks up the index for the palette n in cdb .

Returns the width of spr-idx in cdb .

Returns the height of spr-idx in cdb .

procedure ( sprite cx cy spr-idx [ #:layer layer #:r r #:g g #:b b #:a a #:pal-idx pal-idx #:m m #:mx mx #:my my #:theta theta ] ) → sprite-data? cx : flonum? cy : flonum? spr-idx : exact-nonnegative-integer? layer : byte? = 0 r : byte? = 0 g : byte? = 0 b : byte? = 0 a : flonum? = 1.0 pal-idx : exact-nonnegative-integer? = 0 m : flonum? = 1.0 mx : flonum? = m my : flonum? = m theta : flonum? = 0.0

Returns a sprite instance.

Identifies sprite data.

Returns X offset.

Returns Y offset.

Returns X scale.

Returns Y scale.

Returns sprite index.

Returns sprite layer.

procedure ( layer cx cy [ #:hw hw #:hh hh #:wrap-x? wrap-x? #:wrap-y? wrap-y? #:mx mx #:my my #:theta theta #:mode7 mode7-coeff #:horizon horiz #:fov fov ] ) → layer-data? cx : flonum? cy : flonum? hw : flonum? = +inf.0 hh : flonum? = +inf.0 wrap-x? : boolean? = #f wrap-y? : boolean? = #f mx : flonum? = 1.0 my : flonum? = 1.0 theta : flonum? = 0.0 mode7-coeff : flonum? = 0.0 horiz : flonum? = 0.0 fov : flonum? = 1.0

Returns a layer configuration.

4 mode-lambda color: basic color theory

This module defines helpers for creating color palettes for mode-lambda.

The color gray.

The transparent color.

The color black.

The color white.

procedure (argb a r g b) → color? a : byte? r : byte? g : byte? b : byte?

Constructs a color object.

Identifies colors.

Extracts the alpha value of c .

Extracts the red value of c .

Extracts the green value of c .

Extracts the blue value of c .

TRANSPARENT BLACK base , base , 7 tints of base , and then WHITE Returns a list of colors where the first color is, the next is, followed by 7 shades of, 7 tints of, and then

Returns how-many tints of base . Tints are brighter than their base color.

Returns how-many shades of base . Shades are darker than their base color.

Takes how-many evenly spaced samples of the color wheel with saturation s and brightness b .

Returns a list of 2-vectors that contain indexes into a color wheel of size how-many where each pair are complementary colors.

Returns a list of 3-vectors that contain indexes into a color wheel of size how-many where each triple are analogous colors.

Returns a list of 3-vectors that contain indexes into a color wheel of size how-many where each triple are triadic colors.

Returns a list of 3-vectors that contain indexes into a color wheel of size how-many where each triple are split complementary colors.

Returns a list of 4-vectors that contain indexes into a color wheel of size how-many where each quadruple are tetradic colors.

Returns a list of 4-vectors that contain indexes into a color wheel of size how-many where each quadruple are square colors.

n -vectors that contain indexes into a color wheel of size how-many where each vector of colors are on the vertix of a regular n -gon. (This is a generalization of triadic-idxs square-idxs Returns a list of-vectors that contain indexes into a color wheel of sizewhere each vector of colors are on the vertix of a regular-gon. (This is a generalization ofand.)

5 mode-lambda text: text layout

This module captures some convenience functions for adding font glyphs to the sprite database and laying out horizontal text.

5.1 Static

A list of all graphical ASCII characters.

procedure ( load-font! db [ #:size size #:face face #:family family #:style style #:weight weight #:underlined? underlined? #:smoothing smoothing #:size-in-pixels? size-in-pixels #:hinting hinting #:alphabet alphabet ] ) → font? db : sprite-db? size : ( real-in 0.0 1024.0 ) = 12 face : ( or/c string? #f ) = #f family : ( or/c ' default ' decorative ' roman ' script ' swiss ' modern ' symbol ' system ) = ' default style : ( or/c ' normal ' italic ' slant ) = ' normal weight : ( or/c ' normal ' bold ' light ) = ' normal underlined? : any/c = #f smoothing : ( or/c ' default ' partly-smoothed ' smoothed ' unsmoothed ) = ' default size-in-pixels : any/c = #f hinting : ( or/c ' aligned ' unaligned ) = ' aligned alphabet : ( listof char? ) = *ALL-ASCII*

make-font alphabet to db and returns a font object for use with font-glyph-idx . Uses most of its arguments to callfrom racket/draw to construct a font object, which it uses to add all of the characters intoand returns a font object for use with

5.2 Runtime

load-font! Identifies objects returned from

Looks up the character identifier of the character char of the font the-font in the databvase cdb .

Returns a tree of sprites for the glyphs of the string text such that the center is ( tx , ty ). The sprite are created with the sprite parameters given in the optional arguments.

6 mode-lambda gl: premier backend

This is the production backend for mode-lambda. It is pretty fast, but kind of complicated.

Prepares a function that accepts rendering states and returns a function that draws that rendering state, with optionally specified background color. layers must be less than or equal to GL_MAX_ARRAY_TEXTURE_LAYERS , which is at least 256 , but typically 2048 .

make-gui A symbol to be sent toof lux/chaos/gui

Sets the desired version of OpenGL to use.

This parameter controls the screen filter used to magnify the render surface on to the drawing surface. The default mode ' std , uses sharp pixel-duplicating filtering. The ' crt mode simulates a CRT.

This parameter controls whether to blend sprite pixels across screen pixels. The default is #f , showing sharp sprite pixels. Set to #t to smooth sprites across screen pixels.

If this value is not #f , then it is called once for each layer with (a) the layer identifier, (b) the width of the layer, (c) the height of the layer, and (d) the raw pixel values in ARGB order. Enabling this drastically lowers rendering performance because it forces a pixel buffer read and synchronization.

(screenshot-in-dir! dir) from mode-lambda/shot is an ideal value for this parameter.

7 mode-lambda software: discount backend

This is the reference backend for mode-lambda. It is very slow, and a little complicated, but easier to test than mode-lambda/backend/gl.

Prepares a function that accepts rendering states and returns a function that draws that rendering state.

make-gui A symbol to be sent toof lux/chaos/gui

This parameter controls the path that the post-rendering bitmap is saved to disk. The default is to not save screenshots.

8 mode-lambda shot: screenshot helpers

gl-screenshot! p . Saves the screenshots thatgenerates in