OpenSceneGraph on Android - preet/scratch GitHub Wiki

This page describes a possible method for building OpenSceneGraph (version 3.2.x as of this writing) as a bunch of shared libs for an Android device. The build was successfully tested on a Nexus 4 running Android 4.2.2.

Last tested OpenSceneGraph git commit: 1875d9239c495bc703507dd099f2b931ec2f6eac


###Android NDK Standalone Toolchain Download the Android NDK and install it if you don't already have it. Version r8e and API Level 9 was used in this guide.

Copy a standlone Android toolchain to another directory.

/home/user/Dev/env/sys/android-ndk-r8e/build/tools/make-standalone-toolchain.sh \
--platform=android-9 \
--install-dir=/home/user/Dev/env/sys/android-toolchain-arm/ \
--system=linux-x86_64

###Android-CMake Toolchain Get android.toolchain.cmake from

https://github.com/taka-no-me/android-cmake

We need to make a couple of modifications to build OpenSceneGraph with this CMake toolchain. OpenSceneGraph's build scripts use the ANDROID variable for a separate static Android build. The CMake toolchain defines an ANDROID variable as well, which causes the OpenSceneGraph config to try and build the static Android libs. We need a unique varible for the shared build.

Comment out the ANDROID and BUILD_ANDROID flags and add a ANDROID_SHARED flag (line 1560):

  # set these global flags for cmake client scripts to change behavior
  # set( ANDROID True )
  # set( BUILD_ANDROID True )
  set( ANDROID_SHARED True )

Fix what's possibly a bug when linking against the shared STL with this toolchain by moving the block of code that copies the shared stl locally (line 1138):

  # case of shared STL linkage
  if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl )
   string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" )
   if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" )
    get_filename_component( __libstlname "${__libstl}" NAME )
    execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess )
    if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}")
     message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" )
    endif()
    unset( __fileCopyProcess )
    unset( __libstlname )
   endif()
  endif()

after the block of code that sets up output directories (line 1546):

  # setup output directories
  set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" )
  set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" )

  if(NOT _CMAKE_IN_TRY_COMPILE)
   if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" )
    set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" )
   else()
    set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" )
   endif()
   set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" )
  endif()
  
  #### move the (# case of shared STL linkage) code block here ####

###Changes to OpenSceneGraph The CMake Toolchain file we use will define ANDROID_SHARED, so we use that variable in the CMakeLists files to control CMake. The already existing ANDROID define is for a separate config to build OpenSceneGraph as a static library. Download the OpenSceneGraph source and edit the CMakeLists files as follows:

/CMakeLists.txt

Set SONAMES to FALSE since Android shared objects can't contain version information (line 74):

  SET(OPENSCENEGRAPH_SONAMES FALSE)
  SET(OPENTHREADS_SONAMES FALSE)

/src/OpenThreads/CMakeLists.txt

Skip the CheckAtomicOps test (line 12):

  IF(NOT (ANDROID OR ANDROID_SHARED))
      INCLUDE(CheckAtomicOps)
  ENDIF()

Skip FIND_PACKAGE(Threads) (line 67):

  IF(NOT (ANDROID OR ANDROID_SHARED))
  # Use our modified version of FindThreads.cmake which has Sproc hacks.
      FIND_PACKAGE(Threads)
  ENDIF()

Build OpenThreads against pthreads (line 89):

  IF(ANDROID OR ANDROID_SHARED)
      SUBDIRS(pthreads)

/src/OpenThreads/pthreads/CMakeLists.txt

Don't let OpenThreads assume certain pthread functionality (line 31):

      IF(NOT ANDROID_SHARED)
        # should check?
        ADD_DEFINITIONS(-DHAVE_PTHREAD_TESTCANCEL)
        ADD_DEFINITIONS(-DHAVE_PTHREAD_CANCEL)
        ADD_DEFINITIONS(-DHAVE_PTHREAD_SETCANCELSTATE)
      ENDIF()

/src/osg/CMakeLists.txt

Android's libc has librt built in, so it shouldn't be linked against (line 401):

  IF(NOT ANDROID_SHARED)
     SET(TARGET_EXTERNAL_LIBRARIES
         ${CMAKE_THREAD_LIBS_INIT}
         ${MATH_LIBRARY}
         ${RT_LIBRARY}
         ${DL_LIBRARY}
     )
  ENDIF()

  IF(ANDROID_SHARED)
     SET(TARGET_EXTERNAL_LIBRARIES
         ${CMAKE_THREAD_LIBS_INIT}
         ${MATH_LIBRARY}
         ${DL_LIBRARY}
     )
  ENDIF()

####OpenSceneGraph Plugins OpenSceneGraph has a lot of plugins, most of which won't build with the minimal amount of libs included in the Android NDK. The osgPlugins CMakeLists file (/src/osg/osgPlugins/CMakeLists.txt) should be edited to slim down the plugins to specifically what you need and can satisfy the dependencies for.

