0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Hello OpenXR [Quest編]

Last updated at Posted at 2022-10-29

Quest pro で Native 開発をするべく OpenXR しよう。
既存のプロジェクトを改造ビルドして参考にさせてもらうことで、プロジェクト構成などを理解する。

gradle と cmake の使いこなしをある程度マスターしないと、
ビルドではまる。

NativeActivity + openxr のプロジェクトをひたすらビルドしていく。

開発環境

  • Windows11(64bit)
  • AndroidStudio-2021.3.1
  • OpenXRMobileSDK-0.46 (Quest pro 発売時の最新)
  • VR実機: 初代Quest, QuestPro で実験

OpenXR とは

OpenGL のデスクトップアプリの glfw の VR 版のような感じです。
HMD Tracking や Controller Tracking やボタン操作を入力し、HMD に対して描画するための swapchain を提供するのがコア機能です。

ライブラリとしては openxr_loader です。
https://github.com/KhronosGroup/OpenXR-SDK-Source
で提供されています。

仕様

12. List of Current Extensions

に拡張が列挙されています。

  • XR_EXT_hand_tracking
  • XR_FB_passthrough
  • XR_FB_spatial_entity
  • XR_FB_scene

等は既に記載があります。
一方で、QuestPro で新規に導入された、

  • XR_FB_body_tracking
  • XR_FB_face_tracking
  • XR_FB_eye_tracking_sotial

などは、まだ書いていない。

Android Studio を中心とした開発環境

C++の開発に VSCode などの他のエディタを活用する場合でも、関連するツールのインストール、バージョン合わせに有用なので AndroidStudio をインストールするとよい。

Java

AndroidStudio のインストールフォルダに付属するのでこれを使う。

JAVA_HOME=ANDROID_STUDIO_INSTALL\jre

AndroidSdk

APPDATA下にインストールされる。

ANDROID_HOME=%USERPROFILE%\AppData\Local\Android\Sdk
ANDROID_HOME/platform-toolsadb.exe が入っている。

CMake

ANDROID_HOME下の cmake\3.18.1 などにインストールされる。
cmake.exe と同じパスに ninja.exe があるのでパスを通すと vscode などで便利。
紆余曲折を経て、ANDROID_HOME/cmake/3.22.1 を使ってます。

path の通った cmake に注意

システムに他の cmake が入っている場合はなるべくアンインストールする。
最低でも PATH が通っていない状態にする。
意図しない違うバージョンの cmake が使われたり、 cmake が見つからないエラーが起きる場合がある。
例えば pip install cmakeC:/Python310/Scripts/cmake.exe にPATHを通して使っていたのだが、これは gradle 運用で cmake が見つからないというトラブルが起きた。

gradle のデフォルトは cmake-3.10

https://developer.android.com/studio/projects/install-ndk?hl=ja#default-version

android {
    defaultConfig {
        cmake {
            // ここではない
        }
    }

    externalNativeBuild {
        cmake {
            version "3.22.1" // 👈 こっち
            path "CMakeLists.txt"
        }
    }
}

cmake のバグ?らしく CMake で複数の so を生成すると、
最初の一つ以外が消えてしまう挙動に遭遇。CMake-3.22.1 にしたらなおった。

どうしても version 指定がうまくいかない場合は、
local.properties などに下のように書く方法がある。

cmake.dir = "PATH_TO_CMAKE_3_22_1"

NDK

ANDROID_HOME/ndk/21.4.7075529 などにインストールされる。

追加インストール

Python

OpenXR-SDK の CMake が使っている。
gradle-7.4 デフォルトの CMake-3.10 から find できる一番新しいバージョンである Python-3.7 をインストールする。

OpenXR-SDK の cmake で使われている
find_package(PythonInterp 3) が失敗する。

cmake のバージョンによって発見できる Python のバージョンに限界があります。

cmake python
3.10 3.7
3.18 3.9
3.22 3.10

CMAKE_VERSION\share\cmake-VERSION\Modules\FindPython\Support.cmake などを見ると
対応している python がわかります。

Gradle

コマンドラインで使う場合は追加でダウンロードしてパスを通す。
gradlew を使うことが多いので必要ないことが多い
ANDROID_STUDIO と同じ gradle-7.4 を選ぶ。

ANDROID_STUDIO_INSTALL\plugins\gradle にライブラリは入っているがコマンドラインが無い。

gradle-7.4 で AGP(AndroidGradlePlugin)-7.3.1 を使う

gradlew で gradlew を更新できます。

> cd src/tests/hello_xr
src/tests/hello_xr> .\gradlew --version
7.0.2
src/tests/hello_xr> .\gradlew wrapper --gradle-version 7.4
src/tests/hello_xr> .\gradlew --version
7.4

