xr_nodes.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. /**************************************************************************/
  2. /* xr_nodes.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "xr_nodes.h"
  31. #include "core/config/project_settings.h"
  32. #include "scene/main/viewport.h"
  33. #include "servers/xr/xr_interface.h"
  34. ////////////////////////////////////////////////////////////////////////////////////////////////////
  35. void XRCamera3D::_bind_tracker() {
  36. XRServer *xr_server = XRServer::get_singleton();
  37. ERR_FAIL_NULL(xr_server);
  38. tracker = xr_server->get_tracker(tracker_name);
  39. if (tracker.is_valid()) {
  40. tracker->connect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed));
  41. Ref<XRPose> pose = tracker->get_pose(pose_name);
  42. if (pose.is_valid()) {
  43. set_transform(pose->get_adjusted_transform());
  44. }
  45. }
  46. }
  47. void XRCamera3D::_unbind_tracker() {
  48. if (tracker.is_valid()) {
  49. tracker->disconnect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed));
  50. }
  51. tracker.unref();
  52. }
  53. void XRCamera3D::_changed_tracker(const StringName &p_tracker_name, int p_tracker_type) {
  54. if (p_tracker_name == tracker_name) {
  55. _bind_tracker();
  56. }
  57. }
  58. void XRCamera3D::_removed_tracker(const StringName &p_tracker_name, int p_tracker_type) {
  59. if (p_tracker_name == tracker_name) {
  60. _unbind_tracker();
  61. }
  62. }
  63. void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) {
  64. if (p_pose->get_name() == pose_name) {
  65. set_transform(p_pose->get_adjusted_transform());
  66. }
  67. }
  68. PackedStringArray XRCamera3D::get_configuration_warnings() const {
  69. PackedStringArray warnings = Camera3D::get_configuration_warnings();
  70. if (is_visible() && is_inside_tree()) {
  71. // Warn if the node has a parent which isn't an XROrigin3D!
  72. Node *parent = get_parent();
  73. XROrigin3D *origin = Object::cast_to<XROrigin3D>(parent);
  74. if (parent && origin == nullptr) {
  75. warnings.push_back(RTR("XRCamera3D may not function as expected without an XROrigin3D node as its parent."));
  76. };
  77. }
  78. return warnings;
  79. }
  80. Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
  81. // get our XRServer
  82. XRServer *xr_server = XRServer::get_singleton();
  83. ERR_FAIL_NULL_V(xr_server, Vector3());
  84. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  85. if (xr_interface.is_null()) {
  86. // we might be in the editor or have VR turned off, just call superclass
  87. return Camera3D::project_local_ray_normal(p_pos);
  88. }
  89. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
  90. Size2 viewport_size = get_viewport()->get_camera_rect_size();
  91. Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
  92. Vector3 ray;
  93. // Just use the first view, if multiple views are supported this function has no good result
  94. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  95. Vector2 screen_he = cm.get_viewport_half_extents();
  96. ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized();
  97. return ray;
  98. }
  99. Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
  100. // get our XRServer
  101. XRServer *xr_server = XRServer::get_singleton();
  102. ERR_FAIL_NULL_V(xr_server, Vector2());
  103. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  104. if (xr_interface.is_null()) {
  105. // we might be in the editor or have VR turned off, just call superclass
  106. return Camera3D::unproject_position(p_pos);
  107. }
  108. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector2(), "Camera is not inside scene.");
  109. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  110. // Just use the first view, if multiple views are supported this function has no good result
  111. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  112. Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
  113. p = cm.xform4(p);
  114. p.normal /= p.d;
  115. Point2 res;
  116. res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x;
  117. res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y;
  118. return res;
  119. }
  120. Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const {
  121. // get our XRServer
  122. XRServer *xr_server = XRServer::get_singleton();
  123. ERR_FAIL_NULL_V(xr_server, Vector3());
  124. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  125. if (xr_interface.is_null()) {
  126. // we might be in the editor or have VR turned off, just call superclass
  127. return Camera3D::project_position(p_point, p_z_depth);
  128. }
  129. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
  130. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  131. // Just use the first view, if multiple views are supported this function has no good result
  132. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  133. Vector2 vp_he = cm.get_viewport_half_extents();
  134. Vector2 point;
  135. point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0;
  136. point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
  137. point *= vp_he;
  138. Vector3 p(point.x, point.y, -p_z_depth);
  139. return get_camera_transform().xform(p);
  140. }
  141. Vector<Plane> XRCamera3D::get_frustum() const {
  142. // get our XRServer
  143. XRServer *xr_server = XRServer::get_singleton();
  144. ERR_FAIL_NULL_V(xr_server, Vector<Plane>());
  145. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  146. if (xr_interface.is_null()) {
  147. // we might be in the editor or have VR turned off, just call superclass
  148. return Camera3D::get_frustum();
  149. }
  150. ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
  151. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  152. // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
  153. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  154. return cm.get_projection_planes(get_camera_transform());
  155. }
  156. XRCamera3D::XRCamera3D() {
  157. XRServer *xr_server = XRServer::get_singleton();
  158. ERR_FAIL_NULL(xr_server);
  159. xr_server->connect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker));
  160. xr_server->connect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker));
  161. xr_server->connect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker));
  162. // check if our tracker already exists and if so, bind it...
  163. _bind_tracker();
  164. }
  165. XRCamera3D::~XRCamera3D() {
  166. XRServer *xr_server = XRServer::get_singleton();
  167. ERR_FAIL_NULL(xr_server);
  168. xr_server->disconnect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker));
  169. xr_server->disconnect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker));
  170. xr_server->disconnect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker));
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  173. // XRNode3D is a node that has it's transform updated by an XRPositionalTracker.
  174. // Note that trackers are only available in runtime and only after an XRInterface registers one.
  175. // So we bind by name and as long as a tracker isn't available, our node remains inactive.
  176. void XRNode3D::_bind_methods() {
  177. ClassDB::bind_method(D_METHOD("set_tracker", "tracker_name"), &XRNode3D::set_tracker);
  178. ClassDB::bind_method(D_METHOD("get_tracker"), &XRNode3D::get_tracker);
  179. ADD_PROPERTY(PropertyInfo(Variant::STRING, "tracker", PROPERTY_HINT_ENUM_SUGGESTION), "set_tracker", "get_tracker");
  180. ClassDB::bind_method(D_METHOD("set_pose_name", "pose"), &XRNode3D::set_pose_name);
  181. ClassDB::bind_method(D_METHOD("get_pose_name"), &XRNode3D::get_pose_name);
  182. ADD_PROPERTY(PropertyInfo(Variant::STRING, "pose", PROPERTY_HINT_ENUM_SUGGESTION), "set_pose_name", "get_pose_name");
  183. ClassDB::bind_method(D_METHOD("set_show_when_tracked", "show"), &XRNode3D::set_show_when_tracked);
  184. ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRNode3D::get_show_when_tracked);
  185. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked");
  186. ClassDB::bind_method(D_METHOD("get_is_active"), &XRNode3D::get_is_active);
  187. ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRNode3D::get_has_tracking_data);
  188. ClassDB::bind_method(D_METHOD("get_pose"), &XRNode3D::get_pose);
  189. ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse);
  190. ADD_SIGNAL(MethodInfo("tracking_changed", PropertyInfo(Variant::BOOL, "tracking")));
  191. }
  192. void XRNode3D::_validate_property(PropertyInfo &p_property) const {
  193. XRServer *xr_server = XRServer::get_singleton();
  194. ERR_FAIL_NULL(xr_server);
  195. if (p_property.name == "tracker") {
  196. PackedStringArray names = xr_server->get_suggested_tracker_names();
  197. String hint_string;
  198. for (const String &name : names) {
  199. hint_string += name + ",";
  200. }
  201. p_property.hint_string = hint_string;
  202. } else if (p_property.name == "pose") {
  203. PackedStringArray names = xr_server->get_suggested_pose_names(tracker_name);
  204. String hint_string;
  205. for (const String &name : names) {
  206. hint_string += name + ",";
  207. }
  208. p_property.hint_string = hint_string;
  209. }
  210. }
  211. void XRNode3D::set_tracker(const StringName &p_tracker_name) {
  212. if (tracker.is_valid() && tracker->get_tracker_name() == p_tracker_name) {
  213. // didn't change
  214. return;
  215. }
  216. // just in case
  217. _unbind_tracker();
  218. // copy the name
  219. tracker_name = p_tracker_name;
  220. pose_name = "default";
  221. // see if it's already available
  222. _bind_tracker();
  223. update_configuration_warnings();
  224. notify_property_list_changed();
  225. }
  226. StringName XRNode3D::get_tracker() const {
  227. return tracker_name;
  228. }
  229. void XRNode3D::set_pose_name(const StringName &p_pose_name) {
  230. pose_name = p_pose_name;
  231. // Update pose if we are bound to a tracker with a valid pose
  232. Ref<XRPose> pose = get_pose();
  233. if (pose.is_valid()) {
  234. set_transform(pose->get_adjusted_transform());
  235. }
  236. }
  237. StringName XRNode3D::get_pose_name() const {
  238. return pose_name;
  239. }
  240. void XRNode3D::set_show_when_tracked(bool p_show) {
  241. show_when_tracked = p_show;
  242. _update_visibility();
  243. }
  244. bool XRNode3D::get_show_when_tracked() const {
  245. return show_when_tracked;
  246. }
  247. bool XRNode3D::get_is_active() const {
  248. if (tracker.is_null()) {
  249. return false;
  250. } else if (!tracker->has_pose(pose_name)) {
  251. return false;
  252. } else {
  253. return true;
  254. }
  255. }
  256. bool XRNode3D::get_has_tracking_data() const {
  257. return has_tracking_data;
  258. }
  259. void XRNode3D::trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
  260. // TODO need to link trackers to the interface that registered them so we can call this on the correct interface.
  261. // For now this works fine as in 99% of the cases we only have our primary interface active
  262. XRServer *xr_server = XRServer::get_singleton();
  263. if (xr_server != nullptr) {
  264. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  265. if (xr_interface.is_valid()) {
  266. xr_interface->trigger_haptic_pulse(p_action_name, tracker_name, p_frequency, p_amplitude, p_duration_sec, p_delay_sec);
  267. }
  268. }
  269. }
  270. Ref<XRPose> XRNode3D::get_pose() {
  271. if (tracker.is_valid()) {
  272. return tracker->get_pose(pose_name);
  273. } else {
  274. return Ref<XRPose>();
  275. }
  276. }
  277. void XRNode3D::_bind_tracker() {
  278. ERR_FAIL_COND_MSG(tracker.is_valid(), "Unbind the current tracker first");
  279. XRServer *xr_server = XRServer::get_singleton();
  280. if (xr_server != nullptr) {
  281. tracker = xr_server->get_tracker(tracker_name);
  282. if (tracker.is_null()) {
  283. // It is possible and valid if the tracker isn't available (yet), in this case we just exit
  284. return;
  285. }
  286. tracker->connect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed));
  287. tracker->connect("pose_lost_tracking", callable_mp(this, &XRNode3D::_pose_lost_tracking));
  288. Ref<XRPose> pose = get_pose();
  289. if (pose.is_valid()) {
  290. set_transform(pose->get_adjusted_transform());
  291. _set_has_tracking_data(pose->get_has_tracking_data());
  292. } else {
  293. // Pose has been invalidated or was never set.
  294. _set_has_tracking_data(false);
  295. }
  296. }
  297. }
  298. void XRNode3D::_unbind_tracker() {
  299. if (tracker.is_valid()) {
  300. tracker->disconnect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed));
  301. tracker->disconnect("pose_lost_tracking", callable_mp(this, &XRNode3D::_pose_lost_tracking));
  302. tracker.unref();
  303. _set_has_tracking_data(false);
  304. }
  305. }
  306. void XRNode3D::_changed_tracker(const StringName &p_tracker_name, int p_tracker_type) {
  307. if (tracker_name == p_tracker_name) {
  308. // just in case unref our current tracker
  309. _unbind_tracker();
  310. // get our new tracker
  311. _bind_tracker();
  312. }
  313. }
  314. void XRNode3D::_removed_tracker(const StringName &p_tracker_name, int p_tracker_type) {
  315. if (tracker_name == p_tracker_name) {
  316. // unref our tracker, it's no longer available
  317. _unbind_tracker();
  318. }
  319. }
  320. void XRNode3D::_pose_changed(const Ref<XRPose> &p_pose) {
  321. if (p_pose.is_valid() && p_pose->get_name() == pose_name) {
  322. set_transform(p_pose->get_adjusted_transform());
  323. _set_has_tracking_data(p_pose->get_has_tracking_data());
  324. }
  325. }
  326. void XRNode3D::_pose_lost_tracking(const Ref<XRPose> &p_pose) {
  327. if (p_pose.is_valid() && p_pose->get_name() == pose_name) {
  328. _set_has_tracking_data(false);
  329. }
  330. }
  331. void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) {
  332. // Always update our visibility, we may have set our tracking data
  333. // when conditions weren't right.
  334. _update_visibility();
  335. // Ignore if the has_tracking_data state isn't changing.
  336. if (p_has_tracking_data == has_tracking_data) {
  337. return;
  338. }
  339. // Handle change of has_tracking_data.
  340. has_tracking_data = p_has_tracking_data;
  341. emit_signal(SNAME("tracking_changed"), has_tracking_data);
  342. }
  343. void XRNode3D::_update_visibility() {
  344. // If configured, show or hide the node based on tracking data.
  345. if (show_when_tracked) {
  346. // Only react to this if we have a primary interface.
  347. XRServer *xr_server = XRServer::get_singleton();
  348. if (xr_server != nullptr) {
  349. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  350. if (xr_interface.is_valid()) {
  351. set_visible(has_tracking_data);
  352. }
  353. }
  354. }
  355. }
  356. XRNode3D::XRNode3D() {
  357. XRServer *xr_server = XRServer::get_singleton();
  358. ERR_FAIL_NULL(xr_server);
  359. xr_server->connect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  360. xr_server->connect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  361. xr_server->connect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  362. }
  363. XRNode3D::~XRNode3D() {
  364. _unbind_tracker();
  365. XRServer *xr_server = XRServer::get_singleton();
  366. ERR_FAIL_NULL(xr_server);
  367. xr_server->disconnect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  368. xr_server->disconnect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  369. xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  370. }
  371. PackedStringArray XRNode3D::get_configuration_warnings() const {
  372. PackedStringArray warnings = Node3D::get_configuration_warnings();
  373. if (is_visible() && is_inside_tree()) {
  374. // Warn if the node has a parent which isn't an XROrigin3D!
  375. Node *parent = get_parent();
  376. XROrigin3D *origin = Object::cast_to<XROrigin3D>(parent);
  377. if (parent && origin == nullptr) {
  378. warnings.push_back(RTR("XRNode3D may not function as expected without an XROrigin3D node as its parent."));
  379. };
  380. if (tracker_name == "") {
  381. warnings.push_back(RTR("No tracker name is set."));
  382. }
  383. if (pose_name == "") {
  384. warnings.push_back(RTR("No pose is set."));
  385. }
  386. }
  387. return warnings;
  388. }
  389. ////////////////////////////////////////////////////////////////////////////////////////////////////
  390. void XRController3D::_bind_methods() {
  391. // passthroughs to information about our related joystick
  392. ClassDB::bind_method(D_METHOD("is_button_pressed", "name"), &XRController3D::is_button_pressed);
  393. ClassDB::bind_method(D_METHOD("get_input", "name"), &XRController3D::get_input);
  394. ClassDB::bind_method(D_METHOD("get_float", "name"), &XRController3D::get_float);
  395. ClassDB::bind_method(D_METHOD("get_vector2", "name"), &XRController3D::get_vector2);
  396. ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand);
  397. ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name")));
  398. ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
  399. ADD_SIGNAL(MethodInfo("input_float_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
  400. ADD_SIGNAL(MethodInfo("input_vector2_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value")));
  401. ADD_SIGNAL(MethodInfo("profile_changed", PropertyInfo(Variant::STRING, "role")));
  402. }
  403. void XRController3D::_bind_tracker() {
  404. XRNode3D::_bind_tracker();
  405. if (tracker.is_valid()) {
  406. // bind to input signals
  407. tracker->connect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  408. tracker->connect("button_released", callable_mp(this, &XRController3D::_button_released));
  409. tracker->connect("input_float_changed", callable_mp(this, &XRController3D::_input_float_changed));
  410. tracker->connect("input_vector2_changed", callable_mp(this, &XRController3D::_input_vector2_changed));
  411. tracker->connect("profile_changed", callable_mp(this, &XRController3D::_profile_changed));
  412. }
  413. }
  414. void XRController3D::_unbind_tracker() {
  415. if (tracker.is_valid()) {
  416. // unbind input signals
  417. tracker->disconnect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  418. tracker->disconnect("button_released", callable_mp(this, &XRController3D::_button_released));
  419. tracker->disconnect("input_float_changed", callable_mp(this, &XRController3D::_input_float_changed));
  420. tracker->disconnect("input_vector2_changed", callable_mp(this, &XRController3D::_input_vector2_changed));
  421. tracker->disconnect("profile_changed", callable_mp(this, &XRController3D::_profile_changed));
  422. }
  423. XRNode3D::_unbind_tracker();
  424. }
  425. void XRController3D::_button_pressed(const String &p_name) {
  426. emit_signal(SNAME("button_pressed"), p_name);
  427. }
  428. void XRController3D::_button_released(const String &p_name) {
  429. emit_signal(SNAME("button_released"), p_name);
  430. }
  431. void XRController3D::_input_float_changed(const String &p_name, float p_value) {
  432. emit_signal(SNAME("input_float_changed"), p_name, p_value);
  433. }
  434. void XRController3D::_input_vector2_changed(const String &p_name, Vector2 p_value) {
  435. emit_signal(SNAME("input_vector2_changed"), p_name, p_value);
  436. }
  437. void XRController3D::_profile_changed(const String &p_role) {
  438. emit_signal(SNAME("profile_changed"), p_role);
  439. }
  440. bool XRController3D::is_button_pressed(const StringName &p_name) const {
  441. if (tracker.is_valid()) {
  442. // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type
  443. bool pressed = tracker->get_input(p_name);
  444. return pressed;
  445. } else {
  446. return false;
  447. }
  448. }
  449. Variant XRController3D::get_input(const StringName &p_name) const {
  450. if (tracker.is_valid()) {
  451. return tracker->get_input(p_name);
  452. } else {
  453. return Variant();
  454. }
  455. }
  456. float XRController3D::get_float(const StringName &p_name) const {
  457. if (tracker.is_valid()) {
  458. // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert
  459. Variant input = tracker->get_input(p_name);
  460. switch (input.get_type()) {
  461. case Variant::BOOL: {
  462. bool value = input;
  463. return value ? 1.0 : 0.0;
  464. } break;
  465. case Variant::FLOAT: {
  466. float value = input;
  467. return value;
  468. } break;
  469. default:
  470. return 0.0;
  471. };
  472. } else {
  473. return 0.0;
  474. }
  475. }
  476. Vector2 XRController3D::get_vector2(const StringName &p_name) const {
  477. if (tracker.is_valid()) {
  478. // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert
  479. Variant input = tracker->get_input(p_name);
  480. switch (input.get_type()) {
  481. case Variant::BOOL: {
  482. bool value = input;
  483. return Vector2(value ? 1.0 : 0.0, 0.0);
  484. } break;
  485. case Variant::FLOAT: {
  486. float value = input;
  487. return Vector2(value, 0.0);
  488. } break;
  489. case Variant::VECTOR2: {
  490. Vector2 axis = input;
  491. return axis;
  492. }
  493. default:
  494. return Vector2();
  495. }
  496. } else {
  497. return Vector2();
  498. }
  499. }
  500. XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const {
  501. // get our XRServer
  502. if (!tracker.is_valid()) {
  503. return XRPositionalTracker::TRACKER_HAND_UNKNOWN;
  504. }
  505. return tracker->get_tracker_hand();
  506. }
  507. ////////////////////////////////////////////////////////////////////////////////////////////////////
  508. void XRAnchor3D::_bind_methods() {
  509. ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size);
  510. ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane);
  511. }
  512. Vector3 XRAnchor3D::get_size() const {
  513. return size;
  514. }
  515. Plane XRAnchor3D::get_plane() const {
  516. Vector3 location = get_position();
  517. Basis orientation = get_transform().basis;
  518. Plane plane(orientation.get_column(1).normalized(), location);
  519. return plane;
  520. }
  521. ////////////////////////////////////////////////////////////////////////////////////////////////////
  522. Vector<XROrigin3D *> XROrigin3D::origin_nodes;
  523. PackedStringArray XROrigin3D::get_configuration_warnings() const {
  524. PackedStringArray warnings = Node3D::get_configuration_warnings();
  525. if (is_visible() && is_inside_tree()) {
  526. bool has_camera = false;
  527. for (int i = 0; !has_camera && i < get_child_count(); i++) {
  528. XRCamera3D *camera = Object::cast_to<XRCamera3D>(get_child(i));
  529. if (camera) {
  530. // found it!
  531. has_camera = true;
  532. }
  533. }
  534. if (!has_camera) {
  535. warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node."));
  536. }
  537. }
  538. bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
  539. if (!xr_enabled) {
  540. warnings.push_back(RTR("XR shaders are not enabled in project settings. Stereoscopic output is not supported unless they are enabled. Please enable `xr/shaders/enabled` to use stereoscopic output."));
  541. }
  542. return warnings;
  543. }
  544. void XROrigin3D::_bind_methods() {
  545. ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale);
  546. ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale);
  547. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
  548. ClassDB::bind_method(D_METHOD("set_current", "enabled"), &XROrigin3D::set_current);
  549. ClassDB::bind_method(D_METHOD("is_current"), &XROrigin3D::is_current);
  550. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
  551. }
  552. real_t XROrigin3D::get_world_scale() const {
  553. // get our XRServer
  554. XRServer *xr_server = XRServer::get_singleton();
  555. ERR_FAIL_NULL_V(xr_server, 1.0);
  556. return xr_server->get_world_scale();
  557. }
  558. void XROrigin3D::set_world_scale(real_t p_world_scale) {
  559. // get our XRServer
  560. XRServer *xr_server = XRServer::get_singleton();
  561. ERR_FAIL_NULL(xr_server);
  562. xr_server->set_world_scale(p_world_scale);
  563. }
  564. void XROrigin3D::_set_current(bool p_enabled, bool p_update_others) {
  565. // We run this logic even if current already equals p_enabled as we may have set this previously before we entered our tree.
  566. // This is then called a second time on NOTIFICATION_ENTER_TREE where we actually process activating this origin node.
  567. current = p_enabled;
  568. if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
  569. return;
  570. }
  571. // Notify us of any transform changes
  572. set_notify_local_transform(current);
  573. set_notify_transform(current);
  574. // update XRServer with our current position
  575. if (current) {
  576. XRServer *xr_server = XRServer::get_singleton();
  577. ERR_FAIL_NULL(xr_server);
  578. xr_server->set_world_origin(get_global_transform());
  579. }
  580. // Check if we need to update our other origin nodes accordingly
  581. if (p_update_others) {
  582. if (current) {
  583. for (int i = 0; i < origin_nodes.size(); i++) {
  584. if (origin_nodes[i] != this && origin_nodes[i]->current) {
  585. origin_nodes[i]->_set_current(false, false);
  586. }
  587. }
  588. } else {
  589. // We no longer have a current origin so find the first one we can make current
  590. for (int i = 0; i < origin_nodes.size(); i++) {
  591. if (origin_nodes[i] != this) {
  592. origin_nodes[i]->_set_current(true, false);
  593. return; // we are done.
  594. }
  595. }
  596. }
  597. }
  598. }
  599. void XROrigin3D::set_current(bool p_enabled) {
  600. _set_current(p_enabled, true);
  601. }
  602. bool XROrigin3D::is_current() const {
  603. if (Engine::get_singleton()->is_editor_hint()) {
  604. // return as is
  605. return current;
  606. } else {
  607. return current && is_inside_tree();
  608. }
  609. }
  610. void XROrigin3D::_notification(int p_what) {
  611. // get our XRServer
  612. XRServer *xr_server = XRServer::get_singleton();
  613. ERR_FAIL_NULL(xr_server);
  614. switch (p_what) {
  615. case NOTIFICATION_ENTER_TREE: {
  616. if (!Engine::get_singleton()->is_editor_hint()) {
  617. if (origin_nodes.is_empty()) {
  618. // first entry always becomes current
  619. current = true;
  620. }
  621. origin_nodes.push_back(this);
  622. if (current) {
  623. // set this again so we do whatever setup is needed.
  624. set_current(true);
  625. }
  626. }
  627. } break;
  628. case NOTIFICATION_EXIT_TREE: {
  629. if (!Engine::get_singleton()->is_editor_hint()) {
  630. origin_nodes.erase(this);
  631. if (current) {
  632. // We are no longer current
  633. set_current(false);
  634. }
  635. }
  636. } break;
  637. case NOTIFICATION_LOCAL_TRANSFORM_CHANGED:
  638. case NOTIFICATION_TRANSFORM_CHANGED: {
  639. if (current && !Engine::get_singleton()->is_editor_hint()) {
  640. xr_server->set_world_origin(get_global_transform());
  641. }
  642. } break;
  643. }
  644. if (current) {
  645. // send our notification to all active XE interfaces, they may need to react to it also
  646. for (int i = 0; i < xr_server->get_interface_count(); i++) {
  647. Ref<XRInterface> interface = xr_server->get_interface(i);
  648. if (interface.is_valid() && interface->is_initialized()) {
  649. interface->notification(p_what);
  650. }
  651. }
  652. }
  653. }