OpenXR: Implement spatial entities extension

This commit is contained in:
Bastiaan Olij
2025-01-21 20:03:16 +11:00
parent 9283328fe7
commit eeac57075c
54 changed files with 7550 additions and 17 deletions

View File

@ -7,6 +7,7 @@ Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env_openxr.add_source_files(module_obj, "spatial_entities/*.cpp")
# These are platform dependent
if env["platform"] == "android":

View File

@ -73,7 +73,7 @@ public:
// You should return the pointer to the last struct you define as your result.
// If you are not adding any structs, just return `p_next_pointer`.
// See existing extensions for examples of this implementation.
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we interrogate OpenXRS system abilities.
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we interrogate OpenXR's system abilities.
virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR instance.
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR session.
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when creating OpenXR swap chains.

View File

@ -31,7 +31,7 @@
#pragma once
/*
The OpenXR future extension forms the basis of OpenXRs ability to
The OpenXR future extension forms the basis of OpenXR's ability to
execute logic asynchronously.
Asynchronous functions will return a future object which can be

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
/**************************************************************************/
/* openxr_spatial_anchor.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "../../openxr_util.h"
#include "openxr_spatial_entities.h"
#include "openxr_spatial_entity_extension.h"
// Anchor capability configuration
class OpenXRSpatialCapabilityConfigurationAnchor : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationAnchor, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return anchor_enabled_components; }
protected:
static void _bind_methods();
private:
Vector<XrSpatialComponentTypeEXT> anchor_enabled_components;
XrSpatialCapabilityConfigurationAnchorEXT anchor_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT, nullptr, XR_SPATIAL_CAPABILITY_ANCHOR_EXT, 0, nullptr };
PackedInt64Array _get_enabled_components() const;
};
// Anchor component anchor list
class OpenXRSpatialComponentAnchorList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentAnchorList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_entity_pose(int64_t p_index) const;
private:
Vector<XrPosef> entity_poses;
XrSpatialComponentAnchorListEXT anchor_list = { XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT, nullptr, 0, nullptr };
};
// Persistence configuration
class OpenXRSpatialContextPersistenceConfig : public OpenXRStructureBase {
GDCLASS(OpenXRSpatialContextPersistenceConfig, OpenXRStructureBase);
public:
bool has_valid_configuration() const;
virtual void *get_header(void *p_next) override;
virtual XrStructureType get_structure_type() override;
void add_persistence_context(RID p_persistence_context);
void remove_persistence_context(RID p_persistence_context);
protected:
static void _bind_methods();
private:
Vector<RID> persistence_contexts;
Vector<XrSpatialPersistenceContextEXT> context_handles;
XrSpatialContextPersistenceConfigEXT persistence_config = { XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT, nullptr, 0, nullptr };
};
// Component persistence list
class OpenXRSpatialComponentPersistenceList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentPersistenceList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
XrUuid get_persistent_uuid(int64_t p_index) const;
XrSpatialPersistenceStateEXT get_persistent_state(int64_t p_index) const;
static String get_persistence_state_name(XrSpatialPersistenceStateEXT p_state);
private:
Vector<XrSpatialPersistenceDataEXT> persist_data;
XrSpatialComponentPersistenceListEXT persistence_list = { XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT, nullptr, 0, nullptr };
String _get_persistent_uuid(int64_t p_index) const;
uint64_t _get_persistent_state(int64_t p_index) const;
};
// Anchor tracker, this adds no new logic, it's purely for typing!
class OpenXRAnchorTracker : public OpenXRSpatialEntityTracker {
GDCLASS(OpenXRAnchorTracker, OpenXRSpatialEntityTracker);
protected:
static void _bind_methods();
public:
bool has_uuid() const;
XrUuid get_uuid() const;
void set_uuid(const XrUuid &p_uuid);
private:
XrUuid uuid = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
String _get_uuid() const;
void _set_uuid(const String &p_uuid);
bool uuid_is_equal(const XrUuid &p_a, const XrUuid &p_b);
};
// (Persistent) anchor logic
class OpenXRSpatialAnchorCapability : public OpenXRExtensionWrapper {
GDCLASS(OpenXRSpatialAnchorCapability, OpenXRExtensionWrapper);
public:
enum PersistenceScope {
PERSISTENCE_SCOPE_SYSTEM_MANAGED = XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT,
PERSISTENCE_SCOPE_LOCAL_ANCHORS = XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT,
};
static OpenXRSpatialAnchorCapability *get_singleton();
OpenXRSpatialAnchorCapability();
virtual ~OpenXRSpatialAnchorCapability() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_process() override;
bool is_spatial_anchor_supported();
bool is_spatial_persistence_supported();
// Persistence scopes
bool is_persistence_scope_supported(XrSpatialPersistenceScopeEXT p_scope);
Ref<OpenXRFutureResult> create_persistence_context(XrSpatialPersistenceScopeEXT p_scope, const Callable &p_user_callback = Callable());
XrSpatialPersistenceContextEXT get_persistence_context_handle(RID p_persistence_context) const;
void free_persistence_context(RID p_persistence_context);
Ref<OpenXRAnchorTracker> create_new_anchor(const Transform3D &p_transform, RID p_spatial_context = RID());
void remove_anchor(Ref<OpenXRAnchorTracker> p_anchor_tracker);
Ref<OpenXRFutureResult> persist_anchor(Ref<OpenXRAnchorTracker> p_anchor_tracker, RID p_persistence_context = RID(), const Callable &p_user_callback = Callable());
Ref<OpenXRFutureResult> unpersist_anchor(Ref<OpenXRAnchorTracker> p_anchor_tracker, RID p_persistence_context = RID(), const Callable &p_user_callback = Callable());
static String get_spatial_persistence_scope_name(XrSpatialPersistenceScopeEXT p_scope);
static String get_spatial_persistence_context_result_name(XrSpatialPersistenceContextResultEXT p_result);
protected:
static void _bind_methods();
private:
static OpenXRSpatialAnchorCapability *singleton;
bool spatial_anchor_ext = false;
bool spatial_persistence_ext = false;
bool spatial_persistence_operations_ext = false;
bool spatial_anchor_supported = false;
RID spatial_context;
RID persistence_context;
bool need_discovery = false;
int discovery_cooldown = 0;
Ref<OpenXRFutureResult> discovery_query_result;
Ref<OpenXRSpatialCapabilityConfigurationAnchor> anchor_configuration;
Ref<OpenXRSpatialContextPersistenceConfig> persistence_configuration;
Vector<XrSpatialPersistenceScopeEXT> supported_persistence_scopes;
bool _load_supported_persistence_scopes();
// Persistence scopes
struct PersistenceContextData {
XrSpatialPersistenceScopeEXT scope;
XrSpatialPersistenceContextEXT persistence_context = XR_NULL_HANDLE;
};
mutable RID_Owner<PersistenceContextData> persistence_context_owner;
bool _is_persistence_scope_supported(PersistenceScope p_scope);
Ref<OpenXRFutureResult> _create_persistence_context(PersistenceScope p_scope, Callable p_user_callback = Callable());
uint64_t _get_persistence_context_handle(RID p_persistence_context) const;
void _on_persistence_context_ready(Ref<OpenXRFutureResult> p_future_result, uint64_t p_scope, Callable p_user_callback = Callable());
// Discovery logic
void _on_persistence_context_completed(RID p_persistence_context);
Ref<OpenXRFutureResult> _create_spatial_context();
void _on_spatial_context_created(RID p_spatial_context);
void _on_spatial_discovery_recommended(RID p_spatial_context);
Ref<OpenXRFutureResult> _start_entity_discovery();
void _process_discovery_snapshot(RID p_snapshot);
void _process_update_snapshot(RID p_snapshot);
// Entities
void _on_made_anchor_persistent(Ref<OpenXRFutureResult> p_future_result, RID p_persistence_context, Ref<OpenXRAnchorTracker> p_anchor_tracker, const Callable &p_callback);
void _on_made_anchor_unpersistent(Ref<OpenXRFutureResult> p_future_result, RID p_persistence_context, Ref<OpenXRAnchorTracker> p_anchor_tracker, const Callable &p_callback);
// Trackers
HashMap<XrSpatialEntityIdEXT, Ref<OpenXRAnchorTracker>> anchors;
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC4(xrCreateSpatialAnchorEXT, (XrSpatialContextEXT), spatialContext, (const XrSpatialAnchorCreateInfoEXT *), create_info, (XrSpatialEntityIdEXT *), anchor_entity_id, (XrSpatialEntityEXT *), anchor_entity);
EXT_PROTO_XRRESULT_FUNC5(xrEnumerateSpatialPersistenceScopesEXT, (XrInstance), instance, (XrSystemId), system_id, (uint32_t), persistence_scope_capacity_input, (uint32_t *), persistence_scope_count_output, (XrSpatialPersistenceScopeEXT *), persistence_scopes);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialPersistenceContextAsyncEXT, (XrSession), session, (const XrSpatialPersistenceContextCreateInfoEXT *), create_info, (XrFutureEXT *), future);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialPersistenceContextCompleteEXT, (XrSession), session, (XrFutureEXT), future, (XrCreateSpatialPersistenceContextCompletionEXT *), completion);
EXT_PROTO_XRRESULT_FUNC1(xrDestroySpatialPersistenceContextEXT, (XrSpatialPersistenceContextEXT), persistence_context);
EXT_PROTO_XRRESULT_FUNC3(xrPersistSpatialEntityAsyncEXT, (XrSpatialPersistenceContextEXT), persistence_context, (const XrSpatialEntityPersistInfoEXT *), persist_info, (XrFutureEXT *), future);
EXT_PROTO_XRRESULT_FUNC3(xrPersistSpatialEntityCompleteEXT, (XrSpatialPersistenceContextEXT), persistence_context, (XrFutureEXT), future, (XrPersistSpatialEntityCompletionEXT *), completion);
EXT_PROTO_XRRESULT_FUNC3(xrUnpersistSpatialEntityAsyncEXT, (XrSpatialPersistenceContextEXT), persistence_context, (const XrSpatialEntityUnpersistInfoEXT *), unpersist_info, (XrFutureEXT *), future);
EXT_PROTO_XRRESULT_FUNC3(xrUnpersistSpatialEntityCompleteEXT, (XrSpatialPersistenceContextEXT), persistence_context, (XrFutureEXT), future, (XrUnpersistSpatialEntityCompletionEXT *), completion);
};
VARIANT_ENUM_CAST(OpenXRSpatialAnchorCapability::PersistenceScope);

View File

@ -0,0 +1,504 @@
/**************************************************************************/
/* openxr_spatial_entities.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_entities.h"
#include "../../openxr_api.h"
#include "core/variant/native_ptr.h"
#include "openxr_spatial_entity_extension.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationBaseHeader
void OpenXRSpatialCapabilityConfigurationBaseHeader::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_valid_configuration"), &OpenXRSpatialCapabilityConfigurationBaseHeader::has_valid_configuration);
GDVIRTUAL_BIND(_has_valid_configuration);
GDVIRTUAL_BIND(_get_configuration);
}
bool OpenXRSpatialCapabilityConfigurationBaseHeader::has_valid_configuration() const {
bool is_valid = false;
if (GDVIRTUAL_CALL(_has_valid_configuration, is_valid)) {
return is_valid;
}
return false;
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationBaseHeader::get_configuration() {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_get_configuration, pointer)) {
return reinterpret_cast<XrSpatialCapabilityConfigurationBaseHeaderEXT *>(pointer);
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialEntityTracker
void OpenXRSpatialEntityTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_entity", "entity"), &OpenXRSpatialEntityTracker::set_entity);
ClassDB::bind_method(D_METHOD("get_entity"), &OpenXRSpatialEntityTracker::get_entity);
ADD_PROPERTY(PropertyInfo(Variant::RID, "entity"), "set_entity", "get_entity");
ClassDB::bind_method(D_METHOD("set_spatial_tracking_state", "spatial_tracking_state"), &OpenXRSpatialEntityTracker::_set_spatial_tracking_state);
ClassDB::bind_method(D_METHOD("get_spatial_tracking_state"), &OpenXRSpatialEntityTracker::_get_spatial_tracking_state);
ADD_PROPERTY(PropertyInfo(Variant::INT, "spatial_tracking_state"), "set_spatial_tracking_state", "get_spatial_tracking_state");
ADD_SIGNAL(MethodInfo("spatial_tracking_state_changed", PropertyInfo(Variant::INT, "spatial_tracking_state")));
BIND_ENUM_CONSTANT(ENTITY_TRACKING_STATE_STOPPED);
BIND_ENUM_CONSTANT(ENTITY_TRACKING_STATE_PAUSED);
BIND_ENUM_CONSTANT(ENTITY_TRACKING_STATE_TRACKING);
}
OpenXRSpatialEntityTracker::OpenXRSpatialEntityTracker() {
set_tracker_type(XRServer::TrackerType::TRACKER_ANCHOR);
}
OpenXRSpatialEntityTracker::~OpenXRSpatialEntityTracker() {
if (spatial_entity.is_valid()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
if (se_extension) {
se_extension->free_spatial_entity(spatial_entity);
spatial_entity = RID();
}
}
}
void OpenXRSpatialEntityTracker::set_entity(const RID &p_entity) {
if (spatial_entity.is_valid()) {
if (spatial_entity == p_entity) {
return;
}
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
if (se_extension) {
se_extension->free_spatial_entity(spatial_entity);
spatial_entity = RID();
}
}
spatial_entity = p_entity;
if (p_entity.is_valid()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
XrSpatialEntityIdEXT entity_id = se_extension->get_spatial_entity_id(p_entity);
String tracker_name = String("openxr/spatial_entity/") + String::num_int64(entity_id);
set_tracker_name(tracker_name);
} else {
set_tracker_name("openxr/spatial_entity/null");
}
}
RID OpenXRSpatialEntityTracker::get_entity() const {
return spatial_entity;
}
void OpenXRSpatialEntityTracker::set_spatial_tracking_state(const XrSpatialEntityTrackingStateEXT p_state) {
if (spatial_tracking_state != p_state) {
spatial_tracking_state = p_state;
emit_signal(SNAME("spatial_tracking_state_changed"), spatial_tracking_state);
}
}
void OpenXRSpatialEntityTracker::_set_spatial_tracking_state(const EntityTrackingState p_state) {
set_spatial_tracking_state((XrSpatialEntityTrackingStateEXT)p_state);
}
XrSpatialEntityTrackingStateEXT OpenXRSpatialEntityTracker::get_spatial_tracking_state() const {
return spatial_tracking_state;
}
OpenXRSpatialEntityTracker::EntityTrackingState OpenXRSpatialEntityTracker::_get_spatial_tracking_state() const {
return (EntityTrackingState)get_spatial_tracking_state();
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialComponentData
void OpenXRSpatialComponentData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_capacity", "capacity"), &OpenXRSpatialComponentData::set_capacity);
GDVIRTUAL_BIND(_set_capacity, "capacity");
GDVIRTUAL_BIND(_get_component_type);
GDVIRTUAL_BIND(_get_structure_data, "next");
}
void OpenXRSpatialComponentData::set_capacity(uint32_t p_capacity) {
GDVIRTUAL_CALL(_set_capacity, p_capacity);
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentData::get_component_type() const {
uint64_t component_type = XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT;
if (GDVIRTUAL_CALL(_get_component_type, component_type)) {
return (XrSpatialComponentTypeEXT)component_type;
}
return XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT;
}
void *OpenXRSpatialComponentData::get_structure_data(void *p_next) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_get_structure_data, (uint64_t)p_next, pointer)) {
return reinterpret_cast<void *>(pointer);
}
return p_next;
}
////////////////////////////////////////////////////////////////////////////
// Spatial component bounded2d list
void OpenXRSpatialComponentBounded2DList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_center_pose", "index"), &OpenXRSpatialComponentBounded2DList::get_center_pose);
ClassDB::bind_method(D_METHOD("get_size", "index"), &OpenXRSpatialComponentBounded2DList::get_size);
}
void OpenXRSpatialComponentBounded2DList::set_capacity(uint32_t p_capacity) {
bounded2d_data.resize(p_capacity);
bounded2d_list.boundCount = uint32_t(bounded2d_data.size());
bounded2d_list.bounds = bounded2d_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentBounded2DList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT;
}
void *OpenXRSpatialComponentBounded2DList::get_structure_data(void *p_next) {
bounded2d_list.next = p_next;
return &bounded2d_list;
}
Transform3D OpenXRSpatialComponentBounded2DList::get_center_pose(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, bounded2d_data.size(), Transform3D());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
return openxr_api->transform_from_pose(bounded2d_data[p_index].center);
}
Vector2 OpenXRSpatialComponentBounded2DList::get_size(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, bounded2d_data.size(), Vector2());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Vector2());
const XrExtent2Df &extents = bounded2d_data[p_index].extents;
return Vector2(extents.width, extents.height);
}
////////////////////////////////////////////////////////////////////////////
// Spatial component bounded3d list
void OpenXRSpatialComponentBounded3DList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_center_pose", "index"), &OpenXRSpatialComponentBounded3DList::get_center_pose);
ClassDB::bind_method(D_METHOD("get_size", "index"), &OpenXRSpatialComponentBounded3DList::get_size);
}
void OpenXRSpatialComponentBounded3DList::set_capacity(uint32_t p_capacity) {
bounded3d_data.resize(p_capacity);
bounded3d_list.boundCount = uint32_t(bounded3d_data.size());
bounded3d_list.bounds = bounded3d_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentBounded3DList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT;
}
void *OpenXRSpatialComponentBounded3DList::get_structure_data(void *p_next) {
bounded3d_list.next = p_next;
return &bounded3d_list;
}
Transform3D OpenXRSpatialComponentBounded3DList::get_center_pose(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, bounded3d_data.size(), Transform3D());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
return openxr_api->transform_from_pose(bounded3d_data[p_index].center);
}
Vector3 OpenXRSpatialComponentBounded3DList::get_size(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, bounded3d_data.size(), Vector3());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Vector3());
const XrExtent3Df &extents = bounded3d_data[p_index].extents;
return Vector3(extents.width, extents.height, extents.depth);
}
////////////////////////////////////////////////////////////////////////////
// Spatial component parent list
void OpenXRSpatialComponentParentList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent", "index"), &OpenXRSpatialComponentParentList::get_parent);
}
void OpenXRSpatialComponentParentList::set_capacity(uint32_t p_capacity) {
parent_data.resize(p_capacity);
parent_list.parentCount = uint32_t(parent_data.size());
parent_list.parents = parent_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentParentList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT;
}
void *OpenXRSpatialComponentParentList::get_structure_data(void *p_next) {
parent_list.next = p_next;
return &parent_list;
}
RID OpenXRSpatialComponentParentList::get_parent(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, parent_data.size(), RID());
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, RID());
return se_extension->find_spatial_entity(parent_data[p_index]);
}
////////////////////////////////////////////////////////////////////////////
// Spatial component mesh2d list
void OpenXRSpatialComponentMesh2DList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_transform", "index"), &OpenXRSpatialComponentMesh2DList::get_transform);
ClassDB::bind_method(D_METHOD("get_vertices", "snapshot", "index"), &OpenXRSpatialComponentMesh2DList::get_vertices);
ClassDB::bind_method(D_METHOD("get_indices", "snapshot", "index"), &OpenXRSpatialComponentMesh2DList::get_indices);
}
void OpenXRSpatialComponentMesh2DList::set_capacity(uint32_t p_capacity) {
mesh2d_data.resize(p_capacity);
mesh2d_list.meshCount = uint32_t(mesh2d_data.size());
mesh2d_list.meshes = mesh2d_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentMesh2DList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT;
}
void *OpenXRSpatialComponentMesh2DList::get_structure_data(void *p_next) {
mesh2d_list.next = p_next;
return &mesh2d_list;
}
Transform3D OpenXRSpatialComponentMesh2DList::get_transform(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, mesh2d_data.size(), Transform3D());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
return openxr_api->transform_from_pose(mesh2d_data[p_index].origin);
}
PackedVector2Array OpenXRSpatialComponentMesh2DList::get_vertices(RID p_snapshot, int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, mesh2d_data.size(), PackedVector2Array());
const XrSpatialBufferEXT &buffer = mesh2d_data[p_index].vertexBuffer;
if (buffer.bufferId == XR_NULL_SPATIAL_BUFFER_ID_EXT) {
// We don't have data (yet).
return PackedVector2Array();
}
ERR_FAIL_COND_V(buffer.bufferType != XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT, PackedVector2Array());
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, PackedVector2Array());
return se_extension->get_vector2_buffer(p_snapshot, buffer.bufferId);
}
PackedInt32Array OpenXRSpatialComponentMesh2DList::get_indices(RID p_snapshot, int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, mesh2d_data.size(), PackedInt32Array());
const XrSpatialBufferEXT &buffer = mesh2d_data[p_index].indexBuffer;
if (buffer.bufferId == XR_NULL_SPATIAL_BUFFER_ID_EXT) {
// We don't have data (yet).
return PackedInt32Array();
}
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, PackedInt32Array());
PackedInt32Array ret;
switch (buffer.bufferType) {
case XR_SPATIAL_BUFFER_TYPE_UINT8_EXT: {
PackedByteArray data = se_extension->get_uint8_buffer(p_snapshot, buffer.bufferId);
ret.resize(data.size());
int count = ret.size();
int32_t *ptr = ret.ptrw();
for (int i = 0; i < count; i++) {
ptr[i] = data[i];
}
} break;
case XR_SPATIAL_BUFFER_TYPE_UINT16_EXT: {
Vector<uint16_t> data = se_extension->get_uint16_buffer(p_snapshot, buffer.bufferId);
ret.resize(data.size());
int count = ret.size();
int32_t *ptr = ret.ptrw();
for (int i = 0; i < count; i++) {
ptr[i] = data[i];
}
} break;
case XR_SPATIAL_BUFFER_TYPE_UINT32_EXT: {
Vector<uint32_t> data = se_extension->get_uint32_buffer(p_snapshot, buffer.bufferId);
ret.resize(data.size());
int count = ret.size();
int32_t *ptr = ret.ptrw();
for (int i = 0; i < count; i++) {
ptr[i] = data[i];
}
} break;
default: {
ERR_FAIL_V_MSG(PackedInt32Array(), "OpenXR: Unsupported buffer type for indices.");
} break;
}
return ret;
}
////////////////////////////////////////////////////////////////////////////
// Spatial component mesh3d list
void OpenXRSpatialComponentMesh3DList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_transform", "index"), &OpenXRSpatialComponentMesh3DList::get_transform);
ClassDB::bind_method(D_METHOD("get_mesh", "index"), &OpenXRSpatialComponentMesh3DList::get_mesh);
}
void OpenXRSpatialComponentMesh3DList::set_capacity(uint32_t p_capacity) {
mesh3d_data.resize(p_capacity);
mesh3d_list.meshCount = uint32_t(mesh3d_data.size());
mesh3d_list.meshes = mesh3d_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentMesh3DList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT;
}
void *OpenXRSpatialComponentMesh3DList::get_structure_data(void *p_next) {
mesh3d_list.next = p_next;
return &mesh3d_list;
}
Transform3D OpenXRSpatialComponentMesh3DList::get_transform(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, mesh3d_data.size(), Transform3D());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
return openxr_api->transform_from_pose(mesh3d_data[p_index].origin);
}
Ref<Mesh> OpenXRSpatialComponentMesh3DList::get_mesh(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, mesh3d_data.size(), nullptr);
// TODO implement, need to convert mesh data to Godot mesh resource
return nullptr;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialQueryResultData
void OpenXRSpatialQueryResultData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_capacity"), &OpenXRSpatialQueryResultData::get_capacity);
ClassDB::bind_method(D_METHOD("get_entity_id", "index"), &OpenXRSpatialQueryResultData::_get_entity_id);
ClassDB::bind_method(D_METHOD("get_entity_state", "index"), &OpenXRSpatialQueryResultData::_get_entity_state);
}
void OpenXRSpatialQueryResultData::set_capacity(uint32_t p_capacity) {
entity_ids.resize(p_capacity);
entity_states.resize(p_capacity);
query_result.entityIdCapacityInput = entity_ids.size();
query_result.entityIds = entity_ids.ptrw();
query_result.entityStateCapacityInput = entity_states.size();
query_result.entityStates = entity_states.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialQueryResultData::get_component_type() const {
// This component is always included and has no type.
return XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT;
}
void *OpenXRSpatialQueryResultData::get_structure_data(void *p_next) {
query_result.next = p_next;
query_result.entityIdCountOutput = 0;
query_result.entityStateCountOutput = 0;
return &query_result;
}
XrSpatialEntityIdEXT OpenXRSpatialQueryResultData::get_entity_id(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, entity_ids.size(), XR_NULL_ENTITY);
return entity_ids[p_index];
}
uint64_t OpenXRSpatialQueryResultData::_get_entity_id(int64_t p_index) const {
return (uint64_t)get_entity_id(p_index);
}
XrSpatialEntityTrackingStateEXT OpenXRSpatialQueryResultData::get_entity_state(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, entity_states.size(), XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT);
return entity_states[p_index];
}
OpenXRSpatialEntityTracker::EntityTrackingState OpenXRSpatialQueryResultData::_get_entity_state(int64_t p_index) const {
return (OpenXRSpatialEntityTracker::EntityTrackingState)get_entity_state(p_index);
}
String OpenXRSpatialQueryResultData::get_entity_tracking_state_name(XrSpatialEntityTrackingStateEXT p_tracking_state) {
XR_ENUM_SWITCH(XrSpatialEntityTrackingStateEXT, p_tracking_state)
}

View File

@ -0,0 +1,230 @@
/**************************************************************************/
/* openxr_spatial_entities.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "../../openxr_structure.h"
#include "../openxr_future_extension.h"
#include "scene/resources/mesh.h"
#include "servers/xr/xr_positional_tracker.h"
#define XR_NULL_ENTITY 0x7FFFFFFF
// Wrapper class for XrSpatialCapabilityConfigurationBaseHeaderEXT
class OpenXRSpatialCapabilityConfigurationBaseHeader : public RefCounted {
GDCLASS(OpenXRSpatialCapabilityConfigurationBaseHeader, RefCounted);
protected:
static void _bind_methods();
public:
virtual bool has_valid_configuration() const;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration();
GDVIRTUAL0RC(bool, _has_valid_configuration);
GDVIRTUAL0R(uint64_t, _get_configuration);
};
// Tracker for our spatial entities
class OpenXRSpatialEntityTracker : public XRPositionalTracker {
GDCLASS(OpenXRSpatialEntityTracker, XRPositionalTracker);
public:
enum EntityTrackingState {
ENTITY_TRACKING_STATE_STOPPED = XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT,
ENTITY_TRACKING_STATE_PAUSED = XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT,
ENTITY_TRACKING_STATE_TRACKING = XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT,
};
OpenXRSpatialEntityTracker();
virtual ~OpenXRSpatialEntityTracker();
void set_entity(const RID &p_entity);
RID get_entity() const;
void set_spatial_tracking_state(const XrSpatialEntityTrackingStateEXT p_state);
XrSpatialEntityTrackingStateEXT get_spatial_tracking_state() const;
protected:
static void _bind_methods();
private:
RID spatial_entity;
XrSpatialEntityTrackingStateEXT spatial_tracking_state = XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT;
void _set_spatial_tracking_state(const EntityTrackingState p_state);
EntityTrackingState _get_spatial_tracking_state() const;
};
VARIANT_ENUM_CAST(OpenXRSpatialEntityTracker::EntityTrackingState)
// Wrapper class for our spatial component data returned by discovery queries
class OpenXRSpatialComponentData : public RefCounted {
GDCLASS(OpenXRSpatialComponentData, RefCounted);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity);
virtual XrSpatialComponentTypeEXT get_component_type() const;
virtual void *get_structure_data(void *p_next);
GDVIRTUAL1(_set_capacity, uint32_t);
GDVIRTUAL0RC(uint64_t, _get_component_type);
GDVIRTUAL1RC(uint64_t, _get_structure_data, uint64_t);
};
class OpenXRSpatialComponentBounded2DList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentBounded2DList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_center_pose(int64_t p_index) const;
Vector2 get_size(int64_t p_index) const;
private:
Vector<XrSpatialBounded2DDataEXT> bounded2d_data;
XrSpatialComponentBounded2DListEXT bounded2d_list = { XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT, nullptr, 0, nullptr };
};
class OpenXRSpatialComponentBounded3DList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentBounded3DList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_center_pose(int64_t p_index) const;
Vector3 get_size(int64_t p_index) const;
private:
Vector<XrBoxf> bounded3d_data;
XrSpatialComponentBounded3DListEXT bounded3d_list = { XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT, nullptr, 0, nullptr };
};
class OpenXRSpatialComponentParentList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentParentList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
RID get_parent(int64_t p_index) const;
private:
Vector<XrSpatialEntityIdEXT> parent_data;
XrSpatialComponentParentListEXT parent_list = { XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT, nullptr, 0, nullptr };
};
class OpenXRSpatialComponentMesh2DList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentMesh2DList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_transform(int64_t p_index) const;
PackedVector2Array get_vertices(RID p_snapshot, int64_t p_index) const;
PackedInt32Array get_indices(RID p_snapshot, int64_t p_index) const;
private:
Vector<XrSpatialMeshDataEXT> mesh2d_data;
XrSpatialComponentMesh2DListEXT mesh2d_list = { XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT, nullptr, 0, nullptr };
};
class OpenXRSpatialComponentMesh3DList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentMesh3DList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_transform(int64_t p_index) const;
Ref<Mesh> get_mesh(int64_t p_index) const;
private:
Vector<XrSpatialMeshDataEXT> mesh3d_data;
XrSpatialComponentMesh3DListEXT mesh3d_list = { XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT, nullptr, 0, nullptr };
};
class OpenXRSpatialQueryResultData : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialQueryResultData, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
int64_t get_capacity() const { return entity_ids.size(); }
XrSpatialEntityIdEXT get_entity_id(int64_t p_index) const;
XrSpatialEntityTrackingStateEXT get_entity_state(int64_t p_index) const;
static String get_entity_tracking_state_name(XrSpatialEntityTrackingStateEXT p_tracking_state);
private:
Vector<XrSpatialEntityIdEXT> entity_ids;
Vector<XrSpatialEntityTrackingStateEXT> entity_states;
XrSpatialComponentDataQueryResultEXT query_result = { XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT, nullptr, 0, 0, nullptr, 0, 0, nullptr };
uint64_t _get_entity_id(int64_t p_index) const;
OpenXRSpatialEntityTracker::EntityTrackingState _get_entity_state(int64_t p_index) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,217 @@
/**************************************************************************/
/* openxr_spatial_entity_extension.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "../../openxr_util.h"
#include "../openxr_extension_wrapper.h"
#include "core/templates/rid_owner.h"
#include "core/variant/typed_array.h"
#include "openxr_spatial_entities.h"
// Spatial entity extension
class OpenXRSpatialEntityExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRSpatialEntityExtension, OpenXRExtensionWrapper);
public:
enum Capability {
CAPABILITY_PLANE_TRACKING = XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT,
CAPABILITY_MARKER_TRACKING_QR_CODE = XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT,
CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE = XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT,
CAPABILITY_MARKER_TRACKING_ARUCO_MARKER = XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT,
CAPABILITY_MARKER_TRACKING_APRIL_TAG = XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT,
CAPABILITY_ANCHOR = XR_SPATIAL_CAPABILITY_ANCHOR_EXT,
};
enum ComponentType {
COMPONENT_TYPE_BOUNDED_2D = XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT,
COMPONENT_TYPE_BOUNDED_3D = XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT,
COMPONENT_TYPE_PARENT = XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT,
COMPONENT_TYPE_MESH_3D = XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT,
COMPONENT_TYPE_PLANE_ALIGNMENT = XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT,
COMPONENT_TYPE_MESH_2D = XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT,
COMPONENT_TYPE_POLYGON_2D = XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT,
COMPONENT_TYPE_PLANE_SEMANTIC_LABEL = XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT,
COMPONENT_TYPE_MARKER = XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT,
COMPONENT_TYPE_ANCHOR = XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT,
COMPONENT_TYPE_PERSISTENCE = XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT,
};
static OpenXRSpatialEntityExtension *get_singleton();
OpenXRSpatialEntityExtension();
virtual ~OpenXRSpatialEntityExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual void on_session_destroyed() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
bool get_active() const;
bool supports_capability(XrSpatialCapabilityEXT p_capability);
bool supports_component_type(XrSpatialCapabilityEXT p_capability, XrSpatialComponentTypeEXT p_component_type);
// Spatial contexts
Ref<OpenXRFutureResult> create_spatial_context(const TypedArray<OpenXRSpatialCapabilityConfigurationBaseHeader> &p_capability_configurations, Ref<OpenXRStructureBase> p_next, const Callable &p_user_callback);
bool get_spatial_context_ready(RID p_spatial_context) const;
void free_spatial_context(RID p_spatial_context);
XrSpatialContextEXT get_spatial_context_handle(RID p_spatial_context) const;
// Discovery query
Ref<OpenXRFutureResult> discover_spatial_entities(RID p_spatial_context, const Vector<XrSpatialComponentTypeEXT> &p_component_types, Ref<OpenXRStructureBase> p_next, const Callable &p_user_callback);
// Update query
RID update_spatial_entities(RID p_spatial_context, const LocalVector<RID> &p_entities, const LocalVector<XrSpatialComponentTypeEXT> &p_component_types, Ref<OpenXRStructureBase> p_next);
// Snapshot data
void free_spatial_snapshot(RID p_spatial_snapshot);
XrSpatialSnapshotEXT get_spatial_snapshot_handle(RID p_spatial_snapshot) const;
RID get_spatial_snapshot_context(RID p_spatial_snapshot) const;
bool query_snapshot(RID p_spatial_snapshot, const TypedArray<OpenXRSpatialComponentData> &p_component_data, Ref<OpenXRStructureBase> p_next);
// Buffers from snapshot
String get_string(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
PackedByteArray get_uint8_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
Vector<uint16_t> get_uint16_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
Vector<uint32_t> get_uint32_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
PackedFloat32Array get_float_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
PackedVector2Array get_vector2_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
PackedVector3Array get_vector3_buffer(RID p_spatial_snapshot, XrSpatialBufferIdEXT p_buffer_id) const;
// Entities
RID find_spatial_entity(XrSpatialEntityIdEXT p_entity_id) const;
RID add_spatial_entity(RID p_spatial_context, XrSpatialEntityIdEXT p_entity_id, XrSpatialEntityEXT p_entity);
RID make_spatial_entity(RID p_spatial_context, XrSpatialEntityIdEXT p_entity_id);
XrSpatialEntityIdEXT get_spatial_entity_id(RID p_entity) const;
RID get_spatial_entity_context(RID p_entity) const;
void free_spatial_entity(RID p_entity);
static String get_spatial_capability_name(XrSpatialCapabilityEXT p_capability);
static String get_spatial_component_type_name(XrSpatialComponentTypeEXT p_component_type);
static String get_spatial_feature_name(XrSpatialCapabilityFeatureEXT p_feature);
protected:
static void _bind_methods();
private:
static OpenXRSpatialEntityExtension *singleton;
bool spatial_entity_ext = false;
// Capabilities
struct SpatialEntityCapabality {
Vector<XrSpatialComponentTypeEXT> component_types;
Vector<XrSpatialCapabilityFeatureEXT> features;
};
HashMap<XrSpatialCapabilityEXT, SpatialEntityCapabality> supported_capabilities;
int capabilities_load_state = 0; // 0 = no, 1 = yes, 2 = failed
bool _load_capabilities();
bool _supports_capability(Capability p_capability);
bool _supports_component_type(Capability p_capability, ComponentType p_component_type);
// Spatial context
struct SpatialContextData {
XrSpatialContextEXT spatial_context = XR_NULL_HANDLE;
};
mutable RID_Owner<SpatialContextData> spatial_context_owner;
void _on_context_creation_ready(Ref<OpenXRFutureResult> p_future_result, const Callable &p_user_callback);
uint64_t _get_spatial_context_handle(RID p_spatial_context) const;
// Spatial query
Ref<OpenXRFutureResult> _discover_spatial_entities(RID p_spatial_context, const PackedInt64Array &p_component_types, Ref<OpenXRStructureBase> p_next, const Callable &p_callback);
void _on_discovered_spatial_entities(Ref<OpenXRFutureResult> p_future_result, RID p_discovery_spatial_context, const Callable &p_user_callback);
// Update query
RID _update_spatial_entities(RID p_spatial_context, const TypedArray<RID> &p_entities, const PackedInt64Array &p_component_types, Ref<OpenXRStructureBase> p_next);
// Snapshot data
struct SpatialSnapshotData {
RID spatial_context;
XrSpatialSnapshotEXT spatial_snapshot = XR_NULL_HANDLE;
};
mutable RID_Owner<SpatialSnapshotData> spatial_snapshot_owner;
uint64_t _get_spatial_snapshot_handle(RID p_spatial_snapshot) const;
// Buffers from snapshot
String _get_string(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedByteArray _get_uint8_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedInt32Array _get_uint16_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedInt32Array _get_uint32_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedFloat32Array _get_float_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedVector2Array _get_vector2_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
PackedVector3Array _get_vector3_buffer(RID p_spatial_snapshot, uint64_t p_buffer_id) const;
// Entities
struct SpatialEntityData {
RID spatial_context;
XrSpatialEntityIdEXT entity_id = XR_NULL_ENTITY;
XrSpatialEntityEXT entity = XR_NULL_HANDLE;
};
mutable RID_Owner<SpatialEntityData> spatial_entity_owner;
RID _find_entity(uint64_t p_entity_id);
RID _add_entity(RID p_spatial_context, uint64_t p_entity_id, uint64_t p_entity);
RID _make_entity(RID p_spatial_context, uint64_t p_entity_id);
uint64_t _get_entity_id(RID p_entity) const;
// OpenXR API call wrappers
// Spatial entities
EXT_PROTO_XRRESULT_FUNC5(xrEnumerateSpatialCapabilitiesEXT, (XrInstance), instance, (XrSystemId), system_id, (uint32_t), capability_capacity_input, (uint32_t *), capability_count_output, (XrSpatialCapabilityEXT *), capabilities);
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSpatialCapabilityComponentTypesEXT, (XrInstance), instance, (XrSystemId), systemId, (XrSpatialCapabilityEXT), capability, (XrSpatialCapabilityComponentTypesEXT *), capability_components);
EXT_PROTO_XRRESULT_FUNC6(xrEnumerateSpatialCapabilityFeaturesEXT, (XrInstance), instance, (XrSystemId), systemId, (XrSpatialCapabilityEXT), capability, (uint32_t), capability_feature_capacity_input, (uint32_t *), capability_feature_count_output, (XrSpatialCapabilityFeatureEXT *), capability_features);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialContextAsyncEXT, (XrSession), session, (const XrSpatialContextCreateInfoEXT *), create_info, (XrFutureEXT *), future);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialContextCompleteEXT, (XrSession), session, (XrFutureEXT), future, (XrCreateSpatialContextCompletionEXT *), completion);
EXT_PROTO_XRRESULT_FUNC1(xrDestroySpatialContextEXT, (XrSpatialContextEXT), spatial_context);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialDiscoverySnapshotAsyncEXT, (XrSpatialContextEXT), spatial_context, (const XrSpatialDiscoverySnapshotCreateInfoEXT *), create_info, (XrFutureEXT *), future);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialDiscoverySnapshotCompleteEXT, (XrSpatialContextEXT), spatial_context, (const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT *), create_snapshot_completion_info, (XrCreateSpatialDiscoverySnapshotCompletionEXT *), completion);
EXT_PROTO_XRRESULT_FUNC3(xrQuerySpatialComponentDataEXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialComponentDataQueryConditionEXT *), query_condition, (XrSpatialComponentDataQueryResultEXT *), query_result);
EXT_PROTO_XRRESULT_FUNC1(xrDestroySpatialSnapshotEXT, (XrSpatialSnapshotEXT), snapshot);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialEntityFromIdEXT, (XrSpatialContextEXT), spatial_context, (const XrSpatialEntityFromIdCreateInfoEXT *), create_info, (XrSpatialEntityEXT *), spatial_entity);
EXT_PROTO_XRRESULT_FUNC1(xrDestroySpatialEntityEXT, (XrSpatialEntityEXT), spatial_entity);
EXT_PROTO_XRRESULT_FUNC3(xrCreateSpatialUpdateSnapshotEXT, (XrSpatialContextEXT), spatial_context, (const XrSpatialUpdateSnapshotCreateInfoEXT *), createInfo, (XrSpatialSnapshotEXT *), snapshot);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferStringEXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (char *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferUint8EXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (uint8_t *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferUint16EXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (uint16_t *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferUint32EXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (uint32_t *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferFloatEXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (float *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferVector2fEXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (XrVector2f *), buffer);
EXT_PROTO_XRRESULT_FUNC5(xrGetSpatialBufferVector3fEXT, (XrSpatialSnapshotEXT), snapshot, (const XrSpatialBufferGetInfoEXT *), info, (uint32_t), buffer_capacity_input, (uint32_t *), buffer_count_output, (XrVector3f *), buffer);
};
VARIANT_ENUM_CAST(OpenXRSpatialEntityExtension::Capability);
VARIANT_ENUM_CAST(OpenXRSpatialEntityExtension::ComponentType);

View File

@ -0,0 +1,788 @@
/**************************************************************************/
/* openxr_spatial_marker_tracking.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_marker_tracking.h"
#include "../../openxr_api.h"
#include "core/config/project_settings.h"
#include "openxr_spatial_entity_extension.h"
#include "servers/xr_server.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationQrCode
void OpenXRSpatialCapabilityConfigurationQrCode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enabled_components"), &OpenXRSpatialCapabilityConfigurationQrCode::_get_enabled_components);
}
bool OpenXRSpatialCapabilityConfigurationQrCode::has_valid_configuration() const {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, false);
return capability->is_qrcode_supported();
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationQrCode::get_configuration() {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, nullptr);
if (capability->is_qrcode_supported()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Guaranteed components:
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT);
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// Set up our enabled components.
marker_config.enabledComponentCount = enabled_components.size();
marker_config.enabledComponents = enabled_components.ptr();
// and return this.
return (XrSpatialCapabilityConfigurationBaseHeaderEXT *)&marker_config;
}
return nullptr;
}
PackedInt64Array OpenXRSpatialCapabilityConfigurationQrCode::_get_enabled_components() const {
PackedInt64Array components;
for (const XrSpatialComponentTypeEXT &component_type : enabled_components) {
components.push_back((int64_t)component_type);
}
return components;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationMicroQrCode
void OpenXRSpatialCapabilityConfigurationMicroQrCode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enabled_components"), &OpenXRSpatialCapabilityConfigurationMicroQrCode::_get_enabled_components);
}
bool OpenXRSpatialCapabilityConfigurationMicroQrCode::has_valid_configuration() const {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, false);
return capability->is_micro_qrcode_supported();
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationMicroQrCode::get_configuration() {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, nullptr);
if (capability->is_micro_qrcode_supported()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Guaranteed components:
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT);
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// Set up our enabled components.
marker_config.enabledComponentCount = enabled_components.size();
marker_config.enabledComponents = enabled_components.ptr();
// and return this.
return (XrSpatialCapabilityConfigurationBaseHeaderEXT *)&marker_config;
}
return nullptr;
}
PackedInt64Array OpenXRSpatialCapabilityConfigurationMicroQrCode::_get_enabled_components() const {
PackedInt64Array components;
for (const XrSpatialComponentTypeEXT &component_type : enabled_components) {
components.push_back((int64_t)component_type);
}
return components;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationAruco
void OpenXRSpatialCapabilityConfigurationAruco::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enabled_components"), &OpenXRSpatialCapabilityConfigurationAruco::_get_enabled_components);
ClassDB::bind_method(D_METHOD("set_aruco_dict", "aruco_dict"), &OpenXRSpatialCapabilityConfigurationAruco::_set_aruco_dict);
ClassDB::bind_method(D_METHOD("get_aruco_dict"), &OpenXRSpatialCapabilityConfigurationAruco::_get_aruco_dict);
ADD_PROPERTY(PropertyInfo(Variant::INT, "aruco_dict"), "set_aruco_dict", "get_aruco_dict");
BIND_ENUM_CONSTANT(ARUCO_DICT_4X4_50);
BIND_ENUM_CONSTANT(ARUCO_DICT_4X4_100);
BIND_ENUM_CONSTANT(ARUCO_DICT_4X4_250);
BIND_ENUM_CONSTANT(ARUCO_DICT_4X4_1000);
BIND_ENUM_CONSTANT(ARUCO_DICT_5X5_50);
BIND_ENUM_CONSTANT(ARUCO_DICT_5X5_100);
BIND_ENUM_CONSTANT(ARUCO_DICT_5X5_250);
BIND_ENUM_CONSTANT(ARUCO_DICT_5X5_1000);
BIND_ENUM_CONSTANT(ARUCO_DICT_6X6_50);
BIND_ENUM_CONSTANT(ARUCO_DICT_6X6_100);
BIND_ENUM_CONSTANT(ARUCO_DICT_6X6_250);
BIND_ENUM_CONSTANT(ARUCO_DICT_6X6_1000);
BIND_ENUM_CONSTANT(ARUCO_DICT_7X7_50);
BIND_ENUM_CONSTANT(ARUCO_DICT_7X7_100);
BIND_ENUM_CONSTANT(ARUCO_DICT_7X7_250);
BIND_ENUM_CONSTANT(ARUCO_DICT_7X7_1000);
}
bool OpenXRSpatialCapabilityConfigurationAruco::has_valid_configuration() const {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, false);
return capability->is_aruco_supported();
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationAruco::get_configuration() {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, nullptr);
if (capability->is_aruco_supported()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Guaranteed components:
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT);
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// Set up our enabled components.
marker_config.enabledComponentCount = enabled_components.size();
marker_config.enabledComponents = enabled_components.ptr();
// and return this.
return (XrSpatialCapabilityConfigurationBaseHeaderEXT *)&marker_config;
}
return nullptr;
}
void OpenXRSpatialCapabilityConfigurationAruco::set_aruco_dict(XrSpatialMarkerArucoDictEXT p_dict) {
marker_config.arUcoDict = p_dict;
}
void OpenXRSpatialCapabilityConfigurationAruco::_set_aruco_dict(ArucoDict p_dict) {
set_aruco_dict((XrSpatialMarkerArucoDictEXT)p_dict);
}
XrSpatialMarkerArucoDictEXT OpenXRSpatialCapabilityConfigurationAruco::get_aruco_dict() const {
return marker_config.arUcoDict;
}
OpenXRSpatialCapabilityConfigurationAruco::ArucoDict OpenXRSpatialCapabilityConfigurationAruco::_get_aruco_dict() const {
return (ArucoDict)get_aruco_dict();
}
PackedInt64Array OpenXRSpatialCapabilityConfigurationAruco::_get_enabled_components() const {
PackedInt64Array components;
for (const XrSpatialComponentTypeEXT &component_type : enabled_components) {
components.push_back((int64_t)component_type);
}
return components;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationAprilTag
void OpenXRSpatialCapabilityConfigurationAprilTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enabled_components"), &OpenXRSpatialCapabilityConfigurationAprilTag::_get_enabled_components);
ClassDB::bind_method(D_METHOD("set_april_dict", "april_dict"), &OpenXRSpatialCapabilityConfigurationAprilTag::_set_april_dict);
ClassDB::bind_method(D_METHOD("get_april_dict"), &OpenXRSpatialCapabilityConfigurationAprilTag::_get_april_dict);
ADD_PROPERTY(PropertyInfo(Variant::INT, "april_dict"), "set_april_dict", "get_april_dict");
BIND_ENUM_CONSTANT(APRIL_TAG_DICT_16H5);
BIND_ENUM_CONSTANT(APRIL_TAG_DICT_25H9);
BIND_ENUM_CONSTANT(APRIL_TAG_DICT_36H10);
BIND_ENUM_CONSTANT(APRIL_TAG_DICT_36H11);
}
bool OpenXRSpatialCapabilityConfigurationAprilTag::has_valid_configuration() const {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, false);
return capability->is_april_tag_supported();
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationAprilTag::get_configuration() {
OpenXRSpatialMarkerTrackingCapability *capability = OpenXRSpatialMarkerTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, nullptr);
if (capability->is_april_tag_supported()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Guaranteed components:
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT);
enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// Set up our enabled components.
marker_config.enabledComponentCount = enabled_components.size();
marker_config.enabledComponents = enabled_components.ptr();
// and return this.
return (XrSpatialCapabilityConfigurationBaseHeaderEXT *)&marker_config;
}
return nullptr;
}
void OpenXRSpatialCapabilityConfigurationAprilTag::set_april_dict(XrSpatialMarkerAprilTagDictEXT p_dict) {
marker_config.aprilDict = p_dict;
}
void OpenXRSpatialCapabilityConfigurationAprilTag::_set_april_dict(AprilTagDict p_dict) {
set_april_dict((XrSpatialMarkerAprilTagDictEXT)p_dict);
}
XrSpatialMarkerAprilTagDictEXT OpenXRSpatialCapabilityConfigurationAprilTag::get_april_dict() const {
return marker_config.aprilDict;
}
OpenXRSpatialCapabilityConfigurationAprilTag::AprilTagDict OpenXRSpatialCapabilityConfigurationAprilTag::_get_april_dict() const {
return (AprilTagDict)get_april_dict();
}
PackedInt64Array OpenXRSpatialCapabilityConfigurationAprilTag::_get_enabled_components() const {
PackedInt64Array components;
for (const XrSpatialComponentTypeEXT &component_type : enabled_components) {
components.push_back((int64_t)component_type);
}
return components;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialComponentMarkerList
void OpenXRSpatialComponentMarkerList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_marker_type", "index"), &OpenXRSpatialComponentMarkerList::get_marker_type);
ClassDB::bind_method(D_METHOD("get_marker_id", "index"), &OpenXRSpatialComponentMarkerList::get_marker_id);
ClassDB::bind_method(D_METHOD("get_marker_data", "snapshot", "index"), &OpenXRSpatialComponentMarkerList::get_marker_data);
BIND_ENUM_CONSTANT(MARKER_TYPE_UNKNOWN);
BIND_ENUM_CONSTANT(MARKER_TYPE_QRCODE);
BIND_ENUM_CONSTANT(MARKER_TYPE_MICRO_QRCODE);
BIND_ENUM_CONSTANT(MARKER_TYPE_ARUCO);
BIND_ENUM_CONSTANT(MARKER_TYPE_APRIL_TAG);
BIND_ENUM_CONSTANT(MARKER_TYPE_MAX);
}
void OpenXRSpatialComponentMarkerList::set_capacity(uint32_t p_capacity) {
marker_data.resize(p_capacity);
marker_list.markerCount = uint32_t(marker_data.size());
marker_list.markers = marker_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentMarkerList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT;
}
void *OpenXRSpatialComponentMarkerList::get_structure_data(void *p_next) {
marker_list.next = p_next;
return &marker_list;
}
OpenXRSpatialComponentMarkerList::MarkerType OpenXRSpatialComponentMarkerList::get_marker_type(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, marker_data.size(), MARKER_TYPE_UNKNOWN);
// We can't simply cast these.
// This may give us problems in the future if we get new types through vendor extensions.
switch (marker_data[p_index].capability) {
case XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT: {
return MARKER_TYPE_QRCODE;
} break;
case XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT: {
return MARKER_TYPE_MICRO_QRCODE;
} break;
case XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT: {
return MARKER_TYPE_ARUCO;
} break;
case XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT: {
return MARKER_TYPE_APRIL_TAG;
} break;
default: {
return MARKER_TYPE_UNKNOWN;
} break;
}
}
uint32_t OpenXRSpatialComponentMarkerList::get_marker_id(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, marker_data.size(), 0);
return marker_data[p_index].markerId;
}
Variant OpenXRSpatialComponentMarkerList::get_marker_data(RID p_snapshot, int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, marker_data.size(), Variant());
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, Variant());
const XrSpatialBufferEXT &data = marker_data[p_index].data;
switch (data.bufferType) {
case XR_SPATIAL_BUFFER_TYPE_STRING_EXT: {
return se_extension->get_string(p_snapshot, data.bufferId);
} break;
case XR_SPATIAL_BUFFER_TYPE_UINT8_EXT: {
return se_extension->get_uint8_buffer(p_snapshot, data.bufferId);
} break;
default: {
return Variant();
} break;
}
}
////////////////////////////////////////////////////////////////////////////
// OpenXRMarkerTracker
void OpenXRMarkerTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounds_size", "bounds_size"), &OpenXRMarkerTracker::set_bounds_size);
ClassDB::bind_method(D_METHOD("get_bounds_size"), &OpenXRMarkerTracker::get_bounds_size);
ADD_PROPERTY(PropertyInfo(Variant::INT, "bounds_size"), "set_bounds_size", "get_bounds_size");
ClassDB::bind_method(D_METHOD("set_marker_type", "marker_type"), &OpenXRMarkerTracker::set_marker_type);
ClassDB::bind_method(D_METHOD("get_marker_type"), &OpenXRMarkerTracker::get_marker_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "marker_type"), "set_marker_type", "get_marker_type");
ClassDB::bind_method(D_METHOD("set_marker_id", "marker_id"), &OpenXRMarkerTracker::set_marker_id);
ClassDB::bind_method(D_METHOD("get_marker_id"), &OpenXRMarkerTracker::get_marker_id);
ADD_PROPERTY(PropertyInfo(Variant::INT, "marker_id"), "set_marker_id", "get_marker_id");
// As the type of marker data can vary, we can't make this a property.
ClassDB::bind_method(D_METHOD("set_marker_data", "marker_data"), &OpenXRMarkerTracker::set_marker_data);
ClassDB::bind_method(D_METHOD("get_marker_data"), &OpenXRMarkerTracker::get_marker_data);
}
void OpenXRMarkerTracker::set_bounds_size(const Vector2 &p_bounds_size) {
bounds_size = p_bounds_size;
}
Vector2 OpenXRMarkerTracker::get_bounds_size() const {
return bounds_size;
}
void OpenXRMarkerTracker::set_marker_type(OpenXRSpatialComponentMarkerList::MarkerType p_marker_type) {
marker_type = p_marker_type;
}
OpenXRSpatialComponentMarkerList::MarkerType OpenXRMarkerTracker::get_marker_type() const {
return marker_type;
}
void OpenXRMarkerTracker::set_marker_id(uint32_t p_id) {
marker_id = p_id;
}
uint32_t OpenXRMarkerTracker::get_marker_id() const {
return marker_id;
}
void OpenXRMarkerTracker::set_marker_data(const Variant &p_data) {
marker_data = p_data;
}
Variant OpenXRMarkerTracker::get_marker_data() const {
return marker_data;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialMarkerTrackingCapability
OpenXRSpatialMarkerTrackingCapability *OpenXRSpatialMarkerTrackingCapability::singleton = nullptr;
OpenXRSpatialMarkerTrackingCapability *OpenXRSpatialMarkerTrackingCapability::get_singleton() {
return singleton;
}
OpenXRSpatialMarkerTrackingCapability::OpenXRSpatialMarkerTrackingCapability() {
singleton = this;
}
OpenXRSpatialMarkerTrackingCapability::~OpenXRSpatialMarkerTrackingCapability() {
singleton = nullptr;
}
void OpenXRSpatialMarkerTrackingCapability::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_qrcode_supported"), &OpenXRSpatialMarkerTrackingCapability::is_qrcode_supported);
ClassDB::bind_method(D_METHOD("is_micro_qrcode_supported"), &OpenXRSpatialMarkerTrackingCapability::is_micro_qrcode_supported);
ClassDB::bind_method(D_METHOD("is_aruco_supported"), &OpenXRSpatialMarkerTrackingCapability::is_aruco_supported);
ClassDB::bind_method(D_METHOD("is_april_tag_supported"), &OpenXRSpatialMarkerTrackingCapability::is_april_tag_supported);
}
HashMap<String, bool *> OpenXRSpatialMarkerTrackingCapability::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enabled") && GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enable_marker_tracking")) {
request_extensions[XR_EXT_SPATIAL_MARKER_TRACKING_EXTENSION_NAME] = &spatial_marker_tracking_ext;
}
return request_extensions;
}
void OpenXRSpatialMarkerTrackingCapability::on_session_created(const XrSession p_session) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
if (!spatial_marker_tracking_ext) {
return;
}
se_extension->connect(SNAME("spatial_discovery_recommended"), callable_mp(this, &OpenXRSpatialMarkerTrackingCapability::_on_spatial_discovery_recommended));
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enable_builtin_marker_tracking")) {
// Start by creating our spatial context
_create_spatial_context();
}
}
void OpenXRSpatialMarkerTrackingCapability::on_session_destroyed() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
// Free and unregister our anchors
for (const KeyValue<XrSpatialEntityIdEXT, Ref<OpenXRMarkerTracker>> &marker_tracker : marker_trackers) {
xr_server->remove_tracker(marker_tracker.value);
}
marker_trackers.clear();
// Free our spatial context
if (spatial_context.is_valid()) {
se_extension->free_spatial_context(spatial_context);
spatial_context = RID();
}
se_extension->disconnect(SNAME("spatial_discovery_recommended"), callable_mp(this, &OpenXRSpatialMarkerTrackingCapability::_on_spatial_discovery_recommended));
}
void OpenXRSpatialMarkerTrackingCapability::on_process() {
if (!spatial_context.is_valid()) {
return;
}
// Protection against marker discovery happening too often.
if (discovery_cooldown > 0) {
discovery_cooldown--;
}
// Check if we need to start our discovery.
if (need_discovery && discovery_cooldown == 0 && !discovery_query_result.is_valid()) {
need_discovery = false;
discovery_cooldown = 60; // Set our cooldown to 60 frames, it doesn't need to be an exact science.
_start_entity_discovery();
}
// If we have markers, we do an update query to check for changed positions.
if (!marker_trackers.is_empty()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
// We want updates for all anchors
thread_local LocalVector<RID> entities;
entities.resize(marker_trackers.size());
RID *entity = entities.ptr();
for (const KeyValue<XrSpatialEntityIdEXT, Ref<OpenXRMarkerTracker>> &e : marker_trackers) {
*entity = e.value->get_entity();
entity++;
}
// We just want our anchor component
thread_local LocalVector<XrSpatialComponentTypeEXT> component_types;
component_types.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// And we get our update snapshot, this is NOT async!
RID snapshot = se_extension->update_spatial_entities(spatial_context, entities, component_types, nullptr);
if (snapshot.is_valid()) {
_process_snapshot(snapshot, false);
}
}
}
bool OpenXRSpatialMarkerTrackingCapability::is_qrcode_supported() {
if (!spatial_marker_tracking_ext) {
return false;
}
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
return se_extension->supports_capability(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT);
}
bool OpenXRSpatialMarkerTrackingCapability::is_micro_qrcode_supported() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
return se_extension->supports_capability(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT);
}
bool OpenXRSpatialMarkerTrackingCapability::is_aruco_supported() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
return se_extension->supports_capability(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT);
}
bool OpenXRSpatialMarkerTrackingCapability::is_april_tag_supported() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
return se_extension->supports_capability(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT);
}
////////////////////////////////////////////////////////////////////////////
// Discovery logic
Ref<OpenXRFutureResult> OpenXRSpatialMarkerTrackingCapability::_create_spatial_context() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
TypedArray<OpenXRSpatialCapabilityConfigurationBaseHeader> capability_configurations;
// Create our configuration objects.
// For now we enable all supported markers, will need to give some more user control over this.
if (is_qrcode_supported()) {
qrcode_configuration.instantiate();
capability_configurations.push_back(qrcode_configuration);
}
if (is_micro_qrcode_supported()) {
micro_qrcode_configuration.instantiate();
capability_configurations.push_back(micro_qrcode_configuration);
}
if (is_aruco_supported()) {
aruco_configuration.instantiate();
int aruco_dict = GLOBAL_GET_CACHED(int, "xr/openxr/extensions/spatial_entity/aruco_dict");
aruco_configuration->set_aruco_dict((XrSpatialMarkerArucoDictEXT)(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT + aruco_dict));
capability_configurations.push_back(aruco_configuration);
}
if (is_april_tag_supported()) {
april_tag_configuration.instantiate();
int april_tag_dict = GLOBAL_GET_CACHED(int, "xr/openxr/extensions/spatial_entity/april_tag_dict");
april_tag_configuration->set_april_dict((XrSpatialMarkerAprilTagDictEXT)(XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT + april_tag_dict));
capability_configurations.push_back(april_tag_configuration);
}
if (capability_configurations.is_empty()) {
print_verbose("OpenXR: There are no supported marker types. Marker tracking is not enabled.");
return nullptr;
}
return se_extension->create_spatial_context(capability_configurations, nullptr, callable_mp(this, &OpenXRSpatialMarkerTrackingCapability::_on_spatial_context_created));
}
void OpenXRSpatialMarkerTrackingCapability::_on_spatial_context_created(RID p_spatial_context) {
spatial_context = p_spatial_context;
need_discovery = true;
}
void OpenXRSpatialMarkerTrackingCapability::_on_spatial_discovery_recommended(RID p_spatial_context) {
if (p_spatial_context == spatial_context) {
// Trigger new discovery.
need_discovery = true;
}
}
Ref<OpenXRFutureResult> OpenXRSpatialMarkerTrackingCapability::_start_entity_discovery() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Already running or ran discovery, cancel/clean up.
if (discovery_query_result.is_valid()) {
discovery_query_result->cancel_future();
discovery_query_result.unref();
}
// We want both our anchor and persistence component.
Vector<XrSpatialComponentTypeEXT> component_types;
component_types.push_back(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT);
component_types.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
// Start our new snapshot.
discovery_query_result = se_extension->discover_spatial_entities(spatial_context, component_types, nullptr, callable_mp(this, &OpenXRSpatialMarkerTrackingCapability::_process_snapshot).bind(true));
return discovery_query_result;
}
void OpenXRSpatialMarkerTrackingCapability::_process_snapshot(RID p_snapshot, bool p_is_discovery) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Make a copy of the markers we have right now, so we know which ones to clean up.
LocalVector<XrSpatialEntityIdEXT> current_markers;
if (p_is_discovery) {
current_markers.resize(marker_trackers.size());
int m = 0;
for (const KeyValue<XrSpatialEntityIdEXT, Ref<OpenXRMarkerTracker>> &marker : marker_trackers) {
current_markers[m++] = marker.key;
}
}
// Build our component data.
TypedArray<OpenXRSpatialComponentData> component_data;
// We always need a query result data object.
Ref<OpenXRSpatialQueryResultData> query_result_data;
query_result_data.instantiate();
component_data.push_back(query_result_data);
// Add bounded2D.
Ref<OpenXRSpatialComponentBounded2DList> bounded2d_list;
bounded2d_list.instantiate();
component_data.push_back(bounded2d_list);
// Marker data list.
Ref<OpenXRSpatialComponentMarkerList> marker_list;
if (p_is_discovery) {
marker_list.instantiate();
component_data.push_back(marker_list);
}
if (se_extension->query_snapshot(p_snapshot, component_data, nullptr)) {
// Now loop through our data and update our markers.
int64_t size = query_result_data->get_capacity();
for (int64_t i = 0; i < size; i++) {
XrSpatialEntityIdEXT entity_id = query_result_data->get_entity_id(i);
XrSpatialEntityTrackingStateEXT entity_state = query_result_data->get_entity_state(i);
// Erase it from our current markers (if we have it, else this is ignored).
current_markers.erase(entity_id);
if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT) {
// We should only get this status on update queries.
// We'll remove the marker.
if (marker_trackers.has(entity_id)) {
Ref<OpenXRMarkerTracker> marker_tracker = marker_trackers[entity_id];
marker_tracker->invalidate_pose(SNAME("default"));
marker_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT);
// Remove it from our XRServer.
xr_server->remove_tracker(marker_tracker);
// Remove it from our trackers.
marker_trackers.erase(entity_id);
}
} else {
// Process our entity.
bool add_to_xr_server = false;
Ref<OpenXRMarkerTracker> marker_tracker;
if (marker_trackers.has(entity_id)) {
// We know about this one already.
marker_tracker = marker_trackers[entity_id];
} else {
// Create a new anchor.
marker_tracker.instantiate();
marker_tracker->set_entity(se_extension->make_spatial_entity(se_extension->get_spatial_snapshot_context(p_snapshot), entity_id));
marker_trackers[entity_id] = marker_tracker;
add_to_xr_server = true;
}
// Handle component data.
if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT) {
marker_tracker->invalidate_pose(SNAME("default"));
marker_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT);
// No further component data will be valid in this state, we need to ignore it!
} else if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
Transform3D transform = bounded2d_list->get_center_pose(i);
marker_tracker->set_pose(SNAME("default"), transform, Vector3(), Vector3());
marker_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT);
// Process our component data.
// Set bounds size.
marker_tracker->set_bounds_size(bounded2d_list->get_size(i));
// Set marker data.
if (p_is_discovery) {
marker_tracker->set_marker_type(marker_list->get_marker_type(i));
marker_tracker->set_marker_id(marker_list->get_marker_id(i));
marker_tracker->set_marker_data(marker_list->get_marker_data(p_snapshot, i));
}
}
if (add_to_xr_server) {
// Register with XR server.
xr_server->add_tracker(marker_tracker);
}
}
}
if (p_is_discovery) {
// Remove any markers that are no longer there...
for (const XrSpatialEntityIdEXT &entity_id : current_markers) {
if (marker_trackers.has(entity_id)) {
Ref<OpenXRMarkerTracker> marker_tracker = marker_trackers[entity_id];
// Just in case there are still references out there to this marker,
// reset some stuff.
marker_tracker->invalidate_pose(SNAME("default"));
marker_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT);
// Remove it from our XRServer.
xr_server->remove_tracker(marker_tracker);
// Remove it from our trackers.
marker_trackers.erase(entity_id);
}
}
}
}
// Now that we're done, clean up our snapshot!
se_extension->free_spatial_snapshot(p_snapshot);
// And if this was our discovery snapshot, let's reset it.
if (p_is_discovery && discovery_query_result.is_valid() && discovery_query_result->get_result_value() == p_snapshot) {
discovery_query_result.unref();
}
}

View File

@ -0,0 +1,268 @@
/**************************************************************************/
/* openxr_spatial_marker_tracking.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "openxr_spatial_entities.h"
// QrCode marker tracking capability configuration
class OpenXRSpatialCapabilityConfigurationQrCode : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationQrCode, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return enabled_components; }
protected:
static void _bind_methods();
private:
Vector<XrSpatialComponentTypeEXT> enabled_components;
XrSpatialCapabilityConfigurationQrCodeEXT marker_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT, nullptr, XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT, 0, nullptr };
PackedInt64Array _get_enabled_components() const;
};
// Micro QrCode marker tracking capability configuration
class OpenXRSpatialCapabilityConfigurationMicroQrCode : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationMicroQrCode, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return enabled_components; }
protected:
static void _bind_methods();
private:
Vector<XrSpatialComponentTypeEXT> enabled_components;
XrSpatialCapabilityConfigurationMicroQrCodeEXT marker_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT, nullptr, XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT, 0, nullptr };
PackedInt64Array _get_enabled_components() const;
};
// Aruco marker tracking capability configuration
class OpenXRSpatialCapabilityConfigurationAruco : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationAruco, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
enum ArucoDict {
ARUCO_DICT_4X4_50 = XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT,
ARUCO_DICT_4X4_100 = XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT,
ARUCO_DICT_4X4_250 = XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT,
ARUCO_DICT_4X4_1000 = XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT,
ARUCO_DICT_5X5_50 = XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT,
ARUCO_DICT_5X5_100 = XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT,
ARUCO_DICT_5X5_250 = XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT,
ARUCO_DICT_5X5_1000 = XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT,
ARUCO_DICT_6X6_50 = XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT,
ARUCO_DICT_6X6_100 = XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT,
ARUCO_DICT_6X6_250 = XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT,
ARUCO_DICT_6X6_1000 = XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT,
ARUCO_DICT_7X7_50 = XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT,
ARUCO_DICT_7X7_100 = XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT,
ARUCO_DICT_7X7_250 = XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT,
ARUCO_DICT_7X7_1000 = XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT,
};
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
void set_aruco_dict(XrSpatialMarkerArucoDictEXT p_dict);
XrSpatialMarkerArucoDictEXT get_aruco_dict() const;
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return enabled_components; }
protected:
static void _bind_methods();
private:
Vector<XrSpatialComponentTypeEXT> enabled_components;
XrSpatialCapabilityConfigurationArucoMarkerEXT marker_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT, nullptr, XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT, 0, nullptr, XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT };
PackedInt64Array _get_enabled_components() const;
void _set_aruco_dict(ArucoDict p_dict);
ArucoDict _get_aruco_dict() const;
};
VARIANT_ENUM_CAST(OpenXRSpatialCapabilityConfigurationAruco::ArucoDict);
// April tag marker tracking capability configuration
class OpenXRSpatialCapabilityConfigurationAprilTag : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationAprilTag, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
enum AprilTagDict {
APRIL_TAG_DICT_16H5 = XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT,
APRIL_TAG_DICT_25H9 = XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT,
APRIL_TAG_DICT_36H10 = XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT,
APRIL_TAG_DICT_36H11 = XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT,
};
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
void set_april_dict(XrSpatialMarkerAprilTagDictEXT p_dict);
XrSpatialMarkerAprilTagDictEXT get_april_dict() const;
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return enabled_components; }
protected:
static void _bind_methods();
private:
Vector<XrSpatialComponentTypeEXT> enabled_components;
XrSpatialCapabilityConfigurationAprilTagEXT marker_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT, nullptr, XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT, 0, nullptr, XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT };
PackedInt64Array _get_enabled_components() const;
void _set_april_dict(AprilTagDict p_dict);
AprilTagDict _get_april_dict() const;
};
VARIANT_ENUM_CAST(OpenXRSpatialCapabilityConfigurationAprilTag::AprilTagDict);
// Marker component data
class OpenXRSpatialComponentMarkerList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentMarkerList, OpenXRSpatialComponentData);
public:
enum MarkerType {
MARKER_TYPE_UNKNOWN,
MARKER_TYPE_QRCODE,
MARKER_TYPE_MICRO_QRCODE,
MARKER_TYPE_ARUCO,
MARKER_TYPE_APRIL_TAG,
MARKER_TYPE_MAX
};
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
MarkerType get_marker_type(int64_t p_index) const;
uint32_t get_marker_id(int64_t p_index) const;
Variant get_marker_data(RID p_snapshot, int64_t p_index) const;
protected:
static void _bind_methods();
private:
Vector<XrSpatialMarkerDataEXT> marker_data;
XrSpatialComponentMarkerListEXT marker_list = { XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT, nullptr, 0, nullptr };
};
VARIANT_ENUM_CAST(OpenXRSpatialComponentMarkerList::MarkerType);
// Marker tracker
class OpenXRMarkerTracker : public OpenXRSpatialEntityTracker {
GDCLASS(OpenXRMarkerTracker, OpenXRSpatialEntityTracker);
public:
void set_bounds_size(const Vector2 &p_bounds_size);
Vector2 get_bounds_size() const;
void set_marker_type(OpenXRSpatialComponentMarkerList::MarkerType p_marker_type);
OpenXRSpatialComponentMarkerList::MarkerType get_marker_type() const;
void set_marker_id(uint32_t p_id);
uint32_t get_marker_id() const;
void set_marker_data(const Variant &p_data);
Variant get_marker_data() const;
protected:
static void _bind_methods();
private:
Vector2 bounds_size;
OpenXRSpatialComponentMarkerList::MarkerType marker_type = OpenXRSpatialComponentMarkerList::MarkerType::MARKER_TYPE_UNKNOWN;
uint32_t marker_id = 0;
Variant marker_data;
};
// Marker tracking logic
class OpenXRSpatialMarkerTrackingCapability : public OpenXRExtensionWrapper {
GDCLASS(OpenXRSpatialMarkerTrackingCapability, OpenXRExtensionWrapper);
protected:
static void _bind_methods();
public:
static OpenXRSpatialMarkerTrackingCapability *get_singleton();
OpenXRSpatialMarkerTrackingCapability();
virtual ~OpenXRSpatialMarkerTrackingCapability() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_process() override;
bool is_qrcode_supported();
bool is_micro_qrcode_supported();
bool is_aruco_supported();
bool is_april_tag_supported();
private:
static OpenXRSpatialMarkerTrackingCapability *singleton;
bool spatial_marker_tracking_ext = false;
RID spatial_context;
bool need_discovery = false;
int discovery_cooldown = 0;
Ref<OpenXRFutureResult> discovery_query_result;
Ref<OpenXRSpatialCapabilityConfigurationQrCode> qrcode_configuration;
Ref<OpenXRSpatialCapabilityConfigurationMicroQrCode> micro_qrcode_configuration;
Ref<OpenXRSpatialCapabilityConfigurationAruco> aruco_configuration;
Ref<OpenXRSpatialCapabilityConfigurationAprilTag> april_tag_configuration;
// Discovery logic
Ref<OpenXRFutureResult> _create_spatial_context();
void _on_spatial_context_created(RID p_spatial_context);
void _on_spatial_discovery_recommended(RID p_spatial_context);
Ref<OpenXRFutureResult> _start_entity_discovery();
void _process_snapshot(RID p_snapshot, bool p_is_discovery);
// Trackers
HashMap<XrSpatialEntityIdEXT, Ref<OpenXRMarkerTracker>> marker_trackers;
};

View File

@ -0,0 +1,879 @@
/**************************************************************************/
/* openxr_spatial_plane_tracking.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_plane_tracking.h"
#include "../../openxr_api.h"
#include "core/config/project_settings.h"
#include "scene/resources/3d/box_shape_3d.h"
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/xr_server.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialCapabilityConfigurationPlaneTracking
void OpenXRSpatialCapabilityConfigurationPlaneTracking::_bind_methods() {
ClassDB::bind_method(D_METHOD("supports_mesh_2d"), &OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_mesh_2d);
ClassDB::bind_method(D_METHOD("supports_polygons"), &OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_polygons);
ClassDB::bind_method(D_METHOD("supports_labels"), &OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_labels);
ClassDB::bind_method(D_METHOD("get_enabled_components"), &OpenXRSpatialCapabilityConfigurationPlaneTracking::_get_enabled_components);
}
bool OpenXRSpatialCapabilityConfigurationPlaneTracking::has_valid_configuration() const {
OpenXRSpatialPlaneTrackingCapability *capability = OpenXRSpatialPlaneTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, false);
return capability->is_supported();
}
XrSpatialCapabilityConfigurationBaseHeaderEXT *OpenXRSpatialCapabilityConfigurationPlaneTracking::get_configuration() {
OpenXRSpatialPlaneTrackingCapability *capability = OpenXRSpatialPlaneTrackingCapability::get_singleton();
ERR_FAIL_NULL_V(capability, nullptr);
if (capability->is_supported()) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Guaranteed components:
plane_enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT);
plane_enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT);
// Optional components:
if (get_supports_mesh_2d()) {
plane_enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT);
} else if (get_supports_polygons()) {
plane_enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT);
}
if (get_supports_labels()) {
plane_enabled_components.push_back(XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT);
}
// Set up our enabled components.
plane_config.enabledComponentCount = plane_enabled_components.size();
plane_config.enabledComponents = plane_enabled_components.ptr();
// and return this.
return (XrSpatialCapabilityConfigurationBaseHeaderEXT *)&plane_config;
}
return nullptr;
}
bool OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_mesh_2d() {
if (supports_mesh_2d == -1) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
supports_mesh_2d = se_extension->supports_component_type(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT) ? 1 : 0;
}
return supports_mesh_2d == 1;
}
bool OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_polygons() {
if (supports_polygons == -1) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
supports_polygons = se_extension->supports_component_type(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT) ? 1 : 0;
}
return supports_polygons == 1;
}
bool OpenXRSpatialCapabilityConfigurationPlaneTracking::get_supports_labels() {
if (supports_labels == -1) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, false);
supports_labels = se_extension->supports_component_type(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT) ? 1 : 0;
}
return supports_labels == 1;
}
PackedInt64Array OpenXRSpatialCapabilityConfigurationPlaneTracking::_get_enabled_components() const {
PackedInt64Array components;
for (const XrSpatialComponentTypeEXT &component_type : plane_enabled_components) {
components.push_back((int64_t)component_type);
}
return components;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialComponentPlaneAlignmentList
void OpenXRSpatialComponentPlaneAlignmentList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plane_alignment", "index"), &OpenXRSpatialComponentPlaneAlignmentList::_get_plane_alignment);
BIND_ENUM_CONSTANT(PLANE_ALIGNMENT_HORIZONTAL_UPWARD);
BIND_ENUM_CONSTANT(PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD);
BIND_ENUM_CONSTANT(PLANE_ALIGNMENT_VERTICAL);
BIND_ENUM_CONSTANT(PLANE_ALIGNMENT_ARBITRARY);
}
void OpenXRSpatialComponentPlaneAlignmentList::set_capacity(uint32_t p_capacity) {
plane_alignment_data.resize(p_capacity);
plane_alignment_list.planeAlignmentCount = uint32_t(plane_alignment_data.size());
plane_alignment_list.planeAlignments = plane_alignment_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentPlaneAlignmentList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT;
}
void *OpenXRSpatialComponentPlaneAlignmentList::get_structure_data(void *p_next) {
plane_alignment_list.next = p_next;
return &plane_alignment_list;
}
XrSpatialPlaneAlignmentEXT OpenXRSpatialComponentPlaneAlignmentList::get_plane_alignment(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, plane_alignment_data.size(), XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT);
return plane_alignment_data[p_index];
}
OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment OpenXRSpatialComponentPlaneAlignmentList::_get_plane_alignment(int64_t p_index) const {
return (PlaneAlignment)get_plane_alignment(p_index);
}
////////////////////////////////////////////////////////////////////////////
// Spatial component polygon2d list
void OpenXRSpatialComponentPolygon2DList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_transform", "index"), &OpenXRSpatialComponentPolygon2DList::get_transform);
ClassDB::bind_method(D_METHOD("get_vertices", "snapshot", "index"), &OpenXRSpatialComponentPolygon2DList::get_vertices);
}
void OpenXRSpatialComponentPolygon2DList::set_capacity(uint32_t p_capacity) {
polygon2d_data.resize(p_capacity);
polygon2d_list.polygonCount = uint32_t(polygon2d_data.size());
polygon2d_list.polygons = polygon2d_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentPolygon2DList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT;
}
void *OpenXRSpatialComponentPolygon2DList::get_structure_data(void *p_next) {
polygon2d_list.next = p_next;
return &polygon2d_list;
}
Transform3D OpenXRSpatialComponentPolygon2DList::get_transform(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, polygon2d_data.size(), Transform3D());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
return openxr_api->transform_from_pose(polygon2d_data[p_index].origin);
}
PackedVector2Array OpenXRSpatialComponentPolygon2DList::get_vertices(RID p_snapshot, int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, polygon2d_data.size(), PackedVector2Array());
const XrSpatialBufferEXT &buffer = polygon2d_data[p_index].vertexBuffer;
if (buffer.bufferId == XR_NULL_SPATIAL_BUFFER_ID_EXT) {
// We don't have data (yet).
return PackedVector2Array();
}
ERR_FAIL_COND_V(buffer.bufferType != XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT, PackedVector2Array());
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, PackedVector2Array());
return se_extension->get_vector2_buffer(p_snapshot, buffer.bufferId);
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialComponentPlaneSemanticLabelList
void OpenXRSpatialComponentPlaneSemanticLabelList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plane_semantic_label", "index"), &OpenXRSpatialComponentPlaneSemanticLabelList::_get_plane_semantic_label);
BIND_ENUM_CONSTANT(PLANE_SEMANTIC_LABEL_UNCATEGORIZED);
BIND_ENUM_CONSTANT(PLANE_SEMANTIC_LABEL_FLOOR);
BIND_ENUM_CONSTANT(PLANE_SEMANTIC_LABEL_WALL);
BIND_ENUM_CONSTANT(PLANE_SEMANTIC_LABEL_CEILING);
BIND_ENUM_CONSTANT(PLANE_SEMANTIC_LABEL_TABLE);
}
void OpenXRSpatialComponentPlaneSemanticLabelList::set_capacity(uint32_t p_capacity) {
plane_semantic_label_data.resize(p_capacity);
plane_semantic_label_list.semanticLabelCount = uint32_t(plane_semantic_label_data.size());
plane_semantic_label_list.semanticLabels = plane_semantic_label_data.ptrw();
}
XrSpatialComponentTypeEXT OpenXRSpatialComponentPlaneSemanticLabelList::get_component_type() const {
return XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT;
}
void *OpenXRSpatialComponentPlaneSemanticLabelList::get_structure_data(void *p_next) {
plane_semantic_label_list.next = p_next;
return &plane_semantic_label_list;
}
XrSpatialPlaneSemanticLabelEXT OpenXRSpatialComponentPlaneSemanticLabelList::get_plane_semantic_label(int64_t p_index) const {
ERR_FAIL_INDEX_V(p_index, plane_semantic_label_data.size(), XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT);
return plane_semantic_label_data[p_index];
}
OpenXRSpatialComponentPlaneSemanticLabelList::PlaneSemanticLabel OpenXRSpatialComponentPlaneSemanticLabelList::_get_plane_semantic_label(int64_t p_index) const {
return (PlaneSemanticLabel)get_plane_semantic_label(p_index);
}
////////////////////////////////////////////////////////////////////////////
// OpenXRPlaneTracker
void OpenXRPlaneTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounds_size", "bounds_size"), &OpenXRPlaneTracker::set_bounds_size);
ClassDB::bind_method(D_METHOD("get_bounds_size"), &OpenXRPlaneTracker::get_bounds_size);
ADD_PROPERTY(PropertyInfo(Variant::INT, "bounds_size"), "set_bounds_size", "get_bounds_size");
ClassDB::bind_method(D_METHOD("set_plane_alignment", "plane_alignment"), &OpenXRPlaneTracker::set_plane_alignment);
ClassDB::bind_method(D_METHOD("get_plane_alignment"), &OpenXRPlaneTracker::get_plane_alignment);
ADD_PROPERTY(PropertyInfo(Variant::INT, "plane_alignment"), "set_plane_alignment", "get_plane_alignment");
ClassDB::bind_method(D_METHOD("set_plane_label", "plane_label"), &OpenXRPlaneTracker::set_plane_label);
ClassDB::bind_method(D_METHOD("get_plane_label"), &OpenXRPlaneTracker::get_plane_label);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "plane_label"), "set_plane_label", "get_plane_label");
ClassDB::bind_method(D_METHOD("set_mesh_data", "origin", "vertices", "indices"), &OpenXRPlaneTracker::set_mesh_data, DEFVAL(PackedInt32Array()));
ClassDB::bind_method(D_METHOD("clear_mesh_data"), &OpenXRPlaneTracker::clear_mesh_data);
ClassDB::bind_method(D_METHOD("get_mesh_offset"), &OpenXRPlaneTracker::get_mesh_offset);
ClassDB::bind_method(D_METHOD("get_mesh"), &OpenXRPlaneTracker::get_mesh);
ClassDB::bind_method(D_METHOD("get_shape", "thickness"), &OpenXRPlaneTracker::get_shape, DEFVAL(0.01));
ADD_SIGNAL(MethodInfo("mesh_changed"));
}
void OpenXRPlaneTracker::set_bounds_size(const Vector2 &p_bounds_size) {
if (Math::abs(bounds_size.x - p_bounds_size.x) > 0.001 || Math::abs(bounds_size.y - p_bounds_size.y) > 0.001) {
bounds_size = p_bounds_size;
if (!mesh.has_mesh_data) {
// Bounds changing only effects mesh data if we don't have polygon data.
clear_mesh_data();
emit_signal(SNAME("mesh_changed"));
}
}
}
Vector2 OpenXRPlaneTracker::get_bounds_size() const {
return bounds_size;
}
void OpenXRPlaneTracker::set_plane_alignment(OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment p_plane_alignment) {
if (plane_alignment != p_plane_alignment) {
plane_alignment = p_plane_alignment;
}
}
OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment OpenXRPlaneTracker::get_plane_alignment() const {
return plane_alignment;
}
void OpenXRPlaneTracker::set_plane_label(const String &p_plane_label) {
if (plane_label != p_plane_label) {
plane_label = p_plane_label;
// Also copy to description, should do something nicer here.
set_tracker_desc(plane_label);
}
}
String OpenXRPlaneTracker::get_plane_label() const {
return plane_label;
}
void OpenXRPlaneTracker::set_mesh_data(const Transform3D &p_origin, const PackedVector2Array &p_vertices, const PackedInt32Array &p_indices) {
if (p_vertices.size() < 3) {
if (mesh.has_mesh_data) {
clear_mesh_data();
emit_signal(SNAME("mesh_changed"));
}
} else {
bool has_changed = !mesh.has_mesh_data;
mesh.has_mesh_data = true;
mesh.origin = p_origin;
if (mesh.vertices.size() != p_vertices.size()) {
has_changed = true;
} else {
// Compare the vertices with a bit of margin, we ignore small jittering on vertices.
for (uint32_t i = 0; i < p_vertices.size() && !has_changed; i++) {
const Vector2 &a = p_vertices[i];
const Vector2 &b = mesh.vertices[i];
has_changed = (Math::abs(a.x - b.x) > 0.001) || (Math::abs(a.y - b.y) > 0.001);
}
}
if (has_changed) {
mesh.vertices = p_vertices;
}
// Q: Should we keep our indices list empty if we get polygon data
// and create different meshes/collision shapes as a result?
if (p_indices.is_empty()) {
// Assume polygon, turn into triangle strip...
int count = (p_vertices.size() - 2) * 3;
// If our vertices haven't changed and our indices are already the correct size,
// assume we don't need to rerun this.
if (has_changed || mesh.indices.size() != count) {
has_changed = true;
int offset = 1;
mesh.indices.resize(count);
int32_t *idx = mesh.indices.ptrw();
for (int i = 0; i < count; i += 3) {
idx[i + 0] = 0;
idx[i + 2] = offset++;
idx[i + 1] = offset;
}
}
} else {
if (mesh.indices.size() != p_indices.size()) {
has_changed = true;
} else {
for (uint32_t i = 0; i < p_indices.size() && !has_changed; i++) {
has_changed = mesh.indices[i] != p_indices[i];
}
}
if (has_changed) {
mesh.indices = p_indices;
}
}
if (has_changed) {
mesh.mesh.unref();
mesh.shape3d.unref();
emit_signal(SNAME("mesh_changed"));
}
}
}
void OpenXRPlaneTracker::clear_mesh_data() {
mesh.mesh.unref();
mesh.shape3d.unref();
if (mesh.has_mesh_data) {
mesh.has_mesh_data = false;
mesh.origin = Transform3D();
mesh.vertices.clear();
mesh.indices.clear();
emit_signal(SNAME("mesh_changed"));
}
}
Transform3D OpenXRPlaneTracker::get_mesh_offset() const {
Transform3D offset;
if (mesh.has_mesh_data) {
offset = mesh.origin;
Ref<XRPose> pose = get_pose(SNAME("default"));
if (pose.is_valid()) {
// Q is this offset * transform.inverse?
offset = pose->get_transform().inverse() * offset;
}
// Reference frame will already be applied to pose used on our XRNode3D but we do need to apply our scale
XRServer *xr_server = XRServer::get_singleton();
if (xr_server) {
offset.origin *= xr_server->get_world_scale();
}
}
return offset;
}
Ref<Mesh> OpenXRPlaneTracker::get_mesh() {
// We've already created this? Just return it!
if (mesh.mesh.is_valid()) {
return mesh.mesh;
}
if (mesh.has_mesh_data) {
Ref<ArrayMesh> array_mesh;
Array arr;
// We need our vertices as Vector3
PackedVector3Array vertices;
vertices.resize(mesh.vertices.size());
const Vector2 *read = mesh.vertices.ptr();
Vector3 *write = vertices.ptrw();
for (int v = 0; v < mesh.vertices.size(); v++) {
write[v] = Vector3(read[v].x, read[v].y, 0.0);
}
// Build our array with data.
arr.resize(RS::ARRAY_MAX);
arr[RS::ARRAY_VERTEX] = vertices;
arr[RS::ARRAY_INDEX] = mesh.indices;
// Create our array mesh.
array_mesh.instantiate();
array_mesh->add_surface_from_arrays(Mesh::PrimitiveType::PRIMITIVE_TRIANGLES, arr);
// Cache this.
mesh.mesh = array_mesh;
} else if (bounds_size.x > 0.0 && bounds_size.y > 0.0) {
// We can use a plane mesh here.
Ref<PlaneMesh> plane_mesh;
plane_mesh.instantiate();
plane_mesh->set_orientation(PlaneMesh::Orientation::FACE_Z);
plane_mesh->set_size(bounds_size);
// Cache this.
mesh.mesh = plane_mesh;
} else {
print_verbose("OpenXR: Can't create mesh for plane, no data.");
}
return mesh.mesh;
}
Ref<Shape3D> OpenXRPlaneTracker::get_shape(real_t p_thickness) {
// We've already created this? Just return it!
if (mesh.shape3d.is_valid()) {
return mesh.shape3d;
}
if (mesh.has_mesh_data) {
Ref<ConcavePolygonShape3D> shape;
Vector<Vector3> faces;
// Get some direct access to our data.
int isize = mesh.indices.size();
const Vector2 *vr = mesh.vertices.ptr();
const int32_t *ir = mesh.indices.ptr();
// Find our edges.
HashMap<Edge, int, Edge> edge_counts;
for (int i = 0; i < isize; i += 3) {
for (int j = 0; j < 3; j++) {
Edge e(ir[i + j], ir[i + ((j + 1) % 3)]);
edge_counts[e]++;
}
}
// Find our outer edges.
thread_local LocalVector<Edge> outer_edges;
outer_edges.clear();
for (const KeyValue<Edge, int> &e : edge_counts) {
if (e.value > 1) {
outer_edges.push_back(e.key);
}
}
// Make space for these.
faces.resize(2 * isize + 6 * outer_edges.size());
Vector3 *write = faces.ptrw();
// Add top and bottom.
for (int i = 0; i < isize; i += 3) {
Vector3 a = Vector3(vr[ir[i]].x, vr[ir[i]].y, 0.0);
Vector3 b = Vector3(vr[ir[i + 1]].x, vr[ir[i + 1]].y, 0.0);
Vector3 c = Vector3(vr[ir[i + 2]].x, vr[ir[i + 2]].y, 0.0);
*write++ = a;
*write++ = b;
*write++ = c;
a.z = -p_thickness;
b.z = -p_thickness;
c.z = -p_thickness;
*write++ = a;
*write++ = c;
*write++ = b;
}
// Add outer edges.
for (const Edge &edge : outer_edges) {
Vector3 a = Vector3(vr[edge.a].x, vr[edge.a].y, 0.0);
Vector3 b = Vector3(vr[edge.b].x, vr[edge.b].y, 0.0);
Vector3 c = b + Vector3(0.0, 0.0, -p_thickness);
Vector3 d = a + Vector3(0.0, 0.0, -p_thickness);
*write++ = a;
*write++ = b;
*write++ = c;
*write++ = a;
*write++ = c;
*write++ = d;
}
// Create our shape.
shape.instantiate();
shape->set_faces(faces);
mesh.shape3d = shape;
} else if (bounds_size.x > 0.0 && bounds_size.y > 0.0) {
// We can use a box shape here
Ref<BoxShape3D> box_shape;
box_shape.instantiate();
box_shape->set_size(Vector3(bounds_size.x, bounds_size.y, p_thickness));
mesh.shape3d = box_shape;
}
return mesh.shape3d;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRSpatialPlaneTrackingCapability
OpenXRSpatialPlaneTrackingCapability *OpenXRSpatialPlaneTrackingCapability::singleton = nullptr;
OpenXRSpatialPlaneTrackingCapability *OpenXRSpatialPlaneTrackingCapability::get_singleton() {
return singleton;
}
OpenXRSpatialPlaneTrackingCapability::OpenXRSpatialPlaneTrackingCapability() {
singleton = this;
}
OpenXRSpatialPlaneTrackingCapability::~OpenXRSpatialPlaneTrackingCapability() {
singleton = nullptr;
}
void OpenXRSpatialPlaneTrackingCapability::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_supported"), &OpenXRSpatialPlaneTrackingCapability::is_supported);
}
HashMap<String, bool *> OpenXRSpatialPlaneTrackingCapability::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enabled") && GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enable_plane_tracking")) {
request_extensions[XR_EXT_SPATIAL_PLANE_TRACKING_EXTENSION_NAME] = &spatial_plane_tracking_ext;
}
return request_extensions;
}
void OpenXRSpatialPlaneTrackingCapability::on_session_created(const XrSession p_session) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
if (!spatial_plane_tracking_ext) {
return;
}
spatial_plane_tracking_supported = se_extension->supports_capability(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT);
if (!spatial_plane_tracking_supported) {
// Supported by XR runtime but not by device? We're done.
return;
}
se_extension->connect(SNAME("spatial_discovery_recommended"), callable_mp(this, &OpenXRSpatialPlaneTrackingCapability::_on_spatial_discovery_recommended));
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/spatial_entity/enable_builtin_plane_detection")) {
// Start by creating our spatial context
_create_spatial_context();
}
}
void OpenXRSpatialPlaneTrackingCapability::on_session_destroyed() {
if (!spatial_plane_tracking_supported) {
return;
}
spatial_plane_tracking_supported = false;
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
// Free and unregister our anchors
for (const KeyValue<XrSpatialEntityIdEXT, Ref<OpenXRPlaneTracker>> &plane_tracker : plane_trackers) {
xr_server->remove_tracker(plane_tracker.value);
}
plane_trackers.clear();
// Free our spatial context
if (spatial_context.is_valid()) {
se_extension->free_spatial_context(spatial_context);
spatial_context = RID();
}
se_extension->disconnect(SNAME("spatial_discovery_recommended"), callable_mp(this, &OpenXRSpatialPlaneTrackingCapability::_on_spatial_discovery_recommended));
}
void OpenXRSpatialPlaneTrackingCapability::on_process() {
if (!spatial_context.is_valid()) {
return;
}
// Protection against plane discovery happening too often.
if (discovery_cooldown > 0) {
discovery_cooldown--;
}
// Check if we need to start our discovery.
if (need_discovery && discovery_cooldown == 0 && !discovery_query_result.is_valid()) {
need_discovery = false;
discovery_cooldown = 60; // Set our cooldown to 60 frames, it doesn't need to be an exact science.
_start_entity_discovery();
}
}
bool OpenXRSpatialPlaneTrackingCapability::is_supported() {
return spatial_plane_tracking_supported;
}
////////////////////////////////////////////////////////////////////////////
// Discovery logic
Ref<OpenXRFutureResult> OpenXRSpatialPlaneTrackingCapability::_create_spatial_context() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
TypedArray<OpenXRSpatialCapabilityConfigurationBaseHeader> capability_configurations;
// Create our configuration objects.
plane_configuration.instantiate();
capability_configurations.push_back(plane_configuration);
return se_extension->create_spatial_context(capability_configurations, nullptr, callable_mp(this, &OpenXRSpatialPlaneTrackingCapability::_on_spatial_context_created));
}
void OpenXRSpatialPlaneTrackingCapability::_on_spatial_context_created(RID p_spatial_context) {
spatial_context = p_spatial_context;
need_discovery = true;
}
void OpenXRSpatialPlaneTrackingCapability::_on_spatial_discovery_recommended(RID p_spatial_context) {
if (p_spatial_context == spatial_context) {
// Trigger new discovery.
need_discovery = true;
}
}
Ref<OpenXRFutureResult> OpenXRSpatialPlaneTrackingCapability::_start_entity_discovery() {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL_V(se_extension, nullptr);
// Already running or ran discovery, cancel/clean up.
if (discovery_query_result.is_valid()) {
WARN_PRINT("OpenXR: Starting new discovery before previous discovery has been processed!");
discovery_query_result->cancel_future();
discovery_query_result.unref();
}
// Start our new snapshot.
discovery_query_result = se_extension->discover_spatial_entities(spatial_context, plane_configuration->get_enabled_components(), nullptr, callable_mp(this, &OpenXRSpatialPlaneTrackingCapability::_process_snapshot));
return discovery_query_result;
}
void OpenXRSpatialPlaneTrackingCapability::_process_snapshot(RID p_snapshot) {
OpenXRSpatialEntityExtension *se_extension = OpenXRSpatialEntityExtension::get_singleton();
ERR_FAIL_NULL(se_extension);
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Make a copy of the planes we have right now, so we know which ones to clean up.
LocalVector<XrSpatialEntityIdEXT> current_planes;
current_planes.resize(plane_trackers.size());
int p = 0;
for (const KeyValue<XrSpatialEntityIdEXT, Ref<OpenXRPlaneTracker>> &plane : plane_trackers) {
current_planes[p++] = plane.key;
}
// Build our component data
TypedArray<OpenXRSpatialComponentData> component_data;
// We always need a query result data object
Ref<OpenXRSpatialQueryResultData> query_result_data;
query_result_data.instantiate();
component_data.push_back(query_result_data);
// Add bounded2D
Ref<OpenXRSpatialComponentBounded2DList> bounded2d_list;
bounded2d_list.instantiate();
component_data.push_back(bounded2d_list);
// Plane alignment list
Ref<OpenXRSpatialComponentPlaneAlignmentList> alignment_list;
alignment_list.instantiate();
component_data.push_back(alignment_list);
Ref<OpenXRSpatialComponentMesh2DList> mesh2d_list;
Ref<OpenXRSpatialComponentPolygon2DList> poly2d_list;
if (plane_configuration->get_supports_mesh_2d()) {
mesh2d_list.instantiate();
component_data.push_back(mesh2d_list);
} else if (plane_configuration->get_supports_polygons()) {
poly2d_list.instantiate();
component_data.push_back(poly2d_list);
}
// Plane semantic label
Ref<OpenXRSpatialComponentPlaneSemanticLabelList> label_list;
if (plane_configuration->get_supports_labels()) {
label_list.instantiate();
component_data.push_back(label_list);
}
if (se_extension->query_snapshot(p_snapshot, component_data, nullptr)) {
// Now loop through our data and update our anchors.
// Q we're assuming entity ID, size and state size are equal, is there ever a situation where they would not be?
int64_t size = query_result_data->get_capacity();
for (int64_t i = 0; i < size; i++) {
XrSpatialEntityIdEXT entity_id = query_result_data->get_entity_id(i);
XrSpatialEntityTrackingStateEXT entity_state = query_result_data->get_entity_state(i);
// Erase it from our current planes (if we have it, else this is ignored).
current_planes.erase(entity_id);
if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT) {
// We should only get this status on updates as a prelude to needing to remove this marker.
// So we just update the status.
if (plane_trackers.has(entity_id)) {
Ref<OpenXRPlaneTracker> plane_tracker = plane_trackers[entity_id];
plane_tracker->invalidate_pose(SNAME("default"));
plane_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT);
}
} else {
// Process our entity
bool add_to_xr_server = false;
Ref<OpenXRPlaneTracker> plane_tracker;
if (plane_trackers.has(entity_id)) {
// We know about this one already
plane_tracker = plane_trackers[entity_id];
} else {
// Create a new anchor
plane_tracker.instantiate();
plane_tracker->set_entity(se_extension->make_spatial_entity(se_extension->get_spatial_snapshot_context(p_snapshot), entity_id));
plane_trackers[entity_id] = plane_tracker;
add_to_xr_server = true;
}
// Handle component data
if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT) {
plane_tracker->invalidate_pose(SNAME("default"));
plane_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT);
// No further component data will be valid in this state, we need to ignore it!
} else if (entity_state == XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
Transform3D transform = bounded2d_list->get_center_pose(i);
plane_tracker->set_pose(SNAME("default"), transform, Vector3(), Vector3());
plane_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT);
// Process our component data.
plane_tracker->set_bounds_size(bounded2d_list->get_size(i));
plane_tracker->set_plane_alignment((OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment)alignment_list->get_plane_alignment(i));
if (mesh2d_list.is_valid()) {
plane_tracker->set_mesh_data(mesh2d_list->get_transform(i), mesh2d_list->get_vertices(p_snapshot, i), mesh2d_list->get_indices(p_snapshot, i));
} else if (poly2d_list.is_valid()) {
plane_tracker->set_mesh_data(poly2d_list->get_transform(i), poly2d_list->get_vertices(p_snapshot, i));
} else {
// Just in case we set this before.
plane_tracker->clear_mesh_data();
}
if (label_list.is_valid()) {
switch (label_list->get_plane_semantic_label(i)) {
case XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT: {
plane_tracker->set_plane_label("Uncategorized plane");
} break;
case XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT: {
plane_tracker->set_plane_label("Floor plane");
} break;
case XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT: {
plane_tracker->set_plane_label("Wall plane");
} break;
case XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT: {
plane_tracker->set_plane_label("Ceiling plane");
} break;
case XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT: {
plane_tracker->set_plane_label("Table plane");
} break;
default: {
plane_tracker->set_plane_label("Unknown plane");
} break;
}
}
}
if (add_to_xr_server) {
// Register with XR server
xr_server->add_tracker(plane_tracker);
}
}
}
// Remove any planes that are no longer there...
for (const XrSpatialEntityIdEXT &entity_id : current_planes) {
if (plane_trackers.has(entity_id)) {
Ref<OpenXRPlaneTracker> plane_tracker = plane_trackers[entity_id];
// Just in case there are still references out there to this marker,
// reset some stuff.
plane_tracker->invalidate_pose(SNAME("default"));
plane_tracker->set_spatial_tracking_state(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT);
// Remove it from our XRServer
xr_server->remove_tracker(plane_tracker);
// Remove it from our trackers
plane_trackers.erase(entity_id);
}
}
}
// Now that we're done, clean up our snapshot!
se_extension->free_spatial_snapshot(p_snapshot);
// And if this was our discovery snapshot, lets reset it
if (discovery_query_result.is_valid() && discovery_query_result->get_result_value() == p_snapshot) {
discovery_query_result.unref();
}
}

View File

@ -0,0 +1,254 @@
/**************************************************************************/
/* openxr_spatial_plane_tracking.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "openxr_spatial_entities.h"
#include "openxr_spatial_entity_extension.h"
#include "scene/resources/3d/shape_3d.h"
// Plane tracking capability configuration
class OpenXRSpatialCapabilityConfigurationPlaneTracking : public OpenXRSpatialCapabilityConfigurationBaseHeader {
GDCLASS(OpenXRSpatialCapabilityConfigurationPlaneTracking, OpenXRSpatialCapabilityConfigurationBaseHeader);
public:
virtual bool has_valid_configuration() const override;
virtual XrSpatialCapabilityConfigurationBaseHeaderEXT *get_configuration() override;
bool get_supports_mesh_2d();
bool get_supports_polygons();
bool get_supports_labels();
Vector<XrSpatialComponentTypeEXT> get_enabled_components() const { return plane_enabled_components; }
protected:
static void _bind_methods();
private:
int supports_mesh_2d = -1;
int supports_polygons = -1;
int supports_labels = -1;
Vector<XrSpatialComponentTypeEXT> plane_enabled_components;
XrSpatialCapabilityConfigurationPlaneTrackingEXT plane_config = { XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT, nullptr, XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, 0, nullptr };
PackedInt64Array _get_enabled_components() const;
};
// Plane alignment component data
class OpenXRSpatialComponentPlaneAlignmentList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentPlaneAlignmentList, OpenXRSpatialComponentData);
public:
enum PlaneAlignment {
PLANE_ALIGNMENT_HORIZONTAL_UPWARD = XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT,
PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD = XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT,
PLANE_ALIGNMENT_VERTICAL = XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT,
PLANE_ALIGNMENT_ARBITRARY = XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT,
};
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
XrSpatialPlaneAlignmentEXT get_plane_alignment(int64_t p_index) const;
protected:
static void _bind_methods();
private:
Vector<XrSpatialPlaneAlignmentEXT> plane_alignment_data;
XrSpatialComponentPlaneAlignmentListEXT plane_alignment_list = { XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT, nullptr, 0, nullptr };
PlaneAlignment _get_plane_alignment(int64_t p_index) const;
};
VARIANT_ENUM_CAST(OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment);
class OpenXRSpatialComponentPolygon2DList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentPolygon2DList, OpenXRSpatialComponentData);
protected:
static void _bind_methods();
public:
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
Transform3D get_transform(int64_t p_index) const;
PackedVector2Array get_vertices(RID p_snapshot, int64_t p_index) const;
private:
Vector<XrSpatialPolygon2DDataEXT> polygon2d_data;
XrSpatialComponentPolygon2DListEXT polygon2d_list = { XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT, nullptr, 0, nullptr };
};
// Plane semantic label component data.
class OpenXRSpatialComponentPlaneSemanticLabelList : public OpenXRSpatialComponentData {
GDCLASS(OpenXRSpatialComponentPlaneSemanticLabelList, OpenXRSpatialComponentData);
public:
enum PlaneSemanticLabel {
PLANE_SEMANTIC_LABEL_UNCATEGORIZED = XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT,
PLANE_SEMANTIC_LABEL_FLOOR = XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT,
PLANE_SEMANTIC_LABEL_WALL = XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT,
PLANE_SEMANTIC_LABEL_CEILING = XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT,
PLANE_SEMANTIC_LABEL_TABLE = XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT,
};
virtual void set_capacity(uint32_t p_capacity) override;
virtual XrSpatialComponentTypeEXT get_component_type() const override;
virtual void *get_structure_data(void *p_next) override;
XrSpatialPlaneSemanticLabelEXT get_plane_semantic_label(int64_t p_index) const;
protected:
static void _bind_methods();
private:
Vector<XrSpatialPlaneSemanticLabelEXT> plane_semantic_label_data;
XrSpatialComponentPlaneSemanticLabelListEXT plane_semantic_label_list = { XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT, nullptr, 0, nullptr };
PlaneSemanticLabel _get_plane_semantic_label(int64_t p_index) const;
};
VARIANT_ENUM_CAST(OpenXRSpatialComponentPlaneSemanticLabelList::PlaneSemanticLabel);
// Plane tracker
class OpenXRPlaneTracker : public OpenXRSpatialEntityTracker {
GDCLASS(OpenXRPlaneTracker, OpenXRSpatialEntityTracker);
public:
void set_bounds_size(const Vector2 &p_bounds_size);
Vector2 get_bounds_size() const;
void set_plane_alignment(OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment p_plane_alignment);
OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment get_plane_alignment() const;
void set_plane_label(const String &p_plane_label);
String get_plane_label() const;
void set_mesh_data(const Transform3D &p_origin, const PackedVector2Array &p_vertices, const PackedInt32Array &p_indices = PackedInt32Array());
void clear_mesh_data();
Transform3D get_mesh_offset() const;
Ref<Mesh> get_mesh();
Ref<Shape3D> get_shape(real_t p_thickness = 0.01);
protected:
static void _bind_methods();
private:
Vector2 bounds_size;
OpenXRSpatialComponentPlaneAlignmentList::PlaneAlignment plane_alignment = OpenXRSpatialComponentPlaneAlignmentList::PLANE_ALIGNMENT_HORIZONTAL_UPWARD;
String plane_label;
// Mesh data (if we have this)
struct MeshData {
bool has_mesh_data = false;
Transform3D origin;
PackedVector2Array vertices;
PackedInt32Array indices;
Ref<Mesh> mesh;
Ref<Shape3D> shape3d;
} mesh;
struct Edge {
int32_t a;
int32_t b;
static _FORCE_INLINE_ uint32_t hash(const Edge &p_edge) {
uint32_t h = hash_murmur3_one_32(p_edge.a);
return hash_murmur3_one_32(p_edge.b, h);
}
bool operator==(const Edge &p_edge) const {
return (a == p_edge.a && b == p_edge.b);
}
Edge(int32_t p_a = 0, int32_t p_b = 0) {
a = p_a;
b = p_b;
if (a < b) {
SWAP(a, b);
}
}
};
};
// Plane tracking logic
class OpenXRSpatialPlaneTrackingCapability : public OpenXRExtensionWrapper {
GDCLASS(OpenXRSpatialPlaneTrackingCapability, OpenXRExtensionWrapper);
protected:
static void _bind_methods();
public:
static OpenXRSpatialPlaneTrackingCapability *get_singleton();
OpenXRSpatialPlaneTrackingCapability();
virtual ~OpenXRSpatialPlaneTrackingCapability() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_process() override;
bool is_supported();
private:
static OpenXRSpatialPlaneTrackingCapability *singleton;
bool spatial_plane_tracking_ext = false;
bool spatial_plane_tracking_supported = false;
RID spatial_context;
bool need_discovery = false;
int discovery_cooldown = 0;
Ref<OpenXRFutureResult> discovery_query_result;
Ref<OpenXRSpatialCapabilityConfigurationPlaneTracking> plane_configuration;
// Discovery logic
Ref<OpenXRFutureResult> _create_spatial_context();
void _on_spatial_context_created(RID p_spatial_context);
void _on_spatial_discovery_recommended(RID p_spatial_context);
Ref<OpenXRFutureResult> _start_entity_discovery();
void _process_snapshot(RID p_snapshot);
// Trackers
HashMap<XrSpatialEntityIdEXT, Ref<OpenXRPlaneTracker>> plane_trackers;
};