Vulkan on Android 4 — Model Rendering Part 1

Jin-Long Wu
4 min readOct 22, 2018

--

Photo by Víctor Elvira Ávalos on Unsplash

Our canvas needs some colors on other than just a background. For the purpose, we will dump in a model, draw colors on it with images and lights and finally, make it move around. But in this post, we are going to be done with some prerequisite first.

GLM

OpenGL Mathematics (GLM) is a header only C++ mathematics library for graphics software based on the OpenGL Shading Language (GLSL) specifications.

GLM provides classes and functions designed and implemented with the same naming conventions and functionalities than GLSL so that anyone who knows GLSL, can use GLM as well in C++.

This project isn’t limited to GLSL features. An extension system, based on the GLSL extension conventions, provides extended capabilities: matrix transformations, quaternions, data packing, random numbers, noise, etc…

So, we know what it is used for, but how do we integrate it into our project?

  1. Download GLM from Github, we need only the whole glm sub-directory. No cmake, no doc, no test, .vscode, uitily, .appveyor.yml, …etc.
  2. Place glm somewhere as long as we specify the path to it later in CMakeLists.txt in our project. For me, I put it to app/include directory.
  3. Adds a search path for header files and some definitions in app/CMakeLists.txt.
# glm
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_definitions("-DGLM_FORCE_SIZE_T_LENGTH -DGLM_FORCE_RADIANS -DGLM_FORCE_DEPTH_ZERO_TO_ONE -DGLM_LANG_STL11_FORCED")
add_subdirectory(include/glm)

GLM_FORCE_DEPTH_ZERO_TO_ONE worths a brief explanation. The viewport transform matrix in Vulkan differs with what we use in OpenGL so it requires some adaptions if we still set the same depth range values as we do in OpenGL(glDepthRange).

STB

STB is a collection of single-file libraries for C/C++. However, we are only interested in stb_image.h for image decoding.

We do not add definitions about stb_image.h in CMakeLists.txt due to that the document states that we should include this header file in only one C or C++ file, and add definitions before including it.

Assimp

Assimp might be the only comprehensive model loading library I could ever find. It supports various 3D model formats and is with extra useful features that impressed me. I used to deal with lightweight model loading library which supports a single 3D model format and be happy with it. It is fast and easy to use but I realized it is not the best choice in the long run so I switch to Assimp in the end.

Build

Assimp official website does not offer step-by-step instructions about how to build it on Android. The results I’ve googled are not consistent steps to follow and they vary from version to version, tool to tool. Finally, I discovered a simple way to build it with Android studio. Not a single command is required.

  1. Download Assimp from the official site.
  2. Create a new module of type Android Library with any name we want, like, assimp, and delete anything about testing.
  3. Unzip assimp-4.1.0.zip(in my case) and place the directory assimp-4.1.0 to assimp/src/main.
  4. Specify the path of CMakeLists.txt and add some definitions in build.gradle of our new module. Here I disabled the support for all the 3D formats and enables the support for FBX, BLEND, and OBJ files. Note it is not mandatory to define ASSIMP_ANDROID_JNIIOSYSTEM on Android if we are not going to load assets from the assets folder.
...
android {
defaultConfig {
minSdkVersion 25
targetSdkVersion 28
versionCode 1
versionName "1.0"

externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_shared",
"-DASSIMP_ANDROID_JNIIOSYSTEM=ON",
"-DASSIMP_NO_EXPORT=ON",
"-DASSIMP_BUILD_ASSIMP_TOOLS=OFF",
"-DASSIMP_BUILD_TESTS=OFF",
"-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=FALSE",
"-DASSIMP_BUILD_FBX_IMPORTER=TRUE",
"-DASSIMP_BUILD_BLEND_IMPORTER=TRUE",
"-DASSIMP_BUILD_OBJ_IMPORTER=TRUE"
cppFlags "-fexceptions"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
...
externalNativeBuild {
cmake {
path "src/main/assimp-4.1.0/CMakeLists.txt"
}
}
}
...

5. Build the module and we ‘ll get libassimpd.so and libandroid_jniiosystemd.a(if ASSIMP_ANDROID_JNIIOSYSTEM is defined) for debugging build or libassimp.so and libandroid_jniiosystem.a for release build. Note we’ll get multiple .so and .a files if we specify multiple terms in abiFilters . Our app runs on armeabi-v7a and arm64-v8a so we should build Assimp on the same architectures accordingly.

6. Remove and delete the module since we don’t need it anymore.

Link

That’s all, now we are going to link the libraries for our project.

1. Create folders inside app directory:

app/libs/armeabi-v7a
app/libs/armeabi-v8a

and place our new fresh libraries to corresponding ones.

2. Link the libraries in app/CMakeLists.txt

...
add_library(assimp-lib SHARED IMPORTED)
set_target_properties(assimp-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libassimp.so)
add_library(android_jniiosystem-lib SHARED IMPORTED)
set_target_properties(android_jniiosystem-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libandroid_jniiosystem.a)
...
target_link_libraries( # Specifies the target library.
main

...
assimp-lib
android_jniiosystem-lib )

3. Copy the extracted assimp-4.1.0/include/assimp header files to app/include for later use.

--

--