From c4b058dedef6cf2a5eebdee0e64e09bbfe7a04bc Mon Sep 17 00:00:00 2001 From: Logan Lang Date: Tue, 23 Jan 2024 11:54:52 -0600 Subject: [PATCH] Add OpenXR fb_render_model extension wrapper and OpenXRFBRenderModel node --- CHANGES.md | 1 + SConstruct | 1 + .../cpp/classes/openxr_fb_render_model.cpp | 138 ++++++++++++ .../main/cpp/export/meta_export_plugin.cpp | 26 +++ ...enxr_fb_render_model_extension_wrapper.cpp | 199 ++++++++++++++++++ .../include/classes/openxr_fb_render_model.h | 67 ++++++ .../cpp/include/export/meta_export_plugin.h | 5 + ...openxr_fb_render_model_extension_wrapper.h | 112 ++++++++++ common/src/main/cpp/register_types.cpp | 9 + demo/export_presets.cfg | 1 + demo/main.gd | 12 ++ demo/main.tscn | 16 +- 12 files changed, 583 insertions(+), 4 deletions(-) create mode 100644 common/src/main/cpp/classes/openxr_fb_render_model.cpp create mode 100644 common/src/main/cpp/extensions/openxr_fb_render_model_extension_wrapper.cpp create mode 100644 common/src/main/cpp/include/classes/openxr_fb_render_model.h create mode 100644 common/src/main/cpp/include/extensions/openxr_fb_render_model_extension_wrapper.h diff --git a/CHANGES.md b/CHANGES.md index b82e30d5..39615168 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ - Upgrade Android, Gradle, Godot and Kotlin dependencies - Add XR_FB_face_tracking support - Update to OpenXR 1.0.34 headers +- Add XR_FB_render_model extension wrapper and OpenXRFBRenderModel node ## 2.0.3 - Migrate the export scripts from gdscript to C++ via gdextension diff --git a/SConstruct b/SConstruct index 9f6a5812..4c4e9a52 100644 --- a/SConstruct +++ b/SConstruct @@ -17,6 +17,7 @@ sources = [] sources += Glob("#common/src/main/cpp/*.cpp") sources += Glob("#common/src/main/cpp/export/*.cpp") sources += Glob("#common/src/main/cpp/extensions/*.cpp") +sources += Glob("#common/src/main/cpp/classes/*.cpp") binary_path = '#demo/addons/godotopenxrvendors/.bin' project_name = 'godotopenxrvendors' diff --git a/common/src/main/cpp/classes/openxr_fb_render_model.cpp b/common/src/main/cpp/classes/openxr_fb_render_model.cpp new file mode 100644 index 00000000..56d45b30 --- /dev/null +++ b/common/src/main/cpp/classes/openxr_fb_render_model.cpp @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* openxr_fb_render_model.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 "classes/openxr_fb_render_model.h" + +#include "extensions/openxr_fb_render_model_extension_wrapper.h" + +#include +#include +#include +#include +#include + +using namespace godot; + +void OpenXRFbRenderModel::set_render_model_type(Model p_model) { + render_model_type = p_model; + if (is_inside_tree() && OpenXRFbRenderModelExtensionWrapper::get_singleton()->is_openxr_session_active()) { + load_render_model(); + } +} + +OpenXRFbRenderModel::Model OpenXRFbRenderModel::get_render_model_type() { + return render_model_type; +} + +bool OpenXRFbRenderModel::has_render_model_node() { + return render_model_node != nullptr; +} + +void OpenXRFbRenderModel::load_render_model() { + if (render_model_node != nullptr) { + render_model_node->queue_free(); + render_model_node = nullptr; + } + + String render_model_path; + switch (render_model_type) { + case MODEL_CONTROLLER_LEFT: { + render_model_path = "/model_fb/controller/left"; + } break; + + case MODEL_CONTROLLER_RIGHT: { + render_model_path = "/model_fb/controller/right"; + } break; + + default: { + render_model_path = ""; + } + } + + if (render_model_path.is_empty()) { + return; + } + + PackedByteArray render_model_buffer = OpenXRFbRenderModelExtensionWrapper::get_singleton()->get_buffer(render_model_path); + if (render_model_buffer.is_empty()) { + UtilityFunctions::print_verbose("Failed to load render model buffer from path [", render_model_path, "] in OpenXRFbRenderModel node"); + return; + } + + Ref gltf_document; + gltf_document.instantiate(); + Ref gltf_state; + gltf_state.instantiate(); + + Error err = gltf_document->append_from_buffer(render_model_buffer, "", gltf_state); + if (err != OK) { + UtilityFunctions::print_verbose("Failed to instance render model in OpenXRFbRenderModel node"); + return; + } + + render_model_node = Object::cast_to(gltf_document->generate_scene(gltf_state)); + if (render_model_node) { + add_child(render_model_node); + emit_signal("openxr_fb_render_model_loaded"); + } +} + +Node3D *OpenXRFbRenderModel::get_render_model_node() { + return render_model_node; +} + +void OpenXRFbRenderModel::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + Ref openxr_interface = XRServer::get_singleton()->find_interface("OpenXR"); + if (openxr_interface.is_valid()) { + openxr_interface->connect("session_begun", callable_mp(this, &OpenXRFbRenderModel::load_render_model)); + } + } break; + case NOTIFICATION_ENTER_TREE: { + if (OpenXRFbRenderModelExtensionWrapper::get_singleton()->is_openxr_session_active()) { + load_render_model(); + } + } break; + } +} + +void OpenXRFbRenderModel::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_render_model_node"), &OpenXRFbRenderModel::has_render_model_node); + ClassDB::bind_method(D_METHOD("get_render_model_node"), &OpenXRFbRenderModel::get_render_model_node); + ClassDB::bind_method(D_METHOD("set_render_model_type", "render_model_type"), &OpenXRFbRenderModel::set_render_model_type); + ClassDB::bind_method(D_METHOD("get_render_model_type"), &OpenXRFbRenderModel::get_render_model_type); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_model_type", PROPERTY_HINT_ENUM, "Left Controller,Right Controller"), "set_render_model_type", "get_render_model_type"); + + BIND_ENUM_CONSTANT(MODEL_CONTROLLER_LEFT); + BIND_ENUM_CONSTANT(MODEL_CONTROLLER_RIGHT); + + ADD_SIGNAL(MethodInfo("openxr_fb_render_model_loaded")); +} diff --git a/common/src/main/cpp/export/meta_export_plugin.cpp b/common/src/main/cpp/export/meta_export_plugin.cpp index ed37d72a..17f9212f 100644 --- a/common/src/main/cpp/export/meta_export_plugin.cpp +++ b/common/src/main/cpp/export/meta_export_plugin.cpp @@ -96,6 +96,15 @@ MetaEditorExportPlugin::MetaEditorExportPlugin() { PROPERTY_USAGE_DEFAULT, PASSTHROUGH_NONE_VALUE, false); + _render_model_option = _generate_export_option( + "meta_xr_features/render_model", + "", + Variant::Type::INT, + PROPERTY_HINT_ENUM, + "None,Optional,Required", + PROPERTY_USAGE_DEFAULT, + RENDER_MODEL_NONE_VALUE, + false); _use_anchor_api_option = _generate_export_option( "meta_xr_features/use_anchor_api", "", @@ -193,6 +202,7 @@ TypedArray MetaEditorExportPlugin::_get_export_options(const Ref PASSTHROUGH_NONE_VALUE) { return "\"Passthrough\" requires \"XR Mode\" to be \"OpenXR\".\n"; } + } else if (option == "meta_xr_features/render_model") { + if (!openxr_enabled && _get_int_option(option, RENDER_MODEL_NONE_VALUE) > RENDER_MODEL_NONE_VALUE) { + return "\"Render Model\" 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"; @@ -347,6 +361,18 @@ String MetaEditorExportPlugin::_get_android_manifest_element_contents(const Ref< contents += " \n"; } + // Check for render model + int render_model_value = _get_int_option("meta_xr_features/render_model", RENDER_MODEL_NONE_VALUE); + if (render_model_value > RENDER_MODEL_NONE_VALUE) { + contents += " \n"; + + if (render_model_value == RENDER_MODEL_OPTIONAL_VALUE) { + contents += " \n"; + } else if (render_model_value == RENDER_MODEL_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) { diff --git a/common/src/main/cpp/extensions/openxr_fb_render_model_extension_wrapper.cpp b/common/src/main/cpp/extensions/openxr_fb_render_model_extension_wrapper.cpp new file mode 100644 index 00000000..def308e8 --- /dev/null +++ b/common/src/main/cpp/extensions/openxr_fb_render_model_extension_wrapper.cpp @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* openxr_fb_scene_capture_extension_wrapper.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 "extensions/openxr_fb_render_model_extension_wrapper.h" + +#include +#include +#include + +using namespace godot; + +OpenXRFbRenderModelExtensionWrapper *OpenXRFbRenderModelExtensionWrapper::singleton = nullptr; + +OpenXRFbRenderModelExtensionWrapper *OpenXRFbRenderModelExtensionWrapper::get_singleton() { + if (singleton == nullptr) { + singleton = memnew(OpenXRFbRenderModelExtensionWrapper()); + } + return singleton; +} + +OpenXRFbRenderModelExtensionWrapper::OpenXRFbRenderModelExtensionWrapper() : + OpenXRExtensionWrapperExtension() { + ERR_FAIL_COND_MSG(singleton != nullptr, "An OpenXRFbRenderModelExtensionWrapper singleton already exists."); + + request_extensions[XR_FB_RENDER_MODEL_EXTENSION_NAME] = &fb_render_model_ext; + singleton = this; +} + +OpenXRFbRenderModelExtensionWrapper::~OpenXRFbRenderModelExtensionWrapper() { + cleanup(); +} + +void OpenXRFbRenderModelExtensionWrapper::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_enabled"), &OpenXRFbRenderModelExtensionWrapper::is_enabled); +} + +void OpenXRFbRenderModelExtensionWrapper::cleanup() { + fb_render_model_ext = false; +} + +uint64_t OpenXRFbRenderModelExtensionWrapper::_set_system_properties_and_get_next_pointer(void *next_pointer) { + system_render_model_properties.type = XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB; + system_render_model_properties.next = next_pointer; + system_render_model_properties.supportsRenderModelLoading = false; + return reinterpret_cast(&system_render_model_properties); +} + +godot::Dictionary OpenXRFbRenderModelExtensionWrapper::_get_requested_extensions() { + godot::Dictionary result; + for (auto ext : request_extensions) { + godot::String key = ext.first; + uint64_t value = reinterpret_cast(ext.second); + result[key] = (godot::Variant)value; + } + return result; +} + +void OpenXRFbRenderModelExtensionWrapper::_on_instance_created(uint64_t instance) { + if (fb_render_model_ext) { + bool result = initialize_fb_render_model_extension((XrInstance)instance); + if (!result) { + UtilityFunctions::print("Failed to initialize fb_render_model extension"); + fb_render_model_ext = false; + } + } +} + +void OpenXRFbRenderModelExtensionWrapper::_on_instance_destroyed() { + cleanup(); +} + +void OpenXRFbRenderModelExtensionWrapper::_on_state_ready() { + openxr_session_active = true; +} + +void OpenXRFbRenderModelExtensionWrapper::_on_state_stopping() { + openxr_session_active = false; + paths_fetched = false; +} + +bool OpenXRFbRenderModelExtensionWrapper::is_enabled() const { + return fb_render_model_ext && system_render_model_properties.supportsRenderModelLoading; +} + +void OpenXRFbRenderModelExtensionWrapper::fetch_paths() { + XrResult result; + uint32_t path_count = 0; + + result = xrEnumerateRenderModelPathsFB(SESSION, 0, &path_count, nullptr); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to get path count using FB_render_model extension, error code: ", result); + return; + } + + XrRenderModelPathInfoFB *paths = reinterpret_cast(memalloc(sizeof(XrRenderModelPathInfoFB) * path_count)); + + result = xrEnumerateRenderModelPathsFB(SESSION, path_count, &path_count, paths); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to get paths using FB_render_model extension, error code: ", result); + return; + } + + memfree(paths); + + paths_fetched = true; +} + +PackedByteArray OpenXRFbRenderModelExtensionWrapper::get_buffer(const String &p_path) { + if (!is_enabled()) { + return PackedByteArray(); + } + + if (!paths_fetched) { + fetch_paths(); + } + + XrResult result; + XrPath xr_path = _string_to_xr_path(p_path); + + // get render model properites + XrRenderModelCapabilitiesRequestFB model_capabilities = { + XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB, + nullptr, + XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB + }; + + XrRenderModelPropertiesFB model_properties = { XR_TYPE_RENDER_MODEL_PROPERTIES_FB, &model_capabilities }; + result = xrGetRenderModelPropertiesFB(SESSION, xr_path, &model_properties); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to get XrRenderModelPropertiesFB from XrPath, error code: ", result); + return PackedByteArray(); + } + + // load render model + XrRenderModelBufferFB model_buffer = { XR_TYPE_RENDER_MODEL_BUFFER_FB, nullptr }; + XrRenderModelLoadInfoFB model_info = { XR_TYPE_RENDER_MODEL_LOAD_INFO_FB, nullptr }; + model_info.modelKey = model_properties.modelKey; + result = xrLoadRenderModelFB(SESSION, &model_info, &model_buffer); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to get XrRenderModelBufferFB buffer count output, error code ", result); + return PackedByteArray(); + } + + model_buffer.bufferCapacityInput = model_buffer.bufferCountOutput; + PackedByteArray ret; + ret.resize(model_buffer.bufferCapacityInput); + model_buffer.buffer = ret.ptrw(); + result = xrLoadRenderModelFB(SESSION, &model_info, &model_buffer); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to load XrRenderModelBufferFB buffer, error code ", result); + return PackedByteArray(); + } + + return ret; +} + +XrPath OpenXRFbRenderModelExtensionWrapper::_string_to_xr_path(const String &p_path) { + XrPath xr_path; + XrResult result = xrStringToPath((XrInstance)get_openxr_api()->get_instance(), p_path.utf8().get_data(), &xr_path); + if (XR_FAILED(result)) { + UtilityFunctions::print("Failed to convert string path to XrPath, error code: ", result); + } + return xr_path; +} + +bool OpenXRFbRenderModelExtensionWrapper::initialize_fb_render_model_extension(const XrInstance p_instance) { + GDEXTENSION_INIT_XR_FUNC_V(xrEnumerateRenderModelPathsFB); + GDEXTENSION_INIT_XR_FUNC_V(xrGetRenderModelPropertiesFB); + GDEXTENSION_INIT_XR_FUNC_V(xrLoadRenderModelFB); + GDEXTENSION_INIT_XR_FUNC_V(xrStringToPath); + + return true; +} diff --git a/common/src/main/cpp/include/classes/openxr_fb_render_model.h b/common/src/main/cpp/include/classes/openxr_fb_render_model.h new file mode 100644 index 00000000..f04c95c4 --- /dev/null +++ b/common/src/main/cpp/include/classes/openxr_fb_render_model.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/* openxr_fb_render_model.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. */ +/**************************************************************************/ + +#ifndef OPENXR_FB_RENDER_MODEL_H +#define OPENXR_FB_RENDER_MODEL_H + +#include + +namespace godot { +class OpenXRFbRenderModel : public Node3D { + GDCLASS(OpenXRFbRenderModel, Node3D) + +public: + enum Model { + MODEL_CONTROLLER_LEFT, + MODEL_CONTROLLER_RIGHT, + }; + +private: + Model render_model_type = MODEL_CONTROLLER_LEFT; + Node3D *render_model_node = nullptr; + + void load_render_model(); + +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + void set_render_model_type(Model p_model); + Model get_render_model_type(); + + bool has_render_model_node(); + Node3D *get_render_model_node(); +}; +} //namespace godot + +VARIANT_ENUM_CAST(OpenXRFbRenderModel::Model); + +#endif diff --git a/common/src/main/cpp/include/export/meta_export_plugin.h b/common/src/main/cpp/include/export/meta_export_plugin.h index 9bc06bc7..aa4690d8 100644 --- a/common/src/main/cpp/include/export/meta_export_plugin.h +++ b/common/src/main/cpp/include/export/meta_export_plugin.h @@ -48,6 +48,10 @@ static const int PASSTHROUGH_NONE_VALUE = 0; static const int PASSTHROUGH_OPTIONAL_VALUE = 1; static const int PASSTHROUGH_REQUIRED_VALUE = 2; +static const int RENDER_MODEL_NONE_VALUE = 0; +static const int RENDER_MODEL_OPTIONAL_VALUE = 1; +static const int RENDER_MODEL_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; @@ -89,6 +93,7 @@ class MetaEditorExportPlugin : public OpenXREditorExportPlugin { Dictionary _hand_tracking_option; Dictionary _hand_tracking_frequency_option; Dictionary _passthrough_option; + Dictionary _render_model_option; Dictionary _use_anchor_api_option; Dictionary _use_scene_api_option; Dictionary _use_overlay_keyboard_option; diff --git a/common/src/main/cpp/include/extensions/openxr_fb_render_model_extension_wrapper.h b/common/src/main/cpp/include/extensions/openxr_fb_render_model_extension_wrapper.h new file mode 100644 index 00000000..e15fe76c --- /dev/null +++ b/common/src/main/cpp/include/extensions/openxr_fb_render_model_extension_wrapper.h @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* openxr_fb_render_model_extension_wrapper.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. */ +/**************************************************************************/ + +#ifndef OPENXR_FB_RENDER_MODEL_EXTENSION_WRAPPER_H +#define OPENXR_FB_RENDER_MODEL_EXTENSION_WRAPPER_H + +#include +#include +#include +#include + +#include "util.h" + +using namespace godot; + +// Wrapper for the set of Facebook render model extension. +class OpenXRFbRenderModelExtensionWrapper : public OpenXRExtensionWrapperExtension { + GDCLASS(OpenXRFbRenderModelExtensionWrapper, OpenXRExtensionWrapperExtension); + +public: + uint64_t _set_system_properties_and_get_next_pointer(void *next_pointer) override; + + godot::Dictionary _get_requested_extensions() override; + + void _on_instance_created(uint64_t instance) override; + + void _on_instance_destroyed() override; + + void _on_state_ready() override; + + void _on_state_stopping() override; + + static OpenXRFbRenderModelExtensionWrapper *get_singleton(); + + bool is_enabled() const; + bool is_openxr_session_active() const { return openxr_session_active; } + PackedByteArray get_buffer(const String &p_path); + + OpenXRFbRenderModelExtensionWrapper(); + ~OpenXRFbRenderModelExtensionWrapper(); + +protected: + static void _bind_methods(); + +private: + EXT_PROTO_XRRESULT_FUNC4(xrEnumerateRenderModelPathsFB, + (XrSession), session, + (uint32_t), pathCapacityInput, + (uint32_t *), pathCountOutput, + (XrRenderModelPathInfoFB *), path); + + EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelPropertiesFB, + (XrSession), session, + (XrPath), path, + (XrRenderModelPropertiesFB *), properties); + + EXT_PROTO_XRRESULT_FUNC3(xrLoadRenderModelFB, + (XrSession), session, + (const XrRenderModelLoadInfoFB *), info, + (XrRenderModelBufferFB *), buffer); + + EXT_PROTO_XRRESULT_FUNC3(xrStringToPath, + (XrInstance), instance, + (const char *), pathString, + (XrPath *), path); + + bool initialize_fb_render_model_extension(const XrInstance instance); + + void cleanup(); + + void fetch_paths(); + + String _xr_path_to_string(XrPath p_path); + + XrPath _string_to_xr_path(const String &p_path); + + static OpenXRFbRenderModelExtensionWrapper *singleton; + + std::map request_extensions; + bool fb_render_model_ext = false; + bool paths_fetched = false; + bool openxr_session_active = false; + XrSystemRenderModelPropertiesFB system_render_model_properties; +}; + +#endif // OPENXR_FB_RENDER_MODEL_EXTENSION_WRAPPER_H diff --git a/common/src/main/cpp/register_types.cpp b/common/src/main/cpp/register_types.cpp index 040300b8..ebb8d86a 100644 --- a/common/src/main/cpp/register_types.cpp +++ b/common/src/main/cpp/register_types.cpp @@ -44,17 +44,23 @@ #include "export/pico_export_plugin.h" #include "extensions/openxr_fb_face_tracking_extension_wrapper.h" +#include "extensions/openxr_fb_render_model_extension_wrapper.h" #include "extensions/openxr_fb_scene_capture_extension_wrapper.h" #include "extensions/openxr_fb_scene_extension_wrapper.h" #include "extensions/openxr_fb_spatial_entity_container_extension_wrapper.h" #include "extensions/openxr_fb_spatial_entity_extension_wrapper.h" #include "extensions/openxr_fb_spatial_entity_query_extension_wrapper.h" +#include "classes/openxr_fb_render_model.h" + using namespace godot; void initialize_plugin_module(ModuleInitializationLevel p_level) { switch (p_level) { case MODULE_INITIALIZATION_LEVEL_CORE: { + ClassDB::register_class(); + OpenXRFbRenderModelExtensionWrapper::get_singleton()->register_extension_wrapper(); + ClassDB::register_class(); OpenXRFbSceneCaptureExtensionWrapper::get_singleton()->register_extension_wrapper(); @@ -78,12 +84,15 @@ void initialize_plugin_module(ModuleInitializationLevel p_level) { break; case MODULE_INITIALIZATION_LEVEL_SCENE: { + Engine::get_singleton()->register_singleton("OpenXRFbRenderModelExtensionWrapper", OpenXRFbRenderModelExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbSceneCaptureExtensionWrapper", OpenXRFbSceneCaptureExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityExtensionWrapper", OpenXRFbSpatialEntityExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityQueryExtensionWrapper", OpenXRFbSpatialEntityQueryExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityContainerExtensionWrapper", OpenXRFbSpatialEntityContainerExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbSceneExtensionWrapper", OpenXRFbSceneExtensionWrapper::get_singleton()); Engine::get_singleton()->register_singleton("OpenXRFbFaceTrackingExtensionWrapper", OpenXRFbFaceTrackingExtensionWrapper::get_singleton()); + + ClassDB::register_class(); } break; case MODULE_INITIALIZATION_LEVEL_EDITOR: { diff --git a/demo/export_presets.cfg b/demo/export_presets.cfg index b31d898a..f365a48f 100644 --- a/demo/export_presets.cfg +++ b/demo/export_presets.cfg @@ -214,3 +214,4 @@ meta_xr_features/quest_pro_support=true meta_xr_features/use_anchor_api=true meta_xr_features/face_tracking=1 meta_xr_features/use_scene_api=false +meta_xr_features/render_model=1 diff --git a/demo/main.gd b/demo/main.gd index 09352d72..0a57de0f 100644 --- a/demo/main.gd +++ b/demo/main.gd @@ -1,5 +1,8 @@ extends Node3D +@onready var left_hand_mesh: MeshInstance3D = $XROrigin3D/LeftHand/LeftHandMesh +@onready var right_hand_mesh: MeshInstance3D = $XROrigin3D/RightHand/RightHandMesh + var xr_interface : XRInterface = null # Called when the node enters the scene tree for the first time. @@ -23,3 +26,12 @@ func _on_left_hand_button_pressed(name): if name == "menu_button" and scene_capture: print("Triggering scene capture") scene_capture.request_scene_capture() + + +func _on_left_controller_fb_render_model_render_model_loaded() -> void: + left_hand_mesh.hide() + + +func _on_right_controller_fb_render_model_render_model_loaded() -> void: + right_hand_mesh.hide() + diff --git a/demo/main.tscn b/demo/main.tscn index d1612a93..c688837f 100644 --- a/demo/main.tscn +++ b/demo/main.tscn @@ -60,17 +60,19 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.352791, 0) [node name="LeftHand" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.460909, 0.388594, -0.241118) tracker = &"left_hand" -pose = &"aim" +pose = &"grip" [node name="LeftHandMesh" type="MeshInstance3D" parent="XROrigin3D/LeftHand"] mesh = SubResource("BoxMesh_3kt6b") -[node name="HandTablet" type="MeshInstance3D" parent="XROrigin3D/LeftHand/LeftHandMesh"] -transform = Transform3D(1, 0, 0, 0, 0.919876, 0.392209, 0, -0.392209, 0.919876, 0, 0.208883, -0.236189) +[node name="LeftControllerFbRenderModel" type="OpenXRFbRenderModel" parent="XROrigin3D/LeftHand"] + +[node name="HandTablet" type="MeshInstance3D" parent="XROrigin3D/LeftHand"] +transform = Transform3D(1, 0, 0, 0, -0.392209, 0.919876, 0, -0.919876, -0.392209, 0, -0.236189, -0.208883) mesh = SubResource("QuadMesh_1oamj") surface_material_override/0 = SubResource("StandardMaterial3D_pmc5p") -[node name="Viewport2Din3D" parent="XROrigin3D/LeftHand/LeftHandMesh/HandTablet" instance=ExtResource("2_7whgo")] +[node name="Viewport2Din3D" parent="XROrigin3D/LeftHand/HandTablet" instance=ExtResource("2_7whgo")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.005) screen_size = Vector2(0.35, 0.25) enabled = false @@ -80,10 +82,14 @@ unshaded = true [node name="RightHand" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.478861, 0.468292, -0.241097) tracker = &"right_hand" +pose = &"grip" [node name="RightHandMesh" type="MeshInstance3D" parent="XROrigin3D/RightHand"] mesh = SubResource("BoxMesh_ey3x4") +[node name="RightControllerFbRenderModel" type="OpenXRFbRenderModel" parent="XROrigin3D/RightHand"] +render_model_type = 1 + [node name="EyeGaze" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.977669, 0) tracker = &"/user/eyes_ext" @@ -102,5 +108,7 @@ script = ExtResource("4_fsql8") [node name="Face" parent="Floor/TrackedFace" instance=ExtResource("4_wrwst")] [connection signal="button_pressed" from="XROrigin3D/LeftHand" to="." method="_on_left_hand_button_pressed"] +[connection signal="openxr_fb_render_model_loaded" from="XROrigin3D/LeftHand/LeftControllerFbRenderModel" to="." method="_on_left_controller_fb_render_model_render_model_loaded"] +[connection signal="openxr_fb_render_model_loaded" from="XROrigin3D/RightHand/RightControllerFbRenderModel" to="." method="_on_right_controller_fb_render_model_render_model_loaded"] [editable path="Floor/TrackedFace/Face"]