xr_nodes.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  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 &property) const {
  198. XRServer *xr_server = XRServer::get_singleton();
  199. ERR_FAIL_NULL(xr_server);
  200. if (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. property.hint_string = hint_string;
  207. } else if (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. property.hint_string = hint_string;
  214. }
  215. Node3D::_validate_property(property);
  216. }
  217. void XRNode3D::set_tracker(const StringName p_tracker_name) {
  218. if (tracker.is_valid() && tracker->get_tracker_name() == p_tracker_name) {
  219. // didn't change
  220. return;
  221. }
  222. // just in case
  223. _unbind_tracker();
  224. // copy the name
  225. tracker_name = p_tracker_name;
  226. pose_name = "default";
  227. // see if it's already available
  228. _bind_tracker();
  229. update_configuration_warnings();
  230. notify_property_list_changed();
  231. }
  232. StringName XRNode3D::get_tracker() const {
  233. return tracker_name;
  234. }
  235. void XRNode3D::set_pose_name(const StringName p_pose_name) {
  236. pose_name = p_pose_name;
  237. // Update pose if we are bound to a tracker with a valid pose
  238. Ref<XRPose> pose = get_pose();
  239. if (pose.is_valid()) {
  240. set_transform(pose->get_adjusted_transform());
  241. }
  242. }
  243. StringName XRNode3D::get_pose_name() const {
  244. return pose_name;
  245. }
  246. bool XRNode3D::get_is_active() const {
  247. if (tracker.is_null()) {
  248. return false;
  249. } else if (!tracker->has_pose(pose_name)) {
  250. return false;
  251. } else {
  252. return true;
  253. }
  254. }
  255. bool XRNode3D::get_has_tracking_data() const {
  256. if (tracker.is_null()) {
  257. return false;
  258. } else if (!tracker->has_pose(pose_name)) {
  259. return false;
  260. } else {
  261. return tracker->get_pose(pose_name)->get_has_tracking_data();
  262. }
  263. }
  264. void XRNode3D::trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
  265. // TODO need to link trackers to the interface that registered them so we can call this on the correct interface.
  266. // For now this works fine as in 99% of the cases we only have our primary interface active
  267. XRServer *xr_server = XRServer::get_singleton();
  268. if (xr_server != nullptr) {
  269. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  270. if (xr_interface.is_valid()) {
  271. xr_interface->trigger_haptic_pulse(p_action_name, tracker_name, p_frequency, p_amplitude, p_duration_sec, p_delay_sec);
  272. }
  273. }
  274. }
  275. Ref<XRPose> XRNode3D::get_pose() {
  276. if (tracker.is_valid()) {
  277. return tracker->get_pose(pose_name);
  278. } else {
  279. return Ref<XRPose>();
  280. }
  281. }
  282. void XRNode3D::_bind_tracker() {
  283. ERR_FAIL_COND_MSG(tracker.is_valid(), "Unbind the current tracker first");
  284. XRServer *xr_server = XRServer::get_singleton();
  285. if (xr_server != nullptr) {
  286. tracker = xr_server->get_tracker(tracker_name);
  287. if (tracker.is_null()) {
  288. // It is possible and valid if the tracker isn't available (yet), in this case we just exit
  289. return;
  290. }
  291. tracker->connect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed));
  292. Ref<XRPose> pose = get_pose();
  293. if (pose.is_valid()) {
  294. set_transform(pose->get_adjusted_transform());
  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.unref();
  302. }
  303. }
  304. void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  305. if (p_tracker_name == p_tracker_name) {
  306. // just in case unref our current tracker
  307. _unbind_tracker();
  308. // get our new tracker
  309. _bind_tracker();
  310. }
  311. }
  312. void XRNode3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) {
  313. if (p_tracker_name == p_tracker_name) {
  314. // unref our tracker, it's no longer available
  315. _unbind_tracker();
  316. }
  317. }
  318. void XRNode3D::_pose_changed(const Ref<XRPose> &p_pose) {
  319. if (p_pose.is_valid() && p_pose->get_name() == pose_name) {
  320. set_transform(p_pose->get_adjusted_transform());
  321. }
  322. }
  323. XRNode3D::XRNode3D() {
  324. XRServer *xr_server = XRServer::get_singleton();
  325. ERR_FAIL_NULL(xr_server);
  326. xr_server->connect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  327. xr_server->connect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  328. xr_server->connect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  329. }
  330. XRNode3D::~XRNode3D() {
  331. _unbind_tracker();
  332. XRServer *xr_server = XRServer::get_singleton();
  333. ERR_FAIL_NULL(xr_server);
  334. xr_server->disconnect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker));
  335. xr_server->disconnect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker));
  336. xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
  337. }
  338. TypedArray<String> XRNode3D::get_configuration_warnings() const {
  339. TypedArray<String> warnings = Node::get_configuration_warnings();
  340. if (is_visible() && is_inside_tree()) {
  341. // must be child node of XROrigin!
  342. XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
  343. if (origin == nullptr) {
  344. warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent."));
  345. }
  346. if (tracker_name == "") {
  347. warnings.push_back(RTR("No tracker name is set."));
  348. }
  349. if (pose_name == "") {
  350. warnings.push_back(RTR("No pose is set."));
  351. }
  352. }
  353. return warnings;
  354. }
  355. ////////////////////////////////////////////////////////////////////////////////////////////////////
  356. void XRController3D::_bind_methods() {
  357. // passthroughs to information about our related joystick
  358. ClassDB::bind_method(D_METHOD("is_button_pressed", "name"), &XRController3D::is_button_pressed);
  359. ClassDB::bind_method(D_METHOD("get_value", "name"), &XRController3D::get_value);
  360. ClassDB::bind_method(D_METHOD("get_axis", "name"), &XRController3D::get_axis);
  361. ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand);
  362. ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name")));
  363. ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
  364. ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
  365. ADD_SIGNAL(MethodInfo("input_axis_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value")));
  366. };
  367. void XRController3D::_bind_tracker() {
  368. XRNode3D::_bind_tracker();
  369. if (tracker.is_valid()) {
  370. // bind to input signals
  371. tracker->connect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  372. tracker->connect("button_released", callable_mp(this, &XRController3D::_button_released));
  373. tracker->connect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed));
  374. tracker->connect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed));
  375. }
  376. }
  377. void XRController3D::_unbind_tracker() {
  378. if (tracker.is_valid()) {
  379. // unbind input signals
  380. tracker->disconnect("button_pressed", callable_mp(this, &XRController3D::_button_pressed));
  381. tracker->disconnect("button_released", callable_mp(this, &XRController3D::_button_released));
  382. tracker->disconnect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed));
  383. tracker->disconnect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed));
  384. }
  385. XRNode3D::_unbind_tracker();
  386. }
  387. void XRController3D::_button_pressed(const String &p_name) {
  388. // just pass it on...
  389. emit_signal(SNAME("button_pressed"), p_name);
  390. }
  391. void XRController3D::_button_released(const String &p_name) {
  392. // just pass it on...
  393. emit_signal(SNAME("button_released"), p_name);
  394. }
  395. void XRController3D::_input_value_changed(const String &p_name, float p_value) {
  396. // just pass it on...
  397. emit_signal(SNAME("input_value_changed"), p_name, p_value);
  398. }
  399. void XRController3D::_input_axis_changed(const String &p_name, Vector2 p_value) {
  400. // just pass it on...
  401. emit_signal(SNAME("input_axis_changed"), p_name, p_value);
  402. }
  403. bool XRController3D::is_button_pressed(const StringName &p_name) const {
  404. if (tracker.is_valid()) {
  405. // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type
  406. bool pressed = tracker->get_input(p_name);
  407. return pressed;
  408. } else {
  409. return false;
  410. }
  411. }
  412. float XRController3D::get_value(const StringName &p_name) const {
  413. if (tracker.is_valid()) {
  414. // 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
  415. Variant input = tracker->get_input(p_name);
  416. switch (input.get_type()) {
  417. case Variant::BOOL: {
  418. bool value = input;
  419. return value ? 1.0 : 0.0;
  420. } break;
  421. case Variant::FLOAT: {
  422. float value = input;
  423. return value;
  424. } break;
  425. default:
  426. return 0.0;
  427. };
  428. } else {
  429. return 0.0;
  430. }
  431. }
  432. Vector2 XRController3D::get_axis(const StringName &p_name) const {
  433. if (tracker.is_valid()) {
  434. // 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
  435. Variant input = tracker->get_input(p_name);
  436. switch (input.get_type()) {
  437. case Variant::BOOL: {
  438. bool value = input;
  439. return Vector2(value ? 1.0 : 0.0, 0.0);
  440. } break;
  441. case Variant::FLOAT: {
  442. float value = input;
  443. return Vector2(value, 0.0);
  444. } break;
  445. case Variant::VECTOR2: {
  446. Vector2 axis = input;
  447. return axis;
  448. }
  449. default:
  450. return Vector2();
  451. }
  452. } else {
  453. return Vector2();
  454. }
  455. }
  456. XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const {
  457. // get our XRServer
  458. if (!tracker.is_valid()) {
  459. return XRPositionalTracker::TRACKER_HAND_UNKNOWN;
  460. }
  461. return tracker->get_tracker_hand();
  462. }
  463. ////////////////////////////////////////////////////////////////////////////////////////////////////
  464. void XRAnchor3D::_bind_methods() {
  465. ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size);
  466. ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane);
  467. }
  468. Vector3 XRAnchor3D::get_size() const {
  469. return size;
  470. }
  471. Plane XRAnchor3D::get_plane() const {
  472. Vector3 location = get_position();
  473. Basis orientation = get_transform().basis;
  474. Plane plane(orientation.get_column(1).normalized(), location);
  475. return plane;
  476. }
  477. ////////////////////////////////////////////////////////////////////////////////////////////////////
  478. TypedArray<String> XROrigin3D::get_configuration_warnings() const {
  479. TypedArray<String> warnings = Node::get_configuration_warnings();
  480. if (is_visible() && is_inside_tree()) {
  481. if (tracked_camera == nullptr) {
  482. warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node."));
  483. }
  484. }
  485. bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
  486. if (!xr_enabled) {
  487. warnings.push_back(RTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
  488. }
  489. return warnings;
  490. }
  491. void XROrigin3D::_bind_methods() {
  492. ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale);
  493. ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale);
  494. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
  495. }
  496. void XROrigin3D::set_tracked_camera(XRCamera3D *p_tracked_camera) {
  497. tracked_camera = p_tracked_camera;
  498. }
  499. XRCamera3D *XROrigin3D::get_tracked_camera() const {
  500. return tracked_camera;
  501. }
  502. real_t XROrigin3D::get_world_scale() const {
  503. // get our XRServer
  504. XRServer *xr_server = XRServer::get_singleton();
  505. ERR_FAIL_NULL_V(xr_server, 1.0);
  506. return xr_server->get_world_scale();
  507. }
  508. void XROrigin3D::set_world_scale(real_t p_world_scale) {
  509. // get our XRServer
  510. XRServer *xr_server = XRServer::get_singleton();
  511. ERR_FAIL_NULL(xr_server);
  512. xr_server->set_world_scale(p_world_scale);
  513. }
  514. void XROrigin3D::_notification(int p_what) {
  515. // get our XRServer
  516. XRServer *xr_server = XRServer::get_singleton();
  517. ERR_FAIL_NULL(xr_server);
  518. switch (p_what) {
  519. case NOTIFICATION_ENTER_TREE: {
  520. set_process_internal(true);
  521. } break;
  522. case NOTIFICATION_EXIT_TREE: {
  523. set_process_internal(false);
  524. } break;
  525. case NOTIFICATION_INTERNAL_PROCESS: {
  526. // set our world origin to our node transform
  527. xr_server->set_world_origin(get_global_transform());
  528. // check if we have a primary interface
  529. Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
  530. if (xr_interface.is_valid() && tracked_camera != nullptr) {
  531. // get our positioning transform for our headset
  532. Transform3D t = xr_interface->get_camera_transform();
  533. // now apply this to our camera
  534. tracked_camera->set_transform(t);
  535. }
  536. } break;
  537. }
  538. // send our notification to all active XE interfaces, they may need to react to it also
  539. for (int i = 0; i < xr_server->get_interface_count(); i++) {
  540. Ref<XRInterface> interface = xr_server->get_interface(i);
  541. if (interface.is_valid() && interface->is_initialized()) {
  542. interface->notification(p_what);
  543. }
  544. }
  545. }