Path: blob/master/modules/openxr/extensions/openxr_render_model_extension.cpp
10278 views
/**************************************************************************/1/* openxr_render_model_extension.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "openxr_render_model_extension.h"3132#include "../openxr_api.h"33#include "../openxr_interface.h"3435#include "core/config/project_settings.h"36#include "core/string/print_string.h"37#include "servers/xr_server.h"3839OpenXRRenderModelExtension *OpenXRRenderModelExtension::singleton = nullptr;4041OpenXRRenderModelExtension *OpenXRRenderModelExtension::get_singleton() {42return singleton;43}4445void OpenXRRenderModelExtension::_bind_methods() {46ClassDB::bind_method(D_METHOD("is_active"), &OpenXRRenderModelExtension::is_active);47ClassDB::bind_method(D_METHOD("render_model_create", "render_model_id"), &OpenXRRenderModelExtension::render_model_create);48ClassDB::bind_method(D_METHOD("render_model_destroy", "render_model"), &OpenXRRenderModelExtension::render_model_destroy);49ClassDB::bind_method(D_METHOD("render_model_get_all"), &OpenXRRenderModelExtension::render_model_get_all);50ClassDB::bind_method(D_METHOD("render_model_new_scene_instance", "render_model"), &OpenXRRenderModelExtension::render_model_new_scene_instance);51ClassDB::bind_method(D_METHOD("render_model_get_subaction_paths", "render_model"), &OpenXRRenderModelExtension::render_model_get_subaction_paths);52ClassDB::bind_method(D_METHOD("render_model_get_top_level_path", "render_model"), &OpenXRRenderModelExtension::render_model_get_top_level_path_as_string);53ClassDB::bind_method(D_METHOD("render_model_get_confidence", "render_model"), &OpenXRRenderModelExtension::render_model_get_confidence);54ClassDB::bind_method(D_METHOD("render_model_get_root_transform", "render_model"), &OpenXRRenderModelExtension::render_model_get_root_transform);55ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_count", "render_model"), &OpenXRRenderModelExtension::render_model_get_animatable_node_count);56ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_name", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_name);57ClassDB::bind_method(D_METHOD("render_model_is_animatable_node_visible", "render_model", "index"), &OpenXRRenderModelExtension::render_model_is_animatable_node_visible);58ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_transform", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_transform);5960ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::RID, "render_model")));61ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::RID, "render_model")));62ADD_SIGNAL(MethodInfo("render_model_top_level_path_changed", PropertyInfo(Variant::RID, "render_model")));63}6465OpenXRRenderModelExtension::OpenXRRenderModelExtension() {66singleton = this;67}6869OpenXRRenderModelExtension::~OpenXRRenderModelExtension() {70singleton = nullptr;71}7273HashMap<String, bool *> OpenXRRenderModelExtension::get_requested_extensions() {74HashMap<String, bool *> request_extensions;7576if (GLOBAL_GET("xr/openxr/extensions/render_model")) {77request_extensions[XR_EXT_UUID_EXTENSION_NAME] = &uuid_ext;78request_extensions[XR_EXT_RENDER_MODEL_EXTENSION_NAME] = &render_model_ext;79request_extensions[XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME] = &interaction_render_model_ext;80}8182return request_extensions;83}8485void OpenXRRenderModelExtension::on_instance_created(const XrInstance p_instance) {86// Standard entry points we use.87EXT_INIT_XR_FUNC(xrLocateSpace);88EXT_INIT_XR_FUNC(xrDestroySpace);89EXT_INIT_XR_FUNC(xrPathToString);9091if (render_model_ext) {92EXT_INIT_XR_FUNC(xrCreateRenderModelEXT);93EXT_INIT_XR_FUNC(xrDestroyRenderModelEXT);94EXT_INIT_XR_FUNC(xrGetRenderModelPropertiesEXT);95EXT_INIT_XR_FUNC(xrCreateRenderModelSpaceEXT);96EXT_INIT_XR_FUNC(xrCreateRenderModelAssetEXT);97EXT_INIT_XR_FUNC(xrDestroyRenderModelAssetEXT);98EXT_INIT_XR_FUNC(xrGetRenderModelAssetDataEXT);99EXT_INIT_XR_FUNC(xrGetRenderModelAssetPropertiesEXT);100EXT_INIT_XR_FUNC(xrGetRenderModelStateEXT);101}102103if (interaction_render_model_ext) {104EXT_INIT_XR_FUNC(xrEnumerateInteractionRenderModelIdsEXT);105EXT_INIT_XR_FUNC(xrEnumerateRenderModelSubactionPathsEXT);106EXT_INIT_XR_FUNC(xrGetRenderModelPoseTopLevelUserPathEXT);107}108}109110void OpenXRRenderModelExtension::on_session_created(const XrSession p_session) {111_interaction_data_dirty = true;112}113114void OpenXRRenderModelExtension::on_instance_destroyed() {115xrCreateRenderModelEXT_ptr = nullptr;116xrDestroyRenderModelEXT_ptr = nullptr;117xrGetRenderModelPropertiesEXT_ptr = nullptr;118xrCreateRenderModelSpaceEXT_ptr = nullptr;119xrCreateRenderModelAssetEXT_ptr = nullptr;120xrDestroyRenderModelAssetEXT_ptr = nullptr;121xrGetRenderModelAssetDataEXT_ptr = nullptr;122xrGetRenderModelAssetPropertiesEXT_ptr = nullptr;123xrGetRenderModelStateEXT_ptr = nullptr;124xrEnumerateInteractionRenderModelIdsEXT_ptr = nullptr;125xrEnumerateRenderModelSubactionPathsEXT_ptr = nullptr;126xrGetRenderModelPoseTopLevelUserPathEXT_ptr = nullptr;127128uuid_ext = false;129render_model_ext = false;130interaction_render_model_ext = false;131}132133void OpenXRRenderModelExtension::on_session_destroyed() {134_clear_interaction_data();135_clear_render_model_data();136137// We no longer have valid sync data.138xr_sync_has_run = false;139}140141bool OpenXRRenderModelExtension::on_event_polled(const XrEventDataBuffer &event) {142if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) {143// Mark interaction data as dirty so that we update it on sync.144_interaction_data_dirty = true;145146return true;147} else if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) {148// If our controller bindings changed, its likely our render models change too.149// We should be getting a XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT150// but checking for this scenario just in case.151_interaction_data_dirty = true;152153// Do not consider this handled, we simply do additional logic.154return false;155}156157return false;158}159160void OpenXRRenderModelExtension::on_sync_actions() {161if (!is_active()) {162return;163}164165OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();166ERR_FAIL_NULL(openxr_api);167168// Mark sync as run169xr_sync_has_run = true;170171// Update our interaction data if needed172if (_interaction_data_dirty) {173_update_interaction_data();174}175176// Loop through all of our render models to update our space and state info177LocalVector<RID> owned = render_model_owner.get_owned_list();178179for (const RID &rid : owned) {180RenderModel *render_model = render_model_owner.get_or_null(rid);181if (render_model && render_model->xr_space != XR_NULL_HANDLE) {182XrSpaceLocation render_model_location = {183XR_TYPE_SPACE_LOCATION, // type184nullptr, // next1850, // locationFlags186{ { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose187};188189XrResult result = xrLocateSpace(render_model->xr_space, openxr_api->get_play_space(), openxr_api->get_predicted_display_time(), &render_model_location);190ERR_CONTINUE_MSG(XR_FAILED(result), "OpenXR: Failed to locate render model space [" + openxr_api->get_error_string(result) + "]");191192render_model->confidence = openxr_api->transform_from_location(render_model_location, render_model->root_transform);193194if (!render_model->node_states.is_empty()) {195// Get node states.196XrRenderModelStateGetInfoEXT get_state_info = {197XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, // type198nullptr, // next199openxr_api->get_predicted_display_time() // displayTime200};201202XrRenderModelStateEXT state = {203XR_TYPE_RENDER_MODEL_STATE_EXT, // type204nullptr, // next205render_model->animatable_node_count, // nodeStateCount206render_model->node_states.ptr(), // nodeStates207};208209result = xrGetRenderModelStateEXT(render_model->xr_render_model, &get_state_info, &state);210if (XR_FAILED(result)) {211ERR_PRINT("OpenXR: Failed to update node states [" + openxr_api->get_error_string(result) + "]");212}213}214215XrPath new_path = XR_NULL_PATH;216217if (toplevel_paths.is_empty()) {218// Set this up just once with paths we support here.219toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/left"));220toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/right"));221}222223XrInteractionRenderModelTopLevelUserPathGetInfoEXT info = {224XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, // type225nullptr, // next226(uint32_t)toplevel_paths.size(), // topLevelUserPathCount227toplevel_paths.ptr() // topLevelUserPaths228};229result = xrGetRenderModelPoseTopLevelUserPathEXT(render_model->xr_render_model, &info, &new_path);230if (XR_FAILED(result)) {231ERR_PRINT("OpenXR: Failed to update the top level path for render models [" + openxr_api->get_error_string(result) + "]");232} else if (new_path != render_model->top_level_path) {233print_verbose("OpenXR: Render model top level path changed to " + openxr_api->get_xr_path_name(new_path));234235// Set the new path236render_model->top_level_path = new_path;237238// And broadcast it239// Note, converting an XrPath to a String has overhead, so we won't do this automatically.240emit_signal(SNAME("render_model_top_level_path_changed"), rid);241}242}243}244}245246bool OpenXRRenderModelExtension::is_active() const {247return render_model_ext && interaction_render_model_ext;248}249250void OpenXRRenderModelExtension::_clear_interaction_data() {251for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {252render_model_destroy(e.value);253}254interaction_render_models.clear();255}256257bool OpenXRRenderModelExtension::_update_interaction_data() {258ERR_FAIL_COND_V_MSG(!interaction_render_model_ext, false, "Interaction render model extension hasn't been enabled.");259260OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();261ERR_FAIL_NULL_V(openxr_api, false);262263XrSession session = openxr_api->get_session();264ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);265266// Check if syncActions has been run at least once or there is no point in getting data.267if (!xr_sync_has_run) {268// Do not treat this as an error.269return true;270}271272// If we get this far, no longer mark as dirty.273// Else we just repeat the same error over and over again.274_interaction_data_dirty = false;275276// Obtain interaction info.277XrInteractionRenderModelIdsEnumerateInfoEXT interaction_info = {278XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, // type279nullptr, // next280};281282// Obtain count.283uint32_t interaction_count = 0;284XrResult result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, 0, &interaction_count, nullptr);285if (XR_FAILED(result)) {286// not successful? then we do nothing.287ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction id count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");288}289290// Create some storage291LocalVector<XrRenderModelIdEXT> render_model_interaction_ids;292render_model_interaction_ids.resize(interaction_count);293294// Only need to fetch data if there is something to fetch (/we've got storage).295if (!render_model_interaction_ids.is_empty()) {296// Obtain interaction ids297result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, render_model_interaction_ids.size(), &interaction_count, render_model_interaction_ids.ptr());298if (XR_FAILED(result)) {299ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction ids [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");300}301}302303// Remove render models that are no longer tracked304LocalVector<XrRenderModelIdEXT> erase_ids;305for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {306if (!render_model_interaction_ids.has(e.key)) {307if (e.value.is_valid()) {308render_model_destroy(e.value);309}310311erase_ids.push_back(e.key);312}313}314315// Remove these from our hashmap316for (const XrRenderModelIdEXT &id : erase_ids) {317interaction_render_models.erase(id);318}319320// Now update our models321for (const XrRenderModelIdEXT &id : render_model_interaction_ids) {322if (!interaction_render_models.has(id)) {323// Even if this fails we add it so we don't repeat trying to create it324interaction_render_models[id] = render_model_create(id);325}326}327328return true;329}330331bool OpenXRRenderModelExtension::has_render_model(RID p_render_model) const {332return render_model_owner.owns(p_render_model);333}334335RID OpenXRRenderModelExtension::render_model_create(XrRenderModelIdEXT p_render_model_id) {336ERR_FAIL_COND_V_MSG(!render_model_ext, RID(), "Render model extension hasn't been enabled.");337338OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();339ERR_FAIL_NULL_V(openxr_api, RID());340341XrSession session = openxr_api->get_session();342ERR_FAIL_COND_V(session == XR_NULL_HANDLE, RID());343344RenderModel render_model;345render_model.xr_render_model_id = p_render_model_id;346347// Get a list of supported glTF extensions.348const HashSet<String> supported_gltf_extensions_hash_set = GLTFDocument::get_supported_gltf_extensions_hashset();349Vector<CharString> supported_gltf_extensions_char_string; // Just for temp storage of our c-strings.350supported_gltf_extensions_char_string.resize(supported_gltf_extensions_hash_set.size());351int64_t supported_gltf_extension_index = 0;352for (const String &ext : supported_gltf_extensions_hash_set) {353supported_gltf_extensions_char_string.set(supported_gltf_extension_index, ext.utf8());354supported_gltf_extension_index++;355}356// Now we can convert them to the `const char *` format.357Vector<const char *> supported_gltf_extensions;358supported_gltf_extensions.resize(supported_gltf_extensions_char_string.size());359for (int64_t i = 0; i < supported_gltf_extensions_char_string.size(); i++) {360supported_gltf_extensions.write[i] = supported_gltf_extensions_char_string[i].get_data();361}362363XrRenderModelCreateInfoEXT create_info = {364XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, // type365nullptr, // next366p_render_model_id, // renderModelId367uint32_t(supported_gltf_extensions.size()), // gltfExtensionCount368supported_gltf_extensions.ptr(), // gltfExtensions369};370371XrResult result = xrCreateRenderModelEXT(session, &create_info, &render_model.xr_render_model);372if (XR_FAILED(result)) {373ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");374}375376XrRenderModelPropertiesGetInfoEXT properties_info = {377XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, // type378nullptr, // next379};380381XrRenderModelPropertiesEXT properties;382result = xrGetRenderModelPropertiesEXT(render_model.xr_render_model, &properties_info, &properties);383if (XR_FAILED(result)) {384ERR_PRINT("OpenXR: Failed to get render model properties [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");385} else {386render_model.animatable_node_count = properties.animatableNodeCount;387render_model.render_model_data = _get_render_model_data(properties.cacheId, properties.animatableNodeCount);388}389390// Create space for positioning our asset.391XrRenderModelSpaceCreateInfoEXT space_create_info = {392XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, // type393nullptr, // next394render_model.xr_render_model // renderModel395};396397result = xrCreateRenderModelSpaceEXT(session, &space_create_info, &render_model.xr_space);398if (XR_FAILED(result)) {399ERR_PRINT("OpenXR: Failed to create render model space [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");400}401402if (render_model.animatable_node_count > 0) {403render_model.node_states.resize(render_model.animatable_node_count);404}405406RID new_rid = render_model_owner.make_rid(render_model);407408emit_signal(SNAME("render_model_added"), new_rid);409410return new_rid;411}412413RID OpenXRRenderModelExtension::_render_model_create(uint64_t p_render_model_id) {414RID ret;415416ERR_FAIL_COND_V(p_render_model_id == XR_NULL_RENDER_MODEL_ID_EXT, ret);417418if (is_active()) {419ret = render_model_create(XrRenderModelIdEXT(p_render_model_id));420}421422return ret;423}424425void OpenXRRenderModelExtension::render_model_destroy(RID p_render_model) {426ERR_FAIL_COND_MSG(!render_model_ext, "Render model extension hasn't been enabled.");427428RenderModel *render_model = render_model_owner.get_or_null(p_render_model);429ERR_FAIL_NULL(render_model);430431emit_signal(SNAME("render_model_removed"), p_render_model);432433// Clean up.434if (render_model->xr_space != XR_NULL_HANDLE) {435xrDestroySpace(render_model->xr_space);436}437438render_model->node_states.clear();439440// And destroy our model.441XrResult result = xrDestroyRenderModelEXT(render_model->xr_render_model);442if (XR_FAILED(result)) {443ERR_PRINT("OpenXR: Failed to destroy render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");444}445446render_model_owner.free(p_render_model);447}448449TypedArray<RID> OpenXRRenderModelExtension::render_model_get_all() {450TypedArray<RID> ret;451452LocalVector<RID> rids = render_model_owner.get_owned_list();453454for (const RID &rid : rids) {455ret.push_back(rid);456}457458return ret;459}460461Node3D *OpenXRRenderModelExtension::render_model_new_scene_instance(RID p_render_model) const {462RenderModel *render_model = render_model_owner.get_or_null(p_render_model);463ERR_FAIL_NULL_V(render_model, nullptr);464465if (render_model->render_model_data.is_null()) {466// We never loaded it (don't spam errors here).467return nullptr;468}469470return render_model->render_model_data->new_scene_instance();471}472473PackedStringArray OpenXRRenderModelExtension::render_model_get_subaction_paths(RID p_render_model) {474OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();475ERR_FAIL_NULL_V(openxr_api, PackedStringArray());476477XrInstance instance = openxr_api->get_instance();478ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, PackedStringArray());479480RenderModel *render_model = render_model_owner.get_or_null(p_render_model);481ERR_FAIL_NULL_V(render_model, PackedStringArray());482483PackedStringArray subaction_paths;484485XrInteractionRenderModelSubactionPathInfoEXT subaction_info = {486XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, // type487nullptr, // next488};489490uint32_t capacity;491492XrResult result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, 0, &capacity, nullptr);493if (XR_FAILED(result)) {494ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction path count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");495}496497if (capacity > 0) {498LocalVector<XrPath> paths;499500paths.resize(capacity);501502result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, capacity, &capacity, paths.ptr());503if (XR_FAILED(result)) {504ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction paths [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");505}506507for (uint32_t i = 0; i < capacity; i++) {508char buffer[1024];509uint32_t size = 0;510xrPathToString(instance, paths[i], 1024, &size, buffer);511if (size > 0) {512subaction_paths.push_back(String(buffer));513}514}515}516517return subaction_paths;518}519520XrPath OpenXRRenderModelExtension::render_model_get_top_level_path(RID p_render_model) const {521RenderModel *render_model = render_model_owner.get_or_null(p_render_model);522ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);523524return render_model->top_level_path;525}526527String OpenXRRenderModelExtension::render_model_get_top_level_path_as_string(RID p_render_model) const {528String ret;529530OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();531ERR_FAIL_NULL_V(openxr_api, ret);532533if (is_active() && has_render_model(p_render_model)) {534XrPath path = render_model_get_top_level_path(p_render_model);535if (path == XR_NULL_PATH) {536return "None";537} else {538return openxr_api->get_xr_path_name(path);539}540}541542return ret;543}544545XRPose::TrackingConfidence OpenXRRenderModelExtension::render_model_get_confidence(RID p_render_model) const {546RenderModel *render_model = render_model_owner.get_or_null(p_render_model);547ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);548549return render_model->confidence;550}551552Transform3D OpenXRRenderModelExtension::render_model_get_root_transform(RID p_render_model) const {553XRServer *xr_server = XRServer::get_singleton();554ERR_FAIL_NULL_V(xr_server, Transform3D());555556RenderModel *render_model = render_model_owner.get_or_null(p_render_model);557ERR_FAIL_NULL_V(render_model, Transform3D());558559// Scale our root transform560real_t world_scale = xr_server->get_world_scale();561Transform3D root_transform = render_model->root_transform.scaled(Vector3(world_scale, world_scale, world_scale));562563return xr_server->get_reference_frame() * root_transform;564}565566uint32_t OpenXRRenderModelExtension::render_model_get_animatable_node_count(RID p_render_model) const {567RenderModel *render_model = render_model_owner.get_or_null(p_render_model);568ERR_FAIL_NULL_V(render_model, 0);569570return render_model->animatable_node_count;571}572573String OpenXRRenderModelExtension::render_model_get_animatable_node_name(RID p_render_model, uint32_t p_index) const {574RenderModel *render_model = render_model_owner.get_or_null(p_render_model);575ERR_FAIL_NULL_V(render_model, String());576577if (render_model->render_model_data.is_null()) {578// We never loaded it (don't spam errors here).579return String();580}581582return render_model->render_model_data->get_node_name(p_index);583}584585bool OpenXRRenderModelExtension::render_model_is_animatable_node_visible(RID p_render_model, uint32_t p_index) const {586RenderModel *render_model = render_model_owner.get_or_null(p_render_model);587ERR_FAIL_NULL_V(render_model, false);588589ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, false);590591if (render_model->node_states.is_empty()) {592// Never allocated (don't spam errors here).593return false;594}595596return render_model->node_states[p_index].isVisible;597}598599Transform3D OpenXRRenderModelExtension::render_model_get_animatable_node_transform(RID p_render_model, uint32_t p_index) const {600OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();601ERR_FAIL_NULL_V(openxr_api, Transform3D());602603RenderModel *render_model = render_model_owner.get_or_null(p_render_model);604ERR_FAIL_NULL_V(render_model, Transform3D());605606ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, Transform3D());607608if (render_model->node_states.is_empty()) {609// Never allocated (don't spam errors here).610return Transform3D();611}612613return openxr_api->transform_from_pose(render_model->node_states[p_index].nodePose);614}615616Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_get_render_model_data(XrUuidEXT p_cache_id, uint32_t p_animatable_node_count) {617if (render_model_data_cache.has(p_cache_id)) {618return render_model_data_cache[p_cache_id];619}620621// We don't have this cached, lets load it up622623OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();624ERR_FAIL_NULL_V(openxr_api, nullptr);625626XrSession session = openxr_api->get_session();627ERR_FAIL_COND_V(session == XR_NULL_HANDLE, nullptr);628629XrRenderModelAssetEXT asset;630631XrRenderModelAssetCreateInfoEXT create_info = {632XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, // type633nullptr, // next634p_cache_id // cacheId635};636637XrResult result = xrCreateRenderModelAssetEXT(session, &create_info, &asset);638if (XR_FAILED(result)) {639ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to create render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");640}641642Ref<OpenXRRenderModelData> render_model_data = _load_asset(asset, p_animatable_node_count);643644// We're done with this :)645result = xrDestroyRenderModelAssetEXT(asset);646if (XR_FAILED(result)) {647ERR_PRINT("OpenXR: Failed to destroy render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");648}649650// And cache it651render_model_data_cache[p_cache_id] = render_model_data;652653return render_model_data;654}655656Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_load_asset(XrRenderModelAssetEXT p_asset, uint32_t p_animatable_node_count) {657XrRenderModelAssetDataGetInfoEXT get_info = {658XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, // type659nullptr, // next660};661662XrRenderModelAssetDataEXT asset_data = {663XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, // type664nullptr, // next6650, // bufferCapacityInput;6660, // bufferCountOutput;667nullptr // buffer;668};669670// Obtain required size for the buffer.671XrResult result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);672if (XR_FAILED(result)) {673ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer size [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");674}675ERR_FAIL_COND_V(asset_data.bufferCountOutput == 0, nullptr);676677// Allocate data678PackedByteArray buffer;679buffer.resize(asset_data.bufferCountOutput);680asset_data.buffer = buffer.ptrw();681asset_data.bufferCapacityInput = asset_data.bufferCountOutput;682683// Now get our actual data.684result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);685if (XR_FAILED(result)) {686ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");687}688689// Get the names of any animatable nodes690PackedStringArray node_names;691if (p_animatable_node_count > 0) {692Vector<XrRenderModelAssetNodePropertiesEXT> node_properties;693node_properties.resize(p_animatable_node_count);694695XrRenderModelAssetPropertiesGetInfoEXT properties_info = {696XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, // type697nullptr, // next698};699700XrRenderModelAssetPropertiesEXT asset_properties = {701XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, // type702nullptr, // next703uint32_t(node_properties.size()), // nodePropertyCount704node_properties.ptrw(), // nodeProperties705};706707result = xrGetRenderModelAssetPropertiesEXT(p_asset, &properties_info, &asset_properties);708if (XR_FAILED(result)) {709ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model property info [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");710}711712node_names.resize(p_animatable_node_count);713String *node_names_ptrw = node_names.ptrw();714for (uint32_t i = 0; i < p_animatable_node_count; i++) {715node_names_ptrw[i] = String(node_properties[i].uniqueName);716}717}718719Ref<OpenXRRenderModelData> render_model_data;720render_model_data.instantiate();721722render_model_data->parse_gltf_document(buffer);723render_model_data->set_node_names(node_names);724725return render_model_data;726}727728void OpenXRRenderModelExtension::_clear_render_model_data() {729// Clear our toplevel paths filter.730toplevel_paths.clear();731732// Clear our render model cache.733render_model_data_cache.clear();734735// Loop through all of our render models and destroy them.736LocalVector<RID> owned = render_model_owner.get_owned_list();737for (const RID &rid : owned) {738render_model_destroy(rid);739}740}741742bool OpenXRRenderModelData::parse_gltf_document(const PackedByteArray &p_bytes) {743// State holds our data, document parses GLTF744Ref<GLTFState> new_state;745new_state.instantiate();746Ref<GLTFDocument> new_gltf_document;747new_gltf_document.instantiate();748749Error err = new_gltf_document->append_from_buffer(p_bytes, "", new_state);750if (err != OK) {751ERR_FAIL_V_MSG(false, "OpenXR: Failed to parse GLTF data.");752}753754gltf_document = new_gltf_document;755gltf_state = new_state;756return true;757}758759Node3D *OpenXRRenderModelData::new_scene_instance() {760ERR_FAIL_COND_V(gltf_document.is_null(), nullptr);761ERR_FAIL_COND_V(gltf_state.is_null(), nullptr);762763return Object::cast_to<Node3D>(gltf_document->generate_scene(gltf_state));764}765766void OpenXRRenderModelData::set_node_names(const PackedStringArray &p_node_names) {767node_names = p_node_names;768}769770PackedStringArray OpenXRRenderModelData::get_node_names() const {771return node_names;772}773774const String OpenXRRenderModelData::get_node_name(uint32_t p_node_index) const {775ERR_FAIL_UNSIGNED_INDEX_V(p_node_index, node_names.size(), String());776777return node_names[p_node_index];778}779780OpenXRRenderModelData::OpenXRRenderModelData() {781}782783OpenXRRenderModelData::~OpenXRRenderModelData() {784}785786787