Path: blob/master/modules/openxr/extensions/openxr_render_model_extension.cpp
21179 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#ifdef MODULE_GLTF_ENABLED33#include "../openxr_api.h"34#include "../openxr_interface.h"3536#include "core/config/project_settings.h"37#include "core/string/print_string.h"38#include "servers/xr/xr_server.h"3940OpenXRRenderModelExtension *OpenXRRenderModelExtension::singleton = nullptr;4142OpenXRRenderModelExtension *OpenXRRenderModelExtension::get_singleton() {43return singleton;44}4546void OpenXRRenderModelExtension::_bind_methods() {47ClassDB::bind_method(D_METHOD("is_active"), &OpenXRRenderModelExtension::is_active);48ClassDB::bind_method(D_METHOD("render_model_create", "render_model_id"), &OpenXRRenderModelExtension::render_model_create);49ClassDB::bind_method(D_METHOD("render_model_destroy", "render_model"), &OpenXRRenderModelExtension::render_model_destroy);50ClassDB::bind_method(D_METHOD("render_model_get_all"), &OpenXRRenderModelExtension::render_model_get_all);51ClassDB::bind_method(D_METHOD("render_model_new_scene_instance", "render_model"), &OpenXRRenderModelExtension::render_model_new_scene_instance);52ClassDB::bind_method(D_METHOD("render_model_get_subaction_paths", "render_model"), &OpenXRRenderModelExtension::render_model_get_subaction_paths);53ClassDB::bind_method(D_METHOD("render_model_get_top_level_path", "render_model"), &OpenXRRenderModelExtension::render_model_get_top_level_path_as_string);54ClassDB::bind_method(D_METHOD("render_model_get_confidence", "render_model"), &OpenXRRenderModelExtension::render_model_get_confidence);55ClassDB::bind_method(D_METHOD("render_model_get_root_transform", "render_model"), &OpenXRRenderModelExtension::render_model_get_root_transform);56ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_count", "render_model"), &OpenXRRenderModelExtension::render_model_get_animatable_node_count);57ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_name", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_name);58ClassDB::bind_method(D_METHOD("render_model_is_animatable_node_visible", "render_model", "index"), &OpenXRRenderModelExtension::render_model_is_animatable_node_visible);59ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_transform", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_transform);6061ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::RID, "render_model")));62ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::RID, "render_model")));63ADD_SIGNAL(MethodInfo("render_model_top_level_path_changed", PropertyInfo(Variant::RID, "render_model")));64}6566OpenXRRenderModelExtension::OpenXRRenderModelExtension() {67singleton = this;68}6970OpenXRRenderModelExtension::~OpenXRRenderModelExtension() {71singleton = nullptr;72}7374HashMap<String, bool *> OpenXRRenderModelExtension::get_requested_extensions(XrVersion p_version) {75HashMap<String, bool *> request_extensions;7677if (GLOBAL_GET("xr/openxr/extensions/render_model")) {78if (p_version < XR_API_VERSION_1_1_0) {79// Extension was promoted in OpenXR 1.1, only include it in OpenXR 1.0.80request_extensions[XR_EXT_UUID_EXTENSION_NAME] = &uuid_ext;81}82request_extensions[XR_EXT_RENDER_MODEL_EXTENSION_NAME] = &render_model_ext;83request_extensions[XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME] = &interaction_render_model_ext;84}8586return request_extensions;87}8889void OpenXRRenderModelExtension::on_instance_created(const XrInstance p_instance) {90// Standard entry points we use.91EXT_INIT_XR_FUNC(xrLocateSpace);92EXT_INIT_XR_FUNC(xrDestroySpace);93EXT_INIT_XR_FUNC(xrPathToString);9495if (render_model_ext) {96EXT_INIT_XR_FUNC(xrCreateRenderModelEXT);97EXT_INIT_XR_FUNC(xrDestroyRenderModelEXT);98EXT_INIT_XR_FUNC(xrGetRenderModelPropertiesEXT);99EXT_INIT_XR_FUNC(xrCreateRenderModelSpaceEXT);100EXT_INIT_XR_FUNC(xrCreateRenderModelAssetEXT);101EXT_INIT_XR_FUNC(xrDestroyRenderModelAssetEXT);102EXT_INIT_XR_FUNC(xrGetRenderModelAssetDataEXT);103EXT_INIT_XR_FUNC(xrGetRenderModelAssetPropertiesEXT);104EXT_INIT_XR_FUNC(xrGetRenderModelStateEXT);105}106107if (interaction_render_model_ext) {108EXT_INIT_XR_FUNC(xrEnumerateInteractionRenderModelIdsEXT);109EXT_INIT_XR_FUNC(xrEnumerateRenderModelSubactionPathsEXT);110EXT_INIT_XR_FUNC(xrGetRenderModelPoseTopLevelUserPathEXT);111}112}113114void OpenXRRenderModelExtension::on_session_created(const XrSession p_session) {115_interaction_data_dirty = true;116}117118void OpenXRRenderModelExtension::on_instance_destroyed() {119xrCreateRenderModelEXT_ptr = nullptr;120xrDestroyRenderModelEXT_ptr = nullptr;121xrGetRenderModelPropertiesEXT_ptr = nullptr;122xrCreateRenderModelSpaceEXT_ptr = nullptr;123xrCreateRenderModelAssetEXT_ptr = nullptr;124xrDestroyRenderModelAssetEXT_ptr = nullptr;125xrGetRenderModelAssetDataEXT_ptr = nullptr;126xrGetRenderModelAssetPropertiesEXT_ptr = nullptr;127xrGetRenderModelStateEXT_ptr = nullptr;128xrEnumerateInteractionRenderModelIdsEXT_ptr = nullptr;129xrEnumerateRenderModelSubactionPathsEXT_ptr = nullptr;130xrGetRenderModelPoseTopLevelUserPathEXT_ptr = nullptr;131132uuid_ext = false;133render_model_ext = false;134interaction_render_model_ext = false;135}136137void OpenXRRenderModelExtension::on_session_destroyed() {138_clear_interaction_data();139_clear_render_model_data();140141// We no longer have valid sync data.142xr_sync_has_run = false;143}144145bool OpenXRRenderModelExtension::on_event_polled(const XrEventDataBuffer &event) {146if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) {147// Mark interaction data as dirty so that we update it on sync.148_interaction_data_dirty = true;149150return true;151} else if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) {152// If our controller bindings changed, its likely our render models change too.153// We should be getting a XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT154// but checking for this scenario just in case.155_interaction_data_dirty = true;156157// Do not consider this handled, we simply do additional logic.158return false;159}160161return false;162}163164void OpenXRRenderModelExtension::on_sync_actions() {165if (!is_active()) {166return;167}168169OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();170ERR_FAIL_NULL(openxr_api);171172// Mark sync as run173xr_sync_has_run = true;174175// Update our interaction data if needed176if (_interaction_data_dirty) {177_update_interaction_data();178}179180// Loop through all of our render models to update our space and state info181LocalVector<RID> owned = render_model_owner.get_owned_list();182183for (const RID &rid : owned) {184RenderModel *render_model = render_model_owner.get_or_null(rid);185if (render_model && render_model->xr_space != XR_NULL_HANDLE) {186XrSpaceLocation render_model_location = {187XR_TYPE_SPACE_LOCATION, // type188nullptr, // next1890, // locationFlags190{ { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose191};192193XrResult result = xrLocateSpace(render_model->xr_space, openxr_api->get_play_space(), openxr_api->get_predicted_display_time(), &render_model_location);194ERR_CONTINUE_MSG(XR_FAILED(result), "OpenXR: Failed to locate render model space [" + openxr_api->get_error_string(result) + "]");195196render_model->confidence = openxr_api->transform_from_location(render_model_location, render_model->root_transform);197198if (!render_model->node_states.is_empty()) {199// Get node states.200XrRenderModelStateGetInfoEXT get_state_info = {201XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, // type202nullptr, // next203openxr_api->get_predicted_display_time() // displayTime204};205206XrRenderModelStateEXT state = {207XR_TYPE_RENDER_MODEL_STATE_EXT, // type208nullptr, // next209render_model->animatable_node_count, // nodeStateCount210render_model->node_states.ptr(), // nodeStates211};212213result = xrGetRenderModelStateEXT(render_model->xr_render_model, &get_state_info, &state);214if (XR_FAILED(result)) {215ERR_PRINT("OpenXR: Failed to update node states [" + openxr_api->get_error_string(result) + "]");216}217}218219XrPath new_path = XR_NULL_PATH;220221if (toplevel_paths.is_empty()) {222// Set this up just once with paths we support here.223toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/left"));224toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/right"));225}226227XrInteractionRenderModelTopLevelUserPathGetInfoEXT info = {228XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, // type229nullptr, // next230(uint32_t)toplevel_paths.size(), // topLevelUserPathCount231toplevel_paths.ptr() // topLevelUserPaths232};233result = xrGetRenderModelPoseTopLevelUserPathEXT(render_model->xr_render_model, &info, &new_path);234if (XR_FAILED(result)) {235ERR_PRINT("OpenXR: Failed to update the top level path for render models [" + openxr_api->get_error_string(result) + "]");236} else if (new_path != render_model->top_level_path) {237print_verbose("OpenXR: Render model top level path changed to " + openxr_api->get_xr_path_name(new_path));238239// Set the new path240render_model->top_level_path = new_path;241242// And broadcast it243// Note, converting an XrPath to a String has overhead, so we won't do this automatically.244emit_signal(SNAME("render_model_top_level_path_changed"), rid);245}246}247}248}249250bool OpenXRRenderModelExtension::is_active() const {251return render_model_ext && interaction_render_model_ext;252}253254void OpenXRRenderModelExtension::_clear_interaction_data() {255for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {256render_model_destroy(e.value);257}258interaction_render_models.clear();259}260261bool OpenXRRenderModelExtension::_update_interaction_data() {262ERR_FAIL_COND_V_MSG(!interaction_render_model_ext, false, "Interaction render model extension hasn't been enabled.");263264OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();265ERR_FAIL_NULL_V(openxr_api, false);266267XrSession session = openxr_api->get_session();268ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);269270// Check if syncActions has been run at least once or there is no point in getting data.271if (!xr_sync_has_run) {272// Do not treat this as an error.273return true;274}275276// If we get this far, no longer mark as dirty.277// Else we just repeat the same error over and over again.278_interaction_data_dirty = false;279280// Obtain interaction info.281XrInteractionRenderModelIdsEnumerateInfoEXT interaction_info = {282XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, // type283nullptr, // next284};285286// Obtain count.287uint32_t interaction_count = 0;288XrResult result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, 0, &interaction_count, nullptr);289if (XR_FAILED(result)) {290// not successful? then we do nothing.291ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction id count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");292}293294// Create some storage295LocalVector<XrRenderModelIdEXT> render_model_interaction_ids;296render_model_interaction_ids.resize(interaction_count);297298// Only need to fetch data if there is something to fetch (/we've got storage).299if (!render_model_interaction_ids.is_empty()) {300// Obtain interaction ids301result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, render_model_interaction_ids.size(), &interaction_count, render_model_interaction_ids.ptr());302if (XR_FAILED(result)) {303ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction ids [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");304}305}306307// Remove render models that are no longer tracked308LocalVector<XrRenderModelIdEXT> erase_ids;309for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {310if (!render_model_interaction_ids.has(e.key)) {311if (e.value.is_valid()) {312render_model_destroy(e.value);313}314315erase_ids.push_back(e.key);316}317}318319// Remove these from our hashmap320for (const XrRenderModelIdEXT &id : erase_ids) {321interaction_render_models.erase(id);322}323324// Now update our models325for (const XrRenderModelIdEXT &id : render_model_interaction_ids) {326if (!interaction_render_models.has(id)) {327// Even if this fails we add it so we don't repeat trying to create it328interaction_render_models[id] = render_model_create(id);329}330}331332return true;333}334335bool OpenXRRenderModelExtension::has_render_model(RID p_render_model) const {336return render_model_owner.owns(p_render_model);337}338339RID OpenXRRenderModelExtension::render_model_create(XrRenderModelIdEXT p_render_model_id) {340ERR_FAIL_COND_V_MSG(!render_model_ext, RID(), "Render model extension hasn't been enabled.");341342OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();343ERR_FAIL_NULL_V(openxr_api, RID());344345XrSession session = openxr_api->get_session();346ERR_FAIL_COND_V(session == XR_NULL_HANDLE, RID());347348RenderModel render_model;349render_model.xr_render_model_id = p_render_model_id;350351// Get a list of supported glTF extensions.352const HashSet<String> supported_gltf_extensions_hash_set = GLTFDocument::get_supported_gltf_extensions_hashset();353Vector<CharString> supported_gltf_extensions_char_string; // Just for temp storage of our c-strings.354supported_gltf_extensions_char_string.resize(supported_gltf_extensions_hash_set.size());355int64_t supported_gltf_extension_index = 0;356for (const String &ext : supported_gltf_extensions_hash_set) {357supported_gltf_extensions_char_string.set(supported_gltf_extension_index, ext.utf8());358supported_gltf_extension_index++;359}360// Now we can convert them to the `const char *` format.361Vector<const char *> supported_gltf_extensions;362supported_gltf_extensions.resize(supported_gltf_extensions_char_string.size());363for (int64_t i = 0; i < supported_gltf_extensions_char_string.size(); i++) {364supported_gltf_extensions.write[i] = supported_gltf_extensions_char_string[i].get_data();365}366367XrRenderModelCreateInfoEXT create_info = {368XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, // type369nullptr, // next370p_render_model_id, // renderModelId371uint32_t(supported_gltf_extensions.size()), // gltfExtensionCount372supported_gltf_extensions.ptr(), // gltfExtensions373};374375XrResult result = xrCreateRenderModelEXT(session, &create_info, &render_model.xr_render_model);376if (XR_FAILED(result)) {377ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");378}379380XrRenderModelPropertiesGetInfoEXT properties_info = {381XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, // type382nullptr, // next383};384385XrRenderModelPropertiesEXT properties = {386XR_TYPE_RENDER_MODEL_PROPERTIES_EXT, // type387nullptr, // next388{}, // cacheId3890, // animatableNodeCount390};391392result = xrGetRenderModelPropertiesEXT(render_model.xr_render_model, &properties_info, &properties);393if (XR_FAILED(result)) {394ERR_PRINT("OpenXR: Failed to get render model properties [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");395} else {396render_model.animatable_node_count = properties.animatableNodeCount;397render_model.render_model_data = _get_render_model_data(properties.cacheId, properties.animatableNodeCount);398}399400// Create space for positioning our asset.401XrRenderModelSpaceCreateInfoEXT space_create_info = {402XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, // type403nullptr, // next404render_model.xr_render_model // renderModel405};406407result = xrCreateRenderModelSpaceEXT(session, &space_create_info, &render_model.xr_space);408if (XR_FAILED(result)) {409ERR_PRINT("OpenXR: Failed to create render model space [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");410}411412if (render_model.animatable_node_count > 0) {413render_model.node_states.resize(render_model.animatable_node_count);414}415416RID new_rid = render_model_owner.make_rid(render_model);417418emit_signal(SNAME("render_model_added"), new_rid);419420return new_rid;421}422423RID OpenXRRenderModelExtension::_render_model_create(uint64_t p_render_model_id) {424RID ret;425426ERR_FAIL_COND_V(p_render_model_id == XR_NULL_RENDER_MODEL_ID_EXT, ret);427428if (is_active()) {429ret = render_model_create(XrRenderModelIdEXT(p_render_model_id));430}431432return ret;433}434435void OpenXRRenderModelExtension::render_model_destroy(RID p_render_model) {436ERR_FAIL_COND_MSG(!render_model_ext, "Render model extension hasn't been enabled.");437438RenderModel *render_model = render_model_owner.get_or_null(p_render_model);439ERR_FAIL_NULL(render_model);440441emit_signal(SNAME("render_model_removed"), p_render_model);442443// Clean up.444if (render_model->xr_space != XR_NULL_HANDLE) {445xrDestroySpace(render_model->xr_space);446}447448render_model->node_states.clear();449450// And destroy our model.451XrResult result = xrDestroyRenderModelEXT(render_model->xr_render_model);452if (XR_FAILED(result)) {453ERR_PRINT("OpenXR: Failed to destroy render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");454}455456render_model_owner.free(p_render_model);457}458459TypedArray<RID> OpenXRRenderModelExtension::render_model_get_all() {460TypedArray<RID> ret;461462LocalVector<RID> rids = render_model_owner.get_owned_list();463464for (const RID &rid : rids) {465ret.push_back(rid);466}467468return ret;469}470471Node3D *OpenXRRenderModelExtension::render_model_new_scene_instance(RID p_render_model) const {472RenderModel *render_model = render_model_owner.get_or_null(p_render_model);473ERR_FAIL_NULL_V(render_model, nullptr);474475if (render_model->render_model_data.is_null()) {476// We never loaded it (don't spam errors here).477return nullptr;478}479480return render_model->render_model_data->new_scene_instance();481}482483PackedStringArray OpenXRRenderModelExtension::render_model_get_subaction_paths(RID p_render_model) {484OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();485ERR_FAIL_NULL_V(openxr_api, PackedStringArray());486487XrInstance instance = openxr_api->get_instance();488ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, PackedStringArray());489490RenderModel *render_model = render_model_owner.get_or_null(p_render_model);491ERR_FAIL_NULL_V(render_model, PackedStringArray());492493PackedStringArray subaction_paths;494495XrInteractionRenderModelSubactionPathInfoEXT subaction_info = {496XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, // type497nullptr, // next498};499500uint32_t capacity;501502XrResult result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, 0, &capacity, nullptr);503if (XR_FAILED(result)) {504ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction path count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");505}506507if (capacity > 0) {508LocalVector<XrPath> paths;509510paths.resize(capacity);511512result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, capacity, &capacity, paths.ptr());513if (XR_FAILED(result)) {514ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction paths [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");515}516517for (uint32_t i = 0; i < capacity; i++) {518char buffer[1024];519uint32_t size = 0;520xrPathToString(instance, paths[i], 1024, &size, buffer);521if (size > 0) {522subaction_paths.push_back(String(buffer));523}524}525}526527return subaction_paths;528}529530XrPath OpenXRRenderModelExtension::render_model_get_top_level_path(RID p_render_model) const {531RenderModel *render_model = render_model_owner.get_or_null(p_render_model);532ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);533534return render_model->top_level_path;535}536537String OpenXRRenderModelExtension::render_model_get_top_level_path_as_string(RID p_render_model) const {538String ret;539540OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();541ERR_FAIL_NULL_V(openxr_api, ret);542543if (is_active() && has_render_model(p_render_model)) {544XrPath path = render_model_get_top_level_path(p_render_model);545if (path == XR_NULL_PATH) {546return "None";547} else {548return openxr_api->get_xr_path_name(path);549}550}551552return ret;553}554555XRPose::TrackingConfidence OpenXRRenderModelExtension::render_model_get_confidence(RID p_render_model) const {556RenderModel *render_model = render_model_owner.get_or_null(p_render_model);557ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);558559return render_model->confidence;560}561562Transform3D OpenXRRenderModelExtension::render_model_get_root_transform(RID p_render_model) const {563XRServer *xr_server = XRServer::get_singleton();564ERR_FAIL_NULL_V(xr_server, Transform3D());565566RenderModel *render_model = render_model_owner.get_or_null(p_render_model);567ERR_FAIL_NULL_V(render_model, Transform3D());568569// Scale our root transform570real_t world_scale = xr_server->get_world_scale();571Transform3D root_transform = render_model->root_transform.scaled(Vector3(world_scale, world_scale, world_scale));572573return xr_server->get_reference_frame() * root_transform;574}575576uint32_t OpenXRRenderModelExtension::render_model_get_animatable_node_count(RID p_render_model) const {577RenderModel *render_model = render_model_owner.get_or_null(p_render_model);578ERR_FAIL_NULL_V(render_model, 0);579580return render_model->animatable_node_count;581}582583String OpenXRRenderModelExtension::render_model_get_animatable_node_name(RID p_render_model, uint32_t p_index) const {584RenderModel *render_model = render_model_owner.get_or_null(p_render_model);585ERR_FAIL_NULL_V(render_model, String());586587if (render_model->render_model_data.is_null()) {588// We never loaded it (don't spam errors here).589return String();590}591592return render_model->render_model_data->get_node_name(p_index);593}594595bool OpenXRRenderModelExtension::render_model_is_animatable_node_visible(RID p_render_model, uint32_t p_index) const {596RenderModel *render_model = render_model_owner.get_or_null(p_render_model);597ERR_FAIL_NULL_V(render_model, false);598599ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, false);600601if (render_model->node_states.is_empty()) {602// Never allocated (don't spam errors here).603return false;604}605606return render_model->node_states[p_index].isVisible;607}608609Transform3D OpenXRRenderModelExtension::render_model_get_animatable_node_transform(RID p_render_model, uint32_t p_index) const {610OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();611ERR_FAIL_NULL_V(openxr_api, Transform3D());612613RenderModel *render_model = render_model_owner.get_or_null(p_render_model);614ERR_FAIL_NULL_V(render_model, Transform3D());615616ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, Transform3D());617618if (render_model->node_states.is_empty()) {619// Never allocated (don't spam errors here).620return Transform3D();621}622623return openxr_api->transform_from_pose(render_model->node_states[p_index].nodePose);624}625626Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_get_render_model_data(XrUuidEXT p_cache_id, uint32_t p_animatable_node_count) {627if (render_model_data_cache.has(p_cache_id)) {628return render_model_data_cache[p_cache_id];629}630631// We don't have this cached, lets load it up632633OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();634ERR_FAIL_NULL_V(openxr_api, nullptr);635636XrSession session = openxr_api->get_session();637ERR_FAIL_COND_V(session == XR_NULL_HANDLE, nullptr);638639XrRenderModelAssetEXT asset;640641XrRenderModelAssetCreateInfoEXT create_info = {642XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, // type643nullptr, // next644p_cache_id // cacheId645};646647XrResult result = xrCreateRenderModelAssetEXT(session, &create_info, &asset);648if (XR_FAILED(result)) {649ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to create render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");650}651652Ref<OpenXRRenderModelData> render_model_data = _load_asset(asset, p_animatable_node_count);653654// We're done with this :)655result = xrDestroyRenderModelAssetEXT(asset);656if (XR_FAILED(result)) {657ERR_PRINT("OpenXR: Failed to destroy render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");658}659660// And cache it661render_model_data_cache[p_cache_id] = render_model_data;662663return render_model_data;664}665666Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_load_asset(XrRenderModelAssetEXT p_asset, uint32_t p_animatable_node_count) {667XrRenderModelAssetDataGetInfoEXT get_info = {668XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, // type669nullptr, // next670};671672XrRenderModelAssetDataEXT asset_data = {673XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, // type674nullptr, // next6750, // bufferCapacityInput;6760, // bufferCountOutput;677nullptr // buffer;678};679680// Obtain required size for the buffer.681XrResult result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);682if (XR_FAILED(result)) {683ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer size [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");684}685ERR_FAIL_COND_V(asset_data.bufferCountOutput == 0, nullptr);686687// Allocate data688PackedByteArray buffer;689buffer.resize(asset_data.bufferCountOutput);690asset_data.buffer = buffer.ptrw();691asset_data.bufferCapacityInput = asset_data.bufferCountOutput;692693// Now get our actual data.694result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);695if (XR_FAILED(result)) {696ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");697}698699// Get the names of any animatable nodes700PackedStringArray node_names;701if (p_animatable_node_count > 0) {702Vector<XrRenderModelAssetNodePropertiesEXT> node_properties;703node_properties.resize(p_animatable_node_count);704705XrRenderModelAssetPropertiesGetInfoEXT properties_info = {706XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, // type707nullptr, // next708};709710XrRenderModelAssetPropertiesEXT asset_properties = {711XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, // type712nullptr, // next713uint32_t(node_properties.size()), // nodePropertyCount714node_properties.ptrw(), // nodeProperties715};716717result = xrGetRenderModelAssetPropertiesEXT(p_asset, &properties_info, &asset_properties);718if (XR_FAILED(result)) {719ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model property info [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");720}721722node_names.resize(p_animatable_node_count);723String *node_names_ptrw = node_names.ptrw();724for (uint32_t i = 0; i < p_animatable_node_count; i++) {725node_names_ptrw[i] = String(node_properties[i].uniqueName);726}727}728729Ref<OpenXRRenderModelData> render_model_data;730render_model_data.instantiate();731732render_model_data->parse_gltf_document(buffer);733render_model_data->set_node_names(node_names);734735return render_model_data;736}737738void OpenXRRenderModelExtension::_clear_render_model_data() {739// Clear our toplevel paths filter.740toplevel_paths.clear();741742// Clear our render model cache.743render_model_data_cache.clear();744745// Loop through all of our render models and destroy them.746LocalVector<RID> owned = render_model_owner.get_owned_list();747for (const RID &rid : owned) {748render_model_destroy(rid);749}750}751752bool OpenXRRenderModelData::parse_gltf_document(const PackedByteArray &p_bytes) {753// State holds our data, document parses GLTF754Ref<GLTFState> new_state;755new_state.instantiate();756Ref<GLTFDocument> new_gltf_document;757new_gltf_document.instantiate();758759Error err = new_gltf_document->append_from_buffer(p_bytes, "", new_state);760if (err != OK) {761ERR_FAIL_V_MSG(false, "OpenXR: Failed to parse GLTF data.");762}763764gltf_document = new_gltf_document;765gltf_state = new_state;766return true;767}768769Node3D *OpenXRRenderModelData::new_scene_instance() {770ERR_FAIL_COND_V(gltf_document.is_null(), nullptr);771ERR_FAIL_COND_V(gltf_state.is_null(), nullptr);772773return Object::cast_to<Node3D>(gltf_document->generate_scene(gltf_state));774}775776void OpenXRRenderModelData::set_node_names(const PackedStringArray &p_node_names) {777node_names = p_node_names;778}779780PackedStringArray OpenXRRenderModelData::get_node_names() const {781return node_names;782}783784const String OpenXRRenderModelData::get_node_name(uint32_t p_node_index) const {785ERR_FAIL_UNSIGNED_INDEX_V(p_node_index, node_names.size(), String());786787return node_names[p_node_index];788}789790OpenXRRenderModelData::OpenXRRenderModelData() {791}792793OpenXRRenderModelData::~OpenXRRenderModelData() {794}795#endif // MODULE_GLTF_ENABLED796797798