AGP のバージョン指定は rootProject の最初の方です。

buildscript {
    repositories {
       google()
       mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.3.1" // 👈 AGP のバージョン指定
    }
}

adb によるデバイス接続確認

ANDROID_HOME/platform-tools/adb.exe です。

# 接続されたデバイスを一覧する
$ adb devices -l

NDK練習

最初に、ndk-samples から native_activity をやります。

native_activy は java 無しで CMake を使う小さいサンプルで、300行くらいの c++ で OpenGLES で glClear するだけという手頃なプロジェクトです。
gradle と CMake の構成もシンプルでわかりやすいです。

AndroidStudio で open

native_activity を開く。
ビルドしてデプロイする。

app/build.gradle の構成

抜粋。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    ndkVersion '22.1.7171670'

    defaultConfig {
        applicationId = 'com.example.native_activity'
        minSdkVersion 14
        targetSdkVersion 28
        externalNativeBuild { // 👈 defaultConfig.externalNativeBuild。artuments などが指定できる。cmake 発動しない
            cmake {
                arguments '-DANDROID_STL=c++_static'
            }
        }
    }

    externalNativeBuild { // 👈 defaultConfig.externalNativeBuild と別物。cmake で指定できる内容が違う
        cmake {
            version '3.18.1'
            path 'src/main/cpp/CMakeLists.txt' // 👈 必須ぽい?これが書いてあると cmake が発動する。
        }
    }
}

CMake の構成

main.cpp 単体で so ファイルを作成するシンプルなプロジェクトです。

native-activity/app/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

# build native_app_glue as a static lib
set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}")
add_library(native_app_glue STATIC
    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) # 👈 NDK で必要

# now build app's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror")

# Export ANativeActivity_onCreate(),
# Refer to: https://github.com/android-ndk/ndk/issues/381.
set(CMAKE_SHARED_LINKER_FLAGS
    "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") # 👈 NDK で必要

add_library(native-activity SHARED main.cpp) # 👈 apk の lib に libnative-activity.so を含める

target_include_directories(native-activity PRIVATE
    ${ANDROID_NDK}/sources/android/native_app_glue)

# add lib dependencies
target_link_libraries(native-activity
    android # 👈 システムの so にリンク。apk に含まない
    native_app_glue # android_native_app_glue.c
    EGL # 👈 システムの so にリンク。apk に含まない
    GLESv1_CM # 👈 システムの so にリンク。apk に含まない
    log # 👈 システムの so にリンク。apk に含まない
)

gradle からの cmake 呼び出し

エラーメッセージから得た。
下記のような感じで呼び出しています。

CMAKE_EXE
-HPROJECT_DIR
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_SYSTEM_VERSION=24
-DANDROID_PLATFORM=android-24
-DANDROID_ABI=arm64-v8a
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a
-DANDROID_NDK=ANDROID_HOME\\ndk\\21.4.7075529
-DCMAKE_ANDROID_NDK=ANDROID_HOME\\ndk\\21.4.7075529
-DCMAKE_TOOLCHAIN_FILE=NDK_DIR\\build\\cmake\\android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=ANDROID_HOME\\cmake\\3.18.1\\bin\\ninja.exe
-DCMAKE_CXX_FLAGS=GRDLE_EXTERNAL_NATIVE_BUILD_CMAKE_CPPFLAGS
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=PROJECT_DIR\\build\\intermediates\\cxx\\Debug\\__TMP__\\obj\\arm64-v8a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=PROJECT_DIR\\build\\intermediates\\cxx\\Debug\\__TMP__\\obj\\arm64-v8a
-DCMAKE_BUILD_TYPE=Debug
-BPROJECT_DIR\\.cxx\\Debug\\__TMP__\\arm64-v8a
-GNinja	

ビルド結果を apk に送り込む

gradle から cmake を呼び出したときに CMAKE_LIBRARY_OUTPUT_DIRECTORY が指定されてそこに so が出力されます。

native-activity\app\build\intermediates\cxx\Debug\RANDOM\obj

x86_64\libnative-activity.so
arm64-v8a\libnative-activity.so
x86\libnative-activity.so
armeabi-v7a\libnative-activity.so

abi 毎に4回ビルドされる。

apk の構成

app\build\outputs\apk に出力されます。
zip アーカイブなので中身の lib フォルダを見ると下記のようになっています。
AndroidStudio の ApkAnalyzer で見ることができます。

native-activity\app\build\outputs\apk\debug\app-debug.apk

lib/arm64-v8a/libnative-activity.so
lib/x86_64/libnative-activity.so
lib/armeabi-v7a/libnative-activity.so
lib/x86/libnative-activity.so

要するに

CMakeLists.txt
add_library(LIB_NAME SHARED some.cpp)

libLIB_NAME.so が apk に含まれます。

