Book Image

Android NDK Game Development Cookbook

Book Image

Android NDK Game Development Cookbook

Overview of this book

Android NDK is used for multimedia applications which require direct access to a system's resources. Android NDK is also the key for portability, which in turn provides a reasonably comfortable development and debugging process using familiar tools such as GCC and Clang toolchains. If your wish to build Android games using this amazing framework, then this book is a must-have.This book provides you with a number of clear step-by-step recipes which will help you to start developing mobile games with Android NDK and boost your productivity debugging them on your computer. This book will also provide you with new ways of working as well as some useful tips and tricks that will demonstrably increase your development speed and efficiency.This book will take you through a number of easy-to-follow recipes that will help you to take advantage of the Android NDK as well as some popular C++ libraries. It presents Android application development in C++ and shows you how to create a complete gaming application. You will learn how to write portable multithreaded C++ code, use HTTP networking, play audio files, use OpenGL ES, to render high-quality text, and how to recognize user gestures on multi-touch devices. If you want to leverage your C++ skills in mobile development and add performance to your Android applications, then this is the book for you.
Table of Contents (16 chapters)
Android NDK Game Development Cookbook
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Basic rendering with OpenGL ES


Let us add some graphics to our sample Android application App2. Here, we show how to create an off-screen bitmap, and then copy it to the screen using the OpenGL ES Version 2 or 3 available on your Android device.

Note

Refer to the App3 sample in the book's downloadable code bundle for the full source code.

Getting ready

We assume that the reader is somewhat familiar with OpenGL and the GL Shading Language (GLSL). Refer to http://www.opengl.org/documentation for the desktop OpenGL, and http://www.khronos.org/opengles for the mobile OpenGL ES documentation.

How to do it…

  1. We need to write a simple vertex and fragment GLSL shader that will render our framebuffer on the screen using OpenGL ES. Let's put them directly into jni/Wrappers.cpp as strings. The following code shows the vertex shader:

    static const char g_vShaderStr[] =
       "#version 100\n"
       "precision highp float;\n"
       "attribute vec3 vPosition;\n"
       "attribute vec3 vCoords;\n"
       "varying vec2 Coords;\n"
       "void main()\n"
       "{\n"
       "   Coords = vCoords.xy;\n"
       "   gl_Position = vec4( vPosition, 1.0 );\n"
       "}\n";
  2. The fragment shader is as follows:

    static const char g_fShaderStr[] =
       "#version 100\n"
       "precision highp float;\n"
       "varying vec2 Coords;\n"
       "uniform sampler2D Texture0;\n"
       "void main()\n"
       "{\n"
       "   gl_FragColor = texture2D( Texture0, Coords );\n"
       "}\n";
  3. We will also need the following helper function to load our shaders into OpenGL ES:

    static GLuint LoadShader( GLenum type, const char* shaderSrc )
    {
       GLuint shader = glCreateShader( type );
       glShaderSource ( shader, 1, &shaderSrc, NULL );
       glCompileShader ( shader );
       GLint compiled;
       glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
       GLsizei MaxLength = 0;
       glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &MaxLength );
       char* InfoLog = new char[MaxLength];
       glGetShaderInfoLog( shader, MaxLength, &MaxLength, InfoLog );
       LOGI( "Shader info log: %s\n", InfoLog );
       return shader;
    }

How it works…

We will not go into all the details about the OpenGL ES programming here, and will instead focus on a minimal application (App3) that should initialize the GLView in Java; create fragment and vertex programs, create and fill the vertex array consisting of two triangles that form a single quadrilateral, and then render them with a texture, which is updated from g_FrameBuffer contents. This is it—just draw the offscreen framebuffer. The following is the code to draw the full-screen quad textured with the offscreen buffer content:

  const GLfloat vVertices[] = { -1.0f, -1.0f, 0.0f,
                                -1.0f,  1.0f, 0.0f,
                                 1.0f, -1.0f, 0.0f,
                                -1.0f,  1.0f, 0.0f,
                                1.0f, -1.0f, 0.0f,
                                1.0f,  1.0f, 0.0f
                              };

  const GLfloat vCoords[]   = {  0.0f,  0.0f, 0.0f,
                                 0.0f,  1.0f, 0.0f,
                                 1.0f,  0.0f, 0.0f,
                                 0.0f,  1.0f, 0.0f,
                                 1.0f,  0.0f, 0.0f,
                                 1.0f,  1.0f, 0.0f
                              };
  glUseProgram ( g_ProgramObject );

These attribute variables are declared in a vertex shader. See the value of g_vShaderStr[] in the preceding code.

  GLint Loc1 = glGetAttribLocation(g_ProgramObject,"vPosition");
  GLint Loc2 = glGetAttribLocation(g_ProgramObject,"vCoords");

  glBindBuffer( GL_ARRAY_BUFFER, 0 );
  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
  glVertexAttribPointer(
    Loc1, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
  glVertexAttribPointer(
    Loc2, 3, GL_FLOAT, GL_FALSE, 0, vCoords   );
  glEnableVertexAttribArray( Loc1 );
  glEnableVertexAttribArray( Loc2 );

  glDisable( GL_DEPTH_TEST );
  glDrawArrays( GL_TRIANGLES, 0, 6 );
  glUseProgram( 0 );
  glDisableVertexAttribArray( Loc1 );
  glDisableVertexAttribArray( Loc2 );

We also need a few JNI callbacks. The first one handles the surface size changes, as seen in the following code:

  JNIEXPORT void JNICALLJava_com_packtpub_ndkcookbook_app3_App3Activity_SetSurfaceSize(JNIEnv* env, jclass clazz, int Width, int Height )
  {
    LOGI( "SurfaceSize: %i x %i", Width, Height );
    g_Width  = Width;
    g_Height = Height;
    GLDebug_LoadStaticProgramObject();
    glGenTextures( 1, &g_Texture );
    glBindTexture( GL_TEXTURE_2D, g_Texture );

Disable mip-mapping through the following code:

    glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,ImageWidth, ImageHeight, 0, GL_RGBA,GL_UNSIGNED_BYTE, g_FrameBuffer );
  }

The second callback does the actual frame rendering:

  JNIEXPORT void JNICALL Java_com_packtpub_ndkcookbook_app3_App3Activity_DrawFrame( JNIEnv* env, jobject obj )
  {

Invoke our frame rendering callback through the following code:

    OnDrawFrame();

    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, g_Texture );
    glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,ImageWidth, ImageHeight, GL_RGBA,GL_UNSIGNED_BYTE, g_FrameBuffer );
    GLDebug_RenderTriangle();
  }