From dd1d54491184b9aad4b6d3e53e08c1caa049db97 Mon Sep 17 00:00:00 2001 From: Jens Ahrens Date: Wed, 14 Jun 2023 21:07:08 +0200 Subject: [PATCH] Fix reference offset in GUI and add 3D support for Polhemus tracker --- doc/manual/browser-gui.rst | 2 ++ doc/manual/operation.rst | 10 +++++++++ doc/manual/renderers.rst | 7 +++++- src/gui/qopenglplotter.cpp | 36 ++++++++++++++++++------------ src/trackerpolhemus.cpp | 45 +++++++++++++++++++++++++------------- src/trackerpolhemus.h | 22 +++---------------- 6 files changed, 73 insertions(+), 49 deletions(-) diff --git a/doc/manual/browser-gui.rst b/doc/manual/browser-gui.rst index 41d6d6fe..88834f53 100644 --- a/doc/manual/browser-gui.rst +++ b/doc/manual/browser-gui.rst @@ -1,3 +1,5 @@ +.. _browser-based_gui: + Browser-based GUI ================= diff --git a/doc/manual/operation.rst b/doc/manual/operation.rst index 4298cb55..b2df567c 100644 --- a/doc/manual/operation.rst +++ b/doc/manual/operation.rst @@ -263,6 +263,12 @@ You can calibrate the tracker while the SSR is running by pressing ``Return``. The instantaneous orientation will then be interpreted as straight forward, i.e. upwards on the screen (:math:`\alpha = 90^\circ`\ ). +SSR is progressively (and silently) moving from 2D scenes to 3D scenes. The +:ref:`binaural renderer` can handle head tracking about all +three axes of rotation if the HRIRs are provided in SOFA. We recommend using +the :ref:`browser-based GUI` to monitor the tracking as the +built-in GUI only visualizes tracking along the azimuth. + .. _prep_isense: Preparing InterSense InertiaCube3 @@ -302,6 +308,10 @@ or so. If you want to disable this tracker, use ``./configure --disable-polhemus`` and recompile. +If you are using head tracking about all three axes of rotation, make sure +that the tracking sensor is mounted on the headphones such that the cable +leaves the sensor towards the left relative to the look direction of the user. + Preparing VRPN ^^^^^^^^^^^^^^ diff --git a/doc/manual/renderers.rst b/doc/manual/renderers.rst index 6844fc6a..f4277617 100644 --- a/doc/manual/renderers.rst +++ b/doc/manual/renderers.rst @@ -289,7 +289,7 @@ the outside of the listener's head to the inside. SSR uses HRIRs with an angular resolution of :math:`1^\circ`\ . Thus, the HRIR file contains 720 impulse responses (360 for each ear) stored as a 720-channel .wav-file. The HRIRs all have to be of equal length and -have to be arranged in the following order: +have to be arranged in the following order [#fnsofa]_: - 1st channel: left ear, virtual source position :math:`0^\circ` @@ -330,6 +330,11 @@ the HRIRs. By choosing shorter frames and thus using partitioned convolution the system latency is reduced but computational load is increased. +.. [#fnsofa] Note that the binaural renderer has the currently hidden and + undocumented feature of being able to read HRIRs in `SOFA + `_. It supports head tracking + about all three axes of rotation in this case. + The HRIR sets shipped with SSR ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/gui/qopenglplotter.cpp b/src/gui/qopenglplotter.cpp index 613389a6..e36b4dfa 100644 --- a/src/gui/qopenglplotter.cpp +++ b/src/gui/qopenglplotter.cpp @@ -290,7 +290,7 @@ ssr::QOpenGLPlotter::paintGL() _draw_reference(); _draw_objects(); - + _draw_rubber_band(); } @@ -362,8 +362,10 @@ void ssr::QOpenGLPlotter::_draw_reference() glPushMatrix(); // translate according to reference position - glTranslatef(_scene.get_reference().position.x, - _scene.get_reference().position.y, 0.0f); + glTranslatef(_scene.get_reference().position.x + + _scene.get_reference_offset().position.x, + _scene.get_reference().position.y + + _scene.get_reference_offset().position.y, 0.0f); glPushMatrix(); @@ -383,7 +385,9 @@ void ssr::QOpenGLPlotter::_draw_reference() glTranslatef(0.03f, -0.03f, 0.0f); // rotate according to reference position - glRotatef(_scene.get_reference().orientation.azimuth, 0.0f, 0.0f, 1.0f); + glRotatef(_scene.get_reference().orientation.azimuth + + _scene.get_reference_offset().orientation.azimuth + , 0.0f, 0.0f, 1.0f); glBindTexture(GL_TEXTURE_2D, _listener_shadow_texture); @@ -397,7 +401,9 @@ void ssr::QOpenGLPlotter::_draw_reference() glPopMatrix(); // rotate according to reference position - glRotatef(_scene.get_reference().orientation.azimuth, 0.0f, 0.0f, 1.0f); + glRotatef(_scene.get_reference().orientation.azimuth + + _scene.get_reference_offset().orientation.azimuth + ,0.0f, 0.0f, 1.0f); glBindTexture(GL_TEXTURE_2D, _listener_texture); @@ -414,7 +420,8 @@ void ssr::QOpenGLPlotter::_draw_reference() else { // rotate according to reference position - glRotatef(_scene.get_reference().orientation.azimuth, 0.0f, 0.0f, 1.0f); + glRotatef(_scene.get_reference().orientation.azimuth + + _scene.get_reference_offset().orientation.azimuth, 0.0f, 0.0f, 1.0f); // background color glColor3f(BACKGROUNDCOLOR); @@ -448,8 +455,9 @@ void ssr::QOpenGLPlotter::_draw_reference() // rotate/translate according to reference offset glTranslatef(_scene.get_reference_offset().position.x , _scene.get_reference_offset().position.y, 0.0f); - glRotatef(_scene.get_reference_offset().orientation.azimuth - , 0.0f, 0.0f, 1.0f); + glRotatef(_scene.get_reference().orientation.azimuth + + _scene.get_reference_offset().orientation.azimuth + , 0.0f, 0.0f, 1.0f); // draw cross (showing the reference offset) glBegin(GL_LINES); @@ -491,7 +499,7 @@ void ssr::QOpenGLPlotter::_draw_objects() std::vector output_levels; - + if (_selected_sources_map.size() > 0) { output_levels = _scene.get_source(_selected_sources_map.rbegin()->second).output_levels; @@ -1044,7 +1052,7 @@ void ssr::QOpenGLPlotter::_select_source(int source, bool add_to_selection) // if source does not exist if (source > static_cast(source_buffer_list.size())) - { + { _id_of_last_clicked_source = 0; return; } @@ -1053,13 +1061,13 @@ void ssr::QOpenGLPlotter::_select_source(int source, bool add_to_selection) // iterate to source for (int n = 1; n < source; n++) i++; - + // make its id directly available _id_of_last_clicked_source = i->id; - + // store source and its id _selected_sources_map[source] = _id_of_last_clicked_source; // TODO - + } else if (!_alt_pressed) { @@ -1068,7 +1076,7 @@ void ssr::QOpenGLPlotter::_select_source(int source, bool add_to_selection) } // if source is already selected then deselect it else if (_alt_pressed) _deselect_source(source); - + } void ssr::QOpenGLPlotter::_select_all_sources() diff --git a/src/trackerpolhemus.cpp b/src/trackerpolhemus.cpp index c2bb060d..255b4759 100644 --- a/src/trackerpolhemus.cpp +++ b/src/trackerpolhemus.cpp @@ -36,7 +36,6 @@ #include // std::chrono::milliseconds #include "api.h" // for Publisher -#include "legacy_orientation.h" // for Orientation #include "trackerpolhemus.h" #include "ssr_global.h" #include "apf/stringtools.h" @@ -47,7 +46,7 @@ ssr::TrackerPolhemus::TrackerPolhemus(api::Publisher& controller , const std::string& type, const std::string& ports) : Tracker() , _controller(controller) - , _az_corr(0.0f) + , _corr_quat() , _stop_thread(false) { if (ports == "") @@ -192,7 +191,7 @@ ssr::TrackerPolhemus::_open_serial_port(const char *portname) void ssr::TrackerPolhemus::calibrate() { - _az_corr = _current_data.azimuth; + _corr_quat = inverse(_current_quat); } void @@ -236,7 +235,7 @@ ssr::TrackerPolhemus::_thread() if (error < 1) { - SSR_ERROR("Can not read from serial port. Stopping Polhemus tracker."); + SSR_ERROR("Cannot read from serial port. Stopping Polhemus tracker."); } if ((error = read(_tracker_port, &c, 1))) @@ -245,13 +244,13 @@ ssr::TrackerPolhemus::_thread() } else { - SSR_ERROR("Can not read from serial port."); + SSR_ERROR("Cannot read from serial port."); } } if (line.size() != _line_size) { - _current_data.azimuth = 0.0f; + // simply keep whatever is store in _current_quat continue; } @@ -282,16 +281,32 @@ ssr::TrackerPolhemus::_thread() // sockets are available and each of the 6 degrees of freedom are represented by // 9 bytes, leading to a total of 60 ASCII bytes. - // extract data - lineparse >> _current_data.header - >> _current_data.x - >> _current_data.y - >> _current_data.z - >> _current_data.azimuth - >> _current_data.elevation - >> _current_data.roll; + float header; + float x; + float y; + float z; + float azimuth; + float elevation; + float roll; + // extract data + lineparse >> header + >> x + >> y + >> z + >> azimuth + >> elevation + >> roll; + + // convert to quaternion (-azimuth because the tracker assumes that positive + // z is downwards; rotation by 90 deg because we assume that the sensor is + // mounted such that the cable goes to the left relative to the user's + // orientation) + _current_quat = angles2quat(-azimuth, elevation, roll) + * angles2quat(90, 0, 0); + + // apply calibration _controller.take_control()->reference_rotation_offset( - Orientation(-_current_data.azimuth + _az_corr)); + ssr::quat(_corr_quat * _current_quat)); }; } diff --git a/src/trackerpolhemus.h b/src/trackerpolhemus.h index 507b6853..bc0cf910 100644 --- a/src/trackerpolhemus.h +++ b/src/trackerpolhemus.h @@ -37,6 +37,7 @@ #include // for std::runtime_error #include "tracker.h" +#include "geometry.h" namespace ssr { @@ -62,31 +63,14 @@ class TrackerPolhemus : public Tracker TrackerPolhemus(api::Publisher& controller, const std::string& type , const std::string& ports); - struct tracker_data_t - { - float header; - float x; - float y; - float z; - float azimuth; - float elevation; - float roll; - - // contructor - tracker_data_t() - : header(0.0f), x(0.0f), y(0.0f), z(0.0f) - , azimuth(0.0f), elevation(0.0f), roll(0.0f) - {} - }; - api::Publisher& _controller; - tracker_data_t _current_data; + ssr::quat _current_quat; int _tracker_port; int _open_serial_port(const char *portname); - float _az_corr; ///< correction of the azimuth due to calibration + ssr::quat _corr_quat; ///< correction of the orientation due to calibration std::string::size_type _line_size;