OpenXR-SDK

OpenXR-SDK-Source から hello_xr をやります。

デスクトップは cmake でビルドするだけなので簡単です。

OpenXR-SDK と OpenXR-SDK-Source

OpenXR-SDK-Source には openxr.h が含まれておらず、ビルドすることで生成することができます。
生成物をコミットしたのが OpenXR-SDK です。
openxr_loader.dll などをビルドしたのが OpenXR.Loader.1.0.25.nupkg です。
Windows 環境では openxr_loader が複数の RunTime のどれかをロードするという挙動をするので、
openxr_loader 自体は自前ビルドでよいようです。

Android の openxr_loader はそのデバイスのランタイムをロードする専用の libopenxr_loader.so を、
デバイスのベンダーが配布するという方式のようです。

libopenxr_loader.so は OpenXRMobileSDK に含まれるものを使う

OVR_MobileSdk の中に入っています。

OpenXRMobileSDK\OpenXR\Libs\Android\armeabi-v7a\Debug\libopenxr_loader.so
OpenXRMobileSDK\OpenXR\Libs\Android\armeabi-v7a\Release\libopenxr_loader.so
OpenXRMobileSDK\OpenXR\Libs\Android\arm64-v8a\Debug\libopenxr_loader.so
OpenXRMobileSDK\OpenXR\Libs\Android\arm64-v8a\Release\libopenxr_loader.so

Android の libopenxr_loader.so の自前ビルドは (たぶん) 動かない

OpenXR-SDK もしくは OpenXR-SDK-Source からビルドすることができます。
試しに hello_xr で使ってみたところ
真っ黒になりました。
Quest固有の機能を提供できないので何もしないローダーになっていそう。

OpenXR-SDK-Source の src/tests/hello_xr を Quest 向けにビルドする

native_activiy に、openxr_loader が追加された VR のプロジェクトです。
OpenXR の拡張機能を使わない、HMD Tracking, コントローラ Tracking と入力、VR レンダリング の入ったプロジェクトです。
d3d, opengl, vulkan, windows, android 全対応のため CMake の構成が複雑化しています。

AndroidStudio で hello_xr を開く

AndroidStudio で OpenXR-SDK-Source の src/tests/hello_xr フォルダを開きます。

修正してビルドする

Quest 向けに hello_xr を改造する手順が下記の URL に書いてあります。
https://developer.oculus.com/documentation/native/android/mobile-build-run-hello-xr-app/

  • [cmake]openxr_loader をビルドせずに OpenXRMobileSDK の物を使うようにする
  • [gladle]cmake 引数を変更
  • [AndroidManifest.xml]intent を quest 用に変更

ビルド済みのライブラリを apk に含める

add_library(openxr_loader SHARED IMPORTED) # 👈 SHARED IMPORTED でできます
get_filename_component(
  MOBILE_SDK_DIR
  ${CMAKE_CURRENT_LIST_DIR}/../../OpenXRMobileSDK ABSOLUTE) # 👈 パスを文字列で使うと失敗するときがあったので、絶対パスで回避
set_property(
 TARGET
 openxr_loader
 PROPERTY
 IMPORTED_LOCATION # 👈 IMPORTED_LOCATION でパスを指定します
 ${MOBILE_SDK_DIR}/OpenXR/Libs/Android/${ANDROID_ABI}/release/libopenxr_loader.so
)

APK の構成

hello_xr の apk には以下の so が含まれます。

armeabi-v7a\libXrApiLayer_core_validation.so
armeabi-v7a\libXrApiLayer_api_dump.so
armeabi-v7a\libopenxr_loader.so
armeabi-v7a\libhello_xr.so
armeabi-v7a\libGLESv3.so 👈 たぶん不要
armeabi-v7a\libEGL.so 👈 たぶん不要
arm64-v8a\libXrApiLayer_core_validation.so
arm64-v8a\libXrApiLayer_api_dump.so
arm64-v8a\libopenxr_loader.so
arm64-v8a\libhello_xr.so
arm64-v8a\libGLESv3.so 👈 たぶん不要
arm64-v8a\libEGL.so 👈 たぶん不要

OpenXRMobileSDK のサンプル

v0.46

OVR_MobileSdk から XrHandsFB をやります。
native_activiy に、openxr_loader と XR_EXT_hand_tracking が追加された VR のプロジェクトです。

  • 中規模の C++ フレームワークが ndk-build(Makefile) で記述されている
  • gradle の構成が変則的で、python スクリプト OpenXRMobileSDK/XrSamples/XrHandsFB/Projects/Android/build.py から gradle を実行するためわかりにくい
    • OpenXRMobileSDK/build.gradle + OpenXRMobileSDK/XrSamples/XrHandsFB/Projects/Android/settings.gradle の組み合わせで動くトリックになっている様子
    • そのため AndroidStudio で開けない

