|
@@ -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) {
|