From dbbe335c50d810273c343e2ac6492f109635ebf4 Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Mon, 27 Nov 2023 13:25:41 -0800 Subject: [PATCH] Migrate the export scripts from gdscript to C++ via gdextension --- common/CMakeLists.txt | 70 ++++ common/src/main/cpp/export/export_plugin.cpp | 187 ++++++++++ common/src/main/cpp/export/export_plugin.h | 139 ++++++++ godotopenxrmeta/CMakeLists.txt | 79 +--- .../main/cpp/export/meta_editor_plugin.cpp | 336 ++++++++++++++++++ .../src/main/cpp/export/meta_editor_plugin.h | 105 ++++++ .../src/main/cpp/register_types.cpp | 26 +- 7 files changed, 862 insertions(+), 80 deletions(-) create mode 100644 common/src/main/cpp/export/export_plugin.cpp create mode 100644 common/src/main/cpp/export/export_plugin.h create mode 100644 godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.cpp create mode 100644 godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e2baf29b..ae87aa69 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -17,6 +17,15 @@ else () set(GODOT_CPP_LIB_ABI "arm64") endif () +# Default android platform is android-21 +if (NOT ANDROID_PLATFORM) + set(ANDROID_PLATFORM "android-21") +endif (NOT ANDROID_PLATFORM) + +if (NOT (ANDROID_STL STREQUAL "c++_shared")) + set(ANDROID_STL "c++_shared") +endif (NOT (ANDROID_STL STREQUAL "c++_shared")) + # Default build type is Debug if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) @@ -32,9 +41,63 @@ else () set(OPENXR_MOBILE_LIB_BUILD_TYPE Release) endif (CMAKE_BUILD_TYPE MATCHES Debug) +# Check if ANDROID_NDK is set. +if (NOT ANDROID_NDK) + # Set to ANDROID_NDK_HOME environment variable if it's set. + if (DEFINED ENV{ANDROID_NDK_HOME}) + set(ANDROID_NDK $ENV{ANDROID_NDK_HOME}) + else (DEFINED ENV{ANDROID_NDK_HOME}) + message(WARNING "ANDROID_NDK_HOME is not set") + endif (DEFINED ENV{ANDROID_NDK_HOME}) +endif (NOT ANDROID_NDK) + +# Check if CMAKE_TOOLCHAIN_FILE is set. +if (NOT CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE "${ANDROID_NDK}/build/cmake/android.toolchain.cmake") +endif (NOT CMAKE_TOOLCHAIN_FILE) + +if (NOT DEFINED BITS) + set(BITS 32) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BITS 64) + endif (CMAKE_SIZEOF_VOID_P EQUAL 8) +endif (NOT DEFINED BITS) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + project(common LANGUAGES CXX) +add_definitions(-DANDROID) + +set(GODOT_COMPILE_FLAGS) +set(GODOT_LINKER_FLAGS) + +set(GODOT_LINKER_FLAGS "-Wl") + +set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wlong-long -Wmissing-braces -Wmissing-format-attribute") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label") +set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes") + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes") +endif () + +if (CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") +else () + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") +endif (CMAKE_BUILD_TYPE MATCHES Debug) + ## godot-cpp library set(GODOT_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/godot-cpp") set(GODOT-CPP "godot-cpp") @@ -56,3 +119,10 @@ set_target_properties(${GODOT-CPP} PROPERTIES IMPORTED_LOCATION ${GODOT_CPP_STAT ## OpenXR headers set(OPENXR_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/openxr/include") + + +# Common lib +set(COMMON_LIB_HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../common/src/main/cpp) + +file(GLOB_RECURSE COMMON_LIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../common/src/main/cpp/*.c**) +file(GLOB_RECURSE COMMON_LIB_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../common/src/main/cpp/*.h**) diff --git a/common/src/main/cpp/export/export_plugin.cpp b/common/src/main/cpp/export/export_plugin.cpp new file mode 100644 index 00000000..003ec1b9 --- /dev/null +++ b/common/src/main/cpp/export/export_plugin.cpp @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* export_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT XR */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "export_plugin.h" + +#include + +using namespace godot; + +OpenXREditorExportPlugin::OpenXREditorExportPlugin() {} + +void OpenXREditorExportPlugin::_bind_methods() {} + +Dictionary OpenXREditorExportPlugin::_generate_export_option(const String &name, const String &class_name, + Variant::Type type, + PropertyHint property_hint, + const String &hint_string, + PropertyUsageFlags property_usage, + const Variant &default_value, + bool update_visibility) { + Dictionary option_info; + option_info["name"] = name; + option_info["class_name"] = class_name; + option_info["type"] = type; + option_info["hint"] = property_hint; + option_info["hint_string"] = hint_string; + option_info["usage"] = property_usage; + + Dictionary export_option; + export_option["option"] = option_info; + export_option["default_value"] = default_value; + export_option["update_visibility"] = update_visibility; + + return export_option; +} + +String OpenXREditorExportPlugin::_get_name() const { + return "GodotOpenXR" + _vendor.capitalize(); +} + +String OpenXREditorExportPlugin::_get_android_aar_file_path(bool debug) const { + const String debug_label = debug ? "debug" : "release"; + return "res://addons/godotopenxrvendors/" + _vendor + "/.bin/" + debug_label + "/godotopenxr" + _vendor + "-" + debug_label + ".aar"; +} + +String OpenXREditorExportPlugin::_get_android_maven_central_dependency() const { + return "org.godotengine:godot-openxr-vendors-" + _vendor + ":" + _plugin_version; +} + +String OpenXREditorExportPlugin::_get_vendor_toggle_option_name(const String &vendor_name) const { + return "xr_features/enable_" + vendor_name + "_plugin"; +} + +Dictionary OpenXREditorExportPlugin::_get_vendor_toggle_option(const String &vendor_name) const { + return _generate_export_option( + _get_vendor_toggle_option_name(vendor_name), + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + false, + false + ); +} + +bool OpenXREditorExportPlugin::_is_openxr_enabled() const { + return _get_int_option("xr_features/xr_mode", REGULAR_MODE_VALUE) == OPENXR_MODE_VALUE; +} + +TypedArray OpenXREditorExportPlugin::_get_export_options(const Ref &platform) const { + TypedArray export_options; + if (!_supports_platform(platform)) { + return export_options; + } + + export_options.append(_get_vendor_toggle_option()); + return export_options; +} + +String OpenXREditorExportPlugin::_get_export_option_warning(const Ref &platform, const String &option) const { + if (!_supports_platform(platform)) { + return ""; + } + + if (option != _get_vendor_toggle_option_name()) { + return ""; + } + + if (!_is_openxr_enabled() && _get_bool_option(option)) { + return "\"Enable " + _vendor.capitalize() + " Plugin\" requires \"XR Mode\" to be \"OpenXR\".\n"; + } + + if (_is_vendor_plugin_enabled()) { + for (const String vendor_name : VENDORS_LIST) { + if (vendor_name != _vendor && _is_vendor_plugin_enabled(vendor_name)) { + return "\"Disable " + _vendor.capitalize() + " Plugin before enabling another. Multiple plugins are not supported!\""; + } + } + } + + return ""; +} + +bool OpenXREditorExportPlugin::_supports_platform(const Ref &platform) const { + return platform->is_class(EditorExportPlatformAndroid::get_class_static()); +} + +bool OpenXREditorExportPlugin::_get_bool_option(const String &option) const { + Variant option_enabled = get_option(option); + if (option_enabled.get_type() == Variant::Type::BOOL) { + return option_enabled; + } + return false; +} + +int OpenXREditorExportPlugin::_get_int_option(const String &option, int default_value) const { + Variant option_value = get_option(option); + if (option_value.get_type() == Variant::Type::INT) { + return option_value; + } + return default_value; +} + +PackedStringArray OpenXREditorExportPlugin::_get_android_dependencies(const Ref &platform, bool debug) const { + PackedStringArray dependencies; + if (!_supports_platform(platform)) { + return dependencies; + } + + if (_is_vendor_plugin_enabled() && !_is_android_aar_file_available(debug)) { + dependencies.append(_get_android_maven_central_dependency()); + } + + return dependencies; +} + +PackedStringArray OpenXREditorExportPlugin::_get_android_libraries(const Ref &platform, bool debug) const { + PackedStringArray dependencies; + if (!_supports_platform(platform)) { + return dependencies; + } + + if (_is_vendor_plugin_enabled() && _is_android_aar_file_available(debug)) { + dependencies.append(_get_android_aar_file_path(debug)); + } + + return dependencies; +} + +PackedStringArray OpenXREditorExportPlugin::_get_android_dependencies_maven_repos(const Ref &platform, bool debug) const { + PackedStringArray maven_repos; + if (!_supports_platform(platform)) { + return maven_repos; + } + + if (_is_vendor_plugin_enabled() && !_is_android_aar_file_available(debug) && _plugin_version.ends_with("-SNAPSHOT")) { + maven_repos.append("https://s01.oss.sonatype.org/content/repositories/snapshots/"); + } + return maven_repos; +} diff --git a/common/src/main/cpp/export/export_plugin.h b/common/src/main/cpp/export/export_plugin.h new file mode 100644 index 00000000..597f2cf9 --- /dev/null +++ b/common/src/main/cpp/export/export_plugin.h @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* export_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT XR */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +using namespace godot; + +// Set of supported vendors +static const String META_VENDOR_NAME = "meta"; +static const String PICO_VENDOR_NAME = "pico"; +static const String LYNX_VENDOR_NAME = "lynx"; +static const String KHRONOS_VENDOR_NAME = "khronos"; + +static const String VENDORS_LIST[] = { + META_VENDOR_NAME, + PICO_VENDOR_NAME, + LYNX_VENDOR_NAME, + KHRONOS_VENDOR_NAME, +}; + +// Set of custom feature tags supported by the plugin +static const String EYE_GAZE_INTERACTION_FEATURE = "XR_EXT_eye_gaze_interaction"; + +static const int REGULAR_MODE_VALUE = 0; +static const int OPENXR_MODE_VALUE = 1; + +/// Base class for the vendor editor export plugin +class OpenXREditorExportPlugin: public EditorExportPlugin { + GDCLASS(OpenXREditorExportPlugin, EditorExportPlugin) + +public: + OpenXREditorExportPlugin(); + + String _get_name() const override; + + TypedArray _get_export_options(const Ref &platform) const override; + + String _get_export_option_warning(const Ref &platform, const String &option) const override; + + bool _supports_platform(const Ref &platform) const override; + + PackedStringArray _get_android_dependencies(const Ref &platform, bool debug) const override; + PackedStringArray _get_android_dependencies_maven_repos(const Ref &platform, bool debug) const override; + PackedStringArray _get_android_libraries(const Ref &platform, bool debug) const override; + + void set_vendor_name(const String &vendor_name) { + _vendor = vendor_name; + } + + void set_plugin_version(const String &plugin_version) { + _plugin_version = plugin_version; + } + +protected: + static void _bind_methods(); + + static Dictionary _generate_export_option(const String &name, const String &class_name, + Variant::Type type, + PropertyHint property_hint, + const String &hint_string, + PropertyUsageFlags property_usage, + const Variant &default_value, + bool update_visibility); + + Dictionary _get_vendor_toggle_option() const { + return _get_vendor_toggle_option(_vendor); + } + + Dictionary _get_vendor_toggle_option(const String &vendor_name) const; + + bool _is_openxr_enabled() const; + + bool _get_bool_option(const String &option) const; + + int _get_int_option(const String &option, int default_value) const; + + bool _is_vendor_plugin_enabled(const String &vendor_name) const { + return _get_bool_option(_get_vendor_toggle_option_name(vendor_name)); + } + + bool _is_vendor_plugin_enabled() const { + return _is_vendor_plugin_enabled(_vendor); + } + + bool _is_android_aar_file_available(bool debug) const { + return FileAccess::file_exists(_get_android_aar_file_path(debug)); + } + +private: + /// Path to the Android library aar file. If the return file path is not available, we + /// fall back to the maven central dependency. + String _get_android_aar_file_path(bool debug) const; + + /// Maven central dependency used as fall back when the Android library aar file is not + /// available. + String _get_android_maven_central_dependency() const; + + String _get_vendor_toggle_option_name() const { + return _get_vendor_toggle_option_name(_vendor); + } + + String _get_vendor_toggle_option_name(const String &vendor_name) const; + + + String _vendor; + String _plugin_version; +}; diff --git a/godotopenxrmeta/CMakeLists.txt b/godotopenxrmeta/CMakeLists.txt index 7439e540..55d30c3b 100644 --- a/godotopenxrmeta/CMakeLists.txt +++ b/godotopenxrmeta/CMakeLists.txt @@ -1,52 +1,5 @@ cmake_minimum_required(VERSION 3.22.1) -## Default configs -# Default android abi is arm64-v8a -if (NOT ANDROID_ABI) - set(ANDROID_ABI "arm64-v8a") -endif (NOT ANDROID_ABI) - -# Default android platform is android-21 -if (NOT ANDROID_PLATFORM) - set(ANDROID_PLATFORM "android-21") -endif (NOT ANDROID_PLATFORM) - -# Default build type is Debug -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug) -endif (NOT CMAKE_BUILD_TYPE) - -if (NOT (ANDROID_STL STREQUAL "c++_shared")) - set(ANDROID_STL "c++_shared") -endif (NOT (ANDROID_STL STREQUAL "c++_shared")) - -# Check if ANDROID_NDK is set. -if (NOT ANDROID_NDK) - # Set to ANDROID_NDK_HOME environment variable if it's set. - if (DEFINED ENV{ANDROID_NDK_HOME}) - set(ANDROID_NDK $ENV{ANDROID_NDK_HOME}) - else (DEFINED ENV{ANDROID_NDK_HOME}) - message(WARNING "ANDROID_NDK_HOME is not set") - endif (DEFINED ENV{ANDROID_NDK_HOME}) -endif (NOT ANDROID_NDK) - -# Check if CMAKE_TOOLCHAIN_FILE is set. -if (NOT CMAKE_TOOLCHAIN_FILE) - set(CMAKE_TOOLCHAIN_FILE "${ANDROID_NDK}/build/cmake/android.toolchain.cmake") -endif (NOT CMAKE_TOOLCHAIN_FILE) - -if (NOT DEFINED BITS) - set(BITS 32) - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(BITS 64) - endif (CMAKE_SIZEOF_VOID_P EQUAL 8) -endif (NOT DEFINED BITS) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - - ## Common dependencies include(${CMAKE_SOURCE_DIR}/../common/CMakeLists.txt) @@ -54,35 +7,6 @@ include(${CMAKE_SOURCE_DIR}/../common/CMakeLists.txt) ## Project definition project(godotopenxrmeta LANGUAGES CXX) -add_definitions(-DANDROID) - -set(GODOT_COMPILE_FLAGS) -set(GODOT_LINKER_FLAGS) - -set(GODOT_LINKER_FLAGS "-Wl") - -set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wlong-long -Wmissing-braces -Wmissing-format-attribute") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label") -set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes") - -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Android") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes") -endif () - -if (CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") -else () - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") -endif (CMAKE_BUILD_TYPE MATCHES Debug) - - ## OpenXR Mobile loader library # Sets the path to the OpenXR mobile library directory. set(OPENXR_MOBILE_ROOT_DIR "${CMAKE_SOURCE_DIR}/libs/ovr_openxr_mobile_sdk/OpenXR") @@ -104,6 +28,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED ${ANDROID_SOURCES} ${ANDROID_HEADERS} + ${COMMON_LIB_SOURCES} + ${COMMON_LIB_HEADERS} ) target_include_directories(${CMAKE_PROJECT_NAME} @@ -111,6 +37,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} ${GODOT_CPP_INCLUDE_DIRECTORIES} ${OPENXR_HEADERS_DIR} ${OPENXR_MOBILE_HEADERS_DIR} + ${COMMON_LIB_HEADERS_DIR} ) target_link_libraries(${CMAKE_PROJECT_NAME} diff --git a/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.cpp b/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.cpp new file mode 100644 index 00000000..7a020cc3 --- /dev/null +++ b/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.cpp @@ -0,0 +1,336 @@ +/**************************************************************************/ +/* meta_editor_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT XR */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "meta_editor_plugin.h" + +#include + +using namespace godot; + +void MetaEditorPlugin::_bind_methods() {} + +void MetaEditorPlugin::_enter_tree() { + // Initialize the editor export plugin + meta_export_plugin = memnew(MetaEditorExportPlugin); + meta_export_plugin->set_plugin_version(get_plugin_version()); + + add_export_plugin(meta_export_plugin); +} + +void MetaEditorPlugin::_exit_tree() { + // Clean up the editor export plugin + remove_export_plugin(meta_export_plugin); + memfree(meta_export_plugin); + meta_export_plugin = nullptr; +} + +MetaEditorExportPlugin::MetaEditorExportPlugin() { + set_vendor_name(META_VENDOR_NAME); + + if (eye_tracking_option.is_empty()) { + eye_tracking_option = _generate_export_option( + "meta_xr_features/eye_tracking", + "", + Variant::Type::INT, + PROPERTY_HINT_ENUM, + "None,Optional,Required", + PROPERTY_USAGE_DEFAULT, + EYE_TRACKING_NONE_VALUE, + false + ); + } + + if (hand_tracking_option.is_empty()) { + hand_tracking_option = _generate_export_option( + "meta_xr_features/hand_tracking", + "", + Variant::Type::INT, + PROPERTY_HINT_ENUM, + "None,Optional,Required", + PROPERTY_USAGE_DEFAULT, + HAND_TRACKING_NONE_VALUE, + false + ); + } + + if (hand_tracking_frequency_option.is_empty()) { + hand_tracking_frequency_option = _generate_export_option( + "meta_xr_features/hand_tracking_frequency", + "", + Variant::Type::INT, + PROPERTY_HINT_ENUM, + "Low,High", + PROPERTY_USAGE_DEFAULT, + HAND_TRACKING_FREQUENCY_LOW_VALUE, + false + ); + } + + if (passthrough_option.is_empty()) { + passthrough_option = _generate_export_option( + "meta_xr_features/passthrough", + "", + Variant::Type::INT, + PROPERTY_HINT_ENUM, + "None,Optional,Required", + PROPERTY_USAGE_DEFAULT, + PASSTHROUGH_NONE_VALUE, + false + ); + } + + if (use_anchor_api_option.is_empty()) { + use_anchor_api_option = _generate_export_option( + "meta_xr_features/use_anchor_api", + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + false, + false + ); + } + + if (support_quest_1_option.is_empty()) { + support_quest_1_option = _generate_export_option( + "meta_xr_features/quest_1_support", + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + false, + false + ); + } + + if (support_quest_2_option.is_empty()) { + support_quest_2_option = _generate_export_option( + "meta_xr_features/quest_2_support", + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + true, + false + ); + } + + if (support_quest_3_option.is_empty()) { + support_quest_3_option = _generate_export_option( + "meta_xr_features/quest_3_support", + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + true, + false + ); + } + + if (support_quest_pro_option.is_empty()) { + support_quest_pro_option = _generate_export_option( + "meta_xr_features/quest_pro_support", + "", + Variant::Type::BOOL, + PROPERTY_HINT_NONE, + "", + PROPERTY_USAGE_DEFAULT, + true, + false + ); + } +} + +void MetaEditorExportPlugin::_bind_methods() {} + +TypedArray MetaEditorExportPlugin::_get_export_options(const Ref &platform) const { + TypedArray export_options; + if (!_supports_platform(platform)) { + return export_options; + } + + export_options.append(_get_vendor_toggle_option()); + export_options.append(eye_tracking_option); + export_options.append(hand_tracking_option); + export_options.append(hand_tracking_frequency_option); + export_options.append(passthrough_option); + export_options.append(use_anchor_api_option); + export_options.append(support_quest_1_option); + export_options.append(support_quest_2_option); + export_options.append(support_quest_3_option); + export_options.append(support_quest_pro_option); + + return export_options; +} + +PackedStringArray MetaEditorExportPlugin::_get_supported_devices() const { + PackedStringArray supported_devices; + + if (_get_bool_option("meta_xr_features/quest_1_support")) { + supported_devices.append("quest"); + } + + if (_get_bool_option("meta_xr_features/quest_2_support")) { + supported_devices.append("quest2"); + } + + if (_get_bool_option("meta_xr_features/quest_3_support")) { + supported_devices.append("quest3"); + } + + if (_get_bool_option("meta_xr_features/quest_pro_support")) { + supported_devices.append("questpro"); + } + + return supported_devices; +} + +bool MetaEditorExportPlugin::_is_eye_tracking_enabled() const { + bool eye_tracking_project_setting_enabled = ProjectSettings::get_singleton()->get_setting_with_override("xr/openxr/extensions/eye_gaze_interaction"); + if (!eye_tracking_project_setting_enabled) { + return false; + } + + int eye_tracking_option_value = _get_int_option("meta_xr_features/eye_tracking", EYE_TRACKING_NONE_VALUE); + return eye_tracking_option_value > EYE_TRACKING_NONE_VALUE; +} + +PackedStringArray MetaEditorExportPlugin::_get_export_features(const Ref &platform, bool debug) const { + PackedStringArray features; + if (!_supports_platform(platform)) { + return features; + } + + // Add the eye tracking feature if necessary + if (_is_eye_tracking_enabled()) { + features.append(EYE_GAZE_INTERACTION_FEATURE); + } + + return features; +} + +String MetaEditorExportPlugin::_get_export_option_warning(const Ref &platform, const String &option) const { + if (!_supports_platform(platform)) { + return ""; + } + + bool openxr_enabled = _is_openxr_enabled(); + if (option == "meta_xr_features/eye_tracking") { + bool eye_tracking_project_setting_enabled = ProjectSettings::get_singleton()->get_setting_with_override("xr/openxr/extensions/eye_gaze_interaction"); + int eye_tracking_option_value = _get_int_option("meta_xr_features/eye_tracking", EYE_TRACKING_NONE_VALUE); + if (eye_tracking_option_value > EYE_TRACKING_NONE_VALUE && !eye_tracking_project_setting_enabled) { + return "\"Eye Tracking\" project setting must be enabled!\n"; + } + } else if (option == "meta_xr_features/hand_tracking") { + if (!openxr_enabled && _get_int_option(option, HAND_TRACKING_NONE_VALUE) > HAND_TRACKING_NONE_VALUE) { + return "\"Hand Tracking\" requires \"XR Mode\" to be \"OpenXR\".\n"; + } + } else if (option == "meta_xr_features/passthrough") { + if (!openxr_enabled && _get_int_option(option, PASSTHROUGH_NONE_VALUE) > PASSTHROUGH_NONE_VALUE) { + return "\"Passthrough\" requires \"XR Mode\" to be \"OpenXR\".\n"; + } + } else if (option == "meta_xr_features/use_anchor_api") { + if (!openxr_enabled && _get_bool_option(option)) { + return "\"Use anchor API\" is only valid when \"XR Mode\" is \"OpenXR\".\n"; + } + } + + return OpenXREditorExportPlugin::_get_export_option_warning(platform, option); +} + +String MetaEditorExportPlugin::_get_android_manifest_element_contents(const Ref &platform, bool debug) const { + String contents; + if (!_supports_platform(platform) || !_is_vendor_plugin_enabled()) { + return contents; + } + + // Check for eye tracking + if (_is_eye_tracking_enabled()) { + contents += " \n"; + + int eye_tracking_value = _get_int_option("meta_xr_features/eye_tracking", EYE_TRACKING_NONE_VALUE); + if (eye_tracking_value == EYE_TRACKING_OPTIONAL_VALUE) { + contents += " \n"; + } else if (eye_tracking_value == EYE_TRACKING_REQUIRED_VALUE) { + contents += " \n"; + } + } + + // Check for hand tracking + int hand_tracking_value = _get_int_option("meta_xr_features/hand_tracking", HAND_TRACKING_NONE_VALUE); + if (hand_tracking_value > HAND_TRACKING_NONE_VALUE) { + contents += " \n"; + + if (hand_tracking_value == HAND_TRACKING_OPTIONAL_VALUE) { + contents += " \n"; + } else if (hand_tracking_value == HAND_TRACKING_REQUIRED_VALUE) { + contents += " \n"; + } + } + + // Check for passthrough + int passthrough_mode = _get_int_option("meta_xr_features/passthrough", PASSTHROUGH_NONE_VALUE); + if (passthrough_mode == PASSTHROUGH_OPTIONAL_VALUE) { + contents += " \n"; + } else if (passthrough_mode == PASSTHROUGH_REQUIRED_VALUE) { + contents += " \n"; + } + + // Check for anchor api + bool use_anchor_api = _get_bool_option("meta_xr_features/use_anchor_api"); + if (use_anchor_api) { + contents += " \n"; + } + + return contents; +} + +String MetaEditorExportPlugin::_get_android_manifest_application_element_contents(const Ref &platform, bool debug) const { + String contents; + if (!_supports_platform(platform) || !_is_vendor_plugin_enabled()) { + return contents; + } + + const String supported_devices = String("|").join(_get_supported_devices()); + contents += " \n"; + + bool hand_tracking_enabled = _get_int_option("meta_xr_features/hand_tracking", HAND_TRACKING_NONE_VALUE) > HAND_TRACKING_NONE_VALUE; + if (hand_tracking_enabled) { + int hand_tracking_frequency = _get_int_option("meta_xr_features/hand_tracking_frequency", HAND_TRACKING_FREQUENCY_LOW_VALUE); + const String hand_tracking_frequency_label = (hand_tracking_frequency == HAND_TRACKING_FREQUENCY_LOW_VALUE) ? "LOW" : "HIGH"; + contents += " \n"; + contents += " \n"; + } + + return contents; +} diff --git a/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.h b/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.h new file mode 100644 index 00000000..0181c57d --- /dev/null +++ b/godotopenxrmeta/src/main/cpp/export/meta_editor_plugin.h @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* meta_editor_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT XR */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include + +#include "export/export_plugin.h" + +using namespace godot; + +namespace { + +static const int EYE_TRACKING_NONE_VALUE = 0; +static const int EYE_TRACKING_OPTIONAL_VALUE = 1; +static const int EYE_TRACKING_REQUIRED_VALUE = 2; + +static const int PASSTHROUGH_NONE_VALUE = 0; +static const int PASSTHROUGH_OPTIONAL_VALUE = 1; +static const int PASSTHROUGH_REQUIRED_VALUE = 2; + +static const int HAND_TRACKING_NONE_VALUE = 0; +static const int HAND_TRACKING_OPTIONAL_VALUE = 1; +static const int HAND_TRACKING_REQUIRED_VALUE = 2; + +static const int HAND_TRACKING_FREQUENCY_LOW_VALUE = 0; +static const int HAND_TRACKING_FREQUENCY_HIGH_VALUE = 1; + +static Dictionary eye_tracking_option; +static Dictionary hand_tracking_option; +static Dictionary hand_tracking_frequency_option; +static Dictionary passthrough_option; +static Dictionary use_anchor_api_option; +static Dictionary support_quest_1_option; +static Dictionary support_quest_2_option; +static Dictionary support_quest_3_option; +static Dictionary support_quest_pro_option; + +} // namespace + +class MetaEditorExportPlugin : public OpenXREditorExportPlugin { + GDCLASS(MetaEditorExportPlugin, OpenXREditorExportPlugin); + +public: + MetaEditorExportPlugin(); + + TypedArray _get_export_options(const Ref &platform) const override; + + PackedStringArray _get_export_features(const Ref &platform, bool debug) const override; + + String _get_export_option_warning(const Ref &platform, const String &option) const override; + + String _get_android_manifest_activity_element_contents(const Ref &platform, bool debug) const override; + String _get_android_manifest_application_element_contents(const Ref &platform, bool debug) const override; + String _get_android_manifest_element_contents(const Ref &platform, bool debug) const override; + +protected: + static void _bind_methods(); + +private: + PackedStringArray _get_supported_devices() const; + + bool _is_eye_tracking_enabled() const; +}; + +class MetaEditorPlugin : public EditorPlugin { + GDCLASS(MetaEditorPlugin, EditorPlugin) + +public: + void _enter_tree() override; + void _exit_tree() override; + +protected: + static void _bind_methods(); + +private: + MetaEditorExportPlugin *meta_export_plugin = nullptr; + +}; diff --git a/godotopenxrmeta/src/main/cpp/register_types.cpp b/godotopenxrmeta/src/main/cpp/register_types.cpp index 30e61c36..a49bbdb1 100644 --- a/godotopenxrmeta/src/main/cpp/register_types.cpp +++ b/godotopenxrmeta/src/main/cpp/register_types.cpp @@ -38,15 +38,33 @@ #include "include/openxr_fb_scene_capture_extension_wrapper.h" +#include "export/export_plugin.h" +#include "export/meta_editor_plugin.h" + using namespace godot; void initialize_plugin_module(ModuleInitializationLevel p_level) { - if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) - { - ClassDB::register_class(); - OpenXRFbSceneCaptureExtensionWrapper::get_singleton()->register_extension_wrapper(); + switch(p_level) { + case MODULE_INITIALIZATION_LEVEL_CORE: { + ClassDB::register_class(); + OpenXRFbSceneCaptureExtensionWrapper::get_singleton()->register_extension_wrapper(); + } break; + + case MODULE_INITIALIZATION_LEVEL_EDITOR: { + ClassDB::register_class(); + ClassDB::register_class(); + + EditorPlugins::add_by_type(); + } break; + + case MODULE_INITIALIZATION_LEVEL_SERVERS: + case MODULE_INITIALIZATION_LEVEL_SCENE: + case MODULE_INITIALIZATION_LEVEL_MAX: + break; } + + } void terminate_plugin_module(ModuleInitializationLevel p_level)