Friday, November 14, 2014

Native Android Application Development with C++ and Visual Studio 2015

Visual Studio 2015 ships the new C++ cross platform support which currently provides native C++ library compilation for Android and Windows platforms. iOS compiler support is supposed to be added as well.
That's not all. VS 2015 even provides a new project type called "Native-Activity Application" for Android app development. This pure C/C++ Android application model is mainly used for game development using full screen OpenGL rendering.
The Native-Activity Application project type allows developers to write native Android apps with C++ and even to run and debug those in the Android emulator only by using Visual Studio 2015.
No Xamarin, no Apache Cordova needed, just VS 2015 and pure C/C++.



How it works

It's pretty straightforward to get started.
Just install Visual Studio 2015 including its Secondary installer which adds the required Android development tools.
Start VS 2015 and select the Visual C++ -> Cross Platform -> Native-Activity Application (Android) project type:


The generated template code provides the base native Android app code including simple code for cycling the back buffer clear color inside the engine_draw_frame function.

For this sample here I decided to make the Hello World of 3D computer graphics: a rainbow colored rotating cube. Here's how you can achieve this as well:

Add the cube definitions to the beginning of the main.cpp:

static GLint vertices[][3] =
{
 { -0x10000, -0x10000, -0x10000 },
 { 0x10000, -0x10000, -0x10000 },
 { 0x10000,  0x10000, -0x10000 },
 { -0x10000,  0x10000, -0x10000 },
 { -0x10000, -0x10000,  0x10000 },
 { 0x10000, -0x10000,  0x10000 },
 { 0x10000,  0x10000,  0x10000 },
 { -0x10000,  0x10000,  0x10000 }
};

static GLint colors[][4] =
{
 { 0x00000, 0x00000, 0x00000, 0x10000 },
 { 0x10000, 0x00000, 0x00000, 0x10000 },
 { 0x10000, 0x10000, 0x00000, 0x10000 },
 { 0x00000, 0x10000, 0x00000, 0x10000 },
 { 0x00000, 0x00000, 0x10000, 0x10000 },
 { 0x10000, 0x00000, 0x10000, 0x10000 },
 { 0x10000, 0x10000, 0x10000, 0x10000 },
 { 0x00000, 0x10000, 0x10000, 0x10000 }
};

GLubyte indices[] = {
 0, 4, 5,    0, 5, 1,
 1, 5, 6,    1, 6, 2,
 2, 6, 7,    2, 7, 3,
 3, 7, 4,    3, 4, 0,
 4, 7, 6,    4, 6, 5,
 3, 0, 1,    3, 1, 2
};


And replace the // Initialize GL State statements at the end of the engine_init_display function with this:

 // Initialize GL state.
 glDisable(GL_DITHER);
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
 glClearColor(1.0f, 0.41f, 0.71f, 1.0f); // Hot pink! :D
 glEnable(GL_CULL_FACE);
 glShadeModel(GL_SMOOTH);
 glEnable(GL_DEPTH_TEST);
 glViewport(0, 0, w, h);
 GLfloat ratio = (GLfloat)w / h;
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 glFrustumf(-ratio, ratio, -1, 1, 1, 10);


Finally replace the engine_draw_frame function:

static void engine_draw_frame(struct engine* engine) {
 if (engine->display == NULL) {
  // No display.
  return;
 }

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 glTranslatef(0, 0, -3.0f);
 glRotatef(engine->state.angle * 0.25f, 1, 0, 0);  // X
 glRotatef(engine->state.angle, 0, 1, 0);          // Y

 glEnableClientState(GL_VERTEX_ARRAY);
 glEnableClientState(GL_COLOR_ARRAY);

 glFrontFace(GL_CW);
 glVertexPointer(3, GL_FIXED, 0, vertices);
 glColorPointer(4, GL_FIXED, 0, colors);
 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
 
 eglSwapBuffers(engine->display, engine->surface);
}

You also might want to modify the angle increment so the cube rotates a bit faster, like engine.state.angle += 1.f;

Make sure the project is set to compile as x86 in order to use VS' own Android emulator.
Then F5 and enjoy the magic of the rotating cube.


Now try to add a breakpoint in your engine_draw_frame function. Yes, there's native debugging support for Android apps in Visual Studio 2015. Times are definitely changing at Microsoft.


Sample code

You can download the complete VS 2015 sample solution here.


More resources

This blog post is a nice overview of the new Visual C++ cross platform features.
This short Ch9 video gives a quick intro to the new Visual Studio 2015 C++ cross platform support.
NeHe is a quite old resource for OpenGL tutorials but mostly still useful.

7 comments:

  1. Do people keep saying it's for games only because there is no other built in UI capability, so OpenGL or DirectX are available to produce a UI?

    ReplyDelete
    Replies
    1. It's mainly used for games since most people prefer an already built UI framework with common controls. Of course you can always roll your own UI framework in OGL or DX and in fact there are a few available.

      Delete
  2. ....not sure the VS2015 C# cross platform is full features from Xamarin or not ?

    rickygai@yahoo.com

    ReplyDelete
    Replies
    1. You still need to install Xamarin. The VS 2015 templates point you to the Xamarin download site in case it's not installed.

      Delete
  3. Awesome post, got this running on my phone after updating to KitKat. Would you happen to know where I might find documentation on how to do touch screen input?

    Everywhere I search just leads me to the same m$ blog posts that show off the features of the new VS2015, without actually pointing out any form of documentation/implementation, or dev community.

    I would greatly appreciate any sense of direction haha.

    ReplyDelete
  4. I have read so many articles or reviews however this blog post is genuinely a pleasant article, keep it up.
    Software Development Company in Indore

    ReplyDelete
  5. Good little tutorial, I did have to edit a further bit of code and remove the reset to 0 once the engine.angle>1 as it made the damn thing sit still on the screen

    it's in "void android_main(struct android_app* state) "

    right down the bottom (the bits in the /**/

    if (engine.animating) {
    // Done with events; draw next animation frame.
    engine.state.angle += 1.01f;
    /*if (engine.state.angle > 365) {
    engine.state.angle = 0;
    }*/

    // Drawing is throttled to the screen update rate, so there
    // is no need to do timing here.
    engine_draw_frame(&engine);
    }

    ReplyDelete