Sunday, March 15, 2015

Preview : TotoEngine2D

TotoEngine2D is a library I develop. It exposes a convenient C++ API to display accelerated 2D graphics through OpenGL ES.

I target the Raspberry PI platform. Why the Raspberry PI ? Because at my knowledge, despite the presence of a GPU, the Rasp still doesn't have any descent API for accelerated 2D graphics. At first glimpse, the problem with the Rasp come from his incapacity to deal with OpenGL. Indeed, his architecture allows only the use of OpenGL ES 2.0, which can be seen as a fragment of an old version of OpenGL (2.0). So that's why popular libraries such SDL (accelerated through OpenGL) cannot give their full potential on the Rasp.

If you are interested by the project, have a look to the features of the engine.

Batching

The rendering process in TotoEngine2D is through batching. It means that on every frame, we begin with an empty list of objects to display, then we pile up any number of objects in it. When we filled our list with everything we want to show, we ask to render the whole in the window.

The important things to understand:
- the objects doesn't have any kind of persistence, the object list is totally cleared out after each render call
- the objects are rendered in the window in the same order we piled up them, from back to front

Here is an example of rendering sequence:

Toto2DEngine toto2d;
// sequence :
toto2d.clear(); // fill the window with the default background color
// ...
// pile up our objects
//...
toto2d.swap(); // swap the buffers to render


Atlas map

For performance purpose, only one single texture can be used when triggering the rendering process. In consequence we must work with texture atlas (or sprite sheet). TotoEngine2D manages 2 values of opacity. One pixel in the texture atlas can be full opaque of full transparent. Because texture atlas can only be made from 24bits colors (8bits by channel RGB), one color is sacrificed to play the role of the transparent color. By default this color is the plain fuchsia #FF00FF.

example of sprite sheet as texture atlas

However, several textures atlas can be loaded into memory and switched between different rendering:

toto2d.uploadAtlas(0, "atlas_1.tga"); // attach first atlas on slot 0
toto2d.uploadAtlas(1, "atlas_2.tga"); // attach second atlas on slot 1
toto2d.activeAtlas(0); // active the atlas attached to slot 0

When adding an object to the render list, sub-textures can be selected by the use of simplified UV mappings as axis-aligned rectangles on the texture. Such rectangles are defined by 4-uplet (x, y, w, h) where (x, y) defines the top-left corner coordinate, w the width and h the height. The coordinate system has (0, 0) coordinate at the top-left corner of the texture atlas, the x-axis goes positively on right, the y-axis goes positively on down and the unit is the pixel.

x = 80, y = 45, w = 40, h = 35


Sprites

The sprite object is the most powerfull object implemented in TotoEngine2D. Any single sprite may have its own sub-texture (UV coordinates) and his own transformation 3x3 matrix. It means that all sprites rendered can be independantly scaled, rotated and translated. Also, each sprite can display a different part of the texture atlas. That last feature guarantees the possibility to implement frame-based animation on top of TotoEngine2D.

Additionnally, each single sprite may be applied various color effects, such that tint or saturation.

Here is a sample of code, showing how to add a sprite:

glm::mat3 transf;
toto2d.getSpriteBatcher().addSprite(80, 45, 40, 35, transf);
toto2d.getSpriteBatcher().applyTint(255, 0, 0, 127);

Where (80, 45, 50, 35) defines the sub-texture on the texture atlas as (x, y, w, h) and transf is the 3x3 matrix that will be applied. Finally, a plain red tint (255, 0, 0) is applied with 50%  of intensity (127 as half of 255). Color effects always apply to the last sprite added.



As a result this sprite will render with the identity matrix transformation, so it will show on the top-left corner of the window. Because neither scale not rotation are involved, pixels from the texture align exactly on the pixels of the window. It is a pixel perfect situation, so no texture filtering will be involved.

TotoEngine2D uses the matrices from GLM library so we can find many ways to configure them on the GLM code sample page, Scales and rotations always occur around the (0, 0) origin of the coordinate system, being by default the top-left corner of the window. Translations unit is the pixel,  the x-axis going right and the y-axis going down along the window.

In order to place efficiently and accurately the sprite anywhere on window, TotoEngine2D gives the opportunity to use the TRST tool. The TRST tool is dedicated to set efficiently a matrix as an ordered sequence of Translation, Rotation, Scale, Translation.

