Tutorial 05: transforms

downloadDownload the ready to use Eclipse project for this tutorial

On this journey to making a game; most of the stuff we’ve seen is pretty dull so far. But this is where the fun begins: transformations! No we are not going to turn super sayan but however, we’re going to learn how to position, rotate and scale meshes. This is a basic of any game engine.

In our previous projects we had setup 3 quads to draw the background and ryu. With transforms; you need only one. Yes that’s right: one. The reason for that is that a quad is essentially always the same thing: 4 vertices on the XY plane. So if you need to have one on position x1,y1, and another one on position x2,y2; well you can simply translate the quad to x1,y1, draw, then translate the exact same quad on x2,y2 and redraw; potentially with another texture! There is absolutely no need to define 2 quads for this job.

And now say ok; you have to draw at two different positions; but the quads also don’t have the same size. Well perfect; you can simply scalethe quad to the expected size. If you want to be fancy, you can evenrotate the quad to a PI/6 angle, draw it, then redraw it elsewhere in it’s original shape.

All these operations: translate, scale and rotate are called transforms.

To use them efficiently you have to define one quad. The quad. This quad will have a 0 origin on the bottom left, just like your camera, and a width and height of 1.0f.

    quad = new Mesh(true, 4, 6, 
                new VertexAttribute(Usage.Position, 3, "a_position"),
                new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoords"));   
    quad.setVertices(new float[] {
        0.0f, 0.0f, 0, 0.0f ,1.0f ,
                1.0f, 0.0f, 0, 1.0f, 1.0f,
                1.0f, 1.0f, 0, 1.0f,0.0f,
                0.0f, 1.0f, 0, 0.0f, 0.0f });     
    quad.setIndices(new short[] { 0, 1, 2, 2, 3, 0});

Why? It’s easy to explain:

  • Origin at 0: because if we translate the quad to 0.2f,0.3f, it will have the position 0.2f,0.3f.
  • 1.0f width and height: because if you scale the quad using 0.15f,0.3f, this will be exact size of the quad.

But it’s never that easy folks! What about rotation? Rotation is a bit trickier. To illustrate this, let’s take a spaceship:

A spaceship, courtesy of schollidesign

A spaceship, courtesy of schollidesign

A rotation is done around the origin. So if you rotate by 90 degrees this space ship; this is what happens:

On the left, the rotation by 90 degrees, on the right, the original sprite.

On the left, the rotation by 90 degrees, on the right, the original sprite.

Can you see what’s happening? Originally the bottom left corner is 0,0. Once rotated by 90 degrees, the bottom left corner becomes -1,0.

This is not cool if you want to turn around your ship on it’s same position. So that’s why you need another quad. The centered quad. Basically; it’s the same than the other one except that the center of the quad is 0,0.

    centeredQuad = new Mesh(true, 4, 6, 
                new VertexAttribute(Usage.Position, 3, "a_position"),
                new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoords"));   
    centeredQuad.setVertices(new float[] {
        -.5f, -.5f, 0, 0.0f ,1.0f ,
                .5f, -.5f, 0, 1.0f, 1.0f,
                .5f, 0.5f, 0, 1.0f,0.0f,
                -.5f, 0.5f, 0, 0.0f, 0.0f });     
    centeredQuad.setIndices(new short[] { 0, 1, 2, 2, 3, 0});

This quad is also 1.0f width and height for easy scaling. Now; if you rotate this quad, no matter how; it won’t move and stay centered. These two quads are used for different reasons. It might be easier to draw tiles with a non centered quad, and it might be easier to draw a character in your game using a centered quad. In the end, use the one that suits best your needs.

Now, the code itself to scale, translate and rotate is very simple. The functions are glTranslatef, glRotatef and glScalef. But before we go into that, there is one last thing you need to know about transformations: they transform your whole world.

i.e: if you want to draw something at (0.2f,0.2f) then draw something at (0.1f,0.3f), you cannot simply do that by calling:
translate (0.2f,0.2f)
draw mesh
translate (0.1f,0.3f)
draw mesh

If you do that, you would translate to (0.2f,0.2f) then further translate by (0.1f,0.3f); meaning your 2nd mesh would be at (0.3f,0.5f).

To avoid this; you need to save your world camera. OpenGL provides this by the neat glPushMatrix and glPopMatrix functions.

You now know all that there is to know with transforms. Let’s draw some spaceships!

  @Override
  public void render() {
    GL10 gl = Gdx.graphics.getGL10();
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        cam.update();
        cam.apply(gl);
        
        gl.glPushMatrix(); //save world matrix
        
        spaceShip.bind();
        
        //draw a spaceship of 0.2f size, at 0.3f,0.2f
        gl.glPushMatrix();
        gl.glTranslatef(0.3f, 0.2f, 0.0f);
        gl.glScalef(0.2f, 0.2f, 1.0f);
        quad.render(GL10.GL_TRIANGLES);
        gl.glPopMatrix();
        
        //same, 90 degrees rotated
        gl.glPushMatrix();
        gl.glTranslatef(0.3f, 0.2f, 0.0f);
        gl.glScalef(0.2f, 0.2f, 1.0f);
        gl.glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
        quad.render(GL10.GL_TRIANGLES);
        gl.glPopMatrix();
        
        //also draw using our centered quad
        gl.glPushMatrix();
        gl.glTranslatef(0.3f, 0.6f, 0.0f);
        gl.glScalef(0.2f, 0.2f, 1.0f);
        centeredQuad.render(GL10.GL_TRIANGLES);
        gl.glPopMatrix();
        
        gl.glPushMatrix();
        gl.glTranslatef(0.3f, 0.6f, 0.0f);
        gl.glScalef(0.2f, 0.2f, 1.0f);
        gl.glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
        centeredQuad.render(GL10.GL_TRIANGLES);
        gl.glPopMatrix();
        
        gl.glPopMatrix();
        
  
  }

 

Final result.

Final result.

 

downloadDownload the ready to use Eclipse project for this tutorial

1 Comment

Leave a Reply

css.php