Skip to content

Commit

Permalink
Implement XR_FB_hand_tracking_aim extension wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
devloglogan committed Feb 14, 2024
1 parent 8286944 commit 8fd1c50
Show file tree
Hide file tree
Showing 8 changed files with 551 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Merge GDExtension into a single implementation
- Upgrade Android, Gradle, Godot and Kotlin dependencies
- Add XR_FB_face_tracking support
- Add XR_FB_hand_tracking_aim support

## 2.0.3
- Migrate the export scripts from gdscript to C++ via gdextension
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**************************************************************************/
/* openxr_fb_hand_tracking_aim_extension_wrapper.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT XR */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "extensions/openxr_fb_hand_tracking_aim_extension_wrapper.h"

#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/xr_pose.hpp>

using namespace godot;

OpenXRFbHandTrackingAimExtensionWrapper *OpenXRFbHandTrackingAimExtensionWrapper::singleton = nullptr;

OpenXRFbHandTrackingAimExtensionWrapper *OpenXRFbHandTrackingAimExtensionWrapper::get_singleton() {
if (singleton == nullptr) {
singleton = memnew(OpenXRFbHandTrackingAimExtensionWrapper());
}
return singleton;
}

OpenXRFbHandTrackingAimExtensionWrapper::OpenXRFbHandTrackingAimExtensionWrapper() :
OpenXRExtensionWrapperExtension() {
ERR_FAIL_COND_MSG(singleton != nullptr, "An OpenXRFbHandTrackingAimExtensionWrapper singleton already exists.");

request_extensions[XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME] = &fb_hand_tracking_aim_ext;
singleton = this;
}

OpenXRFbHandTrackingAimExtensionWrapper::~OpenXRFbHandTrackingAimExtensionWrapper() {
cleanup();
}

void OpenXRFbHandTrackingAimExtensionWrapper::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_enabled"), &OpenXRFbHandTrackingAimExtensionWrapper::is_enabled);
}

void OpenXRFbHandTrackingAimExtensionWrapper::cleanup() {
XRServer *xr_server = XRServer::get_singleton();

for (int i = 0; i < Hand::HAND_MAX; i++) {
if (xr_server != nullptr) {
xr_server->remove_tracker(trackers[i]);
}
trackers[i].unref();
}

fb_hand_tracking_aim_ext = false;
}

godot::Dictionary OpenXRFbHandTrackingAimExtensionWrapper::_get_requested_extensions() {
godot::Dictionary result;
for (auto ext : request_extensions) {
godot::String key = ext.first;
uint64_t value = reinterpret_cast<uint64_t>(ext.second);
result[key] = (godot::Variant)value;
}
return result;
}

PackedStringArray OpenXRFbHandTrackingAimExtensionWrapper::_get_suggested_tracker_names() {
PackedStringArray arr = PackedStringArray();
arr.push_back(TRACKER_NAME_LEFT);
arr.push_back(TRACKER_NAME_RIGHT);
return arr;
}

void OpenXRFbHandTrackingAimExtensionWrapper::_on_state_ready() {
is_project_setting_enabled = ProjectSettings::get_singleton()->get_setting_with_override("xr/openxr/extensions/hand_tracking_aim");
if (!is_project_setting_enabled) {
return;
}

XRServer *xr_server = XRServer::get_singleton();
if (xr_server == nullptr) {
return;
}

trackers[Hand::HAND_LEFT].instantiate();
trackers[Hand::HAND_LEFT]->set_tracker_type(XRServer::TRACKER_CONTROLLER);
trackers[Hand::HAND_LEFT]->set_tracker_name(TRACKER_NAME_LEFT);
trackers[Hand::HAND_LEFT]->set_tracker_desc("FB Aim tracker Left");
xr_server->add_tracker(trackers[Hand::HAND_LEFT]);

trackers[Hand::HAND_RIGHT].instantiate();
trackers[Hand::HAND_RIGHT]->set_tracker_type(XRServer::TRACKER_CONTROLLER);
trackers[Hand::HAND_RIGHT]->set_tracker_name(TRACKER_NAME_RIGHT);
trackers[Hand::HAND_RIGHT]->set_tracker_desc("FB Aim tracker Right");
xr_server->add_tracker(trackers[Hand::HAND_RIGHT]);
}

void OpenXRFbHandTrackingAimExtensionWrapper::_on_instance_destroyed() {
cleanup();
}

