12

Deploying Assimp Using Visual Studio and Android NDK for Tegra Devices

 3 years ago
source link: https://codingadventures.me/2015/07/21/deploying-assimp-to-android-using-visual-studio-and-android-ndk/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Deploying Assimp Using Visual Studio and Android NDK for Tegra Devices

Hello folks, welcome back to my blog, hope you are ready for a new adventure. This time I promise it is going to be an adventure with the capital A. I’ve been working on a finite element method algorithm using C++ (and later CUDA) to prove that the latest generation of mobile devices (more specifically the Kepler architecture in the Shield Tablet) is capable of running such complex algorithms.

The Shield is shipped with Android Kit-Kat 4.4 thus using C++ or Java and OpenGL ES 2.0 is not a problem…well not just yet 😀

Setting up the environment is not too difficult too. I used the Tegra Android Development Pack, that installs, all the tools you need to start developing on Android (including extensions for Visual Studio and the whole Eclipse IDE). After a few clicks you have everything up and running.

The Problem

I need to load 3D models. Albeit I could have written my own parser (which I think it could have been less painful) I decided to use Assimp instead. Assimp is a very handy library that can handle a plenitude of different file formats. I’ve used it extensively in all my projects so far. It supports Android and iOS (as it is stated on its GitHub page).

I read the doc a lot, but I found no easy way (well at least under Windows) to generate a Visual Studio solution (sorry I’m a Visual Studio addicted) to compile it using the Android NDK. I searched on the web for a long while and I found a couple of articles that explain how to compile Assimp for Android (this: Assimp on Desktop and Mobile and this other: Compile Assimp Open Source Library For Android). The procedure is quite troublesome, requires Cygwin under Windows and a lot of patience. Luckily in the second article mentioned above, the author posted a pre-compiled assimp 3.0 version lib with headers included.

Download Assimp 3.0 lib for Android here.

Having Assimp already compiled was truly helpful. It saved me a lot of time that I would have spent figuring out how to put everything together.

Here it comes the tricky part. Assimp was compiled as a shared library (an .so). To reference it is pretty easy. The include and the lib path have to be set and then the name of the library specified. Visual Studio doesn’t use the Android.mk (whereas Eclipse does I think) that tells the Ant build and the the apk builder how pack the apk, which local shared lib to include. It is to be done in the project’s properties instead.

After setting up the whole thing, the solution compiled, linked and the apk was created correctly. I was confident that Assimp would be deployed with the apk, but I soon found out it was not. Surprisingly I got this error instead on the tablet when I ran the application:

Unfortunately, NativeActivity has stopped…

Looking at the LogCat I found this error message too:

Figure 1

“java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.shield.fem-1/libShieldFiniteElementMethod.so”,  which told me absolutely nothing about the nature of the problem. Fortunately the only thing I knew I changed was the reference to Assimp. It was clear to me what was that the cause of the problem. But why and how wasn’t explained at all by the log files. It was easy to spot it though. I looked at the output window and libassimp.so (see Figure 2 below) was not included at all.

Output library listFigure 2

The Solutions

I found  two solutions for this issue. I like to call them respectively  “The easy way”, and “The way of pain”. I had already added an external library (I had to use libpng for loading textures), but in that case it went smoothly because it was a static library. Static libraries are .a (or in Windows .lib) files. All the code relating to the library is in this file, and it is directly linked into the program at compile time. Shared libraries are .so (or in Windows .dll, or in OS X .dylib) files. All the code relating to the library is in this file, and it is referenced by programs using it at run-time, reason why it is not deployed with the apk unless explicitly told.

Way of pain

DISCLAIMER: This solution involves rooting your device, so I’m not responsible if warranty voids. Please do it at your own risk. 

This was my first attempt to shove in libassimp. By default all the libraries stored in /system/lib on the device are loaded automatically at startup, so it is very seamless. If any lib is there the running process can use it. I used the command adb shell, (adb is installed as part of the development pack) which gave me access to the bash-like shell on the Tablet. As I was expecting Assimp was not in the system lib folder. My first idea was to upload manually the lib into /system/lib so I ran:

adb push libassimp.so /system/lib
adb push libassimp.so /system/lib

Unless your Android device is rooted and the /system mounted as read-write this is the message you will get:

Failed to copy ‘libassimp.so’ to ‘/system/lib/libassimp.so’: Read-only file system

The only solution as I said is to root your device first. This can be quite painful and it depends on your model. There are a few good guides around. Use google, take a cup of coffee and have a lot of patience. Personally to root mine (a Shield Tegra) I used this guide, and the app adbd Insecure available on google play,  that lets you run adbd in root mode once your device has been rooted.

At this stage I assume your Android friend is rooted so you can finally remount the system folder in order to add read-write permissions. Use this command:

adb shell
root@shieldtablet:/ # mount -o rw,remount /system
adb shell
root@shieldtablet:/ # mount -o rw,remount /system

Later if you want you can restore its original read-only permission by executing:

adb shell
root@shieldtablet:/ # mount -o ro,remount /system
adb shell
root@shieldtablet:/ # mount -o ro,remount /system

OK, at that stage I had permissions to do whatever I wanted with system so I was finally able  to upload Assimp. Execututing again the command adb push showed no error this time:

Figure 3 – Upload has been successful!

At this stage I didn’t have to do anything really. Once the application starts it will load Assimp (and any other libs in there) automatically.

The Easy Way

I found out this easier solution only after I went through hell using the first painful approach (trust me it took me a while to understand how to root the device and which commands to run). Here you don’t need to root your device at all, but you will have to change your code a little bit to dynamically load Assimp (shared libs in general though). Let’s start!

First of all I didn’t know it was possible to upload shared libraries through Visual Studio (d’oh!). I didn’t find it written anywhere (well maybe I didn’t search well) but looking at my projects properties I found this:

Figure 4

In the Ant build it is possible to specify Native library dependencies! At this very stage I would imagine you laughing knowing what I went through with the “way of pain” 😀

Anyway, I set references to Assimp right here, look at figure 5:

project properties 2
Figure 5

Using this approach the shared library is built seamlessly into the apk! The only drawback is that it won’t be loaded automatically! For this  issue another little trick is needed. If you try to execute/debug your program now, you will likely get again the same error message as in Figure 1.

You need to load any shared library before your native activity. To do this a Java class is to be used. Something like:

package com.your.package;
public class Loader extends android.app.NativeActivity
static
System.loadLibrary("assimp");
package com.your.package;

public class Loader extends android.app.NativeActivity 
{
	static
	{
		System.loadLibrary("assimp");
	}
}

It is important that Loader.java goes under the folder src in your project and that it is wrapped in a folder structure that respects your package declaration (I know if you’re a Java guy it is evident for you, but I’m more a C#/C++ one so it took me again a while to figure it out 😛 ).

The last bit: change your AndroidManifest.xml android:hasCode must be equal to True and change the android:name in the activity tag from android.app.NativeActivity to Loader (i.e. the name of your Java class)

<!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. -->
<!-- Our activity is the built-in NativeActivity framework class.  This will take care of integrating with our NDK code. -->

That’s finally it!

Conclusions

I’m a total newbie with Android development and it’s been quite hard for me to figure out how to deploy a shared library in Visual Studio as it wasn’t very intuitive. A lot of examples I found online use command line scripts to compile and/or different IDEs. The most common approach is using an .mk file where properties, libraries etc are defined inside. Mk files are (apparently) completely ignored by VS so it wasn’t possible for me to use one.

I really hope this article can help you. I am looking forward to reading your comments, hoping that there are other simpler ways to achieve what I did today.

See you soon!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK