mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
1215 lines
49 KiB
C++
1215 lines
49 KiB
C++
/**************************************************************************/
|
|
/* openxr_spatial_entity_extension.cpp */
|
|
/**************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* https://godotengine.org */
|
|
/**************************************************************************/
|
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* 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 "openxr_spatial_entity_extension.h"
|
|
|
|
#include "../../openxr_api.h"
|
|
#include "core/config/project_settings.h"
|
|
#include "servers/xr_server.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// OpenXRSpatialEntityExtension
|
|
|
|
OpenXRSpatialEntityExtension *OpenXRSpatialEntityExtension::singleton = nullptr;
|
|
|
|
OpenXRSpatialEntityExtension *OpenXRSpatialEntityExtension::get_singleton() {
|
|
return singleton;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("supports_capability", "capability"), &OpenXRSpatialEntityExtension::_supports_capability);
|
|
ClassDB::bind_method(D_METHOD("supports_component_type", "capability", "component_type"), &OpenXRSpatialEntityExtension::_supports_component_type);
|
|
|
|
ClassDB::bind_method(D_METHOD("create_spatial_context", "capability_configurations", "next", "user_callback"), &OpenXRSpatialEntityExtension::create_spatial_context, DEFVAL(Variant()), DEFVAL(Callable()));
|
|
ClassDB::bind_method(D_METHOD("get_spatial_context_ready", "spatial_context"), &OpenXRSpatialEntityExtension::get_spatial_context_ready);
|
|
ClassDB::bind_method(D_METHOD("free_spatial_context", "spatial_context"), &OpenXRSpatialEntityExtension::free_spatial_context);
|
|
ClassDB::bind_method(D_METHOD("get_spatial_context_handle", "spatial_context"), &OpenXRSpatialEntityExtension::_get_spatial_context_handle);
|
|
|
|
ADD_SIGNAL(MethodInfo("spatial_discovery_recommended", PropertyInfo(Variant::RID, "spatial_context")));
|
|
|
|
// Component_types should be an int array typed to ComponentType(XrSpatialComponentTypeEXT), but we currently don't support that.
|
|
ClassDB::bind_method(D_METHOD("discover_spatial_entities", "spatial_context", "component_types", "next", "user_callback"), &OpenXRSpatialEntityExtension::_discover_spatial_entities, DEFVAL(Variant()), DEFVAL(Callable()));
|
|
ClassDB::bind_method(D_METHOD("update_spatial_entities", "spatial_context", "entities", "component_types", "next"), &OpenXRSpatialEntityExtension::_update_spatial_entities, DEFVAL(Variant()));
|
|
|
|
ClassDB::bind_method(D_METHOD("free_spatial_snapshot", "spatial_snapshot"), &OpenXRSpatialEntityExtension::free_spatial_snapshot);
|
|
ClassDB::bind_method(D_METHOD("get_spatial_snapshot_handle", "spatial_snapshot"), &OpenXRSpatialEntityExtension::_get_spatial_snapshot_handle);
|
|
ClassDB::bind_method(D_METHOD("get_spatial_snapshot_context", "spatial_snapshot"), &OpenXRSpatialEntityExtension::get_spatial_snapshot_context);
|
|
ClassDB::bind_method(D_METHOD("query_snapshot", "spatial_snapshot", "component_data", "next"), &OpenXRSpatialEntityExtension::query_snapshot, DEFVAL(Variant()));
|
|
|
|
ClassDB::bind_method(D_METHOD("get_string", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_string);
|
|
ClassDB::bind_method(D_METHOD("get_uint8_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_uint8_buffer);
|
|
ClassDB::bind_method(D_METHOD("get_uint16_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_uint16_buffer);
|
|
ClassDB::bind_method(D_METHOD("get_uint32_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_uint32_buffer);
|
|
ClassDB::bind_method(D_METHOD("get_float_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_float_buffer);
|
|
ClassDB::bind_method(D_METHOD("get_vector2_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_vector2_buffer);
|
|
ClassDB::bind_method(D_METHOD("get_vector3_buffer", "spatial_snapshot", "buffer_id"), &OpenXRSpatialEntityExtension::_get_vector3_buffer);
|
|
|
|
ClassDB::bind_method(D_METHOD("find_spatial_entity", "entity_id"), &OpenXRSpatialEntityExtension::_find_entity);
|
|
ClassDB::bind_method(D_METHOD("add_spatial_entity", "spatial_context", "entity_id", "entity"), &OpenXRSpatialEntityExtension::_add_entity);
|
|
ClassDB::bind_method(D_METHOD("make_spatial_entity", "spatial_context", "entity_id"), &OpenXRSpatialEntityExtension::_make_entity);
|
|
ClassDB::bind_method(D_METHOD("get_spatial_entity_id", "entity"), &OpenXRSpatialEntityExtension::_get_entity_id);
|
|
ClassDB::bind_method(D_METHOD("get_spatial_entity_context", "entity"), &OpenXRSpatialEntityExtension::get_spatial_entity_context);
|
|
ClassDB::bind_method(D_METHOD("free_spatial_entity", "entity"), &OpenXRSpatialEntityExtension::free_spatial_entity);
|
|
|
|
BIND_ENUM_CONSTANT(CAPABILITY_PLANE_TRACKING);
|
|
BIND_ENUM_CONSTANT(CAPABILITY_MARKER_TRACKING_QR_CODE);
|
|
BIND_ENUM_CONSTANT(CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE);
|
|
BIND_ENUM_CONSTANT(CAPABILITY_MARKER_TRACKING_ARUCO_MARKER);
|
|
BIND_ENUM_CONSTANT(CAPABILITY_MARKER_TRACKING_APRIL_TAG);
|
|
BIND_ENUM_CONSTANT(CAPABILITY_ANCHOR);
|
|
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_BOUNDED_2D);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_BOUNDED_3D);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_PARENT);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_MESH_3D);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_PLANE_ALIGNMENT);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_MESH_2D);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_POLYGON_2D);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_PLANE_SEMANTIC_LABEL);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_MARKER);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_ANCHOR);
|
|
BIND_ENUM_CONSTANT(COMPONENT_TYPE_PERSISTENCE);
|
|
}
|
|
|
|
OpenXRSpatialEntityExtension::OpenXRSpatialEntityExtension() {
|
|
singleton = this;
|
|
}
|
|
|
|
OpenXRSpatialEntityExtension::~OpenXRSpatialEntityExtension() {
|
|
singleton = nullptr;
|
|
}
|
|
|
|
HashMap<String, bool *> OpenXRSpatialEntityExtension::get_requested_extensions() {
|
|
HashMap<String, bool *> request_extensions;
|
|
|
|
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enabled")) {
|
|
request_extensions[XR_EXT_SPATIAL_ENTITY_EXTENSION_NAME] = &spatial_entity_ext;
|
|
}
|
|
|
|
return request_extensions;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::on_instance_created(const XrInstance p_instance) {
|
|
if (spatial_entity_ext) {
|
|
EXT_INIT_XR_FUNC(xrEnumerateSpatialCapabilitiesEXT);
|
|
EXT_INIT_XR_FUNC(xrEnumerateSpatialCapabilityComponentTypesEXT);
|
|
EXT_INIT_XR_FUNC(xrEnumerateSpatialCapabilityFeaturesEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialContextAsyncEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialContextCompleteEXT);
|
|
EXT_INIT_XR_FUNC(xrDestroySpatialContextEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialDiscoverySnapshotAsyncEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialDiscoverySnapshotCompleteEXT);
|
|
EXT_INIT_XR_FUNC(xrQuerySpatialComponentDataEXT);
|
|
EXT_INIT_XR_FUNC(xrDestroySpatialSnapshotEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialEntityFromIdEXT);
|
|
EXT_INIT_XR_FUNC(xrDestroySpatialEntityEXT);
|
|
EXT_INIT_XR_FUNC(xrCreateSpatialUpdateSnapshotEXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferStringEXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferUint8EXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferUint16EXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferUint32EXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferFloatEXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferVector2fEXT);
|
|
EXT_INIT_XR_FUNC(xrGetSpatialBufferVector3fEXT);
|
|
}
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::on_instance_destroyed() {
|
|
supported_capabilities.clear();
|
|
capabilities_load_state = 0;
|
|
|
|
xrEnumerateSpatialCapabilitiesEXT_ptr = nullptr;
|
|
xrEnumerateSpatialCapabilityComponentTypesEXT_ptr = nullptr;
|
|
xrEnumerateSpatialCapabilityFeaturesEXT_ptr = nullptr;
|
|
xrCreateSpatialContextAsyncEXT_ptr = nullptr;
|
|
xrCreateSpatialContextCompleteEXT_ptr = nullptr;
|
|
xrDestroySpatialContextEXT_ptr = nullptr;
|
|
xrCreateSpatialDiscoverySnapshotAsyncEXT_ptr = nullptr;
|
|
xrCreateSpatialDiscoverySnapshotCompleteEXT_ptr = nullptr;
|
|
xrQuerySpatialComponentDataEXT_ptr = nullptr;
|
|
xrDestroySpatialSnapshotEXT_ptr = nullptr;
|
|
xrCreateSpatialEntityFromIdEXT_ptr = nullptr;
|
|
xrDestroySpatialEntityEXT_ptr = nullptr;
|
|
xrCreateSpatialUpdateSnapshotEXT_ptr = nullptr;
|
|
xrGetSpatialBufferStringEXT_ptr = nullptr;
|
|
xrGetSpatialBufferUint8EXT_ptr = nullptr;
|
|
xrGetSpatialBufferUint16EXT_ptr = nullptr;
|
|
xrGetSpatialBufferUint32EXT_ptr = nullptr;
|
|
xrGetSpatialBufferFloatEXT_ptr = nullptr;
|
|
xrGetSpatialBufferVector2fEXT_ptr = nullptr;
|
|
xrGetSpatialBufferVector3fEXT_ptr = nullptr;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::on_session_destroyed() {
|
|
if (!get_active()) {
|
|
return;
|
|
}
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
// Cleanup remaining entity RIDs.
|
|
LocalVector<RID> spatial_entity_rids = spatial_entity_owner.get_owned_list();
|
|
for (const RID &rid : spatial_entity_rids) {
|
|
if (is_print_verbose_enabled()) {
|
|
SpatialEntityData *spatial_entity_data = spatial_entity_owner.get_or_null(rid);
|
|
if (spatial_entity_data) { // Should never be nullptr seeing we called get_owned_list just now, but just in case.
|
|
print_line("OpenXR: Found orphaned spatial entity with ID ", String::num_int64(spatial_entity_data->entity_id));
|
|
}
|
|
}
|
|
|
|
free_spatial_entity(rid);
|
|
}
|
|
|
|
// Cleanup remaining snapshot RIDs.
|
|
LocalVector<RID> spatial_snapshot_rids = spatial_snapshot_owner.get_owned_list();
|
|
if (!spatial_snapshot_rids.is_empty()) {
|
|
print_verbose("OpenXR: Found " + String::num_int64(spatial_snapshot_rids.size()) + " orphaned spatial snapshots"); // Don't have useful data to report here so just report count.
|
|
for (const RID &rid : spatial_snapshot_rids) {
|
|
free_spatial_snapshot(rid);
|
|
}
|
|
}
|
|
|
|
// Clean up all remaining spatial context RIDs.
|
|
LocalVector<RID> spatial_context_rids = spatial_context_owner.get_owned_list();
|
|
if (!spatial_context_rids.is_empty()) {
|
|
print_verbose("OpenXR: Found " + String::num_int64(spatial_context_rids.size()) + " orphaned spatial contexts"); // Don't have useful data to report here so just report count.
|
|
for (const RID &rid : spatial_context_rids) {
|
|
free_spatial_context(rid);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::get_active() const {
|
|
return spatial_entity_ext;
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::_load_capabilities() {
|
|
if (capabilities_load_state == 0) {
|
|
if (!spatial_entity_ext) {
|
|
return false;
|
|
}
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
|
XrInstance instance = openxr_api->get_instance();
|
|
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
|
|
XrSystemId system_id = openxr_api->get_system_id();
|
|
ERR_FAIL_COND_V(system_id == 0, false);
|
|
|
|
// If we fail before this point, this may be called too early.
|
|
// Assume failure so we don't keep trying this unless we succeed.
|
|
capabilities_load_state = 2;
|
|
|
|
// Check our capabilities.
|
|
Vector<XrSpatialCapabilityEXT> capabilities;
|
|
uint32_t capability_size = 0;
|
|
XrResult result = xrEnumerateSpatialCapabilitiesEXT(instance, system_id, 0, &capability_size, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? then exit.
|
|
ERR_FAIL_V_MSG(false, "OpenXR: Failed to get spatial entity capability count [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
if (capability_size > 0) {
|
|
capabilities.resize(capability_size);
|
|
result = xrEnumerateSpatialCapabilitiesEXT(instance, system_id, capabilities.size(), &capability_size, capabilities.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? then exit.
|
|
ERR_FAIL_V_MSG(false, "OpenXR: Failed to get spatial entity capabilities [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
// Loop through capabilities
|
|
for (const XrSpatialCapabilityEXT &capability : capabilities) {
|
|
print_verbose("OpenXR: Found spatial entity capability " + get_spatial_capability_name(capability) + ".");
|
|
|
|
SpatialEntityCapabality &spatial_entity_capability = supported_capabilities[capability];
|
|
|
|
// retrieve component types for this capability
|
|
XrSpatialCapabilityComponentTypesEXT component_types = {
|
|
XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT, // type
|
|
nullptr, // next
|
|
0, // componentTypeCapacityInput
|
|
0, // componentTypeCountOutput
|
|
nullptr // componentTypes
|
|
};
|
|
result = xrEnumerateSpatialCapabilityComponentTypesEXT(instance, system_id, capability, &component_types);
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? just keep going.
|
|
ERR_PRINT("OpenXR: Failed to get spatial entity component type count [" + openxr_api->get_error_string(result) + "]");
|
|
} else if (component_types.componentTypeCountOutput > 0) {
|
|
spatial_entity_capability.component_types.resize(component_types.componentTypeCountOutput);
|
|
component_types.componentTypeCapacityInput = spatial_entity_capability.component_types.size();
|
|
component_types.componentTypeCountOutput = 0;
|
|
component_types.componentTypes = spatial_entity_capability.component_types.ptrw();
|
|
result = xrEnumerateSpatialCapabilityComponentTypesEXT(instance, system_id, capability, &component_types);
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? just keep going.
|
|
ERR_PRINT("OpenXR: Failed to get spatial entity component types [" + openxr_api->get_error_string(result) + "]");
|
|
} else if (is_print_verbose_enabled()) {
|
|
for (const XrSpatialComponentTypeEXT &component_type : spatial_entity_capability.component_types) {
|
|
print_verbose("- component type " + get_spatial_component_type_name(component_type));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieve features for this capability
|
|
result = xrEnumerateSpatialCapabilityFeaturesEXT(instance, system_id, capability, 0, &capability_size, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? just keep going.
|
|
ERR_PRINT("OpenXR: Failed to get spatial entity feature count [" + openxr_api->get_error_string(result) + "]");
|
|
} else if (capability_size > 0) {
|
|
spatial_entity_capability.features.resize(capability_size);
|
|
result = xrEnumerateSpatialCapabilityFeaturesEXT(instance, system_id, capability, spatial_entity_capability.features.size(), &capability_size, spatial_entity_capability.features.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
// Not successful? just keep going.
|
|
ERR_PRINT("OpenXR: Failed to get spatial entity features [" + openxr_api->get_error_string(result) + "]");
|
|
} else if (is_print_verbose_enabled()) {
|
|
for (const XrSpatialCapabilityFeatureEXT &feature : spatial_entity_capability.features) {
|
|
print_verbose("- feature " + get_spatial_feature_name(feature));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
capabilities_load_state = 1; // success!
|
|
}
|
|
|
|
return capabilities_load_state == 1;
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::supports_capability(XrSpatialCapabilityEXT p_capability) {
|
|
if (!_load_capabilities()) {
|
|
return false;
|
|
}
|
|
|
|
return supported_capabilities.has(p_capability);
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::_supports_capability(Capability p_capability) {
|
|
return supports_capability((XrSpatialCapabilityEXT)p_capability);
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::supports_component_type(XrSpatialCapabilityEXT p_capability, XrSpatialComponentTypeEXT p_component_type) {
|
|
if (!_load_capabilities()) {
|
|
return false;
|
|
}
|
|
|
|
if (supported_capabilities.has(p_capability)) {
|
|
return supported_capabilities[p_capability].component_types.has(p_component_type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::_supports_component_type(Capability p_capability, ComponentType p_component_type) {
|
|
return supports_component_type((XrSpatialCapabilityEXT)p_capability, (XrSpatialComponentTypeEXT)p_component_type);
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::on_event_polled(const XrEventDataBuffer &event) {
|
|
if (!get_active()) {
|
|
return false;
|
|
}
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
|
switch (event.type) {
|
|
case XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT: {
|
|
const XrEventDataSpatialDiscoveryRecommendedEXT *eventdata = (const XrEventDataSpatialDiscoveryRecommendedEXT *)&event;
|
|
|
|
// TODO: Should maybe keep a HashMap for a reverse lookup.
|
|
|
|
LocalVector<RID> spatial_context_rids = spatial_context_owner.get_owned_list();
|
|
for (const RID &rid : spatial_context_rids) {
|
|
if (get_spatial_context_handle(rid) == eventdata->spatialContext) {
|
|
emit_signal(SNAME("spatial_discovery_recommended"), rid);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} break;
|
|
default: {
|
|
return false;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Spatial contexts
|
|
|
|
Ref<OpenXRFutureResult> OpenXRSpatialEntityExtension::create_spatial_context(const TypedArray<OpenXRSpatialCapabilityConfigurationBaseHeader> &p_capability_configurations, Ref<OpenXRStructureBase> p_next, const Callable &p_user_callback) {
|
|
if (!get_active()) {
|
|
return nullptr;
|
|
}
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, nullptr);
|
|
|
|
OpenXRFutureExtension *future_api = OpenXRFutureExtension::get_singleton();
|
|
ERR_FAIL_NULL_V(future_api, nullptr);
|
|
|
|
// Parse our configuration.
|
|
Vector<XrSpatialCapabilityConfigurationBaseHeaderEXT *> configuration;
|
|
for (Ref<OpenXRSpatialCapabilityConfigurationBaseHeader> capability_configuration : p_capability_configurations) {
|
|
ERR_FAIL_COND_V(capability_configuration.is_null(), nullptr);
|
|
|
|
XrSpatialCapabilityConfigurationBaseHeaderEXT *config = capability_configuration->get_configuration();
|
|
if (config != nullptr) {
|
|
configuration.push_back(config);
|
|
}
|
|
}
|
|
|
|
void *next = nullptr;
|
|
if (p_next.is_valid()) {
|
|
next = p_next->get_header(next);
|
|
}
|
|
|
|
XrSpatialContextCreateInfoEXT create_info = {
|
|
XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT, // type
|
|
next, // next
|
|
uint32_t(configuration.size()), // capabilityConfigCount
|
|
configuration.is_empty() ? nullptr : configuration.ptr(), // capabilityConfigs
|
|
};
|
|
XrFutureEXT future = XR_NULL_HANDLE;
|
|
XrResult xr_result = xrCreateSpatialContextAsyncEXT(openxr_api->get_session(), &create_info, &future);
|
|
if (XR_FAILED(xr_result)) {
|
|
// Not successful? then exit.
|
|
ERR_FAIL_V_MSG(Ref<OpenXRFutureResult>(), "OpenXR: Failed to create spatial context [" + openxr_api->get_error_string(xr_result) + "]");
|
|
}
|
|
|
|
// Create our future result
|
|
Ref<OpenXRFutureResult> future_result = future_api->register_future(future, callable_mp(this, &OpenXRSpatialEntityExtension::_on_context_creation_ready).bind(p_user_callback));
|
|
|
|
return future_result;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::_on_context_creation_ready(Ref<OpenXRFutureResult> p_future_result, const Callable &p_user_callback) {
|
|
// Complete context creation...
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
XrCreateSpatialContextCompletionEXT completion = {
|
|
XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT, // type
|
|
nullptr, // next
|
|
XR_RESULT_MAX_ENUM, // futureResult
|
|
XR_NULL_HANDLE // spatialContext
|
|
};
|
|
XrResult result = xrCreateSpatialContextCompleteEXT(openxr_api->get_session(), p_future_result->get_future(), &completion);
|
|
if (XR_FAILED(result)) { // Did our xrCreateSpatialContextCompleteEXT call fail?
|
|
// Log issue and fail.
|
|
ERR_FAIL_MSG("OpenXR: Failed to complete spatial context create future [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
if (XR_FAILED(completion.futureResult)) { // Did our completion fail?
|
|
// Log issue and fail.
|
|
ERR_FAIL_MSG("OpenXR: Failed to complete spatial context creation [" + openxr_api->get_error_string(completion.futureResult) + "]");
|
|
}
|
|
|
|
// Wrap our spatial context
|
|
SpatialContextData spatial_context_data;
|
|
spatial_context_data.spatial_context = completion.spatialContext;
|
|
|
|
// Store this as an RID so we keep track of it.
|
|
RID context_rid = spatial_context_owner.make_rid(spatial_context_data);
|
|
|
|
// Set our RID as our result value on our future.
|
|
p_future_result->set_result_value(context_rid);
|
|
|
|
// And perform our callback if we have one.
|
|
if (p_user_callback.is_valid()) {
|
|
p_user_callback.call(context_rid);
|
|
}
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::get_spatial_context_ready(RID p_spatial_context) const {
|
|
SpatialContextData *context_data = spatial_context_owner.get_or_null(p_spatial_context);
|
|
ERR_FAIL_NULL_V(context_data, false);
|
|
|
|
return context_data->spatial_context != XR_NULL_HANDLE;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::free_spatial_context(RID p_spatial_context) {
|
|
SpatialContextData *context_data = spatial_context_owner.get_or_null(p_spatial_context);
|
|
ERR_FAIL_NULL(context_data);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
if (context_data->spatial_context != XR_NULL_HANDLE) {
|
|
// Destroy our spatial context
|
|
XrResult result = xrDestroySpatialContextEXT(context_data->spatial_context);
|
|
if (XR_FAILED(result)) {
|
|
WARN_PRINT("OpenXR: Failed to destroy the spatial context [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
context_data->spatial_context = XR_NULL_HANDLE;
|
|
|
|
// And remove our RID.
|
|
spatial_context_owner.free(p_spatial_context);
|
|
}
|
|
}
|
|
|
|
XrSpatialContextEXT OpenXRSpatialEntityExtension::get_spatial_context_handle(RID p_spatial_context) const {
|
|
SpatialContextData *context_data = spatial_context_owner.get_or_null(p_spatial_context);
|
|
ERR_FAIL_NULL_V(context_data, XR_NULL_HANDLE);
|
|
|
|
return context_data->spatial_context;
|
|
}
|
|
|
|
// For exposing this to GDExtension
|
|
uint64_t OpenXRSpatialEntityExtension::_get_spatial_context_handle(RID p_spatial_context) const {
|
|
return (uint64_t)get_spatial_context_handle(p_spatial_context);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Discovery queries
|
|
|
|
Ref<OpenXRFutureResult> OpenXRSpatialEntityExtension::discover_spatial_entities(RID p_spatial_context, const Vector<XrSpatialComponentTypeEXT> &p_component_types, Ref<OpenXRStructureBase> p_next, const Callable &p_user_callback) {
|
|
if (!get_active()) {
|
|
return nullptr;
|
|
}
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, nullptr);
|
|
|
|
OpenXRFutureExtension *future_api = OpenXRFutureExtension::get_singleton();
|
|
ERR_FAIL_NULL_V(future_api, nullptr);
|
|
|
|
void *next = nullptr;
|
|
if (p_next.is_valid()) {
|
|
next = p_next->get_header(next);
|
|
}
|
|
|
|
// Start our discovery snapshot.
|
|
XrSpatialDiscoverySnapshotCreateInfoEXT create_info = {
|
|
XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT, // type
|
|
next, // next
|
|
(uint32_t)p_component_types.size(), // componentTypeCount
|
|
p_component_types.is_empty() ? nullptr : p_component_types.ptr() // componentTypes
|
|
};
|
|
|
|
XrFutureEXT future;
|
|
XrResult result = xrCreateSpatialDiscoverySnapshotAsyncEXT(get_spatial_context_handle(p_spatial_context), &create_info, &future);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to initiate snapshot discovery [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
// Create our future result
|
|
Ref<OpenXRFutureResult> future_result = future_api->register_future(future, callable_mp(this, &OpenXRSpatialEntityExtension::_on_discovered_spatial_entities).bind(p_spatial_context, p_user_callback));
|
|
|
|
return future_result;
|
|
}
|
|
|
|
// For calls from GDExtension
|
|
Ref<OpenXRFutureResult> OpenXRSpatialEntityExtension::_discover_spatial_entities(RID p_spatial_context, const PackedInt64Array &p_component_types, Ref<OpenXRStructureBase> p_next, const Callable &p_callback) {
|
|
Vector<XrSpatialComponentTypeEXT> component_types;
|
|
component_types.resize(p_component_types.size());
|
|
XrSpatialComponentTypeEXT *ptr = component_types.ptrw();
|
|
for (const int64_t &component_type : p_component_types) {
|
|
*ptr = (XrSpatialComponentTypeEXT)component_type;
|
|
ptr++;
|
|
}
|
|
|
|
return discover_spatial_entities(p_spatial_context, component_types, p_next, p_callback);
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::_on_discovered_spatial_entities(Ref<OpenXRFutureResult> p_future_result, RID p_discovery_spatial_context, const Callable &p_user_callback) {
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
XrSpatialContextEXT xr_spatial_context = get_spatial_context_handle(p_discovery_spatial_context);
|
|
ERR_FAIL_COND(xr_spatial_context == XR_NULL_HANDLE);
|
|
|
|
XrCreateSpatialDiscoverySnapshotCompletionInfoEXT completion_info = {
|
|
XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT, // type
|
|
nullptr, // next
|
|
openxr_api->get_play_space(), // baseSpace
|
|
openxr_api->get_predicted_display_time(), // time
|
|
p_future_result->get_future() // future
|
|
};
|
|
|
|
XrCreateSpatialDiscoverySnapshotCompletionEXT completion = {
|
|
XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT, // type
|
|
nullptr, // next
|
|
XR_SUCCESS, // futureResult
|
|
XR_NULL_HANDLE // snapshot
|
|
};
|
|
XrResult result = xrCreateSpatialDiscoverySnapshotCompleteEXT(xr_spatial_context, &completion_info, &completion);
|
|
|
|
if (XR_FAILED(result)) { // Did our xrCreateSpatialContextCompleteEXT call fail?
|
|
// And log issue.
|
|
ERR_FAIL_MSG("OpenXR: Failed to complete discovery query future [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
if (XR_FAILED(completion.futureResult)) { // Did our completion fail?
|
|
// And log issue.
|
|
ERR_FAIL_MSG("OpenXR: Failed to complete discovery query [" + openxr_api->get_error_string(completion.futureResult) + "]");
|
|
}
|
|
|
|
// Wrap our spatial snapshot
|
|
SpatialSnapshotData snapshot_data;
|
|
snapshot_data.spatial_context = p_discovery_spatial_context;
|
|
snapshot_data.spatial_snapshot = completion.snapshot;
|
|
|
|
// Store this as an RID so we keep track of it.
|
|
RID snapshot_rid = spatial_snapshot_owner.make_rid(snapshot_data);
|
|
|
|
// Set our RID as our result value on our future.
|
|
p_future_result->set_result_value(snapshot_rid);
|
|
|
|
// And perform our callback if we have one.
|
|
if (p_user_callback.is_valid()) {
|
|
p_user_callback.call(snapshot_rid);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Update query
|
|
|
|
RID OpenXRSpatialEntityExtension::update_spatial_entities(RID p_spatial_context, const LocalVector<RID> &p_entities, const LocalVector<XrSpatialComponentTypeEXT> &p_component_types, Ref<OpenXRStructureBase> p_next) {
|
|
if (!get_active()) {
|
|
return RID();
|
|
}
|
|
|
|
ERR_FAIL_COND_V(p_entities.is_empty(), RID());
|
|
|
|
SpatialContextData *context_data = spatial_context_owner.get_or_null(p_spatial_context);
|
|
ERR_FAIL_NULL_V(context_data, RID());
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, RID());
|
|
|
|
// Convert our entity RIDs to XrSpatialEntityEXT
|
|
thread_local LocalVector<XrSpatialEntityEXT> entities;
|
|
|
|
entities.resize(p_entities.size());
|
|
XrSpatialEntityEXT *ptr = entities.ptr();
|
|
for (const RID &rid : p_entities) {
|
|
SpatialEntityData *entity_data = spatial_entity_owner.get_or_null(rid);
|
|
*ptr = entity_data ? entity_data->entity : XR_NULL_HANDLE;
|
|
ptr++;
|
|
}
|
|
|
|
void *next = nullptr;
|
|
if (p_next.is_valid()) {
|
|
next = p_next->get_header(next);
|
|
}
|
|
|
|
SpatialSnapshotData spatial_snapshot_data;
|
|
|
|
// Store the context we used for this discovery query
|
|
spatial_snapshot_data.spatial_context = p_spatial_context;
|
|
|
|
// Do update
|
|
XrSpatialUpdateSnapshotCreateInfoEXT create_info = {
|
|
XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT, // type
|
|
next, // next
|
|
(uint32_t)entities.size(), // entityCount,
|
|
entities.ptr(), // entities
|
|
(uint32_t)p_component_types.size(), // componentTypeCount
|
|
p_component_types.is_empty() ? nullptr : p_component_types.ptr(), // componentTypes
|
|
openxr_api->get_play_space(), // baseSpace
|
|
openxr_api->get_predicted_display_time() // time
|
|
};
|
|
XrResult result = xrCreateSpatialUpdateSnapshotEXT(context_data->spatial_context, &create_info, &spatial_snapshot_data.spatial_snapshot);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create update snapshot [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
// Store our snapshot in an RID and return.
|
|
return spatial_snapshot_owner.make_rid(spatial_snapshot_data);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::_update_spatial_entities(RID p_spatial_context, const TypedArray<RID> &p_entities, const PackedInt64Array &p_component_types, Ref<OpenXRStructureBase> p_next) {
|
|
thread_local LocalVector<RID> entities;
|
|
entities.resize(p_entities.size());
|
|
RID *rids = entities.ptr();
|
|
for (const RID rid : p_entities) {
|
|
*rids = rid;
|
|
rids++;
|
|
}
|
|
|
|
thread_local LocalVector<XrSpatialComponentTypeEXT> component_types;
|
|
component_types.resize(p_component_types.size());
|
|
XrSpatialComponentTypeEXT *ptr = component_types.ptr();
|
|
for (const int64_t &component_type : p_component_types) {
|
|
*ptr = (XrSpatialComponentTypeEXT)component_type;
|
|
ptr++;
|
|
}
|
|
|
|
return update_spatial_entities(p_spatial_context, entities, component_types, p_next);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Snapshot data
|
|
|
|
void OpenXRSpatialEntityExtension::free_spatial_snapshot(RID p_spatial_snapshot) {
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL(snapshot_data);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
if (snapshot_data->spatial_snapshot != XR_NULL_HANDLE) {
|
|
// Destroy our spatial context
|
|
XrResult result = xrDestroySpatialSnapshotEXT(snapshot_data->spatial_snapshot);
|
|
if (XR_FAILED(result)) {
|
|
WARN_PRINT("OpenXR: Failed to destroy the spatial snapshot [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
snapshot_data->spatial_snapshot = XR_NULL_HANDLE;
|
|
}
|
|
|
|
// And remove our RID.
|
|
spatial_snapshot_owner.free(p_spatial_snapshot);
|
|
}
|
|
|
|
XrSpatialSnapshotEXT OpenXRSpatialEntityExtension::get_spatial_snapshot_handle(RID p_spatial_snapshot) const {
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, XR_NULL_HANDLE);
|
|
|
|
return snapshot_data->spatial_snapshot;
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::get_spatial_snapshot_context(RID p_spatial_snapshot) const {
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, RID());
|
|
|
|
return snapshot_data->spatial_context;
|
|
}
|
|
|
|
// For exposing this to GDExtension
|
|
uint64_t OpenXRSpatialEntityExtension::_get_spatial_snapshot_handle(RID p_spatial_snapshot) const {
|
|
return (uint64_t)get_spatial_snapshot_handle(p_spatial_snapshot);
|
|
}
|
|
|
|
bool OpenXRSpatialEntityExtension::query_snapshot(RID p_spatial_snapshot, const TypedArray<OpenXRSpatialComponentData> &p_component_data, Ref<OpenXRStructureBase> p_next) {
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, false);
|
|
|
|
ERR_FAIL_COND_V(p_component_data.is_empty(), false);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
|
Ref<OpenXRSpatialQueryResultData> query_result_data = p_component_data[0];
|
|
ERR_FAIL_COND_V_MSG(query_result_data.is_null(), false, "OpenXR: The first component must be of type OpenXRSpatialQueryResultData");
|
|
|
|
// Gather component types we need to query.
|
|
Vector<XrSpatialComponentTypeEXT> component_types;
|
|
for (Ref<OpenXRSpatialComponentData> component_data : p_component_data) {
|
|
if (component_data.is_valid()) {
|
|
XrSpatialComponentTypeEXT component_type = component_data->get_component_type();
|
|
if (component_type != XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT) {
|
|
component_types.push_back(component_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
void *next = nullptr;
|
|
if (p_next.is_valid()) {
|
|
next = p_next->get_header(next);
|
|
}
|
|
|
|
XrSpatialComponentDataQueryConditionEXT query_condition = {
|
|
XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT, // type
|
|
next, // next
|
|
0, // componentTypeCount
|
|
nullptr // componentTypes
|
|
};
|
|
|
|
query_condition.componentTypeCount = component_types.size();
|
|
query_condition.componentTypes = component_types.ptr();
|
|
|
|
XrSpatialComponentDataQueryResultEXT *query_result = (XrSpatialComponentDataQueryResultEXT *)query_result_data->get_structure_data(nullptr);
|
|
XrResult result = xrQuerySpatialComponentDataEXT(snapshot_data->spatial_snapshot, &query_condition, query_result);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(false, "OpenXR: Failed to query snapshot count [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
// Nothing to do?
|
|
if (query_result->entityIdCountOutput == 0) {
|
|
return true;
|
|
}
|
|
|
|
// This indicates an issue in the XR runtime, we should have a state for every entity so these counts must match.
|
|
ERR_FAIL_COND_V_MSG(query_result->entityIdCountOutput != query_result->entityStateCountOutput, false, "OpenXR: Entity ID count and entity state count don't match!");
|
|
|
|
// Allocate our memory and parse our next structure
|
|
next = nullptr;
|
|
for (Ref<OpenXRSpatialComponentData> component_data : p_component_data) {
|
|
if (component_data.is_valid()) {
|
|
component_data->set_capacity(query_result->entityIdCountOutput);
|
|
XrSpatialComponentTypeEXT component_type = component_data->get_component_type();
|
|
if (component_type != XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT) {
|
|
next = component_data->get_structure_data(next);
|
|
}
|
|
}
|
|
}
|
|
|
|
query_result = (XrSpatialComponentDataQueryResultEXT *)query_result_data->get_structure_data(next);
|
|
result = xrQuerySpatialComponentDataEXT(snapshot_data->spatial_snapshot, &query_condition, query_result);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(false, "OpenXR: Failed to query snapshot data [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Buffers from snapshot
|
|
|
|
String OpenXRSpatialEntityExtension::get_string(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
String ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferStringEXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
LocalVector<char> buffer;
|
|
buffer.resize(count + 1);
|
|
buffer[count] = '\0'; // + 1 and setting a zero terminator just in case runtime is not including this.
|
|
|
|
result = xrGetSpatialBufferStringEXT(snapshot_data->spatial_snapshot, &info, buffer.size(), &count, buffer.ptr());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret = String::utf8(buffer.ptr());
|
|
return ret;
|
|
}
|
|
|
|
PackedByteArray OpenXRSpatialEntityExtension::get_uint8_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
PackedByteArray ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferUint8EXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferUint8EXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, (uint8_t *)ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(PackedByteArray(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Vector<uint16_t> OpenXRSpatialEntityExtension::get_uint16_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
Vector<uint16_t> ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferUint16EXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferUint16EXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(Vector<uint16_t>(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Vector<uint32_t> OpenXRSpatialEntityExtension::get_uint32_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
Vector<uint32_t> ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferUint32EXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferUint32EXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(Vector<uint32_t>(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PackedFloat32Array OpenXRSpatialEntityExtension::get_float_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
PackedFloat32Array ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferFloatEXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferFloatEXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(PackedFloat32Array(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PackedVector2Array OpenXRSpatialEntityExtension::get_vector2_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
PackedVector2Array ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferVector2fEXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
#ifdef REAL_T_IS_DOUBLE
|
|
// OpenXR XrVector2f is using floats, Godot Vector2 is using double, so we need to do a copy.
|
|
LocalVector<XrVector2f> buffer;
|
|
buffer.resize(count);
|
|
|
|
result = xrGetSpatialBufferVector2fEXT(snapshot_data->spatial_snapshot, &info, buffer.size(), &count, buffer.ptr());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
Vector2 *ptr = ret.ptrw();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
ptr[i].x = buffer[i].x;
|
|
ptr[i].y = buffer[i].y;
|
|
}
|
|
#else
|
|
// OpenXR's XrVector2f and Godots Vector2 should be interchangeable.
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferVector2fEXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, (XrVector2f *)ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(PackedVector2Array(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
PackedVector3Array OpenXRSpatialEntityExtension::get_vector3_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const {
|
|
PackedVector3Array ret;
|
|
|
|
SpatialSnapshotData *snapshot_data = spatial_snapshot_owner.get_or_null(p_spatial_snapshot);
|
|
ERR_FAIL_NULL_V(snapshot_data, ret);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, ret);
|
|
|
|
XrSpatialBufferGetInfoEXT info = {
|
|
XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_buffer_id, // bufferId
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
XrResult result = xrGetSpatialBufferVector3fEXT(snapshot_data->spatial_snapshot, &info, 0, &count, nullptr);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer size [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
#ifdef REAL_T_IS_DOUBLE
|
|
// OpenXR XrVector3f is using floats, Godot Vector3 is using double, so we need to do a copy.
|
|
LocalVector<XrVector3f> buffer;
|
|
buffer.resize(count);
|
|
|
|
result = xrGetSpatialBufferVector3fEXT(snapshot_data->spatial_snapshot, &info, buffer.size(), &count, buffer.ptr());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(ret, "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
ret.resize(count);
|
|
Vector3 *ptr = ret.ptrw();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
ptr[i].x = buffer[i].x;
|
|
ptr[i].y = buffer[i].y;
|
|
ptr[i].z = buffer[i].z;
|
|
}
|
|
#else
|
|
// OpenXR's XrVector3f and Godots Vector3 should be interchangeable.
|
|
ret.resize(count);
|
|
|
|
result = xrGetSpatialBufferVector3fEXT(snapshot_data->spatial_snapshot, &info, ret.size(), &count, (XrVector3f *)ret.ptrw());
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(PackedVector3Array(), "OpenXR: Failed to get buffer [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
String OpenXRSpatialEntityExtension::_get_string(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
return get_string(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
}
|
|
|
|
PackedByteArray OpenXRSpatialEntityExtension::_get_uint8_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
return get_uint8_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
}
|
|
|
|
PackedInt32Array OpenXRSpatialEntityExtension::_get_uint16_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
PackedInt32Array ret;
|
|
Vector<uint16_t> buffer = get_uint16_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
|
|
if (!buffer.is_empty()) {
|
|
// We don't have PackedInt16Array so we convert to PackedInt32Array
|
|
|
|
ret.resize(buffer.size());
|
|
|
|
int size = ret.size();
|
|
int32_t *ptr = ret.ptrw();
|
|
for (int i = 0; i < size; i++) {
|
|
ptr[i] = buffer[i];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PackedInt32Array OpenXRSpatialEntityExtension::_get_uint32_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
PackedInt32Array ret;
|
|
Vector<uint32_t> buffer = get_uint32_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
|
|
if (!buffer.is_empty()) {
|
|
// Note, we don't have a UINT32 array that we can use with GDScript and using an INT64 array is overkill.
|
|
// Bit wasteful this but...
|
|
|
|
ret.resize(buffer.size());
|
|
|
|
int size = ret.size();
|
|
int32_t *ptr = ret.ptrw();
|
|
for (int i = 0; i < size; i++) {
|
|
ptr[i] = buffer[i];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PackedFloat32Array OpenXRSpatialEntityExtension::_get_float_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
return get_float_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
}
|
|
|
|
PackedVector2Array OpenXRSpatialEntityExtension::_get_vector2_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
return get_vector2_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
}
|
|
|
|
PackedVector3Array OpenXRSpatialEntityExtension::_get_vector3_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const {
|
|
return get_vector3_buffer(p_spatial_snapshot, (XrSpatialBufferIdEXT)p_buffer_id);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Entities
|
|
|
|
RID OpenXRSpatialEntityExtension::find_spatial_entity(XrSpatialEntityIdEXT p_entity_id) const {
|
|
ERR_FAIL_COND_V(!get_active(), RID());
|
|
|
|
LocalVector<RID> entities = spatial_entity_owner.get_owned_list();
|
|
for (const RID &entity : entities) {
|
|
SpatialEntityData *entity_data = spatial_entity_owner.get_or_null(entity);
|
|
ERR_FAIL_NULL_V(entity_data, RID());
|
|
|
|
if (entity_data->entity_id == p_entity_id) {
|
|
return entity;
|
|
}
|
|
}
|
|
|
|
return RID();
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::_find_entity(uint64_t p_entity_id) {
|
|
return find_spatial_entity((XrSpatialEntityIdEXT)p_entity_id);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::add_spatial_entity(RID p_spatial_context, XrSpatialEntityIdEXT p_entity_id, XrSpatialEntityEXT p_entity) {
|
|
ERR_FAIL_COND_V(!get_active(), RID());
|
|
|
|
// Entity has been created elsewhere, we just register it
|
|
SpatialEntityData spatial_entity_data;
|
|
|
|
spatial_entity_data.spatial_context = p_spatial_context;
|
|
spatial_entity_data.entity_id = p_entity_id;
|
|
spatial_entity_data.entity = p_entity;
|
|
|
|
return spatial_entity_owner.make_rid(spatial_entity_data);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::_add_entity(RID p_spatial_context, uint64_t p_entity_id, uint64_t p_entity) {
|
|
return add_spatial_entity(p_spatial_context, (XrSpatialEntityIdEXT)p_entity_id, (XrSpatialEntityEXT)p_entity);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::make_spatial_entity(RID p_spatial_context, XrSpatialEntityIdEXT p_entity_id) {
|
|
ERR_FAIL_COND_V(!get_active(), RID());
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL_V(openxr_api, RID());
|
|
|
|
SpatialEntityData spatial_entity_data;
|
|
|
|
spatial_entity_data.spatial_context = p_spatial_context;
|
|
spatial_entity_data.entity_id = p_entity_id;
|
|
XrSpatialEntityFromIdCreateInfoEXT create_info = {
|
|
XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT, // type
|
|
nullptr, // next
|
|
p_entity_id //entityId
|
|
};
|
|
XrResult result = xrCreateSpatialEntityFromIdEXT(get_spatial_context_handle(p_spatial_context), &create_info, &spatial_entity_data.entity);
|
|
if (XR_FAILED(result)) {
|
|
ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create spatial entity [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
return spatial_entity_owner.make_rid(spatial_entity_data);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::_make_entity(RID p_spatial_context, uint64_t p_entity_id) {
|
|
return make_spatial_entity(p_spatial_context, (XrSpatialEntityIdEXT)p_entity_id);
|
|
}
|
|
|
|
XrSpatialEntityIdEXT OpenXRSpatialEntityExtension::get_spatial_entity_id(RID p_entity) const {
|
|
SpatialEntityData *entity_data = spatial_entity_owner.get_or_null(p_entity);
|
|
ERR_FAIL_NULL_V(entity_data, XR_NULL_ENTITY);
|
|
|
|
return entity_data->entity_id;
|
|
}
|
|
|
|
uint64_t OpenXRSpatialEntityExtension::_get_entity_id(RID p_entity) const {
|
|
return (uint64_t)get_spatial_entity_id(p_entity);
|
|
}
|
|
|
|
RID OpenXRSpatialEntityExtension::get_spatial_entity_context(RID p_entity) const {
|
|
SpatialEntityData *entity_data = spatial_entity_owner.get_or_null(p_entity);
|
|
ERR_FAIL_NULL_V(entity_data, RID());
|
|
|
|
return entity_data->spatial_context;
|
|
}
|
|
|
|
void OpenXRSpatialEntityExtension::free_spatial_entity(RID p_entity) {
|
|
SpatialEntityData *entity_data = spatial_entity_owner.get_or_null(p_entity);
|
|
ERR_FAIL_NULL(entity_data);
|
|
ERR_FAIL_COND(entity_data->entity == XR_NULL_HANDLE);
|
|
|
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
ERR_FAIL_NULL(openxr_api);
|
|
|
|
XrResult result = xrDestroySpatialEntityEXT_ptr(entity_data->entity);
|
|
if (XR_FAILED(result)) {
|
|
WARN_PRINT("OpenXR: Failed to destroy spatial entity [" + openxr_api->get_error_string(result) + "]");
|
|
}
|
|
|
|
// And remove our RID.
|
|
spatial_entity_owner.free(p_entity);
|
|
}
|
|
|
|
String OpenXRSpatialEntityExtension::get_spatial_capability_name(XrSpatialCapabilityEXT p_capability){
|
|
XR_ENUM_SWITCH(XrSpatialCapabilityEXT, p_capability)
|
|
}
|
|
|
|
String OpenXRSpatialEntityExtension::get_spatial_component_type_name(XrSpatialComponentTypeEXT p_component_type){
|
|
XR_ENUM_SWITCH(XrSpatialComponentTypeEXT, p_component_type)
|
|
}
|
|
|
|
String OpenXRSpatialEntityExtension::get_spatial_feature_name(XrSpatialCapabilityFeatureEXT p_feature) {
|
|
XR_ENUM_SWITCH(XrSpatialCapabilityFeatureEXT, p_feature)
|
|
}
|