目标 使用Qt自带的OpenGL库,自行编译Android Arm64架构可用的Assimp静态库。使用Visual Studio和Qt进行编码,可以在Windows以及Android平台运行。
环境配置 配置Qt Android开发环境 教程在这里
配置Qt VS开发环境 安装插件并设置QtVersions
用VS创建项目,然后用插件创建.pro文件,并添加这一行:
1 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
自动生成的.pro文件只包含很少的配置,后面会添加很多自定义的配置,需要经常进行备份,以防止被覆盖
项目建立 基本工作流 在Qt中修改ui等资源后,VS里不会立即同步,需要先用VS编译一下才会生成对应的中间文件。新添加的界面还需要手动添加到项目里
使用OpenGL 下面开始正式建立项目并在Qt中使用OpenGL,可以参照这个博客:learn opengl with Qt_ItaLink-CSDN博客
核心是继承QOpenGLWidget
并实现三个成员函数,手动添加回调函数。添加控件并提升为这个自定义控件类型,这样自定义回调就可以调用起来了,回调里写初始化和绘制代码。需要注意函数调用时机,我们在外部手动调用update
来及时更新。
其中主要有三个函数:
initializeGL
初始化被调用一次resizeGL
在大小改变时被调用,并触发paintGL
paintGL
在移动、显隐时被调用,updateGL
触发paintGL
外部可以调update
来加入paint
队列,注意文档说了,只是加入处理队列,所以只有最后一次有效。同理,再paint
之外的绘制也会被paint
覆盖。
外部在这三个函数之外要想使用OpenGL的API,需要一个OpenGLWidget
的makeCurrent
(而这三个函数被调之前都已经makeCurrent
,所以内部不需要再调)
底层函数的实现在不同平台下是不一样的,具体头文件的选择需要用宏来控制。头文件在Windows下是QOpenGlFunctions_3_3_Core
,Android下是QOpenGLFunctions_ES2
,具体函数的接口跟glew、glad的一样。
关于平台的判断,Android平台有个宏__ANDROID__
,因为这里只考虑Windows和Android两个平台,所以可以像下面这样简单判断一下:
Platform.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #pragma once #if defined(__ANDROID__) # define PLATFORM_ANDROID true #else # define PLATFORM_WINDOWS true #endif #if !defined(PLATFORM_WINDOWS) # define PLATFORM_WINDOWS false #endif #if !defined(PLATFORM_ANDROID) # define PLATFORM_ANDROID false #endif
配置assimp库 windows的很好编译,用cmake打开CMakeLists就能创建VS解决方案了。需要注意的是,除了链接lib之外,还要把dll放到项目生成的exe同级目录里。
而Android的比较麻烦,我一开始下的是3.0版的,但是发现代码实在太旧了,一大堆错误,本来想自己改的,但是发现根本改不完,错误越来越多……后来才看到别人的文章,要用新一点的版本。链接在这里
基本跟着文章走就行了,需要注意脚本里的写的路径要跟自己的实际安装情况对应,还有cmake得是用android studio装的,不然里面没有ninja, 还要注意第二个脚本的-DANDROID_STL=c++_static
是static 而不是shared (感谢这篇文章 ),不然最后会运行失败。
对于文章里用的两个脚本,我的是这样的:
make_standalone_toolchain.bat
1 2 python E:/SDKs/android-ndk-r18b-windows-x86_64/android-ndk-r18b/build/tools/make_standalone_toolchain.py --arch=arm64 --stl=libc++ --api=28 --install-dir=android-toolchain-api28-arm64 pause
build_assimp.bat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @echo off cls REM *NOTE* Change these based on SET ASSIMP_DIR=./ SET OUTPUT_DIR=assimp-build-arm64v8a SET ANDROID_PATH=E:\SDKs\android-sdk_r24.4.1-windows\android-sdk-windows SET NDK_PATH=E:\SDKs\android-ndk-r18b-windows-x86_64\android-ndk-r18b SET NDK_TOOLCHAIN=%~dp0android-toolchain-api28-arm64 SET CMAKE_TOOLCHAIN=%NDK_PATH%\build\cmake\android.toolchain.cmake SET CMAKE_PATH=D:\DownloadApps\AndroidSDK\cmake\3.18.1 REM *NOTE* Careful if you don't want rm -rf, I use it for testing purposes. rm -rf %OUTPUT_DIR% mkdir %OUTPUT_DIR% REM pushd doesn't seem to work ):< cd %OUTPUT_DIR% if not defined ORIGPATH set ORIGPATH=%PATH% SET PATH=%CMAKE_PATH%\bin;%ANDROID_PATH%\tools;%ANDROID_PATH%\platform-tools;%ORIGPATH% cmake ^ -GNinja ^ -DCMAKE_TOOLCHAIN_FILE=%CMAKE_TOOLCHAIN% ^ -DASSIMP_ANDROID_JNIIOSYSTEM=ON ^ -DANDROID_NDK=%NDK_PATH% ^ -DCMAKE_MAKE_PROGRAM=%CMAKE_PATH%\bin\ninja.exe ^ -DCMAKE_BUILD_TYPE=Release ^ -DANDROID_ABI="arm64-v8a" ^ -DANDROID_NATIVE_API_LEVEL=28 ^ -DANDROID_FORCE_ARM_BUILD=TRUE ^ -DCMAKE_INSTALL_PREFIX=install ^ -DANDROID_STL=c++_static ^ -DCMAKE_CXX_FLAGS=-Wno-c++11-narrowing ^ -DANDROID_TOOLCHAIN=clang ^ -DASSIMP_BUILD_TESTS=OFF ^ ../%ASSIMP_DIR% cmake --build . cd .. pause
Qt里项目右键添加库之后,还要在项目-Build&Run-Build-构建步骤-Build Android APK
添加Additional Libraries,这样才会打进APK里,不然运行时会报错找不到库!实际上这个步骤就是修改了下.pro文件。
最后的项目结构:
项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 3rd/ assimp/ include/ lib/ assimp-vc143-mtd.lib libassimp.so src/ main.cpp MainWindow.cpp MainWindow.h MainWindow.qrc MainWindow.ui MyOpenGLWidget.cpp MyOpenGLWidget.h .pri .pro .vcxproj
最后的.pro配置文件
.pro配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 添加模块 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 指定C++标准 CONFIG += c++17 # 分平台添加链接库,注意VS自动创建的是不对的 win32: LIBS += -L$$PWD/3rd/assimp/lib -lassimp-vc143-mtd unix:!macx: LIBS += -L$$PWD/3rd/assimp/lib/ -lassimp # 库的头文件路径 INCLUDEPATH += $$PWD/3rd/assimp/include DEPENDPATH += $$PWD/3rd/assimp/include # 将库文件打包进APK里 contains(ANDROID_TARGET_ARCH,arm64-v8a) { ANDROID_EXTRA_LIBS = \ $$PWD/3rd/assimp/lib/libassimp.so } # 自定义头文件搜索路径 INCLUDEPATH += $$PWD/src
资源文件路径问题 在Windows下,资源就按照相对项目路径就能加载
而Android下,需要将资源打包到APK中,引用路径也有所不同
在.pro文件里添加配置,把项目目录下的res文件夹都放到assets/res目录(Android打包后的资源路径)里;
1 2 3 4 5 android { ress.files += res/* ress.path = /assets/res INSTALLS += ress }
代码里引用:
1 auto file = QFile("assets:/res/test.txt"); // 注意用std标准库是加载不了的,要用Qt的文件接口
因为前面加了写了平台判断宏,所以一个通用的文件加载接口可以这样写:
LoadBinary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 std ::string GetPlatformPath (const std ::string & path) {#if PLATFORM_ANDROID return "assets:/" + path; #else return path; #endif } std ::vector <char > LoadBinary (const std ::string & path) { auto file = QFile(GetPlatformPath(path).data()); if (!file.open(QIODevice::ReadOnly)) { LogError("Failed to open: " , path); return {}; } auto content = file.readAll(); return std ::vector <char >(content.data(), content.data() + content.size()); }
安卓手机运行 手机连接数据线,设置里进入开发者模式并打开USB调试和允许USB安装应用,并保持屏幕亮起,运行前注意检查编译工具。
后来又添加了lua库,使用qt来编译库的,需要注意的是安卓下面的库只能是动态共享库,生成后缀是.so才行。库项目自带的两个文件不能直接删掉,可以把文件内容清空。之后引入到项目里时还需要注意库名的写法。(可以观察下自动生成的配置是怎么样的)