uint64_t OpenXRFbHandTrackingAimExtensionWrapper::_set_hand_joint_locations_and_get_next_pointer(int32_t p_hand_index, void *p_next_pointer) {
ERR_FAIL_INDEX_V_MSG(p_hand_index, Hand::HAND_MAX, reinterpret_cast<uint64_t>(p_next_pointer), vformat("Invalid hand index %d", p_hand_index));

if (!fb_hand_tracking_aim_ext) {
return reinterpret_cast<uint64_t>(p_next_pointer);
}

aim_state[p_hand_index].type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
aim_state[p_hand_index].next = p_next_pointer;
aim_state[p_hand_index].status = 0;

return reinterpret_cast<uint64_t>(&aim_state[p_hand_index]);
}

void OpenXRFbHandTrackingAimExtensionWrapper::_on_process() {
if (!is_enabled() || !is_project_setting_enabled) {
return;
}

for (int i = 0; i < Hand::HAND_MAX; i++) {
if (!trackers[i].is_valid()) {
continue;
}

XrPosef aim_pose = aim_state[i].aimPose;
XrQuaternionf aim_quat = aim_pose.orientation;
XrVector3f aim_position = aim_pose.position;
Quaternion quat = Quaternion(aim_quat.x, aim_quat.y, aim_quat.z, aim_quat.w);
Vector3 origin = Vector3(aim_position.x, aim_position.y, aim_position.z);

Transform3D transform = Transform3D(quat, origin);
Vector3 linear_velocity = Vector3(0.0, 0.0, 0.0);
Vector3 angular_velocity = Vector3(0.0, 0.0, 0.0);
XRPose::TrackingConfidence confidence = XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_LOW;

if (!(aim_state[i].status & XR_HAND_TRACKING_AIM_VALID_BIT_FB)) {
confidence = XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE;
} else if (aim_state[i].status & XR_HAND_TRACKING_AIM_COMPUTED_BIT_FB) {
confidence = XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_HIGH;
}

trackers[i]->set_pose("default", transform, linear_velocity, angular_velocity, confidence);
trackers[i]->set_input("index_pinch", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB));
trackers[i]->set_input("middle_pinch", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB));
trackers[i]->set_input("ring_pinch", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB));
trackers[i]->set_input("little_pinch", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB));
trackers[i]->set_input("index_pinch_strength", aim_state[i].pinchStrengthIndex);
trackers[i]->set_input("middle_pinch_strength", aim_state[i].pinchStrengthMiddle);
trackers[i]->set_input("ring_pinch_strength", aim_state[i].pinchStrengthRing);
trackers[i]->set_input("little_pinch_strength", aim_state[i].pinchStrengthLittle);
trackers[i]->set_input("system_gesture", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_SYSTEM_GESTURE_BIT_FB));
trackers[i]->set_input("menu_gesture", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB));
trackers[i]->set_input("dominant_hand", (bool)(aim_state[i].status & XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB));
}
}

