Tutorial 3: transformations

In this tutorial you will learn about transformations.

Before starting this tutorial be sure to setup an empty project with the Setup Tutorial.

That's it. :)

Now you know how to transform your entities! next we'll learn about camera and keyboard input!
 
continue to next tutorial -->

#include <NessEngine.h>

 

int _tmain(int argc, _TCHAR* argv[])

{

    Ness::init();

    Ness::Renderer renderer("new project", Ness::Sizei(800,600));

 

    Ness::ScenePtr scene = renderer.create_scene();

 

    Ness::SpritePtr sprite = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite->set_anchor(Ness::Point::HALF);

    sprite->set_size(Ness::Point(200, 200));

    sprite->set_rotation(90);

    sprite->set_position(Ness::Point(100, 100));

 

    Ness::SpritePtr sprite2 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite2->set_anchor(Ness::Point::HALF);

    sprite2->set_size(Ness::Point(200, 200));

    sprite2->set_position(Ness::Point(400, 100));

    sprite2->set_blend_mode(Ness::BLEND_MODE_ADD);

 

    Ness::SpritePtr sprite3 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite3->set_anchor(Ness::Point::HALF);

    sprite3->set_size(Ness::Point(200, 200));

    sprite3->set_scale(Ness::Point(-1.0f, 1.25f));

    sprite3->set_position(Ness::Point(700, 100));

 

    Ness::SpritePtr sprite4 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite4->set_anchor(Ness::Point::HALF);

    sprite4->set_size(Ness::Point(200, 200));

    sprite4->set_position(Ness::Point(100, 500));

    sprite4->set_color(Ness::Color::BLUE);

 

    Ness::SpritePtr sprite5 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite5->set_anchor(Ness::Point::HALF);

    sprite5->set_size(Ness::Point(200, 200));

    sprite5->set_position(Ness::Point(400, 500));

    sprite5->set_opacity(0.75f);

    sprite5->set_blend_mode(Ness::BLEND_MODE_BLEND);

 

    Ness::SpritePtr sprite6 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite6->set_anchor(Ness::Point::HALF);

    sprite6->set_size(Ness::Point(200, 200));

    sprite6->set_position(Ness::Point(700, 500));

    sprite6->set_source_rect(Ness::Rectangle(50, 50, 100, 100));

 

    Ness::Utils::EventsPoller EventsPoller;

    Ness::Utils::ApplicationEvents app;

    EventsPoller.add_handler(app);

 

    while( !app.got_quit() )

    {

        EventsPoller.poll_events();

 

        renderer.start_frame();

        scene->render();

        renderer.end_frame();

    }

}

    Ness::SpritePtr sprite = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite->set_anchor(Ness::Point::HALF);

    sprite->set_size(Ness::Point(200, 200));

    sprite->set_rotation(90);

    sprite->set_position(Ness::Point(100, 100));

    Ness::SpritePtr sprite2 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite2->set_anchor(Ness::Point::HALF);

    sprite2->set_size(Ness::Point(200, 200));

    sprite2->set_position(Ness::Point(400, 100));

    sprite2->set_blend_mode(Ness::BLEND_MODE_ADD);

    Ness::SpritePtr sprite3 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite3->set_anchor(Ness::Point::HALF);

    sprite3->set_size(Ness::Point(200, 200));

    sprite3->set_scale(Ness::Point(-1.0f, 1.25f));

    sprite3->set_position(Ness::Point(700, 100));

    Ness::SpritePtr sprite4 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite4->set_anchor(Ness::Point::HALF);

    sprite4->set_size(Ness::Point(200, 200));

    sprite4->set_position(Ness::Point(100, 500));

    sprite4->set_color(Ness::Color::BLUE);

    Ness::SpritePtr sprite5 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite5->set_anchor(Ness::Point::HALF);

    sprite5->set_size(Ness::Point(200, 200));

    sprite5->set_position(Ness::Point(400, 500));

    sprite5->set_opacity(0.75f);

    sprite5->set_blend_mode(Ness::BLEND_MODE_BLEND);

    Ness::SpritePtr sprite6 = scene->create_sprite("ness-engine/resources/gfx/hello_world.png");

    sprite6->set_anchor(Ness::Point::HALF);

    sprite6->set_size(Ness::Point(200, 200));

    sprite6->set_position(Ness::Point(700, 500));

    sprite6->set_source_rect(Ness::Rectangle(50, 50, 100, 100));

what are we going to get?

at the end of this tutorial we will have this code:

which will produce the following window:

We will now go over the code and explain all the transformations that we applied.

 
But first: what are transformations?

Transformations are any rendering setting you can apply on a sprite. this includes: position, scaling, rotation, color tilt, blending mode, flipping, and more.

 

