Gfx Engine - Bitmaps
Bitmaps (or sprites) are the same type of GFX Engine objects as text, background and any other object that can be added to the scene.
In principle, the previous examples are quite enough to get acquainted with the capabilities of the engine, in particular with the rendering of bitmaps. However, we want to make an application that does not just draw something, but renders something cool and beautiful, right?
Ok, but before moving to the actual example, let's get a little deeper into the bitmaps and ways of dealing with them.
So, in order to draw a bitmap on the screen using GFX Engine, we need to do the following:
- Create a Sprite object and register it in the scene
- Set its parameters, if necessary
- Add an object to the screen inside the
Begin()-End()block in theon_Render()function
Let's look at each step separately.
Step 1 - Initialization
The Sprite class has 4 constructors, each of which can be used to create a new instance of the class object:
Since the class is responsible for rendering the image, the required parameter is the name of the image file. The rest of the parameters are optional and can be set later, but the filename can only be set when the object is created.
As you can see from the source above, the sprite can be immediately initialized with coordinates for rendering or with a full Transform class containing coordinates, rotation angle and mirroring, or you can simply pass the file name.
So overall, Sprite, like any other drawable object in GFX Engine, has the following standard parameters:
-
Math::Transform Transform
Contains information about the current position, rotation, scaling and mirroring -
Math::Color Color
Contains color information. Depending on the class, the meaning of this parameter may vary. For example, for the Sprite class, this parameter is the color key, and for the Text class, the actual color of the text -
int32_t ScreenAngle
Holds current angle of screen rotation (an angle that is used if automatic rotation is enabled) -
bool Visible
Object visibility flag. If it is set tofalse, the object won't be rendered even if it is added to a screen -
Screen* Parent
A pointer to the screen the object is currently added to
All these parameters can be changed arbitrarily both when creating an object and when adding it to the screen using insert modifiers.
Registering a sprite in a scene can be done in two ways. You can either register a sprite and get an identifier automatically,
or you can set a known identifier in advance.
The choice of registration method depends on the application initialization logic.
Step 2 - Setting parameters
In some cases, after creating an object and registering it in the scene, you need to set additional parameters. These parameters are typically unique to the object class and are not part of the list of parameters passed to the constructor. However, after creating an instance of a class, you can also set parameters that are common to all classes.
For example, the following shows the creation and registration of a Sprite class object, followed by setting the color key color and the value of the alpha component:
Step 3 - Rendering
Objects are drawn by adding them to the current screen. The addition is done inside the Begin()-End() block, which is used to inform the engine about the start and end of the process of adding objects, as well as about the orientation control mode and automatic rotation.
An elementary example looks like this:
The Screen::Add() function returns a pointer to the object that was added. Therefore, if it is necessary to dynamically change any object parameters (for example, the position on the screen), in-place setters are used, as shown below:
or more complex example:
The Example
In this example, we will create an animated cubemap.
In truth, the word "animated" in this context is not entirely correct, since in fact the image on the cubemap is static... but the cubemap itself will smoothly scroll along the edges, which will create a three-dimensional effect.
So, the first step, as always, is the initialization of resources in the InitializeResources() function. We load image files for each face of the cubemap:
Note that the identifiers of the loaded resources are passed to the scroller, an instance of the custom Aperture class with the internal structure of which we propose to figure out on our own. We can only note that this class is used to control the process of scrolling images.
Now that the images are loaded, you can start displaying them on the screens in a smoothly changing position, i.e. scrolling. Moreover, you need to make sure that the scrolling occurs as similarly as possible on all modules.
To do this, we use the following approach: one module calculates the scroll offset and sends its value to other modules, which simply use it.
Calculate the current value of the scroll offset in on_Tick() function on module 0 and send it to the neighbours:
Other modules will receive the scroll offset value via
And of course, the rendering is done in on_Render() function:
The principle of rendering is simple: each time the screens are redrawn, an instance of the Aperture class is asked for a list of identifiers for pictures that should currently be drawn on a particular screen, taking into account the shift. Then the images from the list are added to the screen.
And a sort of quasi-animated cubemap comes out!