Building Plugins

Additional libraries can be added to the Android NDK toolchain's SYSROOT to build specific plugins as required. So to be able to build the osgdb_png plugin, libpng must be available in the NDK toolchain that was exported with the make-standalone-toolchain script earlier so that CMake can find it.

To do this, libpng must be built for Android and then the corresponding files placed in:

pathto/ndk-toolchain/sysroot/usr/include
pathto/ndk-toolchain/sysroot/usr/lib

Deploying Plugins

There are a couple of ways you can choose to deploy plugins for an Android application:

1. Installing osg plugins with the application

When a plugin is required, OpenSceneGraph searches for a directory called 'osgPlugins-x.y.z', with x.y.z corresponding to the current release version. When OpenSceneGraph is built and installed, that directory will be created under installpath/libs.

When packaging an Android application you need to be able to access these plugins through a search path. One way to do that is to install them when your application is run for the first time to Internal or External Storage. An example path is:

/storage/emulated/0/org.some.app/osgPlugins-x.y.z

Note that this is just an example, you should always query Android for the specific external or internal storage path

Once the plugins have been installed to the device, you can tell OpenSceneGraph where to look for them like so:

osgDB::Registry::instance()->setLibraryFilePathList("/storage/emulated/0/org.some.app");

OpenSceneGraph will always add the 'osgPlugins.x.y.z' directory to the search path automatically. With the correct search path set, OpenSceneGraph should be able to load the plugins without any issues.

2. Linking against osg plugins directly

Another method for using plugins is to simply link directly against them when you build your application, and then include them as additional shared libraries in your Android project. However, Android is picky about what it will consider a shared library and link against, so we need to ensure that all plugins start with a lib prefix. This requires the following change:

/src/osg/osgPlugins/CMakeLists.txt

Set TARGET_DEFAULT_PREFIX to "libosgdb_" instead of "osgdb_":

SET(TARGET_DEFAULT_PREFIX "libosgdb_")

Now OpenSceneGraph should build the plugins with filenames that start with 'lib', so they can be directly linked against:

-L${OSG_PLUGINS_PATH} -losgdb_xyz

Remember to include the plugins with the rest of the shared libs in the Android APK.

###Building Create a build directory alongside the OpenSceneGraph source directory and run CMake from within the build dir with the following command:

  cmake \
  -DANDROID_STANDALONE_TOOLCHAIN=/home/user/Dev/env/sys/android-toolchain-arm \
  -DCMAKE_TOOLCHAIN_FILE=/home/user/Downloads/build/android-cmake/android.toolchain.cmake \
  -DCMAKE_FIND_ROOT_PATH=/home/user/Dev/env/sys/android-toolchain-arm/sysroot \
  -DLIBRARY_OUTPUT_PATH_ROOT=/home/user/Dev/env/android/osg-git-debug \
  -DCMAKE_INSTALL_PREFIX=/home/user/Dev/env/android/osg-git-debug \
  -DOPENSCENEGRAPH_SONAMES=OFF \
  -DOPENTHREADS_SONAMES=OFF \
  -DBUILD_OSG_APPLICATIONS=OFF \
  -DOSG_GL1_AVAILABLE=OFF \
  -DOSG_GL2_AVAILABLE=OFF \
  -DOSG_GL3_AVAILABLE=OFF \
  -DOSG_GLES1_AVAILABLE=OFF \
  -DOSG_GLES2_AVAILABLE=ON \
  -DOSG_GL_LIBRARY_STATIC=OFF \
  -DOSG_GL_DISPLAYLISTS_AVAILABLE=OFF \
  -DOSG_GL_MATRICES_AVAILABLE=OFF \
  -DOSG_GL_VERTEX_FUNCS_AVAILABLE=OFF \
  -DOSG_GL_VERTEX_ARRAY_FUNCS_AVAILABLE=OFF \
  -DOSG_GL_FIXED_FUNCTION_AVAILABLE=OFF \
  -DOPENGL_INCLUDE_DIR=/home/user/Dev/env/sys/android-toolchain-arm/sysroot/usr/include/GLES2 \
  -DOPENGL_gl_LIBRARY=/home/user/Dev/env/sys/android-toolchain-arm/sysroot/usr/lib/libGLESv2.so \
  -DOSG_WINDOWING_SYSTEM=None \
  -DOSG_USE_QT=OFF \
  -DANDROID_STL=gnustl_shared \
  ../openscenegraph-git

All paths should be corrected for your environment. Note that the path to the standalone NDK toolchain and the CMake toolchain are specified.

CMake will write the appropriate config files in the build directory. Build OpenSceneGraph with

make && make install