summaryrefslogtreecommitdiffhomepage
path: root/facetracknoir
diff options
context:
space:
mode:
Diffstat (limited to 'facetracknoir')
-rw-r--r--facetracknoir/clientfiles/FlightGear/Protocol/headtracker.xml54
-rw-r--r--facetracknoir/clientfiles/FlightGear/readme.txt8
-rw-r--r--facetracknoir/clientfiles/Tir4Fun/npclient.dllbin0 -> 53248 bytes
-rw-r--r--facetracknoir/clientfiles/Tir4Fun/readme.txt9
-rw-r--r--facetracknoir/clientfiles/Tir4Fun/tir4fun.exebin0 -> 36864 bytes
-rw-r--r--facetracknoir/clientfiles/aruco/aruco_create_marker.exebin0 -> 826368 bytes
-rw-r--r--facetracknoir/clientfiles/aruco/test3.jpgbin0 -> 2145 bytes
-rw-r--r--facetracknoir/clientfiles/cfs3/readme.txt27
-rw-r--r--facetracknoir/clientfiles/cfs3/tirviews.dllbin0 -> 109568 bytes
-rw-r--r--facetracknoir/clientfiles/freetracktest/freetracktest.exebin0 -> 398848 bytes
-rw-r--r--facetracknoir/clientfiles/freetracktest/readme.txt20
-rw-r--r--facetracknoir/clientfiles/fs2002 and fs2004/fsuipc.dllbin0 -> 210880 bytes
-rw-r--r--facetracknoir/clientfiles/glovepie/facetracknoir2trackir.pie16
-rw-r--r--facetracknoir/clientfiles/glovepie/readme.txt24
-rw-r--r--facetracknoir/clientfiles/ppjoy/ppjoy mapping for facetracknoir.jpgbin0 -> 155205 bytes
-rw-r--r--facetracknoir/clientfiles/ppjoy/readme.txt24
-rw-r--r--facetracknoir/clientfiles/vjoy/VJoy.dllbin0 -> 94208 bytes
-rw-r--r--facetracknoir/curve-config.cpp117
-rw-r--r--facetracknoir/curve-config.h22
-rw-r--r--facetracknoir/facetracknoir.cpp698
-rw-r--r--facetracknoir/facetracknoir.h165
-rw-r--r--facetracknoir/facetracknoir.icobin0 -> 67134 bytes
-rw-r--r--facetracknoir/facetracknoir.rc2
-rw-r--r--facetracknoir/facetracknoir.ui1727
-rw-r--r--facetracknoir/ftnoir_curves.ui976
-rw-r--r--facetracknoir/ftnoir_keyboardshortcuts.ui217
-rw-r--r--facetracknoir/global-settings.cpp132
-rw-r--r--facetracknoir/global-settings.h93
-rw-r--r--facetracknoir/global-shortcuts.cpp138
-rw-r--r--facetracknoir/images/facetracknoir.pngbin0 -> 26466 bytes
-rw-r--r--facetracknoir/images/settings16.pngbin0 -> 711 bytes
-rw-r--r--facetracknoir/lerp.hpp60
-rw-r--r--facetracknoir/main-facetracknoir.qrc8
-rw-r--r--facetracknoir/main-settings.hpp57
-rw-r--r--facetracknoir/main.cpp72
-rw-r--r--facetracknoir/mingw-version-script.txt8
-rw-r--r--facetracknoir/options.h319
-rw-r--r--facetracknoir/posix-version-script.txt8
-rw-r--r--facetracknoir/qt-moc.h10
-rw-r--r--facetracknoir/rotation.h64
-rw-r--r--facetracknoir/shortcuts.cpp152
-rw-r--r--facetracknoir/shortcuts.h85
-rw-r--r--facetracknoir/timer.hpp66
-rw-r--r--facetracknoir/tracker.cpp195
-rw-r--r--facetracknoir/tracker.h102
-rw-r--r--facetracknoir/tracker_types.cpp44
-rw-r--r--facetracknoir/tracker_types.h19
-rw-r--r--facetracknoir/uielements/curves.pngbin0 -> 2850 bytes
-rw-r--r--facetracknoir/uielements/tools.pngbin0 -> 3053 bytes
49 files changed, 5738 insertions, 0 deletions
diff --git a/facetracknoir/clientfiles/FlightGear/Protocol/headtracker.xml b/facetracknoir/clientfiles/FlightGear/Protocol/headtracker.xml
new file mode 100644
index 00000000..8c14119a
--- /dev/null
+++ b/facetracknoir/clientfiles/FlightGear/Protocol/headtracker.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+
+<PropertyList>
+ <generic>
+ <input>
+ <binary_mode>true</binary_mode>
+ <binary_footer>none</binary_footer>
+ <byte_order>host</byte_order>
+ <record_length>52</record_length>
+
+ <chunk>
+ <name>x</name>
+ <type>double</type>
+ <node>/sim/current-view/x-offset-m</node>
+ </chunk>
+
+ <chunk>
+ <name>y</name>
+ <type>double</type>
+ <node>/sim/current-view/y-offset-m</node>
+ </chunk>
+
+ <chunk>
+ <name>z</name>
+ <type>double</type>
+ <node>/sim/current-view/z-offset-m</node>
+ </chunk>
+
+ <chunk>
+ <name>heading</name>
+ <type>double</type>
+ <node>/sim/current-view/heading-offset-deg</node>
+ </chunk>
+
+ <chunk>
+ <name>pitch</name>
+ <type>double</type>
+ <node>/sim/current-view/pitch-offset-deg</node>
+ </chunk>
+
+ <chunk>
+ <name>roll</name>
+ <type>double</type>
+ <node>/sim/current-view/roll-offset-deg</node>
+ </chunk>
+
+ <chunk>
+ <name>status</name>
+ <type>int</type>
+ <node>/sim/current-view/headtracker-debug-status</node>
+ </chunk>
+ </input>
+ </generic>
+</PropertyList>
diff --git a/facetracknoir/clientfiles/FlightGear/readme.txt b/facetracknoir/clientfiles/FlightGear/readme.txt
new file mode 100644
index 00000000..0b3d9dfe
--- /dev/null
+++ b/facetracknoir/clientfiles/FlightGear/readme.txt
@@ -0,0 +1,8 @@
+Copy Protocol/headtracker.xml to fgdata/Protocol/headtracker.xml
+
+$ fgfs --generic=socket,in,25,localhost,5542,udp,headtracker
+
+Adjust paths as necessary.
+
+cheers,
+-sh 20131008
diff --git a/facetracknoir/clientfiles/Tir4Fun/npclient.dll b/facetracknoir/clientfiles/Tir4Fun/npclient.dll
new file mode 100644
index 00000000..e392442e
--- /dev/null
+++ b/facetracknoir/clientfiles/Tir4Fun/npclient.dll
Binary files differ
diff --git a/facetracknoir/clientfiles/Tir4Fun/readme.txt b/facetracknoir/clientfiles/Tir4Fun/readme.txt
new file mode 100644
index 00000000..d64af301
--- /dev/null
+++ b/facetracknoir/clientfiles/Tir4Fun/readme.txt
@@ -0,0 +1,9 @@
+What is TIR4FUN?
+
+TIR4FUN is a free utility for dedicated gamers. It enables 6DOF POV control with mouse and joystick axes.
+
+Software is provided as it is. Configuration is straightforward. GUI says it all!
+
+Installation:
+
+Copy all files to a directory. Launch tir4fun.exe to bring up the GUI.
diff --git a/facetracknoir/clientfiles/Tir4Fun/tir4fun.exe b/facetracknoir/clientfiles/Tir4Fun/tir4fun.exe
new file mode 100644
index 00000000..a51eced0
--- /dev/null
+++ b/facetracknoir/clientfiles/Tir4Fun/tir4fun.exe
Binary files differ
diff --git a/facetracknoir/clientfiles/aruco/aruco_create_marker.exe b/facetracknoir/clientfiles/aruco/aruco_create_marker.exe
new file mode 100644
index 00000000..4400e80e
--- /dev/null
+++ b/facetracknoir/clientfiles/aruco/aruco_create_marker.exe
Binary files differ
diff --git a/facetracknoir/clientfiles/aruco/test3.jpg b/facetracknoir/clientfiles/aruco/test3.jpg
new file mode 100644
index 00000000..2ff6dbd0
--- /dev/null
+++ b/facetracknoir/clientfiles/aruco/test3.jpg
Binary files differ
diff --git a/facetracknoir/clientfiles/cfs3/readme.txt b/facetracknoir/clientfiles/cfs3/readme.txt
new file mode 100644
index 00000000..e51cebfa
--- /dev/null
+++ b/facetracknoir/clientfiles/cfs3/readme.txt
@@ -0,0 +1,27 @@
+FaceTrackNoIR for
+
+ * Combat Flight Simulator 3 (also works for Over Flanders Fields)
+ * Wings of War
+ * NASCAR Racing Season 2003
+ * Colin McRae Rally 4
+ * Race Driver 2
+ * F1 Challenge
+ * Richard Burns Rally
+
+FaceTrackNoIR was made compatible with these programs with the help of the functions TrackIR provides in the dll TIRViews.dll.
+This dll can be downloaded from the TrackIR website: http://www.naturalpoint.com/trackir/06-support/support-download-software-and-manuals.html
+
+To make the functions work, copy the dll in the FaceTrackNoIR installation folder. Then tick the 'use TIRViews.dll' checkbox for the 'fake TrackIR' game protocol.
+
+Please let us know if you like the program, if you have ideas for improvements or any questions you might have.
+
+
+
+The FaceTrackNoIR team:
+
+Wim Vriend
+Ron Hendriks
+
+
+
+Disclaimer: For usage of 3rd party software like FlightGear, the FaceTrackNoIR team is not responsible. Use it at your own risk. \ No newline at end of file
diff --git a/facetracknoir/clientfiles/cfs3/tirviews.dll b/facetracknoir/clientfiles/cfs3/tirviews.dll
new file mode 100644
index 00000000..a1fb306f
--- /dev/null
+++ b/facetracknoir/clientfiles/cfs3/tirviews.dll
Binary files differ
diff --git a/facetracknoir/clientfiles/freetracktest/freetracktest.exe b/facetracknoir/clientfiles/freetracktest/freetracktest.exe
new file mode 100644
index 00000000..2965a07f
--- /dev/null
+++ b/facetracknoir/clientfiles/freetracktest/freetracktest.exe
Binary files differ
diff --git a/facetracknoir/clientfiles/freetracktest/readme.txt b/facetracknoir/clientfiles/freetracktest/readme.txt
new file mode 100644
index 00000000..ca40906f
--- /dev/null
+++ b/facetracknoir/clientfiles/freetracktest/readme.txt
@@ -0,0 +1,20 @@
+FaceTrackNoIR for Free-track 'enabled' games.
+
+FaceTrackNoIR was made compatible with the Free-track protocol, for which the Free-track source (a well, part of it) was
+translated from Delphi Pascal to C++ (Visual Studio C++, with Qt).
+
+To start the Free-track protocol-server in FaceTrackNoIR, select Free-track in the 'game-protocol' listbox. The program
+'FreeTrackTest.exe' is provided to check, if the protocol-server is running.
+
+FreeTrackTest.exe was created by the Free-track team.
+
+
+
+The FaceTrackNoIR team:
+
+Wim Vriend
+Ron Hendriks
+
+
+
+Disclaimer: For usage of 3rd party software like FreeTrackTest, the FaceTrackNoIR team is not responsible. Use it at your own risk. \ No newline at end of file
diff --git a/facetracknoir/clientfiles/fs2002 and fs2004/fsuipc.dll b/facetracknoir/clientfiles/fs2002 and fs2004/fsuipc.dll
new file mode 100644
index 00000000..264d14c5
--- /dev/null
+++ b/facetracknoir/clientfiles/fs2002 and fs2004/fsuipc.dll
Binary files differ
diff --git a/facetracknoir/clientfiles/glovepie/facetracknoir2trackir.pie b/facetracknoir/clientfiles/glovepie/facetracknoir2trackir.pie
new file mode 100644
index 00000000..d0839e5d
--- /dev/null
+++ b/facetracknoir/clientfiles/glovepie/facetracknoir2trackir.pie
@@ -0,0 +1,16 @@
+//
+// 6 Degrees of Freedom Headtracking with FaceTrackNoIR
+// 2010 by Wim Vriend
+//
+pie.FrameRate = 120Hz
+var.multiply = 1.5
+var.R2D = 57.295781
+FakeTrackIR.pitch=(Joystick.pitch - 0.10) * var.R2D * var.multiply
+FakeTrackIR.yaw=(Joystick.yaw - 0.10) * var.R2D * var.multiply
+FakeTrackIR.roll=(Joystick.roll - 0.10) * var.R2D * var.multiply
+FakeTrackIR.x=(Joystick.x - 0.10) * var.R2D * var.multiply
+FakeTrackIR.y=(Joystick.y - 0.10) * var.R2D * var.multiply
+FakeTrackIR.z=(Joystick.z - 0.10) * var.R2D * var.multiply
+debug = 'pitch='+FakeTrackIR.pitch+' roll='+FakeTrackIR.roll+' yaw='+FakeTrackIR.yaw+' xyz=('+FakeTrackIR.x+','+FakeTrackIR.y+','+FakeTrackIR.z+')'
+//debug = FakeTrackIR.active
+
diff --git a/facetracknoir/clientfiles/glovepie/readme.txt b/facetracknoir/clientfiles/glovepie/readme.txt
new file mode 100644
index 00000000..3639e26b
--- /dev/null
+++ b/facetracknoir/clientfiles/glovepie/readme.txt
@@ -0,0 +1,24 @@
+FaceTrackNoIR for PPJoy 'enabled' games/programs.
+
+FaceTrackNoIR was made compatible with the PPJoy virtual joystick(s), that can be used by various other programs as input. GlovePIE is one of the most powerfull we know (we have also tried tir4fun, but that is quite limited).
+
+To start the PPJoy protocol-server in FaceTrackNoIR, select Virtual Joystick in the 'game-protocol' listbox. The
+settings, necessary to configure PPJoy for FaceTrackNoIR as included in the PPJoy folder.
+
+GlovePIE was made by Carl Kenner and may NOT be used for military purposes. You can download it from the website
+http://glovepie.org/glovepie.php
+
+The script FaceTrackNoIR2TrackIR.PIE, which was included in this folder, surves as an example for GlovePIE. If anyone
+want to use, change or improve it: feel free to do so. In fact, if you do, we would like to receive a copy :-)
+
+Regards,
+
+
+The FaceTrackNoIR team:
+
+Wim Vriend
+Ron Hendriks
+
+
+
+Disclaimer: For usage of 3rd party software like GlovePIE, the FaceTrackNoIR team is not responsible. Use it at your own risk. \ No newline at end of file
diff --git a/facetracknoir/clientfiles/ppjoy/ppjoy mapping for facetracknoir.jpg b/facetracknoir/clientfiles/ppjoy/ppjoy mapping for facetracknoir.jpg
new file mode 100644
index 00000000..052c6899
--- /dev/null
+++ b/facetracknoir/clientfiles/ppjoy/ppjoy mapping for facetracknoir.jpg
Binary files differ
diff --git a/facetracknoir/clientfiles/ppjoy/readme.txt b/facetracknoir/clientfiles/ppjoy/readme.txt
new file mode 100644
index 00000000..20c52111
--- /dev/null
+++ b/facetracknoir/clientfiles/ppjoy/readme.txt
@@ -0,0 +1,24 @@
+FaceTrackNoIR for PPJoy 'enabled' games/programs.
+
+FaceTrackNoIR was made compatible with the PPJoy virtual joystick(s), that can be used by various other programs as input.
+
+To start the PPJoy protocol-server in FaceTrackNoIR, select Virtual Joystick in the 'game-protocol' listbox. The
+settings, necessary to configure PPJoy for FaceTrackNoIR as included in the PPJoy folder, in the file
+PPJoy mapping for FaceTrackNoIR.jpg.
+
+PPJoy was made by Deon van der Westhuysen and is unfortunately not updated anymore. You can download it from the website
+http://shareware.pcmag.com/free/Miscellaneous-Utilities/PPJoy/75176.html, but possibly from others as well...
+
+
+Regards,
+
+
+The FaceTrackNoIR team:
+
+Wim Vriend
+Ron Hendriks
+
+
+
+
+Disclaimer: For usage of 3rd party software like PPJoy, the FaceTrackNoIR team is not responsible. Use it at your own risk. \ No newline at end of file
diff --git a/facetracknoir/clientfiles/vjoy/VJoy.dll b/facetracknoir/clientfiles/vjoy/VJoy.dll
new file mode 100644
index 00000000..e3446675
--- /dev/null
+++ b/facetracknoir/clientfiles/vjoy/VJoy.dll
Binary files differ
diff --git a/facetracknoir/curve-config.cpp b/facetracknoir/curve-config.cpp
new file mode 100644
index 00000000..2bff009a
--- /dev/null
+++ b/facetracknoir/curve-config.cpp
@@ -0,0 +1,117 @@
+#include "facetracknoir/facetracknoir.h"
+#include "facetracknoir/curve-config.h"
+#include <QDebug>
+#include <QCheckBox>
+CurveConfigurationDialog::CurveConfigurationDialog(FaceTrackNoIR *ftnoir, QWidget *parent) :
+ QWidget( parent, Qt::Dialog ), mainApp(ftnoir)
+{
+ ui.setupUi( this );
+ setFont(qApp->font());
+
+ QPoint offsetpos(120, 30);
+ this->move(parent->pos() + offsetpos);
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(mainApp->s.a_x.altp, ui.tx_altp);
+ tie_setting(mainApp->s.a_y.altp, ui.ty_altp);
+ tie_setting(mainApp->s.a_z.altp, ui.tz_altp);
+ tie_setting(mainApp->s.a_yaw.altp, ui.rx_altp);
+ tie_setting(mainApp->s.a_pitch.altp, ui.ry_altp);
+ tie_setting(mainApp->s.a_roll.altp, ui.rz_altp);
+
+ tie_setting(mainApp->s.tcomp_p, ui.tcomp_enable);
+ tie_setting(mainApp->s.tcomp_tz, ui.tcomp_rz);
+
+ tie_setting(mainApp->s.a_x.zero, ui.pos_tx);
+ tie_setting(mainApp->s.a_y.zero, ui.pos_ty);
+ tie_setting(mainApp->s.a_z.zero, ui.pos_tz);
+ tie_setting(mainApp->s.a_yaw.zero, ui.pos_rx);
+ tie_setting(mainApp->s.a_pitch.zero, ui.pos_ry);
+ tie_setting(mainApp->s.a_roll.zero, ui.pos_rz);
+
+ tie_setting(mainApp->s.a_yaw.invert, ui.chkInvertYaw);
+ tie_setting(mainApp->s.a_pitch.invert, ui.chkInvertPitch);
+ tie_setting(mainApp->s.a_roll.invert, ui.chkInvertRoll);
+ tie_setting(mainApp->s.a_x.invert, ui.chkInvertX);
+ tie_setting(mainApp->s.a_y.invert, ui.chkInvertY);
+ tie_setting(mainApp->s.a_z.invert, ui.chkInvertZ);
+
+ // Load the settings from the current .INI-file
+ loadSettings();
+}
+
+void CurveConfigurationDialog::doOK() {
+ save();
+ this->close();
+}
+
+void CurveConfigurationDialog::doCancel() {
+ mainApp->b->revert();
+ loadSettings();
+ close();
+}
+
+//
+// Load the current Settings from the currently 'active' INI-file.
+//
+void CurveConfigurationDialog::loadSettings() {
+ QFunctionConfigurator* configs[6] = {
+ ui.txconfig,
+ ui.tyconfig,
+ ui.tzconfig,
+ ui.rxconfig,
+ ui.ryconfig,
+ ui.rzconfig
+ };
+
+ QFunctionConfigurator* alt_configs[6] = {
+ ui.txconfig_alt,
+ ui.tyconfig_alt,
+ ui.tzconfig_alt,
+ ui.rxconfig_alt,
+ ui.ryconfig_alt,
+ ui.rzconfig_alt
+ };
+
+ QSettings settings("opentrack");
+ QString currentFile = settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini" )
+ .toString();
+
+ for (int i = 0; i < 6; i++)
+ {
+ configs[i]->setConfig(&mainApp->axis(i).curve);
+ alt_configs[i]->setConfig(&mainApp->axis(i).curveAlt);
+ }
+}
+
+//
+// Save the current Settings to the currently 'active' INI-file.
+//
+void CurveConfigurationDialog::save() {
+
+ qDebug() << "save() says: started";
+
+ QSettings settings("opentrack");
+ QString currentFile =
+ settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini" )
+ .toString();
+
+ ui.rxconfig->saveSettings(currentFile);
+ ui.ryconfig->saveSettings(currentFile);
+ ui.rzconfig->saveSettings(currentFile);
+ ui.txconfig->saveSettings(currentFile);
+ ui.tyconfig->saveSettings(currentFile);
+ ui.tzconfig->saveSettings(currentFile);
+
+ ui.txconfig_alt->saveSettings(currentFile);
+ ui.tyconfig_alt->saveSettings(currentFile);
+ ui.tzconfig_alt->saveSettings(currentFile);
+ ui.rxconfig_alt->saveSettings(currentFile);
+ ui.ryconfig_alt->saveSettings(currentFile);
+ ui.rzconfig_alt->saveSettings(currentFile);
+}
diff --git a/facetracknoir/curve-config.h b/facetracknoir/curve-config.h
new file mode 100644
index 00000000..0949cdc4
--- /dev/null
+++ b/facetracknoir/curve-config.h
@@ -0,0 +1,22 @@
+#pragma once
+#include <QWidget>
+#include <QPalette>
+#include "ui_ftnoir_curves.h"
+
+class FaceTrackNoIR;
+
+class CurveConfigurationDialog: public QWidget
+{
+ Q_OBJECT
+public:
+ CurveConfigurationDialog( FaceTrackNoIR *ftnoir, QWidget *parent );
+ void loadSettings();
+private:
+ Ui::UICCurveConfigurationDialog ui;
+ void save();
+ FaceTrackNoIR *mainApp;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
diff --git a/facetracknoir/facetracknoir.cpp b/facetracknoir/facetracknoir.cpp
new file mode 100644
index 00000000..893e79cd
--- /dev/null
+++ b/facetracknoir/facetracknoir.cpp
@@ -0,0 +1,698 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of the some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2011 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* Homepage *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU General Public License as published by the *
+* Free Software Foundation; either version 3 of the License, or (at your *
+* option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but *
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, see <http://www.gnu.org/licenses/>. *
+*********************************************************************************/
+#include "facetracknoir.h"
+#include "shortcuts.h"
+#include "tracker.h"
+#include "curve-config.h"
+#include "opentrack-version.h"
+#include <QDebug>
+
+#if defined(_WIN32)
+# include <windows.h>
+# include <dshow.h>
+#endif
+
+#if defined(__APPLE__)
+# define SONAME "dylib"
+#elif defined(_WIN32)
+# define SONAME "dll"
+#else
+# define SONAME "so"
+#endif
+
+#include <iostream>
+
+#ifdef _MSC_VER
+# define LIB_PREFIX ""
+#else
+# define LIB_PREFIX "lib"
+#endif
+
+#if defined(__unix) || defined(__linux) || defined(__APPLE__)
+# include <unistd.h>
+#endif
+
+static bool get_metadata(DynamicLibrary* lib, QString& longName, QIcon& icon)
+{
+ Metadata* meta;
+ if (!lib->Metadata || ((meta = lib->Metadata()), !meta))
+ return false;
+ meta->getFullName(&longName);
+ meta->getIcon(&icon);
+ delete meta;
+ return true;
+}
+
+static void fill_combobox(const QString& filter, QList<DynamicLibrary*>& list, QComboBox* cbx, QComboBox* cbx2)
+{
+ QDir settingsDir( QCoreApplication::applicationDirPath() );
+ QStringList filenames = settingsDir.entryList( QStringList() << (LIB_PREFIX + filter + SONAME), QDir::Files, QDir::Name );
+ for ( int i = 0; i < filenames.size(); i++) {
+ QIcon icon;
+ QString longName;
+ QString str = filenames.at(i);
+ DynamicLibrary* lib = new DynamicLibrary(str);
+ qDebug() << "Loading" << str;
+ std::cout.flush();
+ if (!get_metadata(lib, longName, icon))
+ {
+ delete lib;
+ continue;
+ }
+ list.push_back(lib);
+ cbx->addItem(icon, longName);
+ if (cbx2)
+ cbx2->addItem(icon, longName);
+ }
+}
+
+FaceTrackNoIR::FaceTrackNoIR(QWidget *parent) :
+ QMainWindow(parent),
+#if defined(_WIN32)
+ keybindingWorker(NULL),
+#else
+ keyCenter(this),
+ keyToggle(this),
+#endif
+ b(bundle("opentrack-ui")),
+ s(b),
+ pose(std::vector<axis_opts*>{&s.a_x, &s.a_y, &s.a_z, &s.a_yaw, &s.a_pitch, &s.a_roll}),
+ timUpdateHeadPose(this),
+ pTrackerDialog(NULL),
+ pSecondTrackerDialog(NULL),
+ pProtocolDialog(NULL),
+ pFilterDialog(NULL),
+ kbd_quit(QKeySequence("Ctrl+Q"), this),
+ looping(false)
+{
+ ui.setupUi(this);
+ setFixedSize(size());
+
+ _keyboard_shortcuts = 0;
+ _curve_config = 0;
+
+ tracker = 0;
+
+ CurveConfigurationDialog* ccd;
+
+ if (!_curve_config)
+ {
+ ccd = new CurveConfigurationDialog( this, this );
+ _curve_config = ccd;
+ } else {
+ ccd = dynamic_cast<CurveConfigurationDialog*>(_curve_config);
+ }
+
+ QDir::setCurrent(QCoreApplication::applicationDirPath());
+
+ fill_profile_cbx();
+
+ connect(ui.btnLoad, SIGNAL(clicked()), this, SLOT(open()));
+ connect(ui.btnSave, SIGNAL(clicked()), this, SLOT(save()));
+ connect(ui.btnSaveAs, SIGNAL(clicked()), this, SLOT(saveAs()));
+
+ connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(showCurveConfiguration()));
+ connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(showKeyboardShortcuts()));
+ connect(ui.btnShowEngineControls, SIGNAL(clicked()), this, SLOT(showTrackerSettings()));
+ connect(ui.btnShowSecondTrackerSettings, SIGNAL(clicked()), this, SLOT(showSecondTrackerSettings()));
+ connect(ui.btnShowServerControls, SIGNAL(clicked()), this, SLOT(showServerControls()));
+ connect(ui.btnShowFilterControls, SIGNAL(clicked()), this, SLOT(showFilterControls()));
+
+ ui.cbxSecondTrackerSource->addItem(QIcon(), "");
+ dlopen_filters.push_back((DynamicLibrary*) NULL);
+ ui.iconcomboFilter->addItem(QIcon(), "");
+
+ fill_combobox("opentrack-proto-*.", dlopen_protocols, ui.iconcomboProtocol, NULL);
+ fill_combobox("opentrack-tracker-*.", dlopen_trackers, ui.iconcomboTrackerSource, ui.cbxSecondTrackerSource);
+ fill_combobox("opentrack-filter-*.", dlopen_filters, ui.iconcomboFilter, NULL);
+
+ tie_setting(s.tracker_dll, ui.iconcomboTrackerSource);
+ tie_setting(s.tracker2_dll, ui.cbxSecondTrackerSource);
+ tie_setting(s.protocol_dll, ui.iconcomboProtocol);
+ tie_setting(s.filter_dll, ui.iconcomboFilter);
+
+ connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(startTracker()));
+ connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stopTracker()));
+
+ GetCameraNameDX();
+
+ connect(ui.iconcomboProfile, SIGNAL(currentIndexChanged(int)), this, SLOT(profileSelected(int)));
+ connect(&timUpdateHeadPose, SIGNAL(timeout()), this, SLOT(showHeadPose()));
+
+#ifndef _WIN32
+ connect(&keyCenter, SIGNAL(activated()), this, SLOT(shortcutRecentered()));
+ connect(&keyToggle, SIGNAL(activated()), this, SLOT(shortcutToggled()));
+#endif
+
+ connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit()));
+ kbd_quit.setEnabled(true);
+}
+
+FaceTrackNoIR::~FaceTrackNoIR() {
+
+ stopTracker();
+ save();
+ if (Libraries)
+ delete Libraries;
+}
+
+QFrame* FaceTrackNoIR::get_video_widget() {
+ return ui.video_frame;
+}
+
+void FaceTrackNoIR::GetCameraNameDX() {
+#if defined(_WIN32)
+ ui.cameraName->setText("No video-capturing device was found in your system: check if it's connected!");
+
+ // Create the System Device Enumerator.
+ HRESULT hr;
+ ICreateDevEnum *pSysDevEnum = NULL;
+ hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
+ if (FAILED(hr))
+ {
+ qDebug() << "GetWDM says: CoCreateInstance Failed!";
+ return;
+ }
+
+ qDebug() << "GetWDM says: CoCreateInstance succeeded!";
+
+ // Obtain a class enumerator for the video compressor category.
+ IEnumMoniker *pEnumCat = NULL;
+ hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+ if (hr == S_OK) {
+ qDebug() << "GetWDM says: CreateClassEnumerator succeeded!";
+
+ IMoniker *pMoniker = NULL;
+ ULONG cFetched;
+ if (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
+ VARIANT varName;
+ VariantInit(&varName);
+ hr = pPropBag->Read(L"FriendlyName", &varName, 0);
+ if (SUCCEEDED(hr))
+ {
+ QString str((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
+ qDebug() << "GetWDM says: Moniker found:" << str;
+ ui.cameraName->setText(str);
+ }
+ VariantClear(&varName);
+
+ pPropBag->Release();
+ }
+ pMoniker->Release();
+ }
+ pEnumCat->Release();
+ }
+ pSysDevEnum->Release();
+#else
+ for (int i = 0; i < 16; i++) {
+ char buf[128];
+ sprintf(buf, "/dev/video%d", i);
+ if (access(buf, R_OK | W_OK) == 0) {
+ ui.cameraName->setText(QString(buf));
+ break;
+ }
+ }
+#endif
+}
+
+void FaceTrackNoIR::open() {
+ QFileDialog dialog(this);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+
+ QString fileName = dialog.getOpenFileName(
+ this,
+ tr("Open the settings file"),
+ QCoreApplication::applicationDirPath() + "/settings/",
+ tr("Settings file (*.ini);;All Files (*)"),
+ NULL);
+
+ if (! fileName.isEmpty() ) {
+ {
+ QSettings settings("opentrack");
+ settings.setValue ("SettingsFile", QFileInfo(fileName).absoluteFilePath());
+ }
+ looping = true;
+ fill_profile_cbx();
+ loadSettings();
+ looping = false;
+ }
+}
+
+void FaceTrackNoIR::save() {
+ b->save();
+
+ QSettings settings("opentrack");
+
+ QString currentFile =
+ settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini")
+ .toString();
+
+#if defined(__unix) || defined(__linux)
+ QByteArray bytes = QFile::encodeName(currentFile);
+ const char* filename_as_asciiz = bytes.constData();
+
+ if (access(filename_as_asciiz, R_OK | W_OK))
+ {
+ QMessageBox::warning(this, "Something went wrong", "Check permissions and ownership for your .ini file!", QMessageBox::Ok, QMessageBox::NoButton);
+ }
+#endif
+}
+
+void FaceTrackNoIR::saveAs()
+{
+ looping = true;
+ QSettings settings("opentrack");
+ QString oldFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save file"),
+ oldFile,
+ tr("Settings file (*.ini);;All Files (*)"));
+ if (!fileName.isEmpty()) {
+
+ QFileInfo newFileInfo ( fileName );
+ if ((newFileInfo.exists()) && (oldFile != fileName)) {
+ QFile newFileFile ( fileName );
+ newFileFile.remove();
+ }
+
+ QFileInfo oldFileInfo ( oldFile );
+ if (oldFileInfo.exists()) {
+ QFile oldFileFile ( oldFile );
+ oldFileFile.copy( fileName );
+ }
+
+ settings.setValue ("SettingsFile", fileName);
+ save();
+ }
+
+ looping = false;
+ fill_profile_cbx();
+}
+
+void FaceTrackNoIR::loadSettings() {
+ b->reload();
+ (dynamic_cast<CurveConfigurationDialog*>(_curve_config))->loadSettings();
+}
+
+void FaceTrackNoIR::updateButtonState(bool running)
+{
+ bool e = !running;
+ ui.iconcomboProfile->setEnabled ( e );
+ ui.btnLoad->setEnabled ( e );
+ ui.btnSaveAs->setEnabled ( e );
+ ui.btnStartTracker->setEnabled ( e );
+ ui.btnStopTracker->setEnabled ( running );
+ ui.iconcomboProtocol->setEnabled ( e );
+ ui.btnShowServerControls->setEnabled ( e );
+ ui.iconcomboFilter->setEnabled ( e );
+ ui.iconcomboTrackerSource->setEnabled(e);
+ ui.cbxSecondTrackerSource->setEnabled(e);
+
+ ui.btnStartTracker->setEnabled(e);
+ ui.btnStopTracker->setEnabled(running);
+}
+
+void FaceTrackNoIR::startTracker( ) {
+ b->save();
+ loadSettings();
+ bindKeyboardShortcuts();
+
+ if (Libraries)
+ delete Libraries;
+ Libraries = new SelectedLibraries(this);
+
+ if (!Libraries->correct)
+ {
+ QMessageBox::warning(this, "Something went wrong", "Tracking can't be initialized, probably protocol prerequisites missing", QMessageBox::Ok, QMessageBox::NoButton);
+ stopTracker();
+ return;
+ }
+
+#if defined(_WIN32)
+ keybindingWorker = new KeybindingWorker(*this, keyCenter, keyToggle);
+ keybindingWorker->start();
+#endif
+
+ if (tracker) {
+ delete tracker;
+ }
+
+ {
+ QSettings settings("opentrack");
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile( currentFile, QSettings::IniFormat );
+
+ for (int i = 0; i < 6; i++)
+ {
+ axis(i).curve.loadSettings(iniFile);
+ axis(i).curveAlt.loadSettings(iniFile);
+ }
+ }
+
+ tracker = new Tracker ( this, s );
+
+ if (pTrackerDialog && Libraries->pTracker) {
+ pTrackerDialog->registerTracker( Libraries->pTracker );
+ }
+
+ if (pFilterDialog && Libraries->pFilter)
+ pFilterDialog->registerFilter(Libraries->pFilter);
+
+ tracker->start();
+
+ ui.video_frame->show();
+
+ timUpdateHeadPose.start(50);
+
+ updateButtonState(true);
+}
+
+void FaceTrackNoIR::stopTracker( ) {
+ ui.game_name->setText("Not connected");
+#if defined(_WIN32)
+ if (keybindingWorker)
+ {
+ keybindingWorker->should_quit = true;
+ keybindingWorker->wait();
+ delete keybindingWorker;
+ keybindingWorker = NULL;
+ }
+#endif
+ timUpdateHeadPose.stop();
+ ui.pose_display->rotateBy(0, 0, 0);
+
+ if (pTrackerDialog) {
+ pTrackerDialog->unRegisterTracker();
+ delete pTrackerDialog;
+ pTrackerDialog = nullptr;
+ }
+ if (pProtocolDialog) {
+ pProtocolDialog->unRegisterProtocol();
+ delete pProtocolDialog;
+ pProtocolDialog = nullptr;
+ }
+ if (pFilterDialog)
+ {
+ pFilterDialog->unregisterFilter();
+ delete pFilterDialog;
+ pFilterDialog = nullptr;
+ }
+ if (pSecondTrackerDialog)
+ {
+ pSecondTrackerDialog->unRegisterTracker();
+ delete pSecondTrackerDialog;
+ pSecondTrackerDialog = nullptr;
+ }
+
+ if ( tracker ) {
+ delete tracker;
+ tracker = 0;
+ if (Libraries) {
+ delete Libraries;
+ Libraries = NULL;
+ }
+ }
+ updateButtonState(false);
+}
+
+void FaceTrackNoIR::showHeadPose() {
+ double newdata[6];
+
+ tracker->getHeadPose(newdata);
+ ui.lcdNumX->display(newdata[TX]);
+ ui.lcdNumY->display(newdata[TY]);
+ ui.lcdNumZ->display(newdata[TZ]);
+
+
+ ui.lcdNumRotX->display(newdata[Yaw]);
+ ui.lcdNumRotY->display(newdata[Pitch]);
+ ui.lcdNumRotZ->display(newdata[Roll]);
+
+ tracker->getOutputHeadPose(newdata);
+
+ ui.pose_display->rotateBy(newdata[Yaw], newdata[Roll], newdata[Pitch]);
+
+ ui.lcdNumOutputPosX->display(newdata[TX]);
+ ui.lcdNumOutputPosY->display(newdata[TY]);
+ ui.lcdNumOutputPosZ->display(newdata[TZ]);
+
+ ui.lcdNumOutputRotX->display(newdata[Yaw]);
+ ui.lcdNumOutputRotY->display(newdata[Pitch]);
+ ui.lcdNumOutputRotZ->display(newdata[Roll]);
+
+ if (_curve_config) {
+ _curve_config->update();
+ }
+ if (Libraries->pProtocol)
+ {
+ const QString name = Libraries->pProtocol->getGameName();
+ ui.game_name->setText(name);
+ }
+}
+
+void FaceTrackNoIR::showTrackerSettings() {
+ if (pTrackerDialog) {
+ delete pTrackerDialog;
+ pTrackerDialog = NULL;
+ }
+
+ DynamicLibrary* lib = dlopen_trackers.value(ui.iconcomboTrackerSource->currentIndex(), (DynamicLibrary*) NULL);
+
+ if (lib) {
+ pTrackerDialog = (ITrackerDialog*) lib->Dialog();
+ if (pTrackerDialog) {
+ auto foo = dynamic_cast<QWidget*>(pTrackerDialog);
+ foo->setFixedSize(foo->size());
+ if (Libraries && Libraries->pTracker)
+ pTrackerDialog->registerTracker(Libraries->pTracker);
+ dynamic_cast<QWidget*>(pTrackerDialog)->show();
+ }
+ }
+}
+
+void FaceTrackNoIR::showSecondTrackerSettings() {
+ if (pSecondTrackerDialog) {
+ delete pSecondTrackerDialog;
+ pSecondTrackerDialog = NULL;
+ }
+
+ DynamicLibrary* lib = dlopen_trackers.value(ui.cbxSecondTrackerSource->currentIndex() - 1, (DynamicLibrary*) NULL);
+
+ if (lib) {
+ pSecondTrackerDialog = (ITrackerDialog*) lib->Dialog();
+ if (pSecondTrackerDialog) {
+ auto foo = dynamic_cast<QWidget*>(pSecondTrackerDialog);
+ foo->setFixedSize(foo->size());
+ if (Libraries && Libraries->pSecondTracker)
+ pSecondTrackerDialog->registerTracker(Libraries->pSecondTracker);
+ dynamic_cast<QWidget*>(pSecondTrackerDialog)->show();
+ }
+ }
+}
+
+void FaceTrackNoIR::showServerControls() {
+ if (pProtocolDialog) {
+ delete pProtocolDialog;
+ pProtocolDialog = NULL;
+ }
+
+ DynamicLibrary* lib = dlopen_protocols.value(ui.iconcomboProtocol->currentIndex(), (DynamicLibrary*) NULL);
+
+ if (lib && lib->Dialog) {
+ pProtocolDialog = (IProtocolDialog*) lib->Dialog();
+ if (pProtocolDialog) {
+ auto foo = dynamic_cast<QWidget*>(pProtocolDialog);
+ foo->setFixedSize(foo->size());
+ dynamic_cast<QWidget*>(pProtocolDialog)->show();
+ }
+ }
+}
+
+void FaceTrackNoIR::showFilterControls() {
+ if (pFilterDialog) {
+ delete pFilterDialog;
+ pFilterDialog = NULL;
+ }
+
+ DynamicLibrary* lib = dlopen_filters.value(ui.iconcomboFilter->currentIndex(), (DynamicLibrary*) NULL);
+
+ if (lib && lib->Dialog) {
+ pFilterDialog = (IFilterDialog*) lib->Dialog();
+ if (pFilterDialog) {
+ auto foo = dynamic_cast<QWidget*>(pFilterDialog);
+ foo->setFixedSize(foo->size());
+ if (Libraries && Libraries->pFilter)
+ pFilterDialog->registerFilter(Libraries->pFilter);
+ dynamic_cast<QWidget*>(pFilterDialog)->show();
+ }
+ }
+}
+void FaceTrackNoIR::showKeyboardShortcuts() {
+
+ if (!_keyboard_shortcuts)
+ {
+ _keyboard_shortcuts = new KeyboardShortcutDialog( this, this );
+ }
+
+ _keyboard_shortcuts->show();
+ _keyboard_shortcuts->raise();
+}
+void FaceTrackNoIR::showCurveConfiguration() {
+
+ if (!_curve_config)
+ {
+ _curve_config = new CurveConfigurationDialog( this, this );
+ }
+
+ if (_curve_config) {
+ _curve_config->show();
+ _curve_config->raise();
+ }
+}
+
+void FaceTrackNoIR::exit() {
+ QCoreApplication::exit(0);
+}
+
+void FaceTrackNoIR::fill_profile_cbx()
+{
+ if (looping)
+ return;
+ QSettings settings("opentrack");
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ qDebug() << "Config file now" << currentFile;
+ QFileInfo pathInfo ( currentFile );
+ setWindowTitle(QString( OPENTRACK_VERSION " :: ") + pathInfo.fileName());
+ QDir settingsDir( pathInfo.dir() );
+ QStringList filters;
+ filters << "*.ini";
+ auto iniFileList = settingsDir.entryList( filters, QDir::Files, QDir::Name );
+ ui.iconcomboProfile->clear();
+ for ( int i = 0; i < iniFileList.size(); i++) {
+ ui.iconcomboProfile->addItem(QIcon(":/images/settings16.png"), iniFileList.at(i));
+ if (iniFileList.at(i) == pathInfo.fileName()) {
+ ui.iconcomboProfile->setCurrentIndex( i );
+ }
+ }
+}
+
+void FaceTrackNoIR::profileSelected(int index)
+{
+ QSettings settings("opentrack");
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QFileInfo pathInfo ( currentFile );
+ settings.setValue ("SettingsFile", pathInfo.absolutePath() + "/" + ui.iconcomboProfile->itemText(index));
+ loadSettings();
+}
+
+#if !defined(_WIN32)
+void FaceTrackNoIR::bind_keyboard_shortcut(QxtGlobalShortcut& key, key_opts& k)
+{
+ key.setShortcut(QKeySequence::fromString(""));
+ key.setDisabled();
+ const int idx = k.key_index;
+ if (idx > 0)
+ {
+ QString seq(global_key_sequences.value(idx, ""));
+ if (!seq.isEmpty())
+ {
+ if (k.shift)
+ seq = "Shift+" + seq;
+ if (k.alt)
+ seq = "Alt+" + seq;
+ if (k.ctrl)
+ seq = "Ctrl+" + seq;
+ key.setShortcut(QKeySequence::fromString(seq, QKeySequence::PortableText));
+ key.setEnabled();
+ } else {
+ key.setDisabled();
+ }
+ }
+}
+#else
+static void bind_keyboard_shortcut(Key& key, key_opts& k)
+{
+ int idx = k.key_index;
+ if (idx > 0)
+ {
+ key.keycode = 0;
+ key.shift = key.alt = key.ctrl = 0;
+ if (idx < global_windows_key_sequences.size())
+ key.keycode = global_windows_key_sequences[idx];
+ key.shift = k.shift;
+ key.alt = k.alt;
+ key.ctrl = k.ctrl;
+ }
+}
+#endif
+
+void FaceTrackNoIR::bindKeyboardShortcuts()
+{
+#if !defined(_WIN32)
+ bind_keyboard_shortcut(keyCenter, s.center_key);
+ bind_keyboard_shortcut(keyToggle, s.toggle_key);
+#else
+ bind_keyboard_shortcut(keyCenter, s.center_key);
+ bind_keyboard_shortcut(keyToggle, s.toggle_key);
+#endif
+ if (tracker) /* running already */
+ {
+#if defined(_WIN32)
+ if (keybindingWorker)
+ {
+ keybindingWorker->should_quit = true;
+ keybindingWorker->wait();
+ delete keybindingWorker;
+ keybindingWorker = NULL;
+ }
+ keybindingWorker = new KeybindingWorker(*this, keyCenter, keyToggle);
+ keybindingWorker->start();
+#endif
+ }
+}
+
+void FaceTrackNoIR::shortcutRecentered()
+{
+ if (s.dingp)
+ QApplication::beep();
+
+ qDebug() << "Center";
+ if (tracker)
+ tracker->do_center = true;
+}
+
+void FaceTrackNoIR::shortcutToggled()
+{
+ if (s.dingp)
+ QApplication::beep();
+
+ qDebug() << "Toggle";
+ if (tracker)
+ tracker->enabled = !tracker->enabled;
+}
diff --git a/facetracknoir/facetracknoir.h b/facetracknoir/facetracknoir.h
new file mode 100644
index 00000000..50a6e0ec
--- /dev/null
+++ b/facetracknoir/facetracknoir.h
@@ -0,0 +1,165 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of the some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* Homepage *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU General Public License as published by the *
+* Free Software Foundation; either version 3 of the License, or (at your *
+* option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but *
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, see <http://www.gnu.org/licenses/>. *
+*********************************************************************************/
+
+#ifndef FaceTrackNoIR_H
+#define FaceTrackNoIR_H
+
+#include <QMainWindow>
+#include <QApplication>
+#include <QFileDialog>
+#include <QListView>
+#include <QPainter>
+#include <QWidget>
+#include <QDialog>
+#include <QUrl>
+#include <QList>
+#include <QKeySequence>
+#include <QtGui>
+#include <QString>
+#include <QByteArray>
+#include <QShortcut>
+#include <vector>
+#if !defined(_WIN32)
+# include "qxt-mini/QxtGlobalShortcut"
+#else
+# include <windows.h>
+#endif
+#include <QThread>
+#include <QDebug>
+
+#include "ui_facetracknoir.h"
+
+#include "facetracknoir/options.h"
+using namespace options;
+
+#include "facetracknoir/main-settings.hpp"
+
+#include "global-settings.h"
+#include "tracker.h"
+#include "facetracknoir/shortcuts.h"
+
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ftnoir_filter_base/ftnoir_filter_base.h"
+
+#include "opentrack-version.h"
+
+class Tracker; // pre-define class to avoid circular includes
+class FaceTrackNoIR;
+
+class KeybindingWorker;
+
+class FaceTrackNoIR : public QMainWindow, IDynamicLibraryProvider
+{
+ Q_OBJECT
+
+public:
+ FaceTrackNoIR(QWidget *parent = 0);
+ ~FaceTrackNoIR();
+
+ QFrame *get_video_widget(); // Get a pointer to the video-widget, to use in the DLL
+ Tracker *tracker;
+ void bindKeyboardShortcuts();
+ DynamicLibrary* current_tracker1() {
+ return dlopen_trackers.value(ui.iconcomboTrackerSource->currentIndex(), (DynamicLibrary*) NULL);
+ }
+ DynamicLibrary* current_tracker2() {
+ return dlopen_trackers.value(ui.cbxSecondTrackerSource->currentIndex() - 1, (DynamicLibrary*) NULL);
+ }
+ DynamicLibrary* current_protocol() {
+ return dlopen_protocols.value(ui.iconcomboProtocol->currentIndex(), (DynamicLibrary*) NULL);
+ }
+ DynamicLibrary* current_filter() {
+ return dlopen_filters.value(ui.iconcomboFilter->currentIndex(), (DynamicLibrary*) NULL);
+ }
+ THeadPoseDOF& axis(int idx) {
+ return *pose.axes[idx];
+ }
+
+#if defined(_WIN32)
+ Key keyCenter;
+ Key keyToggle;
+ KeybindingWorker* keybindingWorker;
+#else
+ QxtGlobalShortcut keyCenter;
+ QxtGlobalShortcut keyToggle;
+#endif
+ pbundle b;
+ main_settings s;
+public slots:
+ void shortcutRecentered();
+ void shortcutToggled();
+
+private:
+ HeadPoseData pose;
+ Ui::OpentrackUI ui;
+ QTimer timUpdateHeadPose; // Timer to display headpose
+
+ ITrackerDialog* pTrackerDialog; // Pointer to Tracker dialog instance (in DLL)
+ ITrackerDialog* pSecondTrackerDialog; // Pointer to the second Tracker dialog instance (in DLL)
+ IProtocolDialog* pProtocolDialog; // Pointer to Protocol dialog instance (in DLL)
+ IFilterDialog* pFilterDialog; // Pointer to Filter dialog instance (in DLL)
+
+ QWidget *_keyboard_shortcuts;
+ QWidget *_curve_config;
+
+ void createIconGroupBox();
+
+ void GetCameraNameDX();
+ void loadSettings();
+ void updateButtonState(bool);
+
+ QList<DynamicLibrary*> dlopen_filters;
+ QList<DynamicLibrary*> dlopen_trackers;
+ QList<DynamicLibrary*> dlopen_protocols;
+ QShortcut kbd_quit;
+
+#ifndef _WIN32
+ void bind_keyboard_shortcut(QxtGlobalShortcut&, key_opts& k);
+#endif
+ void fill_profile_cbx();
+ bool looping;
+
+private slots:
+ void open();
+ void save();
+ void saveAs();
+ void exit();
+ void profileSelected(int index);
+
+ void showTrackerSettings();
+ void showSecondTrackerSettings();
+
+ void showServerControls();
+ void showFilterControls();
+ void showKeyboardShortcuts();
+ void showCurveConfiguration();
+
+ void showHeadPose();
+
+ void startTracker();
+ void stopTracker();
+};
+
+#endif // FaceTrackNoIR_H
diff --git a/facetracknoir/facetracknoir.ico b/facetracknoir/facetracknoir.ico
new file mode 100644
index 00000000..5cac8da1
--- /dev/null
+++ b/facetracknoir/facetracknoir.ico
Binary files differ
diff --git a/facetracknoir/facetracknoir.rc b/facetracknoir/facetracknoir.rc
new file mode 100644
index 00000000..655baa9d
--- /dev/null
+++ b/facetracknoir/facetracknoir.rc
@@ -0,0 +1,2 @@
+#include <windows.h>
+IDI_ICON1 ICON "facetracknoir.ico"
diff --git a/facetracknoir/facetracknoir.ui b/facetracknoir/facetracknoir.ui
new file mode 100644
index 00000000..b257ae30
--- /dev/null
+++ b/facetracknoir/facetracknoir.ui
@@ -0,0 +1,1727 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>WVR</author>
+ <class>OpentrackUI</class>
+ <widget class="QMainWindow" name="OpentrackUI">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>790</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="contextMenuPolicy">
+ <enum>Qt::DefaultContextMenu</enum>
+ </property>
+ <property name="windowTitle">
+ <string>opentrack</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="main-facetracknoir.qrc">
+ <normaloff>:/images/facetracknoir.png</normaloff>:/images/facetracknoir.png</iconset>
+ </property>
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonIconOnly</enum>
+ </property>
+ <property name="animated">
+ <bool>true</bool>
+ </property>
+ <property name="unifiedTitleAndToolBarOnMac">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65535</width>
+ <height>65535</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <widget class="QFrame" name="video_frame">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>130</y>
+ <width>320</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>250</width>
+ <height>187</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="widget4video" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QGroupBox" name="groupBox4logo">
+ <property name="geometry">
+ <rect>
+ <x>100</x>
+ <y>10</y>
+ <width>229</width>
+ <height>121</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="title">
+ <string notr="true"/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_8">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>10</number>
+ </property>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblZ">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLCDNumber" name="lcdNumOutputRotX">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblX">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="lcdNumOutputPosX">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lblRotX">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="lcdNumOutputPosY">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="lcdNumOutputPosZ">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lblRotZ">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblY">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lblRotY">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLCDNumber" name="lcdNumOutputRotY">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLCDNumber" name="lcdNumOutputRotZ">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>5</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="GLWidget" name="pose_display" native="true">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>81</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QGroupBox" name="groupGameProtocol">
+ <property name="geometry">
+ <rect>
+ <x>350</x>
+ <y>270</y>
+ <width>191</width>
+ <height>91</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>180</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Game protocol</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6" rowstretch="6,6" columnstretch="6" rowminimumheight="6,6" columnminimumwidth="6">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboProtocol">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>7</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="btnShowServerControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Change game protocol settings</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QPushButton" name="btnEditCurves">
+ <property name="geometry">
+ <rect>
+ <x>580</x>
+ <y>390</y>
+ <width>171</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>62</width>
+ <height>38</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="cursor">
+ <cursorShape>PointingHandCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Edit the Curve settings</string>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>Mapping</string>
+ </property>
+ <property name="icon">
+ <iconset resource="main-facetracknoir.qrc">
+ <normaloff>:/uielements/curves.png</normaloff>:/uielements/curves.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>98</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel" name="game_name">
+ <property name="geometry">
+ <rect>
+ <x>370</x>
+ <y>40</y>
+ <width>411</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Not connected</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" name="groupFilter">
+ <property name="geometry">
+ <rect>
+ <x>580</x>
+ <y>210</y>
+ <width>171</width>
+ <height>91</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Filter</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboFilter">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>7</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="btnShowFilterControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Change game protocol settings</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QGroupBox" name="groupTrackerSource">
+ <property name="geometry">
+ <rect>
+ <x>350</x>
+ <y>60</y>
+ <width>191</width>
+ <height>91</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Main tracker</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QComboBox" name="iconcomboTrackerSource">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>42</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnShowEngineControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Change tracker settings</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QLabel" name="cameraName">
+ <property name="geometry">
+ <rect>
+ <x>370</x>
+ <y>10</y>
+ <width>411</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="geometry">
+ <rect>
+ <x>350</x>
+ <y>160</y>
+ <width>191</width>
+ <height>91</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Auxiliary tracker</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QComboBox" name="cbxSecondTrackerSource">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>42</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnShowSecondTrackerSettings">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Change tracker settings</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QGroupBox" name="groupStartStop">
+ <property name="geometry">
+ <rect>
+ <x>350</x>
+ <y>400</y>
+ <width>190</width>
+ <height>65</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>GO!</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_9" rowstretch="0" columnstretch="0,0" rowminimumheight="0" columnminimumwidth="0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QPushButton" name="btnStartTracker">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Start the Tracker</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Start</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="btnStopTracker">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Stop the Tracker</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QPushButton" name="btnShortcuts">
+ <property name="geometry">
+ <rect>
+ <x>580</x>
+ <y>340</y>
+ <width>171</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>62</width>
+ <height>38</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="cursor">
+ <cursorShape>PointingHandCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Edit the Keyboard and mouse shortcuts</string>
+ </property>
+ <property name="text">
+ <string>Keys</string>
+ </property>
+ <property name="icon">
+ <iconset resource="main-facetracknoir.qrc">
+ <normaloff>:/uielements/tools.png</normaloff>:/uielements/tools.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>98</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QGroupBox" name="groupProfile">
+ <property name="geometry">
+ <rect>
+ <x>550</x>
+ <y>60</y>
+ <width>231</width>
+ <height>123</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Profile</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5" rowstretch="6,6,6" columnstretch="6,6" rowminimumheight="6,6,6" columnminimumwidth="6,6">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0" colspan="2">
+ <widget class="QComboBox" name="iconcomboProfile">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="btnLoad">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Load an INI-file from a folder</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Load</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="btnSave">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Save the current INI-file</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QPushButton" name="btnSaveAs">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Save the INI-file under another name</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Save As ...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>380</y>
+ <width>141</width>
+ <height>106</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Raw translation</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignHCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMaximumSize</enum>
+ </property>
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::FieldsStayAtSizeHint</enum>
+ </property>
+ <property name="rowWrapPolicy">
+ <enum>QFormLayout::DontWrapRows</enum>
+ </property>
+ <property name="labelAlignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ <property name="formAlignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="lcdNumX">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="lcdNumY">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="lcdNumZ">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="geometry">
+ <rect>
+ <x>160</x>
+ <y>380</y>
+ <width>161</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Raw rotation</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignHCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMaximumSize</enum>
+ </property>
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::FieldsStayAtSizeHint</enum>
+ </property>
+ <property name="rowWrapPolicy">
+ <enum>QFormLayout::DontWrapRows</enum>
+ </property>
+ <property name="labelAlignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ <property name="formAlignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="lcdNumRotX">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="lcdNumRotY">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="lcdNumRotZ">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>false</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>lcdNumRotZ</zorder>
+ <zorder>label_8</zorder>
+ <zorder>label_7</zorder>
+ <zorder>lcdNumRotY</zorder>
+ <zorder>lcdNumRotX</zorder>
+ <zorder>label_9</zorder>
+ </widget>
+ </widget>
+ </widget>
+ <layoutdefault spacing="0" margin="0"/>
+ <customwidgets>
+ <customwidget>
+ <class>GLWidget</class>
+ <extends>QWidget</extends>
+ <header>glwidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="main-facetracknoir.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/facetracknoir/ftnoir_curves.ui b/facetracknoir/ftnoir_curves.ui
new file mode 100644
index 00000000..33421b40
--- /dev/null
+++ b/facetracknoir/ftnoir_curves.ui
@@ -0,0 +1,976 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICCurveConfigurationDialog</class>
+ <widget class="QWidget" name="UICCurveConfigurationDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>970</width>
+ <height>655</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Mapping properties</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: #ccc;</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="styleSheet">
+ <string notr="true">background-color: #ccc;</string>
+ </property>
+ <property name="tabPosition">
+ <enum>QTabWidget::North</enum>
+ </property>
+ <property name="currentIndex">
+ <number>6</number>
+ </property>
+ <widget class="QWidget" name="tabWidgetPage1">
+ <attribute name="title">
+ <string>Yaw</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="rxconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>5</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="rx_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>260</y>
+ <width>166</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="rxconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>5</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage2">
+ <attribute name="title">
+ <string>Pitch</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="ryconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>90</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>90</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>10</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ry_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>260</y>
+ <width>199</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="ryconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>90</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>90</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>10</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage3">
+ <attribute name="title">
+ <string>Roll</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="rzconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>5</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>1</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="rz_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>260</y>
+ <width>271</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="rzconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>180</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>5</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>1</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage4">
+ <attribute name="title">
+ <string>X</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="txconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tx_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>270</y>
+ <width>228</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="txconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage5">
+ <attribute name="title">
+ <string>Y</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="tyconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ty_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>270</y>
+ <width>229</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="tyconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage6">
+ <attribute name="title">
+ <string>Z</string>
+ </attribute>
+ <widget class="QFunctionConfigurator" name="tzconfig" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tz_altp">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>270</y>
+ <width>263</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ <widget class="QFunctionConfigurator" name="tzconfig_alt" native="true">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>300</y>
+ <width>930</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="maxInputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="maxOutputEGU" stdset="0">
+ <number>100</number>
+ </property>
+ <property name="pixPerEGU_Input" stdset="0">
+ <number>28</number>
+ </property>
+ <property name="pixPerEGU_Output" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage7">
+ <attribute name="title">
+ <string>Options</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Center pose</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>RX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rx">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tx">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>RY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="pos_ry">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QDoubleSpinBox" name="pos_ty">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>RZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rz">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tz">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Translation compensation</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="tcomp_enable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tcomp_rz">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Disable Z axis compensation</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="title">
+ <string>Axis inversion</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4" rowstretch="6,6,6" columnstretch="6,6" rowminimumheight="6,6,6" columnminimumwidth="6,6">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinAndMaxSize</enum>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="chkInvertYaw">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="chkInvertX">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="chkInvertPitch">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkInvertY">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="chkInvertRoll">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkInvertZ">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QFunctionConfigurator</class>
+ <extends>QWidget</extends>
+ <header>qfunctionconfigurator.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>pos_rx</tabstop>
+ <tabstop>pos_ry</tabstop>
+ <tabstop>pos_rz</tabstop>
+ <tabstop>ry_altp</tabstop>
+ <tabstop>rz_altp</tabstop>
+ <tabstop>tx_altp</tabstop>
+ <tabstop>ty_altp</tabstop>
+ <tabstop>tz_altp</tabstop>
+ <tabstop>tcomp_enable</tabstop>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>pos_tx</tabstop>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>pos_ty</tabstop>
+ <tabstop>rx_altp</tabstop>
+ <tabstop>pos_tz</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/facetracknoir/ftnoir_keyboardshortcuts.ui b/facetracknoir/ftnoir_keyboardshortcuts.ui
new file mode 100644
index 00000000..5bdc3334
--- /dev/null
+++ b/facetracknoir/ftnoir_keyboardshortcuts.ui
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICKeyboardShortcutDialog</class>
+ <widget class="QWidget" name="UICKeyboardShortcutDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>371</width>
+ <height>125</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Keyboard shortcuts</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkToggleShift">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QComboBox" name="cbxToggleKey">
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Select Number</string>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::InsertAlphabetically</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="textLabel2_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Toggle</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="chkCenterAlt">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QCheckBox" name="chkCenterCtrl">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QComboBox" name="cbxCenterKey">
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Select Number</string>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::InsertAlphabetically</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="textLabel2_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Keyboard</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QCheckBox" name="chkToggleCtrl">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="textLabel2_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Center</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QCheckBox" name="chkToggleAlt">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkCenterShift">
+ <property name="maximumSize">
+ <size>
+ <width>50</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QCheckBox" name="ding">
+ <property name="text">
+ <string>Ding!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/facetracknoir/global-settings.cpp b/facetracknoir/global-settings.cpp
new file mode 100644
index 00000000..3b627860
--- /dev/null
+++ b/facetracknoir/global-settings.cpp
@@ -0,0 +1,132 @@
+#include "global-settings.h"
+
+#if !(defined(_WIN32))
+# include <dlfcn.h>
+#endif
+
+SelectedLibraries* Libraries = NULL;
+
+SelectedLibraries::~SelectedLibraries()
+{
+ if (pTracker) {
+ delete pTracker;
+ pTracker = NULL;
+ }
+
+ if (pSecondTracker) {
+ delete pSecondTracker;
+ pSecondTracker = NULL;
+ }
+
+ if (pFilter)
+ delete pFilter;
+
+ if (pProtocol)
+ delete pProtocol;
+}
+
+SelectedLibraries::SelectedLibraries(IDynamicLibraryProvider* mainApp) :
+ pTracker(NULL), pSecondTracker(NULL), pFilter(NULL), pProtocol(NULL)
+{
+ correct = false;
+ if (!mainApp)
+ return;
+ NULLARY_DYNAMIC_FUNCTION ptr;
+ DynamicLibrary* lib;
+
+ lib = mainApp->current_tracker1();
+
+ if (lib && lib->Constructor) {
+ ptr = (NULLARY_DYNAMIC_FUNCTION) lib->Constructor;
+ pTracker = (ITracker*) ptr();
+ }
+
+ lib = mainApp->current_tracker2();
+
+ if (lib && lib->Constructor) {
+ ptr = (NULLARY_DYNAMIC_FUNCTION) lib->Constructor;
+ pSecondTracker = (ITracker*) ptr();
+ }
+
+ lib = mainApp->current_protocol();
+
+ if (lib && lib->Constructor) {
+ ptr = (NULLARY_DYNAMIC_FUNCTION) lib->Constructor;
+ pProtocol = (IProtocol*) ptr();
+ }
+
+ lib = mainApp->current_filter();
+
+ if (lib && lib->Constructor) {
+ ptr = (NULLARY_DYNAMIC_FUNCTION) lib->Constructor;
+ pFilter = (IFilter*) ptr();
+ }
+
+ // Check if the Protocol-server files were installed OK.
+ // Some servers also create a memory-mapping, for Inter Process Communication.
+ // The handle of the MainWindow is sent to 'The Game', so it can send a message back.
+
+ if (pProtocol)
+ if(!pProtocol->checkServerInstallationOK())
+ return;
+
+ // retrieve pointers to the User Interface and the main Application
+ if (pTracker) {
+ pTracker->StartTracker( mainApp->get_video_widget() );
+ }
+ if (pSecondTracker) {
+ pSecondTracker->StartTracker( mainApp->get_video_widget() );
+ }
+
+ correct = true;
+}
+
+DynamicLibrary::DynamicLibrary(const QString& filename)
+{
+ this->filename = filename;
+#if defined(_WIN32)
+ QString fullPath = QCoreApplication::applicationDirPath() + "/" + this->filename;
+ handle = new QLibrary(fullPath);
+ Dialog = (SETTINGS_FUNCTION) handle->resolve(MAYBE_STDCALL_UNDERSCORE "GetDialog" CALLING_CONVENTION_SUFFIX_VOID_FUNCTION);
+ Constructor = (NULLARY_DYNAMIC_FUNCTION) handle->resolve(MAYBE_STDCALL_UNDERSCORE "GetConstructor" CALLING_CONVENTION_SUFFIX_VOID_FUNCTION);
+ Metadata = (METADATA_FUNCTION) handle->resolve(MAYBE_STDCALL_UNDERSCORE "GetMetadata" CALLING_CONVENTION_SUFFIX_VOID_FUNCTION);
+#else
+ QByteArray latin1 = QFile::encodeName(filename);
+ handle = dlopen(latin1.constData(), RTLD_NOW |
+# ifdef __linux
+ RTLD_DEEPBIND
+# elif defined(__APPLE__)
+ RTLD_LOCAL|RTLD_FIRST|RTLD_NOW
+# else
+ 0
+# endif
+ );
+ if (handle)
+ {
+ fprintf(stderr, "Error, if any: %s\n", dlerror());
+ fflush(stderr);
+ Dialog = (SETTINGS_FUNCTION) dlsym(handle, "GetDialog");
+ fprintf(stderr, "Error, if any: %s\n", dlerror());
+ fflush(stderr);
+ Constructor = (NULLARY_DYNAMIC_FUNCTION) dlsym(handle, "GetConstructor");
+ fprintf(stderr, "Error, if any: %s\n", dlerror());
+ fflush(stderr);
+ Metadata = (METADATA_FUNCTION) dlsym(handle, "GetMetadata");
+ fprintf(stderr, "Error, if any: %s\n", dlerror());
+ fflush(stderr);
+ } else {
+ fprintf(stderr, "Error, if any: %s\n", dlerror());
+ fflush(stderr);
+ }
+#endif
+}
+
+DynamicLibrary::~DynamicLibrary()
+{
+#if defined(_WIN32)
+ handle->unload();
+#else
+ if (handle)
+ (void) dlclose(handle);
+#endif
+}
diff --git a/facetracknoir/global-settings.h b/facetracknoir/global-settings.h
new file mode 100644
index 00000000..6b04b73b
--- /dev/null
+++ b/facetracknoir/global-settings.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#if defined(_WIN32)
+# define CALLING_CONVENTION_SUFFIX_VOID_FUNCTION "@0"
+# ifdef _MSC_VER
+# define MAYBE_STDCALL_UNDERSCORE "_"
+#else
+# define MAYBE_STDCALL_UNDERSCORE ""
+# endif
+#else
+# define CALLING_CONVENTION_SUFFIX_VOID_FUNCTION ""
+# define MAYBE_STDCALL_UNDERSCORE ""
+#endif
+
+#ifdef _MSC_VER
+# define virt_override
+#else
+# define virt_override override
+#endif
+
+#include <cstdio>
+
+#include <QWidget>
+#include <QDebug>
+#include <QString>
+#include <QLibrary>
+#include <QFrame>
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ftnoir_filter_base/ftnoir_filter_base.h"
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+
+#if defined(_WIN32)
+# define CALLING_CONVENTION __stdcall
+#else
+# define CALLING_CONVENTION
+#endif
+
+class IDynamicLibraryProvider;
+
+struct SelectedLibraries {
+public:
+ ITracker* pTracker;
+ ITracker* pSecondTracker;
+ IFilter* pFilter;
+ IProtocol* pProtocol;
+ SelectedLibraries(IDynamicLibraryProvider* main = NULL);
+ ~SelectedLibraries();
+ bool correct;
+};
+
+extern SelectedLibraries* Libraries;
+
+struct Metadata;
+
+extern "C" typedef void* (CALLING_CONVENTION * NULLARY_DYNAMIC_FUNCTION)(void);
+extern "C" typedef Metadata* (CALLING_CONVENTION* METADATA_FUNCTION)(void);
+extern "C" typedef void* (CALLING_CONVENTION* SETTINGS_FUNCTION)(void);
+
+class DynamicLibrary {
+public:
+ DynamicLibrary(const QString& filename);
+ virtual ~DynamicLibrary();
+ SETTINGS_FUNCTION Dialog;
+ NULLARY_DYNAMIC_FUNCTION Constructor;
+ METADATA_FUNCTION Metadata;
+ QString filename;
+private:
+#if defined(_WIN32)
+ QLibrary* handle;
+#else
+ void* handle;
+#endif
+};
+
+struct Metadata
+{
+ Metadata() {}
+ virtual ~Metadata() {}
+
+ virtual void getFullName(QString *strToBeFilled) = 0;
+ virtual void getShortName(QString *strToBeFilled) = 0;
+ virtual void getDescription(QString *strToBeFilled) = 0;
+ virtual void getIcon(QIcon *icon) = 0;
+};
+
+class IDynamicLibraryProvider {
+public:
+ virtual DynamicLibrary* current_tracker1() = 0;
+ virtual DynamicLibrary* current_tracker2() = 0;
+ virtual DynamicLibrary* current_protocol() = 0;
+ virtual DynamicLibrary* current_filter() = 0;
+ virtual QFrame* get_video_widget() = 0;
+};
diff --git a/facetracknoir/global-shortcuts.cpp b/facetracknoir/global-shortcuts.cpp
new file mode 100644
index 00000000..1c10b160
--- /dev/null
+++ b/facetracknoir/global-shortcuts.cpp
@@ -0,0 +1,138 @@
+#include "facetracknoir/facetracknoir.h"
+
+#if defined(_WIN32)
+# ifndef DIRECTINPUT_VERSION
+# define DIRECTINPUT_VERSION 0x800
+# endif
+# include <windows.h>
+# include <dinput.h>
+
+QList<int> global_windows_key_sequences =
+ QList<int>()
+ << 0
+ << DIK_A
+ << DIK_B
+ << DIK_C
+ << DIK_D
+ << DIK_E
+ << DIK_F
+ << DIK_G
+ << DIK_H
+ << DIK_I
+ << DIK_J
+ << DIK_K
+ << DIK_L
+ << DIK_M
+ << DIK_N
+ << DIK_O
+ << DIK_P
+ << DIK_Q
+ << DIK_R
+ << DIK_S
+ << DIK_T
+ << DIK_U
+ << DIK_V
+ << DIK_W
+ << DIK_X
+ << DIK_Y
+ << DIK_Z
+ << DIK_F1
+ << DIK_F2
+ << DIK_F3
+ << DIK_F4
+ << DIK_F5
+ << DIK_F6
+ << DIK_F7
+ << DIK_F8
+ << DIK_F9
+ << DIK_F10
+ << DIK_F11
+ << DIK_F12
+ << DIK_1
+ << DIK_2
+ << DIK_3
+ << DIK_4
+ << DIK_5
+ << DIK_6
+ << DIK_7
+ << DIK_8
+ << DIK_9
+ << DIK_0
+ << DIK_LEFT
+ << DIK_RIGHT
+ << DIK_UP
+ << DIK_DOWN
+ << DIK_PGUP
+ << DIK_DOWN
+ << DIK_HOME
+ << DIK_END
+ << DIK_BACK
+ << DIK_DELETE
+ << DIK_RETURN;
+#endif
+
+QList<QString> global_key_sequences =
+ QList<QString>()
+ << ""
+ << "A"
+ << "B"
+ << "C"
+ << "D"
+ << "E"
+ << "F"
+ << "G"
+ << "H"
+ << "I"
+ << "J"
+ << "K"
+ << "L"
+ << "M"
+ << "N"
+ << "O"
+ << "P"
+ << "Q"
+ << "R"
+ << "S"
+ << "T"
+ << "U"
+ << "V"
+ << "W"
+ << "X"
+ << "Y"
+ << "Z"
+ << "F1"
+ << "F2"
+ << "F3"
+ << "F4"
+ << "F5"
+ << "F6"
+ << "F7"
+ << "F8"
+ << "F9"
+ << "F10"
+ << "F11"
+ << "F12"
+ << "1"
+ << "2"
+ << "3"
+ << "4"
+ << "5"
+ << "6"
+ << "7"
+ << "8"
+ << "9"
+ << "0"
+ << "Left"
+ << "Right"
+ << "Up"
+ << "Down"
+ << "PgUp"
+ << "PgDown"
+ << "Home"
+ << "End"
+ << "Backspace"
+ << "Del"
+ << "Enter"
+;
+
+
diff --git a/facetracknoir/images/facetracknoir.png b/facetracknoir/images/facetracknoir.png
new file mode 100644
index 00000000..41b54524
--- /dev/null
+++ b/facetracknoir/images/facetracknoir.png
Binary files differ
diff --git a/facetracknoir/images/settings16.png b/facetracknoir/images/settings16.png
new file mode 100644
index 00000000..3b31623b
--- /dev/null
+++ b/facetracknoir/images/settings16.png
Binary files differ
diff --git a/facetracknoir/lerp.hpp b/facetracknoir/lerp.hpp
new file mode 100644
index 00000000..0123832a
--- /dev/null
+++ b/facetracknoir/lerp.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "facetracknoir/timer.hpp"
+#include <algorithm>
+#include <cmath>
+
+class lerp {
+private:
+ static const constexpr double eps = 1e-2;
+ double last[2][6], cam[6], dt;
+ Timer t;
+public:
+ lerp() :
+ last { {0,0,0,0,0,0}, {0,0,0,0,0,0} }, cam {0,0,0,0,0,0}, dt(1)
+ {
+ }
+ bool idempotentp(const double* input)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ double diff = fabs(cam[i] - input[i]);
+ if (diff > eps)
+ return false;
+ }
+ return true;
+ }
+
+ void write(const double* cam_, const double* input, double* output)
+ {
+ const double q = t.elapsed();
+ const double d = q/dt;
+
+ bool idem = idempotentp(cam_);
+
+ if (!idem)
+ {
+ dt = q;
+ t.start();
+ }
+
+ const double c = std::max(std::min(1.0, d), 0.0);
+
+ if (!idem)
+ for (int i = 0; i < 6; i++)
+ {
+ last[1][i] = last[0][i];
+ last[0][i] = input[i];
+ cam[i] = cam_[i];
+ }
+
+ for (int i = 0; i < 6; i++)
+ output[i] = last[1][i] + (last[0][i] - last[1][i]) * c;
+ }
+
+ void get_state(double* state)
+ {
+ for (int i = 0; i < 6; i++)
+ state[i] = last[0][i];
+ }
+};
diff --git a/facetracknoir/main-facetracknoir.qrc b/facetracknoir/main-facetracknoir.qrc
new file mode 100644
index 00000000..6cb2e300
--- /dev/null
+++ b/facetracknoir/main-facetracknoir.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/">
+ <file>uielements/tools.png</file>
+ <file>images/settings16.png</file>
+ <file>uielements/curves.png</file>
+ <file>images/facetracknoir.png</file>
+ </qresource>
+</RCC>
diff --git a/facetracknoir/main-settings.hpp b/facetracknoir/main-settings.hpp
new file mode 100644
index 00000000..8e93bd24
--- /dev/null
+++ b/facetracknoir/main-settings.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <QString>
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct key_opts {
+ value<int> key_index;
+ value<bool> ctrl, alt, shift;
+ key_opts(pbundle b, const QString& name) :
+ key_index(b, QString("key-index-%1").arg(name), 0),
+ ctrl(b, QString("key-ctrl-%1").arg(name), 0),
+ alt(b, QString("key-alt-%1").arg(name), 0),
+ shift(b, QString("key-shift-%1").arg(name), 0)
+ {}
+};
+
+struct axis_opts {
+ value<double> zero;
+ value<bool> invert, altp;
+ axis_opts(pbundle b, QString pfx) :
+ zero(b, n(pfx, "zero-pos"), 0),
+ invert(b, n(pfx, "invert-axis"), false),
+ altp(b, n(pfx, "alt-axis-sign"), false)
+ {}
+private:
+ static inline QString n(QString pfx, QString name) {
+ return QString("%1-%2").arg(pfx, name);
+ }
+};
+
+struct main_settings {
+ pbundle b;
+ key_opts center_key;
+ key_opts toggle_key;
+ value<QString> tracker_dll, tracker2_dll, filter_dll, protocol_dll;
+ axis_opts a_x, a_y, a_z, a_yaw, a_pitch, a_roll;
+ value<bool> tcomp_p, tcomp_tz, dingp;
+ main_settings(pbundle b) :
+ b(b),
+ center_key(b, "center"),
+ toggle_key(b, "toggle"),
+ tracker_dll(b, "tracker-dll", ""),
+ tracker2_dll(b, "tracker2-dll", ""),
+ filter_dll(b, "filter-dll", ""),
+ protocol_dll(b, "protocol-dll", ""),
+ a_x(b, "x"),
+ a_y(b, "y"),
+ a_z(b, "z"),
+ a_yaw(b, "yaw"),
+ a_pitch(b, "pitch"),
+ a_roll(b, "roll"),
+ tcomp_p(b, "compensate-translation", true),
+ tcomp_tz(b, "compensate-translation-disable-z-axis", false),
+ dingp(b, "ding", true)
+ {}
+};
diff --git a/facetracknoir/main.cpp b/facetracknoir/main.cpp
new file mode 100644
index 00000000..3143a093
--- /dev/null
+++ b/facetracknoir/main.cpp
@@ -0,0 +1,72 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of the some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* Homepage *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU General Public License as published by the *
+* Free Software Foundation; either version 3 of the License, or (at your *
+* option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but *
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, see <http://www.gnu.org/licenses/>. *
+*********************************************************************************/
+
+#include "facetracknoir.h"
+#include "tracker.h"
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QDebug>
+#include <QList>
+#include <QDir>
+#include <QStringList>
+#include <memory>
+
+#if defined(_WIN32) && defined(_MSC_VER)
+# include <windows.h>
+# ifdef OPENTRACK_BREAKPAD
+# include <exception_handler.h>
+using namespace google_breakpad;
+bool dumpCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded)
+{
+ MessageBoxA(GetDesktopWindow(),
+ "Generating crash dump!\r\n"
+ "Please send the .dmp file to <sthalik@misaki.pl> to help us improve the code.",
+ "opentrack crashed :(",
+ MB_OK | MB_ICONERROR);
+ return succeeded;
+}
+
+# endif
+#endif
+
+int main(int argc, char** argv)
+{
+#if defined(OPENTRACK_BREAKPAD) && defined(_MSC_VER)
+ auto handler = new ExceptionHandler(L".", nullptr, dumpCallback, nullptr, -1);
+#endif
+ QApplication::setAttribute(Qt::AA_X11InitThreads, true);
+ QApplication app(argc, argv);
+ auto w = std::make_shared<FaceTrackNoIR>();
+
+ w->show();
+ app.exec();
+
+ return 0;
+}
+
diff --git a/facetracknoir/mingw-version-script.txt b/facetracknoir/mingw-version-script.txt
new file mode 100644
index 00000000..fe20ad37
--- /dev/null
+++ b/facetracknoir/mingw-version-script.txt
@@ -0,0 +1,8 @@
+{
+ global:
+ GetDialog?0;
+ GetConstructor?0;
+ GetMetadata?0;
+ local:
+ *;
+};
diff --git a/facetracknoir/options.h b/facetracknoir/options.h
new file mode 100644
index 00000000..3fd0e767
--- /dev/null
+++ b/facetracknoir/options.h
@@ -0,0 +1,319 @@
+/* Copyright (c) 2013 Stanislaw Halik
+ *
+ * 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 <QObject>
+#include <QSettings>
+#include <QMap>
+#include <QString>
+#include <QVariant>
+#include <QMutex>
+#include <QMutexLocker>
+#include <memory>
+#include <cassert>
+#include <QWidget>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+#include <QSlider>
+#include <QLineEdit>
+#include <QLabel>
+#include <QCoreApplication>
+
+#ifdef __GNUC__
+# define ov override
+#else
+# define ov
+#endif
+
+#include <QDebug>
+
+namespace options {
+ template<typename T>
+ inline T qcruft_to_t (const QVariant& t);
+
+ template<>
+ inline int qcruft_to_t<int>(const QVariant& t)
+ {
+ return t.toInt();
+ }
+
+ template<>
+ inline QString qcruft_to_t<QString>(const QVariant& t)
+ {
+ return t.toString();
+ }
+
+ template<>
+ inline bool qcruft_to_t<bool>(const QVariant& t)
+ {
+ return t.toBool();
+ }
+
+ template<>
+ inline double qcruft_to_t<double>(const QVariant& t)
+ {
+ return t.toDouble();
+ }
+
+ template<>
+ inline QVariant qcruft_to_t<QVariant>(const QVariant& t)
+ {
+ return t;
+ }
+
+ // snapshot of qsettings group at given time
+ class group {
+ private:
+ QMap<QString, QVariant> map;
+ QString name;
+ public:
+ group(const QString& name) : name(name)
+ {
+ QSettings settings(group::org);
+ QString currentFile =
+ settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile(currentFile, QSettings::IniFormat);
+ iniFile.beginGroup(name);
+ for (auto& k : iniFile.childKeys())
+ map[k] = iniFile.value(k);
+ iniFile.endGroup();
+ }
+ static constexpr const char* org = "opentrack";
+ void save() {
+ QSettings settings(group::org);
+ QString currentFile =
+ settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings s(currentFile, QSettings::IniFormat);
+ s.beginGroup(name);
+ for (auto& k : map.keys())
+ s.setValue(k, map[k]);
+ s.endGroup();
+ }
+ template<typename T>
+ T get(const QString& k) {
+ return qcruft_to_t<T>(map.value(k));
+ }
+
+ void put(const QString& s, const QVariant& d)
+ {
+ map[s] = d;
+ }
+ bool contains(const QString& s)
+ {
+ return map.contains(s);
+ }
+ };
+
+ class impl_bundle : public QObject {
+ Q_OBJECT
+ private:
+ QMutex mtx;
+ const QString group_name;
+ group saved;
+ group transient;
+ impl_bundle(const impl_bundle&) = delete;
+ impl_bundle& operator=(const impl_bundle&) = delete;
+ bool modified;
+ public:
+ impl_bundle(const QString& group_name) :
+ mtx(QMutex::Recursive),
+ group_name(group_name),
+ saved(group_name),
+ transient(saved),
+ modified(false)
+ {
+ }
+ void reload() {
+ QMutexLocker l(&mtx);
+ saved = group(group_name);
+ transient = saved;
+ emit reloaded();
+ }
+
+ std::shared_ptr<impl_bundle> make(const QString& name) {
+ return std::make_shared<impl_bundle>(name);
+ }
+ void store(const QString& name, const QVariant& datum)
+ {
+ QMutexLocker l(&mtx);
+ if (!transient.contains(name) || datum != transient.get<QVariant>(name))
+ {
+ if (!modified)
+ qDebug() << name << transient.get<QVariant>(name) << datum;
+ modified = true;
+ transient.put(name, datum);
+ emit bundleChanged();
+ }
+ }
+ bool contains(const QString& name)
+ {
+ QMutexLocker l(&mtx);
+ return transient.contains(name);
+ }
+ template<typename T>
+ T get(const QString& name) {
+ QMutexLocker l(&mtx);
+ return transient.get<T>(name);
+ }
+ void save()
+ {
+ QMutexLocker l(&mtx);
+ modified = false;
+ saved = transient;
+ transient.save();
+ }
+ void revert()
+ {
+ QMutexLocker l(&mtx);
+ modified = false;
+ transient = saved;
+ emit bundleChanged();
+ }
+
+ bool modifiedp() {
+ QMutexLocker l(&mtx);
+ return modified;
+ }
+ signals:
+ void bundleChanged();
+ void reloaded();
+ };
+
+ typedef std::shared_ptr<impl_bundle> pbundle;
+
+ class base_value : public QObject {
+ Q_OBJECT
+ public:
+ base_value(pbundle b, const QString& name) : b(b), self_name(name) {
+ connect(b.get(), SIGNAL(reloaded()), this, SLOT(reread_value()));
+ }
+ protected:
+ virtual QVariant operator=(const QVariant& datum) = 0;
+ pbundle b;
+ QString self_name;
+ public slots:
+ void reread_value()
+ {
+ this->operator=(b->get<QVariant>(self_name));
+ }
+ public slots:
+#define DEFINE_SLOT(t) void setValue(t datum) { this->operator=(qVariantFromValue(datum)); }
+ DEFINE_SLOT(double)
+ DEFINE_SLOT(int)
+ DEFINE_SLOT(QString)
+ DEFINE_SLOT(bool)
+ signals:
+#define DEFINE_SIGNAL(t) void valueChanged(t);
+ DEFINE_SIGNAL(double)
+ DEFINE_SIGNAL(int)
+ DEFINE_SIGNAL(QString)
+ DEFINE_SIGNAL(bool)
+ };
+
+ template<typename T>
+ class value : public base_value {
+ protected:
+ QVariant operator=(const QVariant& datum) {
+ auto foo = qcruft_to_t<T>(datum);
+ b->store(self_name, qVariantFromValue<T>(foo));
+ emit valueChanged(foo);
+ return datum;
+ }
+ public:
+ static constexpr const Qt::ConnectionType QT_CONNTYPE = Qt::UniqueConnection;
+ static constexpr const Qt::ConnectionType OPT_CONNTYPE = Qt::UniqueConnection;
+ value(pbundle b, const QString& name, T def) :
+ base_value(b, name)
+ {
+ if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
+ {
+ this->operator=(qVariantFromValue<T>(def));
+ }
+ }
+ operator T() { return b->get<T>(self_name); }
+ QVariant operator=(const T& datum)
+ {
+ return this->operator =(qVariantFromValue<T>(datum));
+ }
+ };
+
+ template<typename T, typename Q>
+ inline void tie_setting(value<T>&, Q*);
+
+ template<>
+ inline void tie_setting(value<int>& v, QComboBox* cb)
+ {
+ cb->setCurrentIndex(v);
+ base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<QString>& v, QComboBox* cb)
+ {
+ cb->setCurrentText(v);
+ v = cb->currentText();
+ base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<bool>& v, QCheckBox* cb)
+ {
+ cb->setChecked(v);
+ base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
+ {
+ dsb->setValue(v);
+ base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<int>& v, QSpinBox* sb)
+ {
+ sb->setValue(v);
+ base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<int>& v, QSlider* sl)
+ {
+ sl->setValue(v);
+ base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<QString>& v, QLineEdit* le)
+ {
+ le->setText(v);
+ base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.QT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.OPT_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<QString>& v, QLabel* lb)
+ {
+ lb->setText(v);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.OPT_CONNTYPE);
+ }
+
+ inline pbundle bundle(const QString& group) {
+ return std::make_shared<impl_bundle>(group);
+ }
+}
diff --git a/facetracknoir/posix-version-script.txt b/facetracknoir/posix-version-script.txt
new file mode 100644
index 00000000..97edb9aa
--- /dev/null
+++ b/facetracknoir/posix-version-script.txt
@@ -0,0 +1,8 @@
+{
+ global:
+ GetDialog;
+ GetConstructor;
+ GetMetadata;
+ local:
+ *;
+}; \ No newline at end of file
diff --git a/facetracknoir/qt-moc.h b/facetracknoir/qt-moc.h
new file mode 100644
index 00000000..8ccfffe8
--- /dev/null
+++ b/facetracknoir/qt-moc.h
@@ -0,0 +1,10 @@
+#include <QObject>
+
+// this file exists only such that cmake qt automoc is appeased
+
+class AutomocMe : public QObject {
+ Q_OBJECT
+private:
+ virtual void foo() = 0;
+ AutomocMe() {}
+};
diff --git a/facetracknoir/rotation.h b/facetracknoir/rotation.h
new file mode 100644
index 00000000..22f35abb
--- /dev/null
+++ b/facetracknoir/rotation.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ *
+ * 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.
+ */
+
+#ifndef ROTATION_H
+#define ROTATION_H
+#include <cmath>
+// ----------------------------------------------------------------------------
+class RotationType {
+
+public:
+ RotationType() : a(1.0),b(0.0),c(0.0),d(0.0) {}
+ RotationType(double yaw, double pitch, double roll) { fromEuler(yaw, pitch, roll); }
+ RotationType(double a, double b, double c, double d) : a(a),b(b),c(c),d(d) {}
+
+ RotationType inv(){ // inverse
+ return RotationType(a,-b,-c, -d);
+ }
+
+
+ // conversions
+ // see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
+ void fromEuler(double yaw, double pitch, double roll)
+ {
+
+ double sin_phi = sin(roll/2.0);
+ double cos_phi = cos(roll/2.0);
+ double sin_the = sin(pitch/2.0);
+ double cos_the = cos(pitch/2.0);
+ double sin_psi = sin(yaw/2.0);
+ double cos_psi = cos(yaw/2.0);
+
+ a = cos_phi*cos_the*cos_psi + sin_phi*sin_the*sin_psi;
+ b = sin_phi*cos_the*cos_psi - cos_phi*sin_the*sin_psi;
+ c = cos_phi*sin_the*cos_psi + sin_phi*cos_the*sin_psi;
+ d = cos_phi*cos_the*sin_psi - sin_phi*sin_the*cos_psi;
+ }
+
+ void toEuler(double& yaw, double& pitch, double& roll) const
+ {
+ roll = atan2(2.0*(a*b + c*d), 1.0 - 2.0*(b*b + c*c));
+ pitch = asin(2.0*(a*c - b*d));
+ yaw = atan2(2.0*(a*d + b*c), 1.0 - 2.0*(c*c + d*d));
+ }
+
+ const RotationType operator*(const RotationType& B) const
+ {
+ const RotationType& A = *this;
+ return RotationType(A.a*B.a - A.b*B.b - A.c*B.c - A.d*B.d, // quaternion multiplication
+ A.a*B.b + A.b*B.a + A.c*B.d - A.d*B.c,
+ A.a*B.c - A.b*B.d + A.c*B.a + A.d*B.b,
+ A.a*B.d + A.b*B.c - A.c*B.b + A.d*B.a);
+ }
+
+protected:
+ double a,b,c,d; // quaternion coefficients
+};
+
+
+
+#endif //ROTATION_H
diff --git a/facetracknoir/shortcuts.cpp b/facetracknoir/shortcuts.cpp
new file mode 100644
index 00000000..601bbcc6
--- /dev/null
+++ b/facetracknoir/shortcuts.cpp
@@ -0,0 +1,152 @@
+#include "facetracknoir/facetracknoir.h"
+#include "facetracknoir/shortcuts.h"
+
+KeyboardShortcutDialog::KeyboardShortcutDialog( FaceTrackNoIR *ftnoir, QWidget *parent )
+ : QWidget( parent, Qt::Dialog)
+{
+ ui.setupUi( this );
+
+ QPoint offsetpos(100, 100);
+ this->move(parent->pos() + offsetpos);
+
+ mainApp = ftnoir; // Preserve a pointer to FTNoIR
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ for ( int i = 0; i < global_key_sequences.size(); i++) {
+ ui.cbxCenterKey->addItem(global_key_sequences.at(i));
+ ui.cbxToggleKey->addItem(global_key_sequences.at(i));
+ }
+
+ tie_setting(mainApp->s.center_key.key_index, ui.cbxCenterKey);
+ tie_setting(mainApp->s.center_key.alt, ui.chkCenterAlt);
+ tie_setting(mainApp->s.center_key.shift, ui.chkCenterShift);
+ tie_setting(mainApp->s.center_key.ctrl, ui.chkCenterCtrl);
+
+ tie_setting(mainApp->s.toggle_key.key_index, ui.cbxToggleKey);
+ tie_setting(mainApp->s.toggle_key.alt, ui.chkToggleAlt);
+ tie_setting(mainApp->s.toggle_key.shift, ui.chkToggleShift);
+ tie_setting(mainApp->s.toggle_key.ctrl, ui.chkToggleCtrl);
+
+ tie_setting(mainApp->s.dingp, ui.ding);
+}
+
+//
+// OK clicked on server-dialog
+//
+void KeyboardShortcutDialog::doOK() {
+ mainApp->b->save();
+ this->close();
+ if (mainApp->tracker)
+ mainApp->bindKeyboardShortcuts();
+}
+
+void KeyboardShortcutDialog::doCancel() {
+ mainApp->b->revert();
+ close();
+}
+
+#if defined(_WIN32)
+#include <windows.h>
+
+KeybindingWorkerImpl::~KeybindingWorkerImpl() {
+ if (dinkeyboard) {
+ dinkeyboard->Unacquire();
+ dinkeyboard->Release();
+ }
+ if (din)
+ din->Release();
+}
+
+KeybindingWorkerImpl::KeybindingWorkerImpl(FaceTrackNoIR& w, Key keyCenter, Key keyToggle)
+: din(0), dinkeyboard(0), kCenter(keyCenter), kToggle(keyToggle), window(w), should_quit(true)
+{
+ if (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din, NULL) != DI_OK) {
+ qDebug() << "setup DirectInput8 Creation failed!" << GetLastError();
+ return;
+ }
+ if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) {
+ din->Release();
+ din = 0;
+ qDebug() << "setup CreateDevice function failed!" << GetLastError();
+ return;
+ }
+ if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) {
+ qDebug() << "setup SetDataFormat function failed!" << GetLastError();
+ dinkeyboard->Release();
+ dinkeyboard = 0;
+ din->Release();
+ din = 0;
+ return;
+ }
+
+ if (dinkeyboard->SetCooperativeLevel((HWND) window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) {
+ dinkeyboard->Release();
+ din->Release();
+ din = 0;
+ dinkeyboard = 0;
+ qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError();
+ return;
+ }
+ if (dinkeyboard->Acquire() != DI_OK)
+ {
+ dinkeyboard->Release();
+ din->Release();
+ din = 0;
+ dinkeyboard = 0;
+ qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError();
+ return;
+ }
+ should_quit = false;
+}
+
+static bool isKeyPressed( const Key *key, const BYTE *keystate ) {
+ bool shift;
+ bool ctrl;
+ bool alt;
+
+ if (keystate[key->keycode] & 0x80) {
+ shift = ( (keystate[DIK_LSHIFT] & 0x80) || (keystate[DIK_RSHIFT] & 0x80) );
+ ctrl = ( (keystate[DIK_LCONTROL] & 0x80) || (keystate[DIK_RCONTROL] & 0x80) );
+ alt = ( (keystate[DIK_LALT] & 0x80) || (keystate[DIK_RALT] & 0x80) );
+
+ //
+ // If one of the modifiers is needed and not pressed, return false.
+ //
+ if (key->shift && !shift) return false;
+ if (key->ctrl && !ctrl) return false;
+ if (key->alt && !alt) return false;
+
+ //
+ // All is well!
+ //
+ return true;
+ }
+ return false;
+}
+
+#define PROCESS_KEY(k, s) \
+ if (isKeyPressed(&k, keystate) && (!k.ever_pressed ? (k.timer.start(), k.ever_pressed = true) : k.timer.restart() > 100)) \
+ window.s();
+
+void KeybindingWorkerImpl::run() {
+ BYTE keystate[256];
+ while (!should_quit)
+ {
+ if (dinkeyboard->GetDeviceState(256, (LPVOID)keystate) != DI_OK) {
+ qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError();
+ Sleep(25);
+ continue;
+ }
+
+ PROCESS_KEY(kCenter, shortcutRecentered);
+ PROCESS_KEY(kToggle, shortcutToggled);
+
+ Sleep(25);
+ }
+}
+
+#else
+#endif
diff --git a/facetracknoir/shortcuts.h b/facetracknoir/shortcuts.h
new file mode 100644
index 00000000..f8c34be7
--- /dev/null
+++ b/facetracknoir/shortcuts.h
@@ -0,0 +1,85 @@
+#pragma once
+#include <QWidget>
+#include <QElapsedTimer>
+#include <QThread>
+#include <QMessageBox>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QSettings>
+#include "ui_ftnoir_keyboardshortcuts.h"
+
+class FaceTrackNoIR;
+
+class KeyboardShortcutDialog: public QWidget
+{
+ Q_OBJECT
+public:
+
+ KeyboardShortcutDialog( FaceTrackNoIR *ftnoir, QWidget *parent );
+private:
+ Ui::UICKeyboardShortcutDialog ui;
+ FaceTrackNoIR *mainApp;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+extern QList<QString> global_key_sequences;
+
+#if defined(_WIN32)
+extern QList<int> global_windows_key_sequences;
+# undef DIRECTINPUT_VERSION
+# define DIRECTINPUT_VERSION 0x0800
+# include <windows.h>
+# include <dinput.h>
+
+struct Key {
+ BYTE keycode;
+ bool shift;
+ bool ctrl;
+ bool alt;
+ bool ever_pressed;
+ QElapsedTimer timer;
+public:
+ Key() : keycode(0), shift(false), ctrl(false), alt(false), ever_pressed(false)
+ {
+ }
+};
+#else
+typedef unsigned char BYTE;
+struct Key { int foo; };
+#endif
+
+#if defined(_WIN32)
+class KeybindingWorkerImpl {
+private:
+ LPDIRECTINPUT8 din;
+ LPDIRECTINPUTDEVICE8 dinkeyboard;
+ Key kCenter;
+ Key kToggle;
+ FaceTrackNoIR& window;
+public:
+ volatile bool should_quit;
+ ~KeybindingWorkerImpl();
+ KeybindingWorkerImpl(FaceTrackNoIR& w, Key keyCenter, Key keyToggle);
+ void run();
+};
+#else
+class KeybindingWorkerImpl {
+public:
+ KeybindingWorkerImpl(FaceTrackNoIR& w, Key keyCenter, Key keyToggle);
+ void run() {}
+};
+#endif
+
+class KeybindingWorker : public QThread, public KeybindingWorkerImpl {
+ Q_OBJECT
+public:
+ KeybindingWorker(FaceTrackNoIR& w, Key keyCenter, Key keyToggle) : KeybindingWorkerImpl(w, keyCenter, keyToggle)
+ {
+ }
+ void run() {
+ KeybindingWorkerImpl::run();
+ }
+};
diff --git a/facetracknoir/timer.hpp b/facetracknoir/timer.hpp
new file mode 100644
index 00000000..e3fa38de
--- /dev/null
+++ b/facetracknoir/timer.hpp
@@ -0,0 +1,66 @@
+#pragma once
+#include <time.h>
+#if defined (_WIN32)
+# include <windows.h>
+# define CLOCK_MONOTONIC 0
+static inline void clock_gettime(int, struct timespec* ts)
+{
+ static LARGE_INTEGER freq;
+
+ if (!freq.QuadPart)
+ (void) QueryPerformanceFrequency(&freq);
+
+ LARGE_INTEGER d;
+
+ (void) QueryPerformanceCounter(&d);
+
+ d.QuadPart *= 1000000000L;
+ d.QuadPart /= freq.QuadPart;
+
+ ts->tv_sec = d.QuadPart / 1000000000L;
+ ts->tv_nsec = d.QuadPart % 1000000000L;
+}
+
+#else
+# if defined(__MACH__)
+# define CLOCK_MONOTONIC 0
+# include <inttypes.h>
+# include <mach/mach_time.h>
+static inline void clock_gettime(int, struct timespec* ts)
+{
+ static mach_timebase_info_data_t sTimebaseInfo;
+ uint64_t state, nsec;
+ if ( sTimebaseInfo.denom == 0 ) {
+ (void) mach_timebase_info(&sTimebaseInfo);
+ }
+ state = mach_absolute_time();
+ nsec = state * sTimebaseInfo.numer / sTimebaseInfo.denom;
+ ts->tv_sec = nsec / 1000000000L;
+ ts->tv_nsec = nsec % 1000000000L;
+}
+# endif
+#endif
+class Timer {
+private:
+ struct timespec state;
+ long conv(const struct timespec& cur)
+ {
+ return (cur.tv_sec - state.tv_sec) * 1000000000L + (cur.tv_nsec - state.tv_nsec);
+ }
+public:
+ Timer() {
+ start();
+ }
+ long start() {
+ struct timespec cur;
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+ int ret = conv(cur);
+ state = cur;
+ return ret;
+ }
+ long elapsed() {
+ struct timespec cur;
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+ return conv(cur);
+ }
+};
diff --git a/facetracknoir/tracker.cpp b/facetracknoir/tracker.cpp
new file mode 100644
index 00000000..094264ff
--- /dev/null
+++ b/facetracknoir/tracker.cpp
@@ -0,0 +1,195 @@
+/* Copyright (c) 2012-2013 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * 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.
+ */
+
+/*
+ * this file appeared originally in facetracknoir, was rewritten completely
+ * following opentrack fork.
+ *
+ * originally written by Wim Vriend.
+ */
+
+#include "tracker.h"
+#include "facetracknoir.h"
+#include <opencv2/core/core.hpp>
+#include <cmath>
+#include <algorithm>
+
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+
+Tracker::Tracker(FaceTrackNoIR *parent , main_settings& s) :
+ mainApp(parent),
+ s(s),
+ should_quit(false),
+ do_center(false),
+ enabled(true)
+{
+}
+
+Tracker::~Tracker()
+{
+ should_quit = true;
+ wait();
+}
+
+static void get_curve(double pos, double& out, THeadPoseDOF& axis) {
+ bool altp = (pos < 0) && axis.opts.altp;
+ if (altp) {
+ out = (axis.opts.invert ? -1 : 1) * axis.curveAlt.getValue(pos);
+ axis.curve.setTrackingActive( false );
+ axis.curveAlt.setTrackingActive( true );
+ }
+ else {
+ out = (axis.opts.invert ? -1 : 1) * axis.curve.getValue(pos);
+ axis.curve.setTrackingActive( true );
+ axis.curveAlt.setTrackingActive( false );
+ }
+ out += axis.opts.zero;
+}
+
+static void t_compensate(double* input, double* output, bool rz)
+{
+ const auto H = input[Yaw] * M_PI / -180;
+ const auto P = input[Pitch] * M_PI / -180;
+ const auto B = input[Roll] * M_PI / 180;
+
+ const auto cosH = cos(H);
+ const auto sinH = sin(H);
+ const auto cosP = cos(P);
+ const auto sinP = sin(P);
+ const auto cosB = cos(B);
+ const auto sinB = sin(B);
+
+ double foo[] = {
+ cosH * cosB - sinH * sinP * sinB,
+ - sinB * cosP,
+ sinH * cosB + cosH * sinP * sinB,
+ cosH * sinB + sinH * sinP * cosB,
+ cosB * cosP,
+ sinB * sinH - cosH * sinP * cosB,
+ - sinH * cosP,
+ - sinP,
+ cosH * cosP,
+ };
+
+ cv::Mat rmat(3, 3, CV_64F, foo);
+ const cv::Mat tvec(3, 1, CV_64F, input);
+ cv::Mat ret = rmat * tvec;
+
+ const int max = !rz ? 3 : 2;
+
+ for (int i = 0; i < max; i++)
+ output[i] = ret.at<double>(i);
+}
+
+/** QThread run method @override **/
+void Tracker::run() {
+ T6DOF offset_camera;
+
+ double newpose[6] = {0};
+ int sleep_ms = 15;
+
+ if (Libraries->pTracker)
+ sleep_ms = std::min(sleep_ms, 1000 / Libraries->pTracker->preferredHz());
+
+ if (Libraries->pSecondTracker)
+ sleep_ms = std::min(sleep_ms, 1000 / Libraries->pSecondTracker->preferredHz());
+
+ qDebug() << "tracker Hz:" << 1000 / sleep_ms;
+
+#if defined(_WIN32)
+ (void) timeBeginPeriod(1);
+#endif
+
+ for (;;)
+ {
+ t.start();
+
+ if (should_quit)
+ break;
+
+ if (Libraries->pSecondTracker) {
+ Libraries->pSecondTracker->GetHeadPoseData(newpose);
+ }
+
+ if (Libraries->pTracker) {
+ Libraries->pTracker->GetHeadPoseData(newpose);
+ }
+
+ {
+ QMutexLocker foo(&mtx);
+
+ for (int i = 0; i < 6; i++)
+ mainApp->axis(i).headPos = newpose[i];
+
+ if (do_center) {
+ for (int i = 0; i < 6; i++)
+ offset_camera.axes[i] = mainApp->axis(i).headPos;
+
+ do_center = false;
+
+ if (Libraries->pFilter)
+ Libraries->pFilter->reset();
+ }
+
+ T6DOF target_camera, target_camera2, new_camera;
+
+ if (enabled)
+ {
+ for (int i = 0; i < 6; i++)
+ target_camera.axes[i] = mainApp->axis(i).headPos;
+
+ target_camera2 = target_camera - offset_camera;
+ }
+
+ if (Libraries->pFilter) {
+ Libraries->pFilter->FilterHeadPoseData(target_camera2.axes, new_camera.axes);
+ } else {
+ new_camera = target_camera2;
+ }
+
+ for (int i = 0; i < 6; i++) {
+ get_curve(new_camera.axes[i], output_camera.axes[i], mainApp->axis(i));
+ }
+
+ if (mainApp->s.tcomp_p)
+ t_compensate(output_camera.axes, output_camera.axes, mainApp->s.tcomp_tz);
+
+ if (Libraries->pProtocol) {
+ Libraries->pProtocol->sendHeadposeToGame( output_camera.axes ); // degrees & centimeters
+ }
+ }
+
+ const long q = std::max(0L, sleep_ms * 1000L - std::max(0L, t.elapsed()));
+
+ usleep(q);
+ }
+#if defined(_WIN32)
+ (void) timeEndPeriod(1);
+#endif
+
+ for (int i = 0; i < 6; i++)
+ {
+ mainApp->axis(i).curve.setTrackingActive(false);
+ mainApp->axis(i).curveAlt.setTrackingActive(false);
+ }
+}
+
+void Tracker::getHeadPose( double *data ) {
+ QMutexLocker foo(&mtx);
+ for (int i = 0; i < 6; i++)
+ {
+ data[i] = mainApp->axis(i).headPos;
+ }
+}
+
+void Tracker::getOutputHeadPose( double *data ) {
+ QMutexLocker foo(&mtx);
+ for (int i = 0; i < 6; i++)
+ data[i] = output_camera.axes[i];
+}
diff --git a/facetracknoir/tracker.h b/facetracknoir/tracker.h
new file mode 100644
index 00000000..46440c32
--- /dev/null
+++ b/facetracknoir/tracker.h
@@ -0,0 +1,102 @@
+#ifndef __TRACKER_H__
+#define __TRACKER_H__
+
+#include <QThread>
+#include <QMessageBox>
+#include <QLineEdit>
+#include <QPoint>
+#include <QWaitCondition>
+#include <QList>
+#include <QPainterPath>
+#include <QDebug>
+#include <QMutex>
+#include "global-settings.h"
+#include <ftnoir_tracker_base/ftnoir_tracker_types.h>
+#include <vector>
+
+#include <qfunctionconfigurator/functionconfig.h>
+#include "tracker_types.h"
+#include "facetracknoir/main-settings.hpp"
+#include "facetracknoir/options.h"
+#include "facetracknoir/timer.hpp"
+using namespace options;
+
+class FaceTrackNoIR; // pre-define parent-class to avoid circular includes
+
+class THeadPoseDOF {
+private:
+ THeadPoseDOF(const THeadPoseDOF &) = delete;
+ THeadPoseDOF& operator=(const THeadPoseDOF&) = delete;
+public:
+ THeadPoseDOF(QString primary,
+ QString secondary,
+ int maxInput1,
+ int maxOutput1,
+ int maxInput2,
+ int maxOutput2,
+ axis_opts* opts) :
+ headPos(0),
+ curve(primary, maxInput1, maxOutput1),
+ curveAlt(secondary, maxInput2, maxOutput2),
+ opts(*opts)
+ {
+ QSettings settings("opentrack");
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile( currentFile, QSettings::IniFormat );
+ curve.loadSettings(iniFile);
+ curveAlt.loadSettings(iniFile);
+ }
+ volatile double headPos;
+ FunctionConfig curve;
+ FunctionConfig curveAlt;
+ axis_opts& opts;
+};
+
+class Tracker : protected QThread {
+ Q_OBJECT
+
+private:
+ FaceTrackNoIR *mainApp;
+ QMutex mtx;
+ main_settings& s;
+ volatile bool should_quit;
+ Timer t;
+protected:
+ void run();
+
+public:
+ Tracker( FaceTrackNoIR *parent, main_settings& s);
+ ~Tracker();
+
+ void getHeadPose(double *data);
+ void getOutputHeadPose(double *data);
+ volatile bool do_center;
+ volatile bool enabled;
+
+ T6DOF output_camera;
+
+ void start() { QThread::start(); }
+};
+
+class HeadPoseData {
+public:
+ THeadPoseDOF* axes[6];
+ HeadPoseData(std::vector<axis_opts*> opts)
+ {
+ axes[TX] = new THeadPoseDOF("tx","tx_alt", 100, 100, 100, 100, opts[TX]);
+ axes[TY] = new THeadPoseDOF("ty","ty_alt", 100, 100, 100, 100, opts[TY]);
+ axes[TZ] = new THeadPoseDOF("tz","tz_alt", 100, 100, 100, 100, opts[TZ]);
+ axes[Yaw] = new THeadPoseDOF("rx", "rx_alt", 180, 180, 180, 180, opts[Yaw]);
+ axes[Pitch] = new THeadPoseDOF("ry", "ry_alt", 90, 90, 90, 90, opts[Pitch]);
+ axes[Roll] = new THeadPoseDOF("rz", "rz_alt", 180, 180, 180, 180, opts[Roll]);
+ }
+ ~HeadPoseData()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ delete axes[i];
+ }
+ }
+};
+
+#endif
diff --git a/facetracknoir/tracker_types.cpp b/facetracknoir/tracker_types.cpp
new file mode 100644
index 00000000..dec4ff81
--- /dev/null
+++ b/facetracknoir/tracker_types.cpp
@@ -0,0 +1,44 @@
+#include "tracker_types.h"
+#include "rotation.h"
+
+#define PI 3.14159265358979323846264
+#define D2R PI/180.0
+#define R2D 180.0/PI
+
+T6DOF operator-(const T6DOF& A, const T6DOF& B)
+{
+ RotationType R_A(A.axes[Yaw]*D2R, A.axes[Pitch]*D2R, A.axes[Roll]*D2R);
+ RotationType R_B(B.axes[Yaw]*D2R, B.axes[Pitch]*D2R, B.axes[Roll]*D2R);
+ RotationType R_C = R_A * R_B.inv();
+
+ T6DOF C;
+ R_C.toEuler(C.axes[Yaw], C.axes[Pitch], C.axes[Roll]);
+ C.axes[Yaw] *= R2D;
+ C.axes[Pitch] *= R2D;
+ C.axes[Roll] *= R2D;
+
+ C.axes[TX] = A.axes[TX] - B.axes[TX];
+ C.axes[TY] = A.axes[TY] - B.axes[TY];
+ C.axes[TZ] = A.axes[TZ] - B.axes[TZ];
+ //C.frame_number?
+ return C;
+}
+
+T6DOF operator+(const T6DOF& A, const T6DOF& B)
+{
+ RotationType R_A(A.axes[Yaw]*D2R, A.axes[Pitch]*D2R, A.axes[Roll]*D2R);
+ RotationType R_B(B.axes[Yaw]*D2R, B.axes[Pitch]*D2R, B.axes[Roll]*D2R);
+ RotationType R_C = R_A * R_B;
+
+ T6DOF C;
+ R_C.toEuler(C.axes[Yaw], C.axes[Pitch], C.axes[Roll]);
+ C.axes[Yaw] *= R2D;
+ C.axes[Pitch] *= R2D;
+ C.axes[Roll] *= R2D;
+
+ C.axes[TX] = A.axes[TX] + B.axes[TX];
+ C.axes[TY] = A.axes[TY] + B.axes[TY];
+ C.axes[TZ] = A.axes[TZ] + B.axes[TZ];
+ //C.frame_number?
+ return C;
+}
diff --git a/facetracknoir/tracker_types.h b/facetracknoir/tracker_types.h
new file mode 100644
index 00000000..a367371e
--- /dev/null
+++ b/facetracknoir/tracker_types.h
@@ -0,0 +1,19 @@
+#ifndef __TRACKER_TYPES_H__
+#define __TRACKER_TYPES_H__
+
+#include "ftnoir_tracker_base/ftnoir_tracker_types.h"
+
+struct T6DOF {
+public:
+ double axes[6];
+
+ T6DOF() {
+ for (int i = 0; i < 6; i++)
+ axes[i] = 0;
+ }
+};
+
+T6DOF operator-(const T6DOF& A, const T6DOF& B); // get new pose with respect to reference pose B
+T6DOF operator+(const T6DOF& A, const T6DOF& B); // get new pose with respect to reference pose B^-1
+
+#endif //__TRACKER_TYPES_H__
diff --git a/facetracknoir/uielements/curves.png b/facetracknoir/uielements/curves.png
new file mode 100644
index 00000000..fe21fa15
--- /dev/null
+++ b/facetracknoir/uielements/curves.png
Binary files differ
diff --git a/facetracknoir/uielements/tools.png b/facetracknoir/uielements/tools.png
new file mode 100644
index 00000000..2da8f9f5
--- /dev/null
+++ b/facetracknoir/uielements/tools.png
Binary files differ