Here is our 4th installment of the Real OpenGL ES 2.0 2D tutorial series! We will cover transformations in this tutorial like translation, rotation and scaling. In our previous tutorial we already translated our image using our touchscreen, the thing is however, it is not the only way we can do translations. So what is wise? We will continue with the source code from the previous tutorial.

## Matrices vs Vertices

In OpenGL you can transform your objects in multiple ways, here are two:

- Using Matrices
- Changing vertex information directly

Choosing the right way is not that straightforward as some of you may think. Traditionally, matrices are the way to go with transformations, and in case of 3D rendering it probably is the best method. You probably also need matrices if you want to use complex shaders where you need model/projection space information. But we are doing 2D rendering exclusively so what of it?

Using matrices for each sprite will use a lot of OpenGL calls, which are quite slow on Android. Also, we often do not use those complex shaders in 2d games. Matrix transformations are quicker as they need less calculations as apposed to changing the vertices directly but the question is; is the speed slowdown of all the OpenGL calls bigger? We haven’t discussed texture atlases yet, but they are also crucial in this discussion. A texture atlas is a large texture on which you have placed all your 2D images/textures. This way you only have to load 1 texture into memory and all triangles are drawn using that texture, making it possible to do only 1 pass of triangle rendering. The performance is in both cases quite implementation specific but we feel that in most 2D applications the dynamic vertices are the best way to go, you could optimize your engine quite a bit this way.

We are not here to start a debate, we just want to show you 1 way of doing this. Perhaps we will handle the other methods (matrices, hardware skinning, etc.) as well in the future.

## A Sprite class

We are well aware that the code we provide is not the most efficient/correct/safe but we do that for a reason, we only want to show the basics so that you can create the stuff we explain in the most basic way without clutter so that it is easy to comprehend. If we do add all the safeguards, error checks, OOP, etc. it will be a lot more complex for most developers just starting with Android developing so we leave it out our examples. Furthermore, most people are only searching for quick snippets and this way they can read the code very quick and retrieve what they need. For this tutorial we will add a class holding the information of our sprite so that it is easier to see how how the different transforms work.

A sprite is a two-dimensional image or animation that is integrated into a larger scene. Our images are exactly that so we will hold all information about our image in a class we called **Sprite**. In computer graphics order is very important when translating, scaling and rotating. Rotating for example is always done around an origin, but what origin? In our example we will do the transformation each frame again from the base position. We do it in the order scaling, rotating and translating. This will result in an image scaling and rotating around its own midpoint and then translated to the correct location.

Here is our Sprite class:

class Sprite { float angle; float scale; RectF base; PointF translation; public Sprite() { // Initialise our intital size around the 0,0 point base = new RectF(-50f,50f, 50f, -50f); // Initial translation translation = new PointF(50f,50f); // We start with our inital size scale = 1f; // We start in our inital angle angle = 0f; } public void translate(float deltax, float deltay) { // Update our location. translation.x += deltax; translation.y += deltay; } public void scale(float deltas) { scale += deltas; } public void rotate(float deltaa) { angle += deltaa; } public float[] getTransformedVertices() { // Start with scaling float x1 = base.left * scale; float x2 = base.right * scale; float y1 = base.bottom * scale; float y2 = base.top * scale; // We now detach from our Rect because when rotating, // we need the seperate points, so we do so in opengl order PointF one = new PointF(x1, y2); PointF two = new PointF(x1, y1); PointF three = new PointF(x2, y1); PointF four = new PointF(x2, y2); // We create the sin and cos function once, // so we do not have calculate them each time. float s = (float) Math.sin(angle); float c = (float) Math.cos(angle); // Then we rotate each point one.x = x1 * c - y2 * s; one.y = x1 * s + y2 * c; two.x = x1 * c - y1 * s; two.y = x1 * s + y1 * c; three.x = x2 * c - y1 * s; three.y = x2 * s + y1 * c; four.x = x2 * c - y2 * s; four.y = x2 * s + y2 * c; // Finally we translate the sprite to its correct position. one.x += translation.x; one.y += translation.y; two.x += translation.x; two.y += translation.y; three.x += translation.x; three.y += translation.y; four.x += translation.x; four.y += translation.y; // We now return our float array of vertices. return new float[] { one.x, one.y, 0.0f, two.x, two.y, 0.0f, three.x, three.y, 0.0f, four.x, four.y, 0.0f, }; } }

Our sprite always has a base (5), this is the original position, which we place around our origin (11). From our base we need the values to scale (4), rotate (3) and translate (6). In our constructor we setup our inital state. Because our image is 100 pixels by pixels in size and we have placed it around the origin, we start our translation 50 pixels in both directions (14), so that our image is fully on our screen on the left bottom. We start by setting our scale factor to 1 (17), which means that it is at normal size. Our angle is zero (20), which results in the image on its initial rotation.

### Scaling

Our sprite also handles the scaling process. You can pass a delta scale value to the scale function (31). The actual scaling is done in the **getTransformedVertices** function. This function returns are newly transformed vertices. Because we have our base around the origin (0,0) we can just multiply the vertex locations with the scaling factor (44-47) to get a scaled sprite.

### Rotating

After we have scaled our sprite we have to rotate it. Rotating a point **P** around the origin **O** can be done using the following formula (point **R** is the resulting point, **a** is the angle of rotation):

Rx = cos(a) * (Px-Ox) – sin(a) * (Py-Oy) +Ox

Ry = sin(a) * (Px-Ox) + cos(a) * (Py-Oy) +Oy

Because our point **O** is at (0,0) we can simplify (by filling in the zero’s) the formula as:

Rx = cos(a) *Px – sin(a) *Py

Ry = sin(a) *Px + cos(a) *Py

Before we can rotate our vertices we have to create all four points from our rect (51-54). We apply the above formula on all points (62-69). We now have rotated the vertices around the sprites origin (0,0).

### Translation

The last step of our transformation steps is the translation. We just add the x and y translation offset to our scaled and rotated points (72-79). That is all. The return value of the function **getTransformedVertices** is a new float array with all our transformed vertices which we can use in our render process. Let’s take a look at our other functions, we probably need to change some of them.

## Our sprite instance

In the **GLRenderer** class, we should remove the ‘*Rect image*‘ line and replace it by:

public Sprite sprite;

In the constructor of the same class, we should remove the ‘*image = new Rect();*‘ line and replace it by:

sprite = new Sprite();

So now we have created our class and we have a new instance of that class. Let’s use it

## New touch functionality

To be able to showcase our new transformation functions we have to alter our input control function a little bit:

public void processTouchEvent(MotionEvent event) { // Get the half of screen value int screenhalf = (int) (mScreenWidth / 2); int screenheightpart = (int) (mScreenHeight / 3); if(event.getX()<screenhalf) { // Left screen touch if(event.getY() < screenheightpart) sprite.scale(-0.01f); else if(event.getY() < (screenheightpart*2)) sprite.translate(-10f, -10f); else sprite.rotate(0.01f); } else { // Right screen touch if(event.getY() < screenheightpart) sprite.scale(0.01f); else if(event.getY() < (screenheightpart*2)) sprite.translate(10f, 10f); else sprite.rotate(-0.01f); } }

We have divided our screen in a 2×3 grid of touch areas. Each area has its own functionality; rotating, scaling and translating the sprite in both directions.

## SetupTriangle

At the beginning of our rendering process, we setup our triangle information, we have to change this a little bit to incorporate our new sprite class functionality. Here is our new smaller function:

public void SetupTriangle() { // Get information of sprite. vertices = sprite.getTransformedVertices(); // The order of vertexrendering for a quad indices = new short[] {0, 1, 2, 0, 2, 3}; // The vertex buffer. ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(indices); drawListBuffer.position(0); }

Instead of setting up all the values for our vertices, we just retrieve the transformed vertices of our sprite instance and assign that to our vertices float array (4).

## Updating our draw information

The last thing we have to do is updating the vertices information we pass to the render function. We have changed the **TranslateSprite()** function into the following function:

public void UpdateSprite() { // Get new transformed vertices vertices = sprite.getTransformedVertices(); // The vertex buffer. ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); }

We get our new transformed vertices from our sprite and assign it to our vertices array. The rest is the same as before, the vertices are used in a FloatBuffer and that buffer is used in our render function.

The final thing to add is a call to the **UpdateSprite** function in the **onDrawFrame** function, just before the call to the Render function.

Run your project and you are able to scale, rotate and translate the image by touching the screen!

### Files

### Tutorial index

- A real Open GL ES 2.0 2D tutorial part 1: Rendering a triangle
- A real Open GL ES 2.0 2D tutorial part 2: Rendering an Images
- A real Open GL ES 2.0 2D tutorial part 3: Handling Input
- A real Open GL ES 2.0 2D tutorial part 4: Transforming Images
- A real Open GL ES 2.0 2D tutorial part 5: Knowing the OpenGL texture system
- A real Open GL ES 2.0 2D tutorial part 6: Screens and dimensions
- A real Open GL ES 2.0 2D tutorial part 7: Texture Atlas
- A real Open GL ES 2.0 2D tutorial part 8: Rendering Text