In later tutorials you will learn that transformations can be inherited and how to take advantage of that. for now, we will just go over the basic transformations.

 

You can read more about this subject here.

 

Sprite 1: rotation, size & anchor

Let's look over the code that creates the first sprite, the one at the top-left corner:

We are already familiar with the function create_sprite from previous tutorials, and we know it will create an entity that will render a texture on the screen.

 

Next we call sprite->set_anchor(). the concept of anchor is very important but also very simple: imagine that your screen is a corkboard and every sprite on it is a little image attached with a pin. when talking about the position of the sprite, we are actually talking about the position of the pin attaching it. when rotating the sprite, it's easy to imagine how it will rotate around the pin holding it to the board:

Position, scaling, size and rotation are all around the anchor point. the default anchor is the top-left corner of the sprite.

 

So what does it mean to set sprite->set_anchor(Ness::Point::HALF) ?  the anchor values range from 0.0f to 1.0f, relative to the size of the sprite. Ness::Point::HALF is a constant point equal to (0.5f, 0.5f). so by setting the anchor point to HALF, we actually set the position of the pin to the center of the sprite.  our sprite now looks like this:

 

and when we rotate it, it will rotate around the center.

 

After setting the anchor we call sprite->set_size(). this is simply the size of the sprite to render.

sprite->set_rotation(90) rotates the sprite 90 degrees clockwise, and since our anchor is the center of the sprite it will rotate in the same place, around the center.

 

lastly, we set the position of the sprite to be (100, 100). remember when talking about anchors we mentioned that the position of a sprite is the position of its anchor on screen? since our anchor is the center of the sprite, if it was positioned (0, 0) it means the sprite center was at position (0, 0), which means 3/4 of the sprite was out of screen.

 

As you can see once you get the concept of anchors and positioning rotating sprites is very easy and efficient. next transformations will also be simple.

 

Sprite 2: blending

Let's look at the code of the second sprite (top middle):

Everything is pretty much the same, except for the last line: 

sprite2->set_blend_mode(Ness::BLEND_MODE_ADD);

 

So what is blend mode? blend mode is how to combine the new pixels we render with the pixels below. in this example we set additive blending, which means every new pixel is added to the pixels already on screen, making it brighter.  we have the following blending modes:

 

BLEND_MODE_NONE: no blending at all, every pixel replace the pixel below. this mode also does not support opacity or transparent background.

BLEND_MODE_BLEND: the most common blending you'll use. merge the new pixels with the pixels below based on opacity (and texture's alpha channels). this mode is used mostly to enable transparent backgrounds to sprites.

BLEND_MODE_ADD: additive blending, every new pixel is added to the pixel below. this creates effect of "brighten", because the sprite you render can only make it brighter. black pixels are invisible, since 'value + 0 = value'.

BLEND_MODE_MOD: modulate blending, every pixel is multiplied with the color of the pixel below. when talking graphics, usually every pixel is represented by 3 floats, red green and blue, ranging from 0.0f to 1.0f. so when you merge by multiplying, the output image will always be darker (and white will be invisible). you can say that modulate blending is the opposite of additive blending.

 

Sprite 3: scale

Let's look at the code of the top right sprite:

This sprite transformation is set_scale().

Scaling multiplies the size of the sprite to enlarge or shrink it. you can also scale a whole node, which will also affect the position of the entities inside it and will create a "zoom-in" or "zoom-out" effect.

 

you can also use scaling to flip entities: negative scale will flip the sprite on the given axis, so for example scaling of (-1.0f, 1.0f) will flip the object horizontally. 

 

Sprite 4: color

Let's look at the code of the bottom left sprite:

The trasnformation we apply on this sprite turns it bluish. set_color() receive a color object containing 4 components (red, green, blue and opacity) with values ranging from 0.0f to 1.0f. every pixel in the sprite source texture will be multiplied by those values.

 

so multiplying this sprite with the color blue means we don't add any extra blue color to it, but instead we zero red and green, leaving only the blue parts. as you can imagine, color tilting can only make the image darker, not brighter (similar to the modulate blending).

 

Sprite 5: opacity

Let's look at the code of the bottom center sprite:

Here we change it's opacity. you can also change the opacity by calling set_color() with a color containing alpha < 1.0f. note that in order for the opacity to take effect, we also need to set blending mode to BLEND.

 

Sprite 6: source rect

Let's look at the code of the bottom right sprite:

in this sprite we change the source rect. the source rectangle is the part of the texture we are actually rendering on the screen, stretching to the sprite full size. the most common use of source rect is to create sprite sheets, containing many images on a single texture file.

Help ness-engine grow!