diff options
Diffstat (limited to 'modules/webxr/webxr_interface_js.cpp')
-rw-r--r-- | modules/webxr/webxr_interface_js.cpp | 103 |
1 files changed, 86 insertions, 17 deletions
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 5415423fb8..c6213d1aae 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -41,6 +41,7 @@ #include "scene/main/window.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/rendering_server_globals.h" +#include "servers/xr/xr_hand_tracker.h" #include <emscripten.h> #include <stdlib.h> @@ -49,22 +50,23 @@ void _emwebxr_on_session_supported(char *p_session_mode, int p_supported) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); String session_mode = String(p_session_mode); interface->emit_signal(SNAME("session_supported"), session_mode, p_supported ? true : false); } -void _emwebxr_on_session_started(char *p_reference_space_type) { +void _emwebxr_on_session_started(char *p_reference_space_type, char *p_enabled_features) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); String reference_space_type = String(p_reference_space_type); - static_cast<WebXRInterfaceJS *>(interface.ptr())->_set_reference_space_type(reference_space_type); + interface->_set_reference_space_type(reference_space_type); + interface->_set_enabled_features(p_enabled_features); interface->emit_signal(SNAME("session_started")); } @@ -72,7 +74,7 @@ void _emwebxr_on_session_ended() { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); interface->uninitialize(); @@ -83,7 +85,7 @@ void _emwebxr_on_session_failed(char *p_message) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); interface->uninitialize(); @@ -96,17 +98,17 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(int p_event_type, i XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); - ((WebXRInterfaceJS *)interface.ptr())->_on_input_event(p_event_type, p_input_source_id); + interface->_on_input_event(p_event_type, p_input_source_id); } extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + Ref<WebXRInterfaceJS> interface = xr_server->find_interface("WebXR"); ERR_FAIL_COND(interface.is_null()); StringName signal_name = StringName(p_signal_name); @@ -149,14 +151,14 @@ String WebXRInterfaceJS::get_requested_reference_space_types() const { return requested_reference_space_types; } -void WebXRInterfaceJS::_set_reference_space_type(String p_reference_space_type) { - reference_space_type = p_reference_space_type; -} - String WebXRInterfaceJS::get_reference_space_type() const { return reference_space_type; } +String WebXRInterfaceJS::get_enabled_features() const { + return enabled_features; +} + bool WebXRInterfaceJS::is_input_source_active(int p_input_source_id) const { ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, false); return input_sources[p_input_source_id].active; @@ -256,7 +258,9 @@ bool WebXRInterfaceJS::initialize() { return false; } - // we must create a tracker for our head + enabled_features.clear(); + + // We must create a tracker for our head. head_transform.basis = Basis(); head_transform.origin = Vector3(); head_tracker.instantiate(); @@ -265,7 +269,7 @@ bool WebXRInterfaceJS::initialize() { head_tracker->set_tracker_desc("Players head"); xr_server->add_tracker(head_tracker); - // make this our primary interface + // Make this our primary interface. xr_server->set_primary_interface(this); // Clear render_targetsize to make sure it gets reset to the new size. @@ -301,6 +305,14 @@ void WebXRInterfaceJS::uninitialize() { head_tracker.unref(); } + for (int i = 0; i < HAND_MAX; i++) { + if (hand_trackers[i].is_valid()) { + xr_server->remove_hand_tracker(i == 0 ? "/user/left" : "/user/right"); + + hand_trackers[i].unref(); + } + } + if (xr_server->get_primary_interface() == this) { // no longer our primary interface xr_server->set_primary_interface(nullptr); @@ -321,7 +333,8 @@ void WebXRInterfaceJS::uninitialize() { } texture_cache.clear(); - reference_space_type = ""; + reference_space_type.clear(); + enabled_features.clear(); initialized = false; }; }; @@ -572,6 +585,9 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) { float buttons[10]; int axes_count; float axes[10]; + int has_hand_data; + float hand_joints[WEBXR_HAND_JOINT_MAX * 16]; + float hand_radii[WEBXR_HAND_JOINT_MAX]; input_source.active = godot_webxr_update_input_source( p_input_source_id, @@ -584,7 +600,10 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) { &button_count, buttons, &axes_count, - axes); + axes, + &has_hand_data, + hand_joints, + hand_radii); if (!input_source.active) { if (input_source.tracker.is_valid()) { @@ -683,6 +702,56 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) { touches[touch_index].position = position; } } + + if (p_input_source_id < 2) { + Ref<XRHandTracker> hand_tracker = hand_trackers[p_input_source_id]; + if (has_hand_data) { + // Transform orientations to match Godot Humanoid skeleton. + const Basis bone_adjustment( + Vector3(-1.0, 0.0, 0.0), + Vector3(0.0, 0.0, -1.0), + Vector3(0.0, -1.0, 0.0)); + + if (unlikely(hand_tracker.is_null())) { + hand_tracker.instantiate(); + hand_tracker->set_hand(p_input_source_id == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT); + + // These flags always apply, since WebXR doesn't give us enough insight to be more fine grained. + BitField<XRHandTracker::HandJointFlags> joint_flags(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID | XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID | XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED | XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED); + for (int godot_joint = 0; godot_joint < XRHandTracker::HAND_JOINT_MAX; godot_joint++) { + hand_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)godot_joint, joint_flags); + } + + hand_trackers[p_input_source_id] = hand_tracker; + xr_server->add_hand_tracker(p_input_source_id == 0 ? "/user/left" : "/user/right", hand_tracker); + } + + hand_tracker->set_has_tracking_data(true); + for (int webxr_joint = 0; webxr_joint < WEBXR_HAND_JOINT_MAX; webxr_joint++) { + XRHandTracker::HandJoint godot_joint = (XRHandTracker::HandJoint)(webxr_joint + 1); + + Transform3D joint_transform = _js_matrix_to_transform(hand_joints + (16 * webxr_joint)); + joint_transform.basis *= bone_adjustment; + hand_tracker->set_hand_joint_transform(godot_joint, joint_transform); + + hand_tracker->set_hand_joint_radius(godot_joint, hand_radii[webxr_joint]); + } + + // WebXR doesn't have a palm joint, so we calculate it by finding the middle of the middle finger metacarpal bone. + { + // 10 is the WebXR middle finger metacarpal joint, and 12 is the offset to the transform origin. + const float *start_pos = hand_joints + (10 * 16) + 12; + // 11 is the WebXR middle finger phalanx proximal joint, and 12 is the offset to the transform origin. + const float *end_pos = hand_joints + (11 * 16) + 12; + Transform3D palm_transform; + palm_transform.origin = (Vector3(start_pos[0], start_pos[1], start_pos[2]) + Vector3(end_pos[0], end_pos[1], end_pos[2])) / 2.0; + hand_tracker->set_hand_joint_transform(XRHandTracker::HAND_JOINT_PALM, palm_transform); + } + + } else if (hand_tracker.is_valid()) { + hand_tracker->set_has_tracking_data(false); + } + } } void WebXRInterfaceJS::_on_input_event(int p_event_type, int p_input_source_id) { |