Monday, June 11, 2012

A nice segue from Decade Engine to Mobile Development.

A friend is in the process of writing a nice iPad application. I shall not go into any detail regarding the app as it is his idea and not mine to share. The app needs to render the earth, allow the user to rotate the planet, zoom in and out to country level and also allow the user to touch anywhere on the globe and if a country has been pressed, that country is highlighted and this information available to the app layer.

To date he has been using an open framework called whirlyglobe. This is a pretty impressive framework and I would recommend that you check it out but after testing it on an iPad2 and 'The New iPad' within the app it seemed a little slow. Vector files are being used to highlight the country, raising it above others when selected. All of this is in very high detail and looks excellent, but this detail does come at a cost. The response on the iPad is sluggish and would probably be even more so when there is an app sitting above it.

When looking into how we could improve the performance, I suggested that I could use the concepts that I developed when programming the original Decade Engine, along with the new features I have been learning with converting the original engine to OpenGL 3/OpenGL ES 2.0.

Here is the first rendering from Decade Mobile. Please note that this video was recorded off my Mac Mini but the same code (with minor changes which I shall document in a latter post) has been built and runs on an iPad and iPhone.

The textures used in this video have been purchased from here. Since zooming is only required to the country level and not to the cm or meter level as was possible in the original Decade Engine, I thought it overkill to use the procedural sphere technique so instead just use a normal sphere. Some webgl code for generating the vertices for a sphere can be found here.
Sphere Generation (Vertex and Index Buffer) Code

void Sphere::Create(const Vector3 center, GLfloat radius, GLuint precision)
    vector vertices;
    GLuint latitudeBands = precision;
    GLuint longitudeBands = precision;
    for (GLuint latNumber = 0; latNumber <= latitudeBands; latNumber++)
        GLfloat theta = latNumber * M_PI / latitudeBands;
        GLfloat sinTheta = sinf(theta);
        GLfloat cosTheta = cosf(theta);
        for (GLuint longNumber = 0; longNumber <= longitudeBands; longNumber++)
            GLfloat phi = longNumber * 2 * M_PI / longitudeBands;
            GLfloat sinPhi = sinf(phi);
            GLfloat cosPhi = cosf(phi);
            GLfloat x = cosPhi * sinTheta;
            GLfloat y = cosTheta;
            GLfloat z = sinPhi * sinTheta;
            GLfloat u = 1.0f - ((GLfloat)longNumber / (GLfloat)longitudeBands);
            GLfloat v = (GLfloat)latNumber / (GLfloat)latitudeBands;
            VERTEX_POSITION_UV0 vertex;
            vertex.Position = Point4(radius * x, radius * y, radius * z, 1.0f);
            vertex.U0 = u;
            vertex.V0 = 1.0f - v;
    vector indices;
    for (GLuint latNumber = 0; latNumber < latitudeBands; latNumber++)
        for (GLuint longNumber = 0; longNumber < longitudeBands; longNumber++)
            GLuint first = (latNumber * (longitudeBands + 1)) + longNumber;
            GLuint second = first + longitudeBands + 1;
            indices.push_back(first + 1);
            indices.push_back(second + 1);
            indices.push_back(first + 1);

    vertexBuffer.Create((float*)vertices, VERTEX_POSITION_UV0::GetFloatsInFormat(), vertices,size(), VERTEX_POSITION_UV0::GetFormat());
    indexBuffer.Create(indices, indices.size());

void Sphere::Bind()

void Sphere::Render()
    vertexBuffer.Render(&indexBuffer, GL_TRIANGLES);
Vertex Shader

uniform mat4 mvp;

in vec4 position;
in vec2 uv0;

out vec2 textureCoord0;

void main (void)
    textureCoord0 = uv0;
    gl_Position = mvp * position;
Fragment Shader

in vec2 textureCoord0;
uniform sampler2D texture0;

out vec4 fragColor;

void main(void)
    fragColor = texture(texture0, textureCoord0);

No comments:

Post a Comment