OpenGL ES SDK for Android ARM Developer Center
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Asset Loading

Using the Open Asset Importer to load models into OpenGL ES.

Introduction

The source for this sample can be found in the folder of the SDK.

In the tutorials so far all of the geometry for the scenes has been hand crafted and stored in the source files for the examples. We've hinted in previous tutorials that in the real-world, people use modeling tools to create their scenes. In this tutorial we'll show you how to load simple model files using the Open Asset Importer library.

Setting up the Project

For the tutorial, the Simple Cube project will be used as a starting point. So either copy the project or create a completely new project and include the source files from the previous project. The project should be called AssetLoading and the package name should be called com.arm.malideveloper.openglessdk.assetloading. Finally, the activity class (previously SimpleCube.java) should be renamed AssetLoading.java using Eclipse (this ensures references to the activity get updated elsewhere in the project).

Using external libraries in Android

In order to use the Open Asset Importer library we first need to build it for Android and then reference it as part of our build process. The Open Asset Importer library uses a CMake based build system which makes it easy to build for most platforms. To make things a bit easier we have provided a prebuilt version of the the Open Asset Importer library for Android and the required header files in the assimp folder of the tutorial. If you want to build the Open Asset Importer for Android yourself, there are a few tutorials and open source projects to add Android support to CMake available online.

To use this prebuilt library we have to tell CMake where to find the built library and its header files. The CMake code for importing the library look like this:

add_library(assimp SHARED IMPORTED)
set_property(TARGET assimp PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/assimp/libassimp.so)
target_include_directories(${sample} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/assimp/include)
target_link_libraries(${sample} assimp)
add_custom_command(TARGET ${sample} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/assimp/libassimp.so ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})

The first line declares that the library already exists and so the build system knows it doesn't have to try and build it. The rest of the file describes the various parameters of a prebuilt library. It tells the CMake build system the name of the module (so we can reference it elsewhere), the name of the actually library file, and the location of the header files which any project using the library will need. Those header files will automatically get added to the include path of any project referencing this library.

You can use similar code to include other prebuilt libraries for CMake.

Using the Open Asset Importer library.

The Open Asset Importer can load models store in a variety of different formats. It converts these to a standard internal format which we can access in a consistent manner. Different model formats can store a variety of different features, for example:

  • Geometry
  • Normals
  • Textures
  • Materials
  • Bones
  • Animation

For this tutorial we are going to focus on just geometry.

The Open Asset Importer loads the geometry into one or more meshes, and stores a list of faces which index into the geometry. This is very similar to how OpenGL ES works with glDrawElements, you give OpenGL ES a list of all the vertices you require and then give it a list of indices into that list to draw your polygons.

We will load the model in the setupGraphics function. First we pass in the data to the Open Asset Importer:

std::string sphere = "s 0 0 0 10";
scene = aiImportFileFromMemory(sphere.c_str(), sphere.length(), 0, ".nff");
if(!scene)
{
LOGE("Open Asset Importer could not load scene. \n");
return false;
}

The Open Asset Importer is capable of loading model files directly, however, because loading files on Android from native code is non-trivial, we are using a buffer instead. We define a buffer which represents a model file, pass that to the Open Asset Importer along with a hint to tell it which file format we are using. Here we are using the Neutral File Format (documentation can be found here). This particular buffer represents a sphere (s) at the origin (0 0 0) with radius 10.

After we've loaded a file into the Open Asset Importer, we simply extract all the vertices in the meshes into and array and then all the indices into another.

int vertices_accumulation = 0;
/* Go through each mesh in the scene. */
for (int i = 0; i < scene->mNumMeshes; i++)
{
/* Add all the vertices in the mesh to our array. */
for (int j = 0; j < scene->mMeshes[i]->mNumVertices; j++)
{
const aiVector3D& vector = scene->mMeshes[i]->mVertices[j];
vertices.push_back(vector.x);
vertices.push_back(vector.y);
vertices.push_back(vector.z);
}
/*
* Add all the indices in the mesh to our array.
* Indices are listed in the Open Asset importer relative to the mesh they are in.
* Because we are adding all vertices from all meshes to one array we must add an offset
* to the indices to correct for this.
*/
for (unsigned int j = 0 ; j < scene->mMeshes[i]->mNumFaces ; j++)
{
const aiFace& face = scene->mMeshes[i]->mFaces[j];
indices.push_back(face.mIndices[0] + vertices_accumulation);
indices.push_back(face.mIndices[1] + vertices_accumulation);
indices.push_back(face.mIndices[2] + vertices_accumulation);
}
/* Keep track of number of vertices loaded so far to use as an offset for the indices. */
vertices_accumulation += scene->mMeshes[i]->mNumVertices;
}

If you want to load textures, animation, or any of the other advance features it is slightly more complex. Have a look a the Open Asset Importer documentation for more information.

We then pass these arrays to OpenGL ES in the renderFrame function:

glUseProgram(glProgram);
/* Use the vertex data loaded from the Open Asset Importer. */
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, &vertices[0]);
glEnableVertexAttribArray(vertexLocation);
/* We're using vertices as the colour data here for simplicity. */
glVertexAttribPointer(vertexColourLocation, 3, GL_FLOAT, GL_FALSE, 0, &vertices[0]);
glEnableVertexAttribArray(vertexColourLocation);
glUniformMatrix4fv(projectionLocation, 1, GL_FALSE, projectionMatrix);
glUniformMatrix4fv(modelViewLocation, 1, GL_FALSE, modelViewMatrix);
/* Use the index data loaded from the Open Asset Importer. */
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);

And there you have it, now instead of specifying your models manually, you can load them from your favourite modeling software. This means that you can create, edit, and fine tune your models in a 3D modeling application and then import directly into your application. The Open Asset Importer supports around 40 different import formats, including all the most popular one, so it should cover most assets you might want to use.

Building and Running the Application

Follow the Getting Started Guide from Building Android samples onwards to build and run the application.

Next Steps

You can extend this sample to load normals and textures as well. The Texture Cube and Lighting may be useful here.