xr_nodes.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /*************************************************************************/
  2. /* xr_nodes.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  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::_notification(int p_what) {
  36. switch (p_what) {
  37. case NOTIFICATION_ENTER_TREE: {
  38. // need to find our XROrigin3D parent and let it know we're its camera!
  39. XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
  40. if (origin != nullptr) {
  41. origin->set_tracked_camera(this);
  42. }
  43. } break;
  44. case NOTIFICATION_EXIT_TREE: {
  45. // need to find our XROrigin3D parent and let it know we're no longer its camera!
  46. XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
  47. if (origin != nullptr && origin->get_tracked_camera() == this) {
  48. origin->set_tracked_camera(nullptr);
  49. }
  50. } break;
  51. }
  52. }
  53. void XRCamera3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  54. if (p_tracker_name == tracker_name) {
  55. XRServer *xr_server = XRServer::get_singleton();
  56. ERR_FAIL_NULL(xr_server);
  57. tracker = xr_server->get_tracker(p_tracker_name);
  58. if (tracker.is_valid()) {
  59. tracker->connect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed));
  60. Ref<XRPose> pose = tracker->get_pose(pose_name);
  61. if (pose.is_valid()) {
  62. set_transform(pose->get_adjusted_transform());
  63. }
  64. }
  65. }
  66. }
  67. void XRCamera3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  68. if (p_tracker_name == tracker_name) {
  69. if (tracker.is_valid()) {
  70. tracker->disconnect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed));
  71. }
  72. tracker.unref();
  73. }
  74. }
  75. void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) {
  76. if (p_pose->get_name() == pose_name) {
  77. set_transform(p_pose->get_adjusted_transform());
  78. }
  79. }
  80. TypedArray<String> XRCamera3D::get_configuration_warnings() const {
  81. TypedArray<String> warnings = Node::get_configuration_warnings();
  82. if (is_visible() && is_inside_tree()) {
  83. // must be child node of XROrigin3D!
  84. XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
  85. if (origin == nullptr) {
  86. warnings.push_back(RTR("XRCamera3D must have an XROrigin3D node as its parent."));
  87. };
  88. }
  89. return warnings;
  90. };
  91. Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
  92. // get our XRServer
  93. XRServer *xr_server = XRServer::get_singleton();
  94. ERR_FAIL_NULL_V(xr_server, Vector3());
  95. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  96. if (xr_interface.is_null()) {
  97. // we might be in the editor or have VR turned off, just call superclass
  98. return Camera3D::project_local_ray_normal(p_pos);
  99. }
  100. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
  101. Size2 viewport_size = get_viewport()->get_camera_rect_size();
  102. Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
  103. Vector3 ray;
  104. // Just use the first view, if multiple views are supported this function has no good result
  105. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  106. Vector2 screen_he = cm.get_viewport_half_extents();
  107. 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();
  108. return ray;
  109. };
  110. Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
  111. // get our XRServer
  112. XRServer *xr_server = XRServer::get_singleton();
  113. ERR_FAIL_NULL_V(xr_server, Vector2());
  114. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  115. if (xr_interface.is_null()) {
  116. // we might be in the editor or have VR turned off, just call superclass
  117. return Camera3D::unproject_position(p_pos);
  118. }
  119. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector2(), "Camera is not inside scene.");
  120. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  121. // Just use the first view, if multiple views are supported this function has no good result
  122. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  123. Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
  124. p = cm.xform4(p);
  125. p.normal /= p.d;
  126. Point2 res;
  127. res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x;
  128. res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y;
  129. return res;
  130. };
  131. Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const {
  132. // get our XRServer
  133. XRServer *xr_server = XRServer::get_singleton();
  134. ERR_FAIL_NULL_V(xr_server, Vector3());
  135. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  136. if (xr_interface.is_null()) {
  137. // we might be in the editor or have VR turned off, just call superclass
  138. return Camera3D::project_position(p_point, p_z_depth);
  139. }
  140. ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
  141. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  142. // Just use the first view, if multiple views are supported this function has no good result
  143. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  144. Vector2 vp_he = cm.get_viewport_half_extents();
  145. Vector2 point;
  146. point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0;
  147. point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
  148. point *= vp_he;
  149. Vector3 p(point.x, point.y, -p_z_depth);
  150. return get_camera_transform().xform(p);
  151. };
  152. Vector<Plane> XRCamera3D::get_frustum() const {
  153. // get our XRServer
  154. XRServer *xr_server = XRServer::get_singleton();
  155. ERR_FAIL_NULL_V(xr_server, Vector<Plane>());
  156. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  157. if (xr_interface.is_null()) {
  158. // we might be in the editor or have VR turned off, just call superclass
  159. return Camera3D::get_frustum();
  160. }
  161. ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
  162. Size2 viewport_size = get_viewport()->get_visible_rect().size;
  163. // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
  164. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
  165. return cm.get_projection_planes(get_camera_transform());
  166. };
  167. XRCamera3D::XRCamera3D() {
  168. XRServer *xr_server = XRServer::get_singleton();
  169. ERR_FAIL_NULL(xr_server);
  170. xr_server->connect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker));
  171. xr_server->connect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker));
  172. xr_server->connect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker));
  173. }
  174. XRCamera3D::~XRCamera3D() {
  175. XRServer *xr_server = XRServer::get_singleton();
  176. ERR_FAIL_NULL(xr_server);
  177. xr_server->disconnect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker));
  178. xr_server->disconnect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker));
  179. xr_server->disconnect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker));
  180. }
  181. ////////////////////////////////////////////////////////////////////////////////////////////////////
  182. // XRNode3D is a node that has it's transform updated by an XRPositionalTracker.
  183. // Note that trackers are only available in runtime and only after an XRInterface registers one.
  184. // So we bind by name and as long as a tracker isn't available, our node remains inactive.
  185. void XRNode3D::_bind_methods() {
  186. ClassDB::bind_method(D_METHOD("set_tracker", "tracker_name"), &XRNode3D::set_tracker);
  187. ClassDB::bind_method(D_METHOD("get_tracker"), &XRNode3D::get_tracker);
  188. ADD_PROPERTY(PropertyInfo(Variant::STRING, "tracker", PROPERTY_HINT_ENUM_SUGGESTION), "set_tracker", "get_tracker");
  189. ClassDB::bind_method(D_METHOD("set_pose_name", "pose"), &XRNode3D::set_pose_name);
  190. ClassDB::bind_method(D_METHOD("get_pose_name"), &XRNode3D::get_pose_name);
  191. ADD_PROPERTY(PropertyInfo(Variant::STRING, "pose", PROPERTY_HINT_ENUM_SUGGESTION), "set_pose_name", "get_pose_name");
  192. ClassDB::bind_method(D_METHOD("get_is_active"), &XRNode3D::get_is_active);
  193. ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRNode3D::get_has_tracking_data);
  194. ClassDB::bind_method(D_METHOD("get_pose"), &XRNode3D::get_pose);
  195. ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse);
  196. };
  197. void XRNode3D::_validate_property(PropertyInfo &p_property) const {
  198. XRServer *xr_server = XRServer::get_singleton();
  199. ERR_FAIL_NULL(xr_server);
  200. if (p_property.name == "tracker") {
  201. PackedStringArray names = xr_server->get_suggested_tracker_names();
  202. String hint_string;
  203. for (const String &name : names) {
  204. hint_string += name + ",";
  205. }
  206. p_property.hint_string = hint_string;
  207. } else if (p_property.name == "pose") {
  208. PackedStringArray names = xr_server->get_suggested_pose_names(tracker_name);
  209. String hint_string;
  210. for (const String &name : names) {
  211. hint_string += name + ",";
  212. }
  213. p_property.hint_string = hint_string;
  214. }
  215. }
  216. void XRNode3D::set_tracker(const StringName p_tracker_name) {
  217. if (tracker.is_valid() && tracker->get_tracker_name() == p_tracker_name) {
  218. // didn't change
  219. return;
  220. }
  221. // just in case
  222. _unbind_tracker();
  223. // copy the name
  224. tracker_name = p_tracker_name;
  225. pose_name = "default";
  226. // see if it's already available
  227. _bind_tracker();
  228. update_configuration_warnings();
  229. notify_property_list_changed();
  230. }
  231. StringName XRNode3D::get_tracker() const {
  232. return tracker_name;
  233. }
  234. void XRNode3D::set_pose_name(const StringName p_pose_name) {
  235. pose_name = p_pose_name;
  236. // Update pose if we are bound to a tracker with a valid pose
  237. Ref<XRPose> pose = get_pose();
  238. if (pose.is_valid()) {
  239. set_transform(pose->get_adjusted_transform());
  240. }
  241. }
  242. StringName XRNode3D::get_pose_name() const {
  243. return pose_name;
  244. }
  245. bool XRNode3D::get_is_active() const {
  246. if (tracker.is_null()) {
  247. return false;
  248. } else if (!tracker->has_pose(pose_name)) {
  249. return false;
  250. } else {
  251. return true;
  252. }
  253. }
  254. bool XRNode3D::get_has_tracking_data() const {
  255. if (tracker.is_null()) {
  256. return false;
  257. } else if (!tracker->has_pose(pose_name)) {
  258. return false;
  259. } else {
  260. return tracker->get_pose(pose_name)->get_has_tracking_data();
  261. }
  262. }
  263. void XRNode3D::trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
  264. // TODO need to link trackers to the interface that registered them so we can call this on the correct interface.
  265. // For now this works fine as in 99% of the cases we only have our primary interface active
  266. XRServer *xr_server = XRServer::get_singleton();
  267. if (xr_server != nullptr) {
  268. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  269. if (xr_interface.is_valid()) {
  270. xr_interface->trigger_haptic_pulse(p_action_name, tracker_name, p_frequency, p_amplitude, p_duration_sec, p_delay_sec);
  271. }
  272. }
  273. }
  274. Ref<XRPose> XRNode3D::get_pose() {
  275. if (tracker.is_valid()) {
  276. return tracker->get_pose(pose_name);
  277. } else {
  278. return Ref<XRPose>();
  279. }
  280. }
  281. void XRNode3D::_bind_tracker() {
  282. ERR_FAIL_COND_MSG(tracker.is_valid(), "Unbind the current tracker first");
  283. XRServer *xr_server = XRServer::get_singleton();
  284. if (xr_server != nullptr) {
  285. tracker = xr_server->get_tracker(tracker_name);
  286. if (tracker.is_null()) {
  287. // It is possible and valid if the tracker isn't available (yet), in this case we just exit
  288. return;
  289. }
  290. tracker->connect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed));
  291. Ref<XRPose> pose = get_pose();
  292. if (pose.is_valid()) {
  293. set_transform(pose->get_adjusted_transform());
  294. }
  295. }
  296. }
  297. void XRNode3D::_unbind_tracker() {
  298. if (tracker.is_valid()) {
  299. tracker->disconnect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed));
  300. tracker.unref();
  301. }
  302. }
  303. void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  304. if (p_tracker_name == p_tracker_name) {
  305. // just in case unref our current tracker
  306. _unbind_tracker();
  307. // get our new tracker
  308. _bind_tracker();
  309. }
  310. }
  311. void XRNode3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  312. if (p_tracker_name == p_tracker_name) {
  313. // unref our tracker, it's no longer available
  314. _unbind_tracker();
  315. }
  316. }
  317. void XRNode3D::_pose_changed(const Ref<XRPose> &p_pose) {
  318. if (p_pose.is_valid() && p_pose->get_name() == pose_name) {
  319. set_transform(p_pose->get_adjusted_transform());
  320. }
  321. }
  322. XRNode3D::XRNode3D() {
  323. XRServer *xr_server = XRServer::get_singleton();
  324. ERR_FAIL_NULL(xr_server);
  325. xr_server->connect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  326. xr_server->connect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  327. xr_server->connect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  328. }
  329. XRNode3D::~XRNode3D() {
  330. _unbind_tracker();
  331. XRServer *xr_server = XRServer::get_singleton();
  332. ERR_FAIL_NULL(xr_server);
  333. xr_server->disconnect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  334. xr_server->disconnect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  335. xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  336. }
  337. TypedArray<String> XRNode3D::get_configuration_warnings() const {
  338. TypedArray<String> warnings = Node::get_configuration_warnings();
  339. if (is_visible() && is_inside_tree()) {
  340. // must be child node of XROrigin!
  341. XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
  342. if (origin == nullptr) {
  343. warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent."));
  344. }
  345. if (tracker_name == "") {
  346. warnings.push_back(RTR("No tracker name is set."));
  347. }
  348. if (pose_name == "") {
  349. warnings.push_back(RTR("No pose is set."));
  350. }
  351. }
  352. return warnings;
  353. }
  354. ////////////////////////////////////////////////////////////////////////////////////////////////////
  355. void XRController3D::_bind_methods() {
  356. // passthroughs to information about our related joystick
  357. ClassDB::bind_method(D_METHOD("is_button_pressed", "name"), &XRController3D::is_button_pressed);
  358. ClassDB::bind_method(D_METHOD("get_value", "name"), &XRController3D::get_value);
  359. ClassDB::bind_method(D_METHOD("get_axis", "name"), &XRController3D::get_axis);
  360. ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand);
  361. ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name")));
  362. ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
  363. ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
  364. ADD_SIGNAL(MethodInfo("input_axis_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value")));
  365. };
  366. void XRController3D::_bind_tracker() {
  367. XRNode3D::_bind_tracker();
  368. if (tracker.is_valid()) {
  369. // bind to input signals
  370. tracker->connect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  371. tracker->connect("button_released", callable_mp(this, &XRController3D::_button_released));
  372. tracker->connect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed));
  373. tracker->connect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed));
  374. }
  375. }
  376. void XRController3D::_unbind_tracker() {
  377. if (tracker.is_valid()) {
  378. // unbind input signals
  379. tracker->disconnect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  380. tracker->disconnect("button_released", callable_mp(this, &XRController3D::_button_released));
  381. tracker->disconnect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed));
  382. tracker->disconnect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed));
  383. }
  384. XRNode3D::_unbind_tracker();
  385. }
  386. void XRController3D::_button_pressed(const String &p_name) {
  387. // just pass it on...
  388. emit_signal(SNAME("button_pressed"), p_name);
  389. }
  390. void XRController3D::_button_released(const String &p_name) {
  391. // just pass it on...
  392. emit_signal(SNAME("button_released"), p_name);
  393. }
  394. void XRController3D::_input_value_changed(const String &p_name, float p_value) {
  395. // just pass it on...
  396. emit_signal(SNAME("input_value_changed"), p_name, p_value);
  397. }
  398. void XRController3D::_input_axis_changed(const String &p_name, Vector2 p_value) {
  399. // just pass it on...
  400. emit_signal(SNAME("input_axis_changed"), p_name, p_value);
  401. }
  402. bool XRController3D::is_button_pressed(const StringName &p_name) const {
  403. if (tracker.is_valid()) {
  404. // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type
  405. bool pressed = tracker->get_input(p_name);
  406. return pressed;
  407. } else {
  408. return false;
  409. }
  410. }
  411. float XRController3D::get_value(const StringName &p_name) const {
  412. if (tracker.is_valid()) {
  413. // 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
  414. Variant input = tracker->get_input(p_name);
  415. switch (input.get_type()) {
  416. case Variant::BOOL: {
  417. bool value = input;
  418. return value ? 1.0 : 0.0;
  419. } break;
  420. case Variant::FLOAT: {
  421. float value = input;
  422. return value;
  423. } break;
  424. default:
  425. return 0.0;
  426. };
  427. } else {
  428. return 0.0;
  429. }
  430. }
  431. Vector2 XRController3D::get_axis(const StringName &p_name) const {
  432. if (tracker.is_valid()) {
  433. // 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
  434. Variant input = tracker->get_input(p_name);
  435. switch (input.get_type()) {
  436. case Variant::BOOL: {
  437. bool value = input;
  438. return Vector2(value ? 1.0 : 0.0, 0.0);
  439. } break;
  440. case Variant::FLOAT: {
  441. float value = input;
  442. return Vector2(value, 0.0);
  443. } break;
  444. case Variant::VECTOR2: {
  445. Vector2 axis = input;
  446. return axis;
  447. }
  448. default:
  449. return Vector2();
  450. }
  451. } else {
  452. return Vector2();
  453. }
  454. }
  455. XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const {
  456. // get our XRServer
  457. if (!tracker.is_valid()) {
  458. return XRPositionalTracker::TRACKER_HAND_UNKNOWN;
  459. }
  460. return tracker->get_tracker_hand();
  461. }
  462. ////////////////////////////////////////////////////////////////////////////////////////////////////
  463. void XRAnchor3D::_bind_methods() {
  464. ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size);
  465. ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane);
  466. }
  467. Vector3 XRAnchor3D::get_size() const {
  468. return size;
  469. }
  470. Plane XRAnchor3D::get_plane() const {
  471. Vector3 location = get_position();
  472. Basis orientation = get_transform().basis;
  473. Plane plane(orientation.get_column(1).normalized(), location);
  474. return plane;
  475. }
  476. ////////////////////////////////////////////////////////////////////////////////////////////////////
  477. TypedArray<String> XROrigin3D::get_configuration_warnings() const {
  478. TypedArray<String> warnings = Node::get_configuration_warnings();
  479. if (is_visible() && is_inside_tree()) {
  480. if (tracked_camera == nullptr) {
  481. warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node."));
  482. }
  483. }
  484. bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
  485. if (!xr_enabled) {
  486. warnings.push_back(RTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
  487. }
  488. return warnings;
  489. }
  490. void XROrigin3D::_bind_methods() {
  491. ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale);
  492. ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale);
  493. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
  494. }
  495. void XROrigin3D::set_tracked_camera(XRCamera3D *p_tracked_camera) {
  496. tracked_camera = p_tracked_camera;
  497. }
  498. XRCamera3D *XROrigin3D::get_tracked_camera() const {
  499. return tracked_camera;
  500. }
  501. real_t XROrigin3D::get_world_scale() const {
  502. // get our XRServer
  503. XRServer *xr_server = XRServer::get_singleton();
  504. ERR_FAIL_NULL_V(xr_server, 1.0);
  505. return xr_server->get_world_scale();
  506. }
  507. void XROrigin3D::set_world_scale(real_t p_world_scale) {
  508. // get our XRServer
  509. XRServer *xr_server = XRServer::get_singleton();
  510. ERR_FAIL_NULL(xr_server);
  511. xr_server->set_world_scale(p_world_scale);
  512. }
  513. void XROrigin3D::_notification(int p_what) {
  514. // get our XRServer
  515. XRServer *xr_server = XRServer::get_singleton();
  516. ERR_FAIL_NULL(xr_server);
  517. switch (p_what) {
  518. case NOTIFICATION_ENTER_TREE: {
  519. set_process_internal(true);
  520. } break;
  521. case NOTIFICATION_EXIT_TREE: {
  522. set_process_internal(false);
  523. } break;
  524. case NOTIFICATION_INTERNAL_PROCESS: {
  525. // set our world origin to our node transform
  526. xr_server->set_world_origin(get_global_transform());
  527. // check if we have a primary interface
  528. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  529. if (xr_interface.is_valid() && tracked_camera != nullptr) {
  530. // get our positioning transform for our headset
  531. Transform3D t = xr_interface->get_camera_transform();
  532. // now apply this to our camera
  533. tracked_camera->set_transform(t);
  534. }
  535. } break;
  536. }
  537. // send our notification to all active XE interfaces, they may need to react to it also
  538. for (int i = 0; i < xr_server->get_interface_count(); i++) {
  539. Ref<XRInterface> interface = xr_server->get_interface(i);
  540. if (interface.is_valid() && interface->is_initialized()) {
  541. interface->notification(p_what);
  542. }
  543. }
  544. }