diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 784b107abc4..850a3d7b3a1 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -8,19 +8,16 @@ Import("env_modules") env_openxr = env_modules.Clone() -# Thirdparty source files +# Thirdparty source files. thirdparty_obj = [] -# Khronos OpenXR loader +# Khronos OpenXR loader. # Needs even for build against shared library, at least the defines used in public headers. if env["platform"] == "android": - # may need to set OPENXR_ANDROID_VERSION_SUFFIX env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"]) env_openxr.AppendUnique(CPPDEFINES=[("JSON_USE_EXCEPTION", 0)]) - - # may need to include java parts of the openxr loader elif env["platform"] == "linuxbsd": env_openxr.AppendUnique(CPPDEFINES=["XR_OS_LINUX"]) @@ -31,7 +28,6 @@ elif env["platform"] == "linuxbsd": env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_EGL", "XRDEPENDENCIES_USE_GLAD"]) env_openxr.Prepend(CPPPATH=["#thirdparty/glad"]) - # FIXME: Review what needs to be set for Android and macOS. # FreeBSD uses non-standard getenv functions. if not sys.platform.startswith("freebsd"): env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"]) @@ -40,12 +36,9 @@ elif env["platform"] == "windows": elif env["platform"] == "macos": env_openxr.AppendUnique(CPPDEFINES=["XR_OS_APPLE"]) - # There does not seem to be a XR_USE_PLATFORM_XYZ for Apple + # There does not seem to be a XR_USE_PLATFORM_XYZ for Apple. -# may need to check and set: -# - XR_USE_TIMESPEC - if env["builtin_openxr"]: thirdparty_dir = "#thirdparty/openxr" @@ -68,16 +61,15 @@ if env["builtin_openxr"]: env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"]) - # add in external jsoncpp dependency + # Add in external jsoncpp dependency. thirdparty_jsoncpp_dir = thirdparty_dir + "/src/external/jsoncpp/src/lib_json/" env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_reader.cpp") env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_value.cpp") env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_writer.cpp") - # add in load + # Add in the OpenXR loader. if env["platform"] != "android": - # On Android the openxr_loader is provided by separate plugins for each device - # Build the engine using object files + # On Android the OpenXR loader is handled by the configuration in 'platform/android/java/app/build.gradle'. khrloader_obj = [] env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table_core.c") @@ -93,13 +85,12 @@ if env["builtin_openxr"]: env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_properties.cpp") env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp") env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp") env.modules_sources += khrloader_obj env.modules_sources += thirdparty_obj -# Godot source files +# Godot source files. module_obj = [] diff --git a/modules/openxr/editor/openxr_editor_plugin.cpp b/modules/openxr/editor/openxr_editor_plugin.cpp index 09b4ef3203b..353b55c4a9a 100644 --- a/modules/openxr/editor/openxr_editor_plugin.cpp +++ b/modules/openxr/editor/openxr_editor_plugin.cpp @@ -35,6 +35,119 @@ #include "editor/editor_node.h" #include "editor/gui/editor_bottom_panel.h" #include "editor/settings/editor_command_palette.h" +#include "platform/android/export/export_plugin.h" + +#include + +//////////////////////////////////////////////////////////////////////////// +// OpenXRExportPlugin + +bool OpenXRExportPlugin::supports_platform(const Ref &p_export_platform) const { + return p_export_platform->is_class(EditorExportPlatformAndroid::get_class_static()); +} + +bool OpenXRExportPlugin::is_openxr_mode() const { + int xr_mode_index = get_option("xr_features/xr_mode"); + + return xr_mode_index == XR_MODE_OPENXR; +} + +String OpenXRExportPlugin::_get_export_option_warning(const Ref &p_export_platform, const String &p_option_name) const { + if (!supports_platform(p_export_platform)) { + return String(); + } + + bool gradle_build = get_option("gradle_build/use_gradle_build"); + if (is_openxr_mode() && !gradle_build) { + return "\"Use Gradle Build\" must be enabled when xr_mode is set to \"OpenXR\"."; + } + + return String(); +} + +PackedStringArray OpenXRExportPlugin::get_android_dependencies(const Ref &p_export_platform, bool p_debug) const { + PackedStringArray ret; + + if (!supports_platform(p_export_platform)) { + return ret; + } + + if (is_openxr_mode()) { + // Loader is always identified by the full API version even if we're initializing for OpenXR 1.0. + int major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION); + int minor = XR_VERSION_MINOR(XR_CURRENT_API_VERSION); + int patch = XR_VERSION_PATCH(XR_CURRENT_API_VERSION); + String openxr_loader = "org.khronos.openxr:openxr_loader_for_android:" + String::num_int64(major) + "." + String::num_int64(minor) + "." + String::num_int64(patch); + + ret.push_back(openxr_loader); + } + + return ret; +} + +PackedStringArray OpenXRExportPlugin::_get_export_features(const Ref &p_export_platform, bool p_debug) const { + PackedStringArray features; + + if (!supports_platform(p_export_platform) || !is_openxr_mode()) { + return features; + } + + // Placeholder for now + + return features; +} + +String OpenXRExportPlugin::get_android_manifest_element_contents(const Ref &p_export_platform, bool p_debug) const { + String contents; + + if (!supports_platform(p_export_platform) || !is_openxr_mode()) { + return contents; + } + + contents += R"n( + + + + + + + + + + + + + + + + +)n"; + + return contents; +} + +String OpenXRExportPlugin::get_android_manifest_activity_element_contents(const Ref &p_export_platform, bool p_debug) const { + String contents; + + if (!supports_platform(p_export_platform) || !is_openxr_mode()) { + return contents; + } + + contents += R"n( + + + + + + + +)n"; + + return contents; +} + +//////////////////////////////////////////////////////////////////////////// +// OpenXREditorPlugin void OpenXREditorPlugin::edit(Object *p_node) { if (Object::cast_to(p_node)) { @@ -64,3 +177,19 @@ OpenXREditorPlugin::OpenXREditorPlugin() { add_control_to_container(CONTAINER_TOOLBAR, select_runtime); #endif } + +void OpenXREditorPlugin::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + // Initialize our export plugin + openxr_export_plugin.instantiate(); + add_export_plugin(openxr_export_plugin); + } break; + case NOTIFICATION_EXIT_TREE: { + // Clean up our export plugin + remove_export_plugin(openxr_export_plugin); + + openxr_export_plugin.unref(); + } + } +} diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h index 119f72d1ced..d11e4c045ca 100644 --- a/modules/openxr/editor/openxr_editor_plugin.h +++ b/modules/openxr/editor/openxr_editor_plugin.h @@ -34,8 +34,27 @@ #include "openxr_binding_modifier_editor.h" #include "openxr_select_runtime.h" +#include "editor/export/editor_export_plugin.h" #include "editor/plugins/editor_plugin.h" +class OpenXRExportPlugin : public EditorExportPlugin { + GDCLASS(OpenXRExportPlugin, EditorExportPlugin) + +public: + virtual bool supports_platform(const Ref &p_export_platform) const override; + virtual PackedStringArray get_android_dependencies(const Ref &p_export_platform, bool p_debug) const override; + +protected: + virtual String _get_export_option_warning(const Ref &p_export_platform, const String &p_option_name) const override; + + virtual PackedStringArray _get_export_features(const Ref &p_export_platform, bool p_debug) const override; + virtual String get_android_manifest_element_contents(const Ref &p_export_platform, bool p_debug) const override; + virtual String get_android_manifest_activity_element_contents(const Ref &p_export_platform, bool p_debug) const override; + +private: + bool is_openxr_mode() const; +}; + class OpenXREditorPlugin : public EditorPlugin { GDCLASS(OpenXREditorPlugin, EditorPlugin); @@ -53,4 +72,10 @@ public: virtual void make_visible(bool p_visible) override; OpenXREditorPlugin(); + +protected: + void _notification(int p_what); + +private: + Ref openxr_export_plugin; }; diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index c6cea8f31b2..17f9aba3b2c 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -299,6 +299,29 @@ task validateJavaVersion { } } +/* + * Older versions of our vendor plugin include a loader that we no longer need. + * This code ensures those are removed. + */ +tasks.withType( com.android.build.gradle.internal.tasks.MergeNativeLibsTask) { + doFirst { + externalLibNativeLibs.each { jniDir -> + if (jniDir.getCanonicalPath().contains("godot-openxr-") || jniDir.getCanonicalPath().contains("godotopenxr")) { + // Delete the 'libopenxr_loader.so' files from the vendors plugin so we only use the version from the + // openxr loader dependency. + File armFile = new File(jniDir, "arm64-v8a/libopenxr_loader.so") + if (armFile.exists()) { + armFile.delete() + } + File x86File = new File(jniDir, "x86_64/libopenxr_loader.so") + if (x86File.exists()) { + x86File.delete() + } + } + } + } +} + /* When they're scheduled to run, the copy*AARToAppModule tasks generate dependencies for the 'app' module, so we're ensuring the ':app:preBuild' task is set to run after those tasks. diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 77cf8a589d7..38f7da285f1 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -15,6 +15,8 @@ ext.versions = [ // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated. ndkVersion : '28.1.13356709', splashscreenVersion: '1.0.1', + // 'openxrLoaderVersion' should be set to XR_CURRENT_API_VERSION, see 'thirdparty/openxr' + openxrLoaderVersion: '1.1.53', openxrVendorsVersion: '4.2.0-stable', junitVersion : '1.3.0', espressoCoreVersion: '3.7.0', diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index 666a14784b9..890b64861ce 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -203,6 +203,8 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:2.2.1" implementation "org.bouncycastle:bcprov-jdk15to18:1.78" + implementation "org.khronos.openxr:openxr_loader_for_android:$versions.openxrLoaderVersion" + // Android XR dependencies androidImplementation "org.godotengine:godot-openxr-vendors-androidxr:$versions.openxrVendorsVersion" // Meta dependencies @@ -217,3 +219,26 @@ dependencies { androidTestImplementation "androidx.test:runner:$versions.testRunnerVersion" androidTestUtil "androidx.test:orchestrator:$versions.testOrchestratorVersion" } + +/* + * Older versions of our vendor plugin include a loader that we no longer need. + * This code ensures those are removed. + */ +tasks.withType( com.android.build.gradle.internal.tasks.MergeNativeLibsTask) { + doFirst { + externalLibNativeLibs.each { jniDir -> + if (jniDir.getCanonicalPath().contains("godot-openxr-") || jniDir.getCanonicalPath().contains("godotopenxr")) { + // Delete the 'libopenxr_loader.so' files from the vendors plugin so we only use the version from the + // openxr loader dependency. + File armFile = new File(jniDir, "arm64-v8a/libopenxr_loader.so") + if (armFile.exists()) { + armFile.delete() + } + File x86File = new File(jniDir, "x86_64/libopenxr_loader.so") + if (x86File.exists()) { + x86File.delete() + } + } + } + } +} diff --git a/thirdparty/README.md b/thirdparty/README.md index 32574438207..979c1b4d889 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -888,6 +888,8 @@ Exclude: `*.{def,expsym,in,json,map,pom,rc,txt}` - All dotfiles +Additional: +- Update `openxrLoaderVersion` in `platform/android/java/app/config.gradle` ## pcre2