summaryrefslogtreecommitdiffhomepage
path: root/tracker-tobii
diff options
context:
space:
mode:
Diffstat (limited to 'tracker-tobii')
-rw-r--r--tracker-tobii/CMakeLists.txt22
-rw-r--r--tracker-tobii/lang/nl_NL.ts22
-rw-r--r--tracker-tobii/lang/ru_RU.ts22
-rw-r--r--tracker-tobii/lang/stub.ts22
-rw-r--r--tracker-tobii/lang/zh_CN.ts22
-rw-r--r--tracker-tobii/tobii.cpp121
-rw-r--r--tracker-tobii/tobii.h61
-rw-r--r--tracker-tobii/tobii.qrc5
-rw-r--r--tracker-tobii/tobii.ui67
-rw-r--r--tracker-tobii/tobii_dialog.cpp20
-rw-r--r--tracker-tobii/tobii_logo.pngbin0 -> 1768 bytes
11 files changed, 384 insertions, 0 deletions
diff --git a/tracker-tobii/CMakeLists.txt b/tracker-tobii/CMakeLists.txt
new file mode 100644
index 00000000..8273d43e
--- /dev/null
+++ b/tracker-tobii/CMakeLists.txt
@@ -0,0 +1,22 @@
+if(WIN32)
+ # https://www.nuget.org/packages/Tobii.StreamEngine.Native/
+ set(SDK_TOBII "" CACHE PATH "Tobii Stream Engine path")
+endif()
+if(WIN32 AND SDK_TOBII)
+ otr_module(tracker-tobii)
+
+ if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
+ set(arch "x86")
+ else()
+ set(arch "x64")
+ endif()
+
+ target_include_directories(${self} SYSTEM PRIVATE "${SDK_TOBII}/include")
+ target_link_directories(${self} PRIVATE "${SDK_TOBII}/lib/${arch}")
+
+ set(dll "${SDK_TOBII}/lib/${arch}/tobii_stream_engine.dll")
+ set(lib tobii_stream_engine.lib)
+
+ target_link_libraries(${self} ${lib})
+ install(FILES ${dll} DESTINATION ${opentrack-libexec})
+endif()
diff --git a/tracker-tobii/lang/nl_NL.ts b/tracker-tobii/lang/nl_NL.ts
new file mode 100644
index 00000000..538a31f1
--- /dev/null
+++ b/tracker-tobii/lang/nl_NL.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>tobii_metadata</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tobii_ui</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-tobii/lang/ru_RU.ts b/tracker-tobii/lang/ru_RU.ts
new file mode 100644
index 00000000..b86ba010
--- /dev/null
+++ b/tracker-tobii/lang/ru_RU.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>tobii_metadata</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tobii_ui</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-tobii/lang/stub.ts b/tracker-tobii/lang/stub.ts
new file mode 100644
index 00000000..566900a3
--- /dev/null
+++ b/tracker-tobii/lang/stub.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>tobii_metadata</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tobii_ui</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-tobii/lang/zh_CN.ts b/tracker-tobii/lang/zh_CN.ts
new file mode 100644
index 00000000..908770e3
--- /dev/null
+++ b/tracker-tobii/lang/zh_CN.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>tobii_metadata</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tobii_ui</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished">请确保 Tobii 应用程序正在运行,跟踪功能处于激活状态. </translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-tobii/tobii.cpp b/tracker-tobii/tobii.cpp
new file mode 100644
index 00000000..8cf59335
--- /dev/null
+++ b/tracker-tobii/tobii.cpp
@@ -0,0 +1,121 @@
+/* Copyright (c) 2023, Khoa Nguyen <khoanguyen@3forcom.com>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#include "tobii.h"
+#include "compat/math-imports.hpp"
+
+#include <QMutexLocker>
+
+static constexpr double rad_to_deg = 180.0 * M_1_PI;
+static constexpr double mm_to_cm = 0.1;
+
+static void url_receiver(char const* url, void* user_data)
+{
+ char* buffer = (char*)user_data;
+ if (*buffer != '\0')
+ return; // only keep first value
+
+ if (strlen(url) < 256)
+ strcpy(buffer, url);
+}
+
+static void head_pose_callback(tobii_head_pose_t const* head_pose, void* user_data)
+{
+ // Store the latest head pose data in the supplied storage
+ tobii_head_pose_t* head_pose_storage = (tobii_head_pose_t*)user_data;
+ *head_pose_storage = *head_pose;
+}
+
+tobii_tracker::tobii_tracker() = default;
+
+tobii_tracker::~tobii_tracker()
+{
+ QMutexLocker lck(&mtx);
+ if (device)
+ {
+ tobii_head_pose_unsubscribe(device);
+ tobii_device_destroy(device);
+ }
+ if (api)
+ {
+ tobii_api_destroy(api);
+ }
+}
+
+module_status tobii_tracker::start_tracker(QFrame*)
+{
+ QMutexLocker lck(&mtx);
+ tobii_error_t tobii_error = tobii_api_create(&api, nullptr, nullptr);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ return error("Failed to initialize the Tobii Stream Engine API.");
+ }
+
+ char url[256] = { 0 };
+ tobii_error = tobii_enumerate_local_device_urls(api, url_receiver, url);
+ if (tobii_error != TOBII_ERROR_NO_ERROR || url[0] == '\0')
+ {
+ tobii_api_destroy(api);
+ return error("No stream engine compatible device(s) found.");
+ }
+
+ tobii_error = tobii_device_create(api, url, &device);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ tobii_api_destroy(api);
+ return error(QString("Failed to connect to %1.").arg(url));
+ }
+
+ tobii_error = tobii_head_pose_subscribe(device, head_pose_callback, &latest_head_pose);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ tobii_device_destroy(device);
+ tobii_api_destroy(api);
+ return error("Failed to subscribe to head pose stream.");
+ }
+
+ return status_ok();
+}
+
+void tobii_tracker::data(double* data)
+{
+ QMutexLocker lck(&mtx);
+ tobii_error_t tobii_error = tobii_device_process_callbacks(device);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ return;
+ }
+
+ // Tobii coordinate system is different from OpenTrack's
+ // Tobii: +x is to the right, +y is up, +z is towards the user
+ // Rotation xyz is in radians, x is pitch, y is yaw, z is roll
+
+ if (latest_head_pose.position_validity == TOBII_VALIDITY_VALID)
+ {
+ data[TX] = -latest_head_pose.position_xyz[0] * mm_to_cm;
+ data[TY] = latest_head_pose.position_xyz[1] * mm_to_cm;
+ data[TZ] = latest_head_pose.position_xyz[2] * mm_to_cm;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[0] == TOBII_VALIDITY_VALID)
+ {
+ data[Pitch] = latest_head_pose.rotation_xyz[0] * rad_to_deg;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[1] == TOBII_VALIDITY_VALID)
+ {
+ data[Yaw] = -latest_head_pose.rotation_xyz[1] * rad_to_deg;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[2] == TOBII_VALIDITY_VALID)
+ {
+ data[Roll] = latest_head_pose.rotation_xyz[2] * rad_to_deg;
+ }
+}
+
+OPENTRACK_DECLARE_TRACKER(tobii_tracker, tobii_dialog, tobii_metadata)
diff --git a/tracker-tobii/tobii.h b/tracker-tobii/tobii.h
new file mode 100644
index 00000000..414f9f64
--- /dev/null
+++ b/tracker-tobii/tobii.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2023, Khoa Nguyen <khoanguyen@3forcom.com>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#pragma once
+#include "api/plugin-api.hpp"
+#include "ui_tobii.h"
+
+#include <tobii/tobii.h>
+#include <tobii/tobii_streams.h>
+
+#include <QMutex>
+
+class tobii_tracker : public ITracker
+{
+public:
+ tobii_tracker();
+ ~tobii_tracker() override;
+ module_status start_tracker(QFrame*) override;
+ void data(double* data) override;
+
+private:
+ tobii_api_t* api = nullptr;
+ tobii_device_t* device = nullptr;
+
+ tobii_head_pose_t latest_head_pose{
+ .timestamp_us = 0LL,
+ .position_validity = TOBII_VALIDITY_INVALID,
+ .position_xyz = { 0.f, 0.f, 0.f },
+ .rotation_validity_xyz = { TOBII_VALIDITY_INVALID, TOBII_VALIDITY_INVALID, TOBII_VALIDITY_INVALID },
+ .rotation_xyz = { 0.f, 0.f, 0.f },
+ };
+
+ QMutex mtx;
+};
+
+class tobii_dialog : public ITrackerDialog
+{
+ Q_OBJECT
+
+ Ui::tobii_ui ui;
+
+public:
+ tobii_dialog();
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class tobii_metadata : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override { return tr("Tobii Eye Tracker"); }
+ QIcon icon() override { return QIcon(":/images/tobii_logo.png"); }
+};
diff --git a/tracker-tobii/tobii.qrc b/tracker-tobii/tobii.qrc
new file mode 100644
index 00000000..3e785ef7
--- /dev/null
+++ b/tracker-tobii/tobii.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/images">
+ <file>tobii_logo.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-tobii/tobii.ui b/tracker-tobii/tobii.ui
new file mode 100644
index 00000000..71b29c12
--- /dev/null
+++ b/tracker-tobii/tobii.ui
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>tobii_ui</class>
+ <widget class="QWidget" name="tobii_ui">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>278</width>
+ <height>58</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Tobii Eye Tracker</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>:/images/tobii_logo.png</normaloff>:/images/tobii_logo.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Please make sure the Tobii Experience application is running and tracking is active.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="tobii.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/tracker-tobii/tobii_dialog.cpp b/tracker-tobii/tobii_dialog.cpp
new file mode 100644
index 00000000..689cae38
--- /dev/null
+++ b/tracker-tobii/tobii_dialog.cpp
@@ -0,0 +1,20 @@
+#include "tobii.h"
+
+tobii_dialog::tobii_dialog() // NOLINT(cppcoreguidelines-pro-type-member-init)
+{
+ ui.setupUi(this);
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+}
+
+void tobii_dialog::doOK()
+{
+ // s.b->save();
+ close();
+}
+
+void tobii_dialog::doCancel()
+{
+ close();
+}
diff --git a/tracker-tobii/tobii_logo.png b/tracker-tobii/tobii_logo.png
new file mode 100644
index 00000000..3502b37b
--- /dev/null
+++ b/tracker-tobii/tobii_logo.png
Binary files differ