OpenSceneGraph是一款多平臺的3D場景渲染引擎API,以下簡稱為了OSG,OSG的官網(wǎng)只提供了源碼,想使用的話必須自己手動編譯。這里以官方的3.6.5版為例講解如何編譯出能在Android平臺使用的so包,并成功的運行官方的Demo程序。
官方推薦編譯最好在Linux環(huán)境下進行,但因為我的Android Studio卻是運行在Windows 10下的,所以最好的辦法就是給Windows10裝了個Linux子系統(tǒng),進入“程序和功能”>“啟用和關閉Windows功能”。然后在拉到底部勾選“適用于Linux的Windows子系統(tǒng)”,等上幾分鐘后這個功能就裝完了。

然后在Windows10的應用市場Microsoft Store里搜索Ubuntu并安裝就可以了,這樣裝出來的Ubuntu是沒有圖形界面只有字符界面的,但已經(jīng)能滿足編譯的需要了。
首次進入Ubuntu時會要求你創(chuàng)建一個用戶號,然后對Ubuntu該升級的升級,改安裝的安裝,主要是把cmake gcc這些程序都要裝上。
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install cmake -y
sudo apt install build-essential -y
然后去Github上下載最新的3.6.5的壓縮包:https://github.com/openscenegraph/OpenSceneGraph/tree/OpenSceneGraph-3.6.5
然后對壓縮包進行解壓,為了便于之后將其復制到Ubuntu下使用,我就直接解壓到c盤并且把目錄命名為了OpenSceneGraph。
由于OSG的編譯還需要用到第三方的代碼包3rdParty,但可惜的是官網(wǎng)并并不提供下載,需要我們自己到網(wǎng)上去下載,然后將下載的壓縮包解壓到OpenSceneGraph\3rdParty目錄下。
接著我們需要下載Android NDK,可以直接從官網(wǎng)上下載:
https://developer.android.google.cn/ndk/downloads/index.html?hl=zh-cn
下載完畢后依舊是解壓到C盤。
OSG和NDK都下載解壓完了之后就可以復制到Ubuntu系統(tǒng)上了,這里我推薦用命令方式復制:
sudo cp -r /mnt/c/OpenSceneGraph /home/xrf/
sudo cp -r /mnt/c/android-ndk-r21c /home/xrf/
在OSG365目錄下新一個build目錄用以存放編譯后的文件,并授予全部的權限,如果忘記授權的話可能會因為權限不夠而報錯。
cd OpenSceneGraph
mkdir build
cd ../..
sudo chmod -R 777 osg365/
接著直接在build目錄下開始編譯
cmake ../ -DANDROID_NDK=/home/xrf/Android/android-ndk-r21e -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=/home/xrf/Android/android-ndk-r21e/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DCMAKE_CXX_FLAGS="-std=c++11 -frtti -fexceptions -stdlib=libc++ -DUSE_ZLIB" -DANDROID_TOOLCHAIN=clang -DANDROID_PLATFORM=android-19 -DANDROID_ABI=armeabi-v7a -DCMAKE_INSTALL_PREFIX="./armeabi-v7a" -DOPENGL_PROFILE="GLES2" -DDYNAMIC_OPENTHREADS=OFF -DDYNAMIC_OPENSCENEGRAPH=OFF
參數(shù)中的DANROID_PLATFORM表示的是Android API最低支持的版本號。
DANDROID_ABI有的是armeabi-v7a,這個是arm的32位版本,如果想要64位的可以換成arm64-v8a。

