Graphics backends - Liquid War 6
Description: Liquid War 6

Next: Core algorithm, Previous: Architecture, Up: Hacker's guide

### 3.6 Graphics backends

#### 3.6.1 Modularity

Liquid War 6 has a modular architecture which allows the programmer (and the player) to plug pretty much any rendering/graphics backend, provided this one is... developped.

As of 2009 the only available backend was `mod-gl1`, it would display the game using 3D acceleration, if available, through the SDL library, using its GL bindings.

As of 2012, other backends are begin developped, the idea is that each backend can provide the user with enough visual feedback to play, and convey input informations to the core engine.

The rest of the game is exactly the same, this means playing with `mod-gl1` you can do exactly the same things than with `mod-caca`.

#### 3.6.2 List of backends

- mod-gl1

  Liquid War 6 has a modular architecture which allows the programmer (and the player) to plug pretty much any rendering/graphics backend, provided this one is... developped.

  As of 2009 the only available backend is still `mod-gl1`, it will display the game using 3D acceleration, if available, through the SDL library, using its GL bindings.

  Additionnally, versions available for Microsoft Windows and Mac OS X will probably never any other backends available. For technical reasons, those platforms do not have the flexibility of GNU/Linux and do not allow graphical libraries to be loaded dynamically. In practice, both of them require hacks that override the standard `main` function. Microsoft Windows has its `WinMain` instead, and Mac OS X is even more pedantic, requiring graphical functions to be executed in the main thread. So `mod-gl1` is just linked statically in those versions, and the modularity of the game is purely theorical on these platforms.

  This `mod-gl1` module is really one of the key stones of Liquid War 6, and if you want to change graphical things, it's definitely the place to hack on. The source is in `src/lib/gfx/mod-gl1`.

  The `mod-gl1` backend requires “moderate” hardware, but it still does require hardware acceleration. Pure software rendering through mesa for instance, won't be enough.

  So if you're running Xorg on GNU/Linux and there's a DRI driver for your card, the game should run fine.

  On the programmer side, the counterpart is that one should not rely on fancy OpenGL features. Textures have a maximum size of 512x512 for instance. Of course some maps are bigger than this but this means that internally, `mod-gl1` splits them into smaller tiles, and displays those tiles one by one.

  Inside the `mod-gl1` backend, the `src/lib/gfx/mod-gl1/gl-utils` directory contains lots of common structures, factorized functions which can (and should, if appliable) be used.

- mod-gles2

  This is under development, the idea is to provide an alternative renderer based on OpenGL ES 2, which could be used on standard computers but also on mobile platforms.

  Work in progress, don't hold your breath.

- mod-soft

  This is under development, the idea is to provide a very basic rendered which can be compiled pretty much anywhere as long as SDL is available, since it does use software rendering only.

  Work in progress, don't hold your breath.

- mod-caca

  This is under heavy development, the idea is to provide a basic yet surprising alternative text-based renderer, using libcaca.

#### 3.6.3 How to write a new backend

The starting point for any hack are the files `src/lib/gfx/gfx.h`. This is where the API is defined.

Basically, the type `lw6gfx_backend_t` contains all the required callbacks. You must provide an implementation for each function.

Let's take an example, taken from `mod-gl1`. When calling `lw6gfx_get_video_mode` and passing it a first argument which is a valid `mod-gl1` backend, the function `mod_gl1_utils_get_video_mode` will be called. How this is done is a little C casting wizardry.

To understand how this works, read the files:

- `src/lib/gfx/gfx-api.c`: contains all the functions which are part of the API and can be called elsewhere in the code.
- `src/lib/gfx/gfx-register.c`: contains the code that allows a module to be loaded/unloaded at runtime. Will act differently if the games is compile with the `--allinone` flag, but for the caller this is transparent, just create and destroy backend, period.
- `src/lib/gfx/mod-gl1/mod-gl1-backend.c`: this is where the module actually binds its internal functions with the callbacks defined in the `lw6gfx_backend_s` struct. None of these internal functions should be called directly, code in `libdsp` for instance should only refer to the `lw6gfx_...` bindings. Reading the code in `src/lib/gfx/gfx-test.c` shows how these functions can be called, and in which order.

All the functions should be defined, but some of them are obviously more important. The two most critical functions are:

- `pump_events` This is used to process inputs. The function should update a `lw6gui_input_s` struct and return it to the caller. How this done is really up to the backend, it happens that all SDL based backends (`mod-gl1`, `mod-gles2` and `mod-soft`) share the same code for this, but another backend could do this differently, there's no real need to use SDL.

  Only, the returned input should behave correctly when queried with function from `libgui`. As a consequence, one needs to have a look at `libgui` to understand how input works. A look at `src/lib/gfx/shared-sdl/shared-sdl-event.c` is a good example of this, as this file contains the implementation for SDL-based input.

  See libgfx reference. See libgui reference.

- `display` By far the most complicated function, this one is called on each display loop to render the game. It's always used in the same thread, so need not be reentrant, and on some platforms (eg Mac OS X) it will even be called in the main thread (this can be of some importance regarding some libraries such as SDL).

  Still, beware, the `game_state` object it uses can change on the fly while rendering. In that case “changing” means that fighters can move and gradients be updated but the global structure won't change. So any pointer on a fighter will still be valid after it's been obtained, but the renderer should not expect the game to be static. In practice this is not really a problem. If you are curious, you can look in `libdsp` how and when this function is called.

  A very important parameter is `mask`, depending on its value, the backend should, or not, display the menu, or the map, or both, etc. The reference for this are the `LW6GUI_DISPLAY_...` constants in `src/lib/gui/gui.h`.

  As a starting point, implementing menu display before anything else is probably the best bet, since without menus it's hard to do anything within the game.

  To test out a backend, one can either launch the full game using the “under development” backend, or launch the test suite by typing `./liquidwar6gfx-test 1` in `./src/lib/gfx`.

  See libdsp reference.