XrSamples/XrHandsFB

XrSamples は ndk-build を使う方式記述されているので、以下の Makefile に C++ の構成が記述されている。

  • XrSamples\XrHandsFB\Projects\Android\jni\Android.mk
  • OpenXR\Projects\AndroidPrebuilt\jni\Android.mk
  • SampleXrFramework\Projects\Android\jni\Android.mk
  • SampleCommon\Projects\Android\jni\Android.mk
  • 3rdParty\khronos\ktx\makefiles\androidprebuilt\jni\Android.mk
  • 3rdParty\khronos\ktx\makefiles\android\jni\Android.mk

OpenXRMobileSDK/XrSamples/XrHandsFB/Projects/Android/build.py を実行する。
=> OpenXRMobileSDK\XrSamples\XrHandsFB\Projects\Android\build\outputs\apk\release\xrhandsfb-release.apk

OpenXRMobileSDK/bin/scripts/ovrbuild.py を呼び出していて、
ovrbuild は、環境変数 ANDROID_HOMEANDROID_NDK_HOME を必要としている。
build.py 実行前に追加の環境変数 ANDROID_NDK_HOME = $ANDROID_HOME/ndk/25.1.8937393 のように設定します。

build.gradle と CMake を書いてみた

AndroidStudio で開けるようになった。
首尾よく Quest で動きました。

OpenXR のプロジェクト最小構成はだいたい以下の通り。

  • libopenxr_loader.so を含む(OpenXRMobileSDK に含まれるものを使う)
    • openxr/openxr.h は OpenXR-SDK を使う
    • openxr/openxr.h に含まれていない新しい拡張のヘッダは OpenXRMobileSDK に入っている。
  • NativeActivity で Java 抜き
    • NativeActivity を実装する so を含む(自作する)
  • OpenGLES で描画する
  • Quest 専用の intent com.oculus.intent.category.VR が必要

VSCode で C++ の開発をする

gradle からの CMake の呼び出しが

CMAKE_EXE
-HPROJECT_DIR
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_SYSTEM_VERSION=24
-DANDROID_PLATFORM=android-24
-DANDROID_ABI=arm64-v8a
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a
-DANDROID_NDK=ANDROID_HOME\\ndk\\21.4.7075529
-DCMAKE_ANDROID_NDK=ANDROID_HOME\\ndk\\21.4.7075529
-DCMAKE_TOOLCHAIN_FILE=NDK_DIR\\build\\cmake\\android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=ANDROID_HOME\\cmake\\3.18.1\\bin\\ninja.exe
-DCMAKE_CXX_FLAGS=GRDLE_EXTERNAL_NATIVE_BUILD_CMAKE_CPPFLAGS
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=PROJECT_DIR\\build\\intermediates\\cxx\\Debug\\__TMP__\\obj\\arm64-v8a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=PROJECT_DIR\\build\\intermediates\\cxx\\Debug\\__TMP__\\obj\\arm64-v8a
-DCMAKE_BUILD_TYPE=Debug
-BPROJECT_DIR\\.cxx\\Debug\\__TMP__\\arm64-v8a
-GNinja	

という感じなので以下のような設定で、vscode の F7 で cmake ビルドし、
lsp として clangd を稼働させることができます。
build 結果は ${workspaceFolder}/build に出ます。
必要に応じて、gradle の android.defaultConfig.externalNativeBuild.cmake.arguments の内容を追記します。

.vscode/settings.json
{
    "cmake.sourceDirectory": "${workspaceFolder}/DIR_TO_CMAKELISTS_TXT",
    "cmake.configureSettings": {
        "CMAKE_TOOLCHAIN_FILE": "${env:ANDROID_HOME}/ndk/21.4.7075529/build/cmake/android.toolchain.cmake",
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
        "ANDROID": "ON",
        "ANDROID_PLATFORM": "android-28",
        "ANDROID_ABI": "arm64-v8a",
        "CMAKE_ANDROID_ARCH_ABI": "arm64-v8a",
    },
    "clangd.arguments": [
        "--compile-commands-dir=${workspaceFolder}/build" // 👈 GRADLE_PROJECT\app\.cxx\Debug\RANDOM\arm64-v8a\compile_commands.json
    ],
}

gradle の出力する compile_commands.json を参照させることもできるけど、
パスに乱数が含まれていて不便。
おそらく、${workspaceFolder}/build を指定する方が便利。

LÖVR

lua で VR するフレームワーク。
まだ、ビルドして動作するところまでしか見てない。

apk 作成も含めて全部 CMake でビルドするようになっていて gradle が無い。
AndroidStudio からデバッガをアタッチしたいので gradle プロジェクトを作る。

TODO:

関連

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?