然后用make命令開始生成靜態(tài)庫文件,這個過程大約要一兩個小時,中途會看到一些warning級別的警告,這都不用擔心,只要能進行到100%就可以,否則就可能是3rdParty包的某些文件出了問題,建議重新下載出問題的代碼文件。
全部完成后記得執(zhí)行make install命令,這個命令結束后會在build目錄下多出一個armeabi-v7a目錄,里包含了include、src和obj三個子目錄。接著再用系統(tǒng)命令把armeabi-v7a目錄復制到Windows的C盤下;
sudo cp -r armeabi-v7a/ /mnt/c/
至此與Ubuntu相關的操作就結束了。概括起來就是我們在Ubuntu下用cmake、make、make install完成了OSG靜態(tài)庫文件的編譯。
接著該是Android Studio出場的時候了,首先導入OSG的示例項目osgAndroidExampleG2。這個Demo是以NDK方式創(chuàng)建的,而且配置的路徑都是針對Linux環(huán)境設置的,所以需要手動修改。
Application.mk內容如下:
#ANDROID APPLICATION MAKEFILE
APP_BUILD_SCRIPT := $(call my-dir)/Android.mk
#APP_PROJECT_PATH := $(call my-dir)
APP_OPTIM := release
APP_PLATFORM := android-19
APP_STL := c++_static
APP_CPPFLAGS := -fexceptions -frtti
APP_ABI := armeabi-v7a
APP_MODULES := osgNativeLib
APP_PLATFORM指定的最低本要和Cmake命令里的DANDROID_PLATFORM一致,然后再修改Android.mk內容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := osgNativeLib
### Main Install dir
OSG_ANDROID_DIR := C:/osg365
NDK_ROOT := C:/Users/你的目錄/AppData/Local/Android/Sdk/ndk/21.4.7075529
LIBDIR := $(OSG_ANDROID_DIR)/obj/local/armeabi
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_ARM_NEON := true
LIBDIR := $(OSG_ANDROID_DIR)/obj/local/armeabi-v7a
endif
### Add all source file names to be included in lib separated by a whitespace
LOCAL_C_INCLUDES:= $(OSG_ANDROID_DIR)/include
LOCAL_CFLAGS := -fdata-sections -ffunction-sections -DANDROID
LOCAL_CPPFLAGS := -frtti -fno-exceptions -fdata-sections -ffunction-sections -DANDROID
LOCAL_LDLIBS := -llog -lGLESv2 -lz
LOCAL_SRC_FILES := osgNativeLib.cpp OsgMainApp.cpp OsgAndroidNotifyHandler.cpp
LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/llvm-libc++/include
LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
LOCAL_LDFLAGS := -L $(LIBDIR) \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_dds.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_openflight.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_tga.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_rgb.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_osgterrain.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_osg.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_ive.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgviewer.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgvolume.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgtext.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgterrain.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgsim.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgshadow.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgparticle.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osgfx.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osganimation.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_deprecated_osg.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgvolume.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgtext.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgterrain.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgsim.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgshadow.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgparticle.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgmanipulator.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osgfx.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osganimation.a \
-l$(OSG_ANDROID_DIR)/lib/osgPlugins-3.6.5/libosgdb_serializers_osg.a \
-l$(OSG_ANDROID_DIR)/lib/libosgViewer.a \
-l$(OSG_ANDROID_DIR)/lib/libosgVolume.a \
-l$(OSG_ANDROID_DIR)/lib/libosgTerrain.a \
-l$(OSG_ANDROID_DIR)/lib/libosgText.a \
-l$(OSG_ANDROID_DIR)/lib/libosgShadow.a \
-l$(OSG_ANDROID_DIR)/lib/libosgSim.a \
-l$(OSG_ANDROID_DIR)/lib/libosgParticle.a \
-l$(OSG_ANDROID_DIR)/lib/libosgManipulator.a \
-l$(OSG_ANDROID_DIR)/lib/libosgGA.a \
-l$(OSG_ANDROID_DIR)/lib/libosgFX.a \
-l$(OSG_ANDROID_DIR)/lib/libosgdb.a \
-l$(OSG_ANDROID_DIR)/lib/libosgAnimation.a \
-l$(OSG_ANDROID_DIR)/lib/libosgUtil.a \
-l$(OSG_ANDROID_DIR)/lib/libosg.a \
-l$(OSG_ANDROID_DIR)/lib/libOpenThreads.a
include $(BUILD_SHARED_LIBRARY)
OSG_ANDROID_DIR指的是之前從Ubuntu復制出來的armeabi-v7a目錄,為了便于管理而改名成了osg365。
NDK_ROOT指的是Windows版的NDK,注意NDK的版本要和Ubuntu使用的版本一致,不然會出報錯。
此外也要注意路徑中用的斜杠是“/”。
然后刪除AndroidManifest.xml里的uses-sdk節(jié)點,因為節(jié)點里的內容可以在app模塊的build.gradle里配置。
接著就開始配置app的build.gradle文件,并實現(xiàn)Android Studio對C/C++代碼的自動提示功能
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.anroid.nativecxx"
minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
moduleName "osgNativeLib"
abiFilters "armeabi-v7a"
}
}
...
packagingOptions {
pickFirst 'lib/armeabi-v7a/libosgNativeLib.so'
}
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDir "src/main/libs"
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
}
}
ndkVersion '21.4.7075529'
}
修改local.properties用于指定ndk的目錄,注意把路徑改成你的本地路徑
ndk.dir=C\:\\Users\\你的目錄\\AppData\\Local\\Android\\Sdk\\ndk\\21.4.7075529
sdk.dir=C\:\\Users\\你的目錄\\AppData\\Local\\Android\\Sdk
最后記得在Windows的“環(huán)境變量”下配置ndk的路徑到path下,配完后重啟下Android Studio后,選中jni并打開“Terminal”窗口輸入ndk-build。一切成功后就可以在src/main/libs下看到生成的so文件了。

so文件被稱為動態(tài)鏈接文件,相當于Windows下的dll文件,Java會將so文件作為函數(shù)庫引入,并將定義為native類型的方法和so內的相關函數(shù)鏈接起來。
為了方便看到編譯后的效果,需要從官網(wǎng)下載示例文件,這里有cow.osg為例。先將這個文件用DeviceFileExplorer上傳到app的files目錄下,再修改代碼:
@Override
protected void onResume() {
super.onResume();
mView.onResume();
String address = getFilesDir()+File.separator+"cow.osg";
osgNativeLib.loadObject(address);
}
然后編譯一下就可以看到效果了。