void OpenXRFbHandTrackingAimExtensionWrapper::add_project_setting() {
String p_name = "xr/openxr/extensions/hand_tracking_aim";
if (!ProjectSettings::get_singleton()->has_setting(p_name)) {
ProjectSettings::get_singleton()->set_setting(p_name, false);
}

ProjectSettings::get_singleton()->set_initial_value(p_name, false);
Dictionary property_info;
property_info["name"] = p_name;
property_info["type"] = Variant::Type::BOOL;
property_info["hint"] = PROPERTY_HINT_NONE;
ProjectSettings::get_singleton()->add_property_info(property_info);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**************************************************************************/
/* openxr_fb_hand_tracking_aim_extension_wrapper.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT XR */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#ifndef OPENXR_FB_HAND_TRACKING_AIM_EXTENSION_WRAPPER_H
#define OPENXR_FB_HAND_TRACKING_AIM_EXTENSION_WRAPPER_H

#include <openxr/openxr.h>
#include <godot_cpp/classes/open_xr_extension_wrapper_extension.hpp>
#include <godot_cpp/classes/open_xr_interface.hpp>
#include <godot_cpp/classes/xr_positional_tracker.hpp>

#include <map>

using namespace godot;

// Wrapper for the set of Facebook XR hand tracking aim extension.
class OpenXRFbHandTrackingAimExtensionWrapper : public OpenXRExtensionWrapperExtension {
GDCLASS(OpenXRFbHandTrackingAimExtensionWrapper, OpenXRExtensionWrapperExtension);

public:
using Hand = OpenXRInterface::Hand;

godot::Dictionary _get_requested_extensions() override;

PackedStringArray _get_suggested_tracker_names() override;

void _on_state_ready() override;

void _on_instance_destroyed() override;

uint64_t _set_hand_joint_locations_and_get_next_pointer(int32_t p_hand_index, void *p_next_pointer) override;

bool is_enabled() {
return fb_hand_tracking_aim_ext;
}

void _on_process() override;

void add_project_setting();

static OpenXRFbHandTrackingAimExtensionWrapper *get_singleton();

OpenXRFbHandTrackingAimExtensionWrapper();
~OpenXRFbHandTrackingAimExtensionWrapper();

protected:
static void _bind_methods();

private:
std::map<godot::String, bool *> request_extensions;

void cleanup();

static OpenXRFbHandTrackingAimExtensionWrapper *singleton;

const String TRACKER_NAME_LEFT = "/user/fbhandaim/left";
const String TRACKER_NAME_RIGHT = "/user/fbhandaim/right";

bool fb_hand_tracking_aim_ext = false;
bool is_project_setting_enabled = false;

Ref<XRPositionalTracker> trackers[Hand::HAND_MAX];

XrHandTrackingAimStateFB aim_state[Hand::HAND_MAX];
};

#endif // OPENXR_FB_HAND_TRACKING_AIM_EXTENSION_WRAPPER_H
39 changes: 20 additions & 19 deletions common/src/main/cpp/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@
#include "export/pico_export_plugin.h"

#include "extensions/openxr_fb_face_tracking_extension_wrapper.h"
#include "extensions/openxr_fb_scene_extension_wrapper.h"
#include "extensions/openxr_fb_hand_tracking_aim_extension_wrapper.h"
#include "extensions/openxr_fb_scene_capture_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_extension_wrapper.h"
#include "extensions/openxr_fb_scene_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_container_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_query_extension_wrapper.h"

using namespace godot;

void initialize_plugin_module(ModuleInitializationLevel p_level)
{
switch(p_level) {
void initialize_plugin_module(ModuleInitializationLevel p_level) {
switch (p_level) {
case MODULE_INITIALIZATION_LEVEL_CORE: {
ClassDB::register_class<OpenXRFbSceneCaptureExtensionWrapper>();
OpenXRFbSceneCaptureExtensionWrapper::get_singleton()->register_extension_wrapper();
Expand All @@ -73,6 +73,9 @@ void initialize_plugin_module(ModuleInitializationLevel p_level)

ClassDB::register_class<OpenXRFbFaceTrackingExtensionWrapper>();
OpenXRFbFaceTrackingExtensionWrapper::get_singleton()->register_extension_wrapper();

ClassDB::register_class<OpenXRFbHandTrackingAimExtensionWrapper>();
OpenXRFbHandTrackingAimExtensionWrapper::get_singleton()->register_extension_wrapper();
} break;

case MODULE_INITIALIZATION_LEVEL_SERVERS:
Expand All @@ -85,6 +88,7 @@ void initialize_plugin_module(ModuleInitializationLevel p_level)
Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityContainerExtensionWrapper", OpenXRFbSpatialEntityContainerExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbSceneExtensionWrapper", OpenXRFbSceneExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbFaceTrackingExtensionWrapper", OpenXRFbFaceTrackingExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbHandTrackingAimExtensionWrapper", OpenXRFbHandTrackingAimExtensionWrapper::get_singleton());
} break;

case MODULE_INITIALIZATION_LEVEL_EDITOR: {
Expand All @@ -103,29 +107,26 @@ void initialize_plugin_module(ModuleInitializationLevel p_level)

ClassDB::register_class<PicoEditorPlugin>();
EditorPlugins::add_by_type<PicoEditorPlugin>();

OpenXRFbHandTrackingAimExtensionWrapper::get_singleton()->add_project_setting();
} break;

case MODULE_INITIALIZATION_LEVEL_MAX:
break;
}


}

void terminate_plugin_module(ModuleInitializationLevel p_level)
{
void terminate_plugin_module(ModuleInitializationLevel p_level) {
}

extern "C"
{
GDExtensionBool GDE_EXPORT plugin_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
extern "C" {
GDExtensionBool GDE_EXPORT plugin_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);

init_obj.register_initializer(initialize_plugin_module);
init_obj.register_terminator(terminate_plugin_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
init_obj.register_initializer(initialize_plugin_module);
init_obj.register_terminator(terminate_plugin_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);

return init_obj.init();
}
return init_obj.init();
}
}
Loading

0 comments on commit 8fd1c50

Please sign in to comment.