Here is n example of TRST usage to rotate, scale and place a sprite exactly at the window center:

glm::mat3 transf;
float t2x, t2y, rot, sx, sy, t1x, t1y;
// the first translation places the sprite to match his local center with the origin:
t1x = -20.0f;
t1y = -17.5f;
// from that we can scale and rotate safely around the local center of the sprite:
sx = 4.0f; // horizontal scale x4
sy = 4.0f; // vertical scale x4
rot = 0.43f; // rotation in radians, approx. 25°
// finally we place the sprite at the center of the 320x240 window:
t2x = 160.0f; 
t2y = 120.0f;
// configure the matrix with the TRST tool
Utils::mat3TRST(t2x, t2y, rot, sx, sy, t1x, t1y, transf);
// add the sprite in the batch list
toto2d.getSpriteBatcher().addSprite(80, 45, 40, 35, transf);
toto2d.getSpriteBatcher().applyTint(255, 0, 0, 127);

Tiles

The tile object is a similar to the sprite, but it is constrained to reach better performance. First, several tile objects can share the same sub-texture. Additionnaly, the tile supports only the translation transformation, so it can be neither scaled nor rotated. Finally, the tile doesn't support color effect.

Here is an example of code, showing how to display 2 tiles sharing the same sub-texture:

int uvID;
toto2d.getSimpleTileBatcher().addUV(5, 173, 30, 25, uvID);
toto2d.getSimpleTileBatcher().addTile(uvID, 50.0f, 100.0f);
toto2d.getSimpleTileBatcher().addTile(uvID, 90.0f, 100.0f);

Where the addUV method registers a sub-texture as a 4-uplet (x, y, w, h) and link it to an ID. Then that ID can be used several times through the addTile method, specifying the position of the top-left corners of the tiles in the window.



Repeat tiles

The repeat tile object is similar to the tile object, but allowing to draw large rectangular surfaces in the window, with repetition of the sub-texture.

Here is an example of code, showing how to display a large surface with a small sub-texture:

int uvID;
toto2d.getRepeatTileBatcher().addUV(82, 205, 32, 16, uvID);
toto2d.getRepeatTileBatcher().addTiles(uvID, 50.0f, 20.0f, 220.0f, 200.0f, 0.0f, 0.0f);

Where addUV method registers the following sub-texture:



And addTiles method draws large rectangle with top-left corner coordinate (50, 20), width 220 and height 200:


Here is the complete signature of the addTiles method:

addTiles(int uvId, float x, float y, float width, float height, float scrollX, float scrollY)

The two last parameters scrollX and scrollY allow to scroll within the sub-texture, meaning that the starting point in the texture that matches the top-left corner of the drawn area will be moved. This feature allows to animate large background translations very easily and efficiently.


Distortion effects

The repeat tile object supports various distortion effects that will affect how the sub-texture will be mapped into the drawn area.

Starting from that 42x42 sub-texture:

Drawing it in a 320x240 window:



Here is the result of a vertical wave distortion with amplitude 10, period 84 and phase 0;

applyDistoWaveVertical(10.0f, 84.0f, 0.0f)

Same parameters but with a horizontal wave distortion:

applyDistoWaveHorizontal(10.0f, 84.0f, 0.0f)

Both cumulated:



Here is the result of horizontal accordeon dirstortion wit amplitude 20, period 84 and phase 0;

applyDistoAccordeonHorizontal(20.0f, 84.0f, 0.0f)

Same parameters but with a vertical accordeon distortion:

applyDistoAccordeonVertical(20.0f, 84.0f, 0.0f)

Both cumulated:



Camera

TotoEngine2D manages a camera system. The most simple feature is to scroll the content in the window. For example, starting from that scene:



 we can move our camera 50 pixels right and 100 pixels down:

toto2d.setCamera(50.0f, 100.0f); 

Resulting in scrolling all the content 50 pixels left and 100 pixels up, showing large drawn area that was outside the window before:



The camera allows us to do more, by focusing directly on a part of the window with opporunity to add a zoom effect:

toto2d.setCameraLookAt(200.0f, 175.0f, 2.0f);

Resulting in scrolling all the content such that the (200, 175) point becomes center of the window and zooming it by a factor 2 around that new center:





No comments:

Post a Comment