summaryrefslogtreecommitdiffhomepage
path: root/opentrack
diff options
context:
space:
mode:
Diffstat (limited to 'opentrack')
-rw-r--r--opentrack/export.hpp7
-rw-r--r--opentrack/main-settings.hpp59
-rw-r--r--opentrack/mappings.hpp88
-rw-r--r--opentrack/options.hpp392
-rw-r--r--opentrack/plugin-api.hpp11
-rw-r--r--opentrack/plugin-qt-api.hpp63
-rw-r--r--opentrack/plugin-support.cpp215
-rw-r--r--opentrack/plugin-support.h51
-rw-r--r--opentrack/pose.hpp44
-rw-r--r--opentrack/qcopyable-mutex.hpp37
-rw-r--r--opentrack/quat.hpp66
-rw-r--r--opentrack/timer.hpp68
-rw-r--r--opentrack/tracker.cpp187
-rw-r--r--opentrack/tracker.h47
14 files changed, 1335 insertions, 0 deletions
diff --git a/opentrack/export.hpp b/opentrack/export.hpp
new file mode 100644
index 00000000..8c8bdc69
--- /dev/null
+++ b/opentrack/export.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#ifdef _WIN32
+# define OPENTRACK_LINKAGE __declspec(dllexport)
+#else
+# define OPENTRACK_LINKAGE
+#endif
+#define OPENTRACK_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LINKAGE
diff --git a/opentrack/main-settings.hpp b/opentrack/main-settings.hpp
new file mode 100644
index 00000000..6f78ac82
--- /dev/null
+++ b/opentrack/main-settings.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <QString>
+#include "opentrack/options.hpp"
+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;
+ value<int> src;
+ axis_opts(pbundle b, QString pfx, int idx) :
+ zero(b, n(pfx, "zero-pos"), 0),
+ invert(b, n(pfx, "invert-axis"), false),
+ altp(b, n(pfx, "alt-axis-sign"), false),
+ src(b, n(pfx, "source-index"), idx)
+ {}
+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", TX),
+ a_y(b, "y", TY),
+ a_z(b, "z", TZ),
+ a_yaw(b, "yaw", Yaw),
+ a_pitch(b, "pitch", Pitch),
+ a_roll(b, "roll", Roll),
+ tcomp_p(b, "compensate-translation", true),
+ tcomp_tz(b, "compensate-translation-disable-z-axis", false),
+ dingp(b, "ding", true)
+ {}
+};
diff --git a/opentrack/mappings.hpp b/opentrack/mappings.hpp
new file mode 100644
index 00000000..86126db9
--- /dev/null
+++ b/opentrack/mappings.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <QSettings>
+#include "options.hpp"
+using namespace options;
+#include "../qfunctionconfigurator/functionconfig.h"
+#include "main-settings.hpp"
+
+class Mapping {
+public:
+ Mapping(QString primary,
+ QString secondary,
+ int maxInput1,
+ int maxOutput1,
+ int maxInput2,
+ int maxOutput2,
+ axis_opts& opts) :
+ curve(maxInput1, maxOutput1),
+ curveAlt(maxInput2, maxOutput2),
+ opts(opts),
+ name1(primary),
+ name2(secondary)
+ {
+ // XXX TODO move all this qsettings boilerplate into a single header -sh 20141004
+ QSettings settings("opentrack");
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile(currentFile, QSettings::IniFormat);
+ curve.loadSettings(iniFile, primary);
+ curveAlt.loadSettings(iniFile, secondary);
+ }
+ Map curve;
+ Map curveAlt;
+ axis_opts& opts;
+ QString name1, name2;
+};
+
+class Mappings {
+private:
+ Mapping axes[6];
+public:
+ Mappings(std::vector<axis_opts*> opts) :
+ axes {
+ Mapping("tx","tx_alt", 100, 100, 100, 100, *opts[TX]),
+ Mapping("ty","ty_alt", 100, 100, 100, 100, *opts[TY]),
+ Mapping("tz","tz_alt", 100, 100, 100, 100, *opts[TZ]),
+ Mapping("rx", "rx_alt", 180, 180, 180, 180, *opts[Yaw]),
+ Mapping("ry", "ry_alt", 180, 180, 180, 180, *opts[Pitch]),
+ Mapping("rz", "rz_alt", 180, 180, 180, 180, *opts[Roll])
+ }
+ {}
+
+ inline Mapping& operator()(int i) { return axes[i]; }
+ inline const Mapping& operator()(int i) const { return axes[i]; }
+
+ void load_mappings()
+ {
+ 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++)
+ {
+ axes[i].curve.loadSettings(iniFile, axes[i].name1);
+ axes[i].curveAlt.loadSettings(iniFile, axes[i].name2);
+ }
+ }
+ void save_mappings()
+ {
+ 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++)
+ {
+ axes[i].curve.saveSettings(iniFile, axes[i].name1);
+ axes[i].curveAlt.saveSettings(iniFile, axes[i].name2);
+ }
+ }
+
+ void invalidate_unsaved()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i].curve.invalidate_unsaved_settings();
+ axes[i].curveAlt.invalidate_unsaved_settings();
+ }
+ }
+};
diff --git a/opentrack/options.hpp b/opentrack/options.hpp
new file mode 100644
index 00000000..6c15d729
--- /dev/null
+++ b/opentrack/options.hpp
@@ -0,0 +1,392 @@
+/* Copyright (c) 2013-2014 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 <memory>
+#include <tuple>
+#include <map>
+#include <string>
+
+#include <QObject>
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWidget>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+#include <QSlider>
+#include <QLineEdit>
+#include <QLabel>
+#include <QCoreApplication>
+
+#include <cinttypes>
+
+#include <QDebug>
+
+namespace options {
+ template<typename k, typename v>
+ using map = std::map<k, v>;
+ using std::string;
+
+ template<typename t>
+ // don't elide usages of the function, qvariant default implicit
+ // conversion results in nonsensical runtime behavior -sh
+ inline t qcruft_to_t (const QVariant& datum);
+
+ 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:
+ map<string, QVariant> kvs;
+ string name;
+ static const QString ini_pathname()
+ {
+ QSettings settings(group::org);
+ return settings.value("SettingsFile",
+ QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ }
+ public:
+ group(const string& name) : name(name)
+ {
+ QSettings conf(ini_pathname(), QSettings::IniFormat);
+ auto q_name = QString::fromStdString(name);
+ conf.beginGroup(q_name);
+ for (auto& k_ : conf.childKeys())
+ {
+ auto tmp = k_.toUtf8();
+ string k(tmp);
+ kvs[k] = conf.value(k_);
+ }
+ conf.endGroup();
+ }
+ static constexpr const char* org = "opentrack";
+
+ void save()
+ {
+ QSettings s(ini_pathname(), QSettings::IniFormat);
+ auto q_name = QString::fromStdString(name);
+ s.beginGroup(q_name);
+ for (auto& i : kvs)
+ {
+ auto k = QString::fromStdString(i.first);
+ s.setValue(k, i.second);
+ }
+ s.endGroup();
+ }
+
+ template<typename t>
+ t get(const string& k)
+ {
+ return qcruft_to_t<t>(kvs[k]);
+ }
+
+ void put(const string& s, const QVariant& d)
+ {
+ kvs[s] = d;
+ }
+
+ bool contains(const string& s)
+ {
+ return kvs.count(s) != 0;
+ }
+ };
+
+ class impl_bundle : public QObject {
+ protected:
+ QMutex mtx;
+ const string group_name;
+ group saved;
+ group transient;
+ bool modified;
+ impl_bundle(const impl_bundle&) = delete;
+ impl_bundle& operator=(const impl_bundle&) = delete;
+ public:
+ impl_bundle(const string& group_name) :
+ mtx(QMutex::Recursive),
+ group_name(group_name),
+ saved(group_name),
+ transient(saved),
+ modified(false)
+ {
+ }
+
+ string name() { return group_name; }
+
+ void reload() {
+ QMutexLocker l(&mtx);
+ saved = group(group_name);
+ transient = saved;
+ modified = false;
+ }
+
+ bool store_kv(const string& name, const QVariant& datum)
+ {
+ QMutexLocker l(&mtx);
+
+ auto old = transient.get<QVariant>(name);
+ if (!transient.contains(name) || datum != old)
+ {
+ modified = true;
+ transient.put(name, datum);
+ return true;
+ }
+ return false;
+ }
+ bool contains(const string& name)
+ {
+ QMutexLocker l(&mtx);
+ return transient.contains(name);
+ }
+ template<typename t>
+ t get(const string& name)
+ {
+ QMutexLocker l(&mtx);
+ return transient.get<t>(name);
+ }
+ void save()
+ {
+ QMutexLocker l(&mtx);
+ modified = false;
+ saved = transient;
+ transient.save();
+ }
+
+ bool modifiedp() {
+ QMutexLocker l(&mtx);
+ return modified;
+ }
+ };
+
+ class opt_bundle;
+
+ namespace
+ {
+ template<typename k, typename v, typename cnt = int>
+ struct opt_singleton
+ {
+ public:
+ using pbundle = std::shared_ptr<v>;
+ using tt = std::tuple<cnt, pbundle>;
+ private:
+ QMutex implsgl_mtx;
+ map<k, tt> implsgl_data;
+ public:
+ opt_singleton() : implsgl_mtx(QMutex::Recursive) {}
+
+ static opt_singleton<k, v>& datum()
+ {
+ static auto ret = std::make_shared<opt_singleton<k, v>>();
+ return *ret;
+ }
+
+ pbundle bundle(const k& key)
+ {
+ QMutexLocker l(&implsgl_mtx);
+
+ if (implsgl_data.count(key) != 0)
+ return std::get<1>(implsgl_data[key]);
+
+ auto shr = std::make_shared<v>(key);
+ implsgl_data[key] = tt(cnt(1), shr);
+ return shr;
+ }
+
+ void bundle_decf(const k& key)
+ {
+ QMutexLocker l(&implsgl_mtx);
+
+ if (--std::get<0>(implsgl_data[key]) == 0)
+ implsgl_data.erase(key);
+ }
+
+ ~opt_singleton() { implsgl_data.clear(); }
+ };
+
+ using pbundle = std::shared_ptr<opt_bundle>;
+ using t_fact = opt_singleton<string, opt_bundle>;
+ }
+
+ static inline t_fact::pbundle bundle(const string name) { return t_fact::datum().bundle(name); }
+
+ class opt_bundle : public impl_bundle
+ {
+ public:
+ opt_bundle() : impl_bundle("i-have-no-name") {}
+ opt_bundle(const string& group_name) : impl_bundle(group_name)
+ {
+ qDebug() << "bundle +" << QString::fromStdString(group_name);
+ }
+
+ ~opt_bundle()
+ {
+ qDebug() << "bundle -" << QString::fromStdString(group_name);
+ t_fact::datum().bundle_decf(group_name);
+ }
+ };
+
+ class base_value : public QObject
+ {
+ Q_OBJECT
+#define DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
+#define DEFINE_SIGNAL(t) void valueChanged(t)
+ public:
+ base_value(pbundle b, const string& name) : b(b), self_name(name) {}
+ signals:
+ DEFINE_SIGNAL(double);
+ DEFINE_SIGNAL(int);
+ DEFINE_SIGNAL(bool);
+ DEFINE_SIGNAL(QString);
+ protected:
+ pbundle b;
+ string self_name;
+
+ template<typename t>
+ void store(const t& datum)
+ {
+ if (b->store_kv(self_name, datum))
+ emit valueChanged(static_cast<t>(datum));
+ }
+ public slots:
+ DEFINE_SLOT(double)
+ DEFINE_SLOT(int)
+ DEFINE_SLOT(QString)
+ DEFINE_SLOT(bool)
+ };
+
+ static inline string string_from_qstring(const QString& datum)
+ {
+ auto tmp = datum.toUtf8();
+ return string(tmp.constData());
+ }
+
+ template<typename t>
+ class value : public base_value {
+ public:
+ t operator=(const t datum)
+ {
+ store(datum);
+ return datum;
+ }
+ static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection;
+ static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::UniqueConnection;
+ value(pbundle b, const string& name, t def) : base_value(b, name)
+ {
+ if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
+ *this = def;
+ }
+ value(pbundle b, const QString& name, t def) : value(b, string_from_qstring(name), def) {}
+ value(pbundle b, const char* name, t def) : value(b, string(name), def) {}
+
+ operator t()
+ {
+ return b->get<t>(self_name);
+ }
+ };
+
+ 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);
+ v = cb->currentIndex();
+ base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_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.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_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.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_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.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_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.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<int>& v, QSlider* sl)
+ {
+ sl->setValue(v);
+ v = sl->value();
+ base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_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.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_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.SAFE_CONNTYPE);
+ }
+}
diff --git a/opentrack/plugin-api.hpp b/opentrack/plugin-api.hpp
new file mode 100644
index 00000000..a8996d63
--- /dev/null
+++ b/opentrack/plugin-api.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "export.hpp"
+
+enum Axis {
+ TX = 0, TY, TZ, Yaw, Pitch, Roll
+};
+
+#ifndef OPENTRACK_CROSS_ONLY
+# include "plugin-qt-api.hpp"
+#endif \ No newline at end of file
diff --git a/opentrack/plugin-qt-api.hpp b/opentrack/plugin-qt-api.hpp
new file mode 100644
index 00000000..a2aa0d19
--- /dev/null
+++ b/opentrack/plugin-qt-api.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <QString>
+#include <QFrame>
+#include <QIcon>
+
+struct Metadata
+{
+public:
+ virtual QString name() = 0;
+ virtual QIcon icon() = 0;
+ virtual ~Metadata() {};
+};
+
+// XXX TODO get rid of QString/QFrame to fix ABI woes
+// will lead plugins from different C++ runtimes working -sh 20141004
+
+// XXX TODO make public QWidget the mess -sh 20141004
+
+struct IFilter
+{
+public:
+ virtual ~IFilter() {};
+ virtual void filter(const double *target_camera_position, double *new_camera_position) = 0;
+};
+
+struct IFilterDialog : public QWidget
+{
+ virtual ~IFilterDialog() {}
+ virtual void registerFilter(IFilter* tracker) = 0;
+ virtual void unregisterFilter() = 0;
+};
+
+struct IProtocol
+{
+public:
+ virtual ~IProtocol() {};
+ virtual bool correct() = 0;
+ virtual void pose( const double* headpose ) = 0;
+ virtual QString game_name() = 0;
+};
+
+struct IProtocolDialog : public QWidget
+{
+ virtual ~IProtocolDialog() {}
+ virtual void register_protocol(IProtocol *protocol) = 0;
+ virtual void unregister_protocol() = 0;
+};
+
+struct ITracker
+{
+public:
+ virtual ~ITracker() {};
+ virtual void start_tracker( QFrame* frame ) = 0;
+ virtual void data(double *data) = 0;
+};
+
+struct ITrackerDialog : public QWidget
+{
+ virtual ~ITrackerDialog() {}
+ virtual void register_tracker(ITracker *tracker) = 0;
+ virtual void unregister_tracker() = 0;
+};
diff --git a/opentrack/plugin-support.cpp b/opentrack/plugin-support.cpp
new file mode 100644
index 00000000..694f3e19
--- /dev/null
+++ b/opentrack/plugin-support.cpp
@@ -0,0 +1,215 @@
+#include <cstdio>
+#include <cinttypes>
+#include "plugin-support.h"
+#include <QCoreApplication>
+#include <QFile>
+#include <QDir>
+
+#ifndef _WIN32
+# include <dlfcn.h>
+#endif
+
+SelectedLibraries::~SelectedLibraries()
+{
+}
+
+template<typename t>
+static ptr<t> make_instance(ptr<dylib> lib)
+{
+ ptr<t> ret = nullptr;
+ if (lib && lib->Constructor)
+ ret = ptr<t>(reinterpret_cast<t*>(reinterpret_cast<CTOR_FUNPTR>(lib->Constructor)()));
+ qDebug() << "lib" << (lib ? lib->filename : "<null>") << "ptr" << (intptr_t)ret.get();
+ return ret;
+}
+
+SelectedLibraries::SelectedLibraries(QFrame* frame, dylibtr t, dylibtr p, dylibtr f) :
+ pTracker(nullptr),
+ pFilter(nullptr),
+ pProtocol(nullptr),
+ correct(false)
+{
+ pTracker = make_instance<ITracker>(t);
+ pProtocol = make_instance<IProtocol>(p);
+ pFilter = make_instance<IFilter>(f);
+
+ if (!pTracker|| !pProtocol)
+ {
+ qDebug() << "load failure tracker" << (intptr_t)pTracker.get() << "protocol" << (intptr_t)pProtocol.get();
+ return;
+ }
+
+ if (pProtocol)
+ if(!pProtocol->correct())
+ {
+ qDebug() << "protocol load failure";
+ return;
+ }
+
+ pTracker->start_tracker(frame);
+
+ correct = true;
+}
+
+#if defined(__APPLE__)
+# define SONAME "dylib"
+#elif defined(_WIN32)
+# define SONAME "dll"
+#else
+# define SONAME "so"
+#endif
+
+#include <iostream>
+
+#ifdef _MSC_VER
+# error "No support for MSVC anymore"
+#else
+# define LIB_PREFIX "lib"
+#endif
+
+static bool get_metadata(ptr<dylib> lib, QString& name, QIcon& icon)
+{
+ Metadata* meta;
+ if (!lib->Meta || ((meta = lib->Meta()), !meta))
+ return false;
+ name = meta->name();
+ icon = meta->icon();
+ delete meta;
+ return true;
+}
+
+QList<ptr<dylib>> dylib::enum_libraries()
+{
+#define BASE "opentrack-"
+#define SUFF "-*.*"
+ const char* filters_n[] = { BASE "filter" SUFF,
+ BASE "tracker" SUFF,
+ BASE "proto" SUFF };
+ const Type filters_t[] = { Filter, Tracker, Protocol };
+
+ QDir settingsDir( QCoreApplication::applicationDirPath() );
+
+ QList<ptr<dylib>> ret;
+
+ for (int i = 0; i < 3; i++)
+ {
+ QString filter = filters_n[i];
+ auto t = filters_t[i];
+ 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);
+ auto lib = std::make_shared<dylib>(str, t);
+ qDebug() << "Loading" << str;
+ std::cout.flush();
+ if (!get_metadata(lib, longName, icon))
+ continue;
+ ret.push_back(lib);
+ }
+ }
+
+ return ret;
+}
+
+dylib::dylib(const QString& filename, Type t) :
+ type(t),
+ Dialog(nullptr),
+ Constructor(nullptr),
+ Meta(nullptr)
+{
+ this->filename = filename;
+#if defined(_WIN32)
+ QString fullPath = QCoreApplication::applicationDirPath() + "/" + this->filename;
+ handle = new QLibrary(fullPath);
+
+ struct _foo {
+ static bool die(QLibrary*& l, bool failp)
+ {
+ if (failp)
+ {
+ qDebug() << "failed" << l->errorString();
+ delete l;
+ l = nullptr;
+ }
+ return failp;
+ }
+ };
+
+ if (_foo::die(handle, !handle->load()))
+ return;
+
+ Dialog = (CTOR_FUNPTR) handle->resolve("GetDialog");
+ if (_foo::die(handle, !Dialog))
+ return;
+
+ Constructor = (CTOR_FUNPTR) handle->resolve("GetConstructor");
+ if (_foo::die(handle, !Constructor))
+ return;
+
+ Meta = (METADATA_FUNPTR) handle->resolve("GetMetadata");
+ if (_foo::die(handle, !Meta))
+ return;
+#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
+ );
+
+ struct _foo {
+ static bool err(void*& handle)
+ {
+ const char* err = dlerror();
+ if (err)
+ {
+ fprintf(stderr, "Error, ignoring: %s\n", err);
+ fflush(stderr);
+ dlclose(handle);
+ handle = nullptr;
+ return true;
+ }
+ return false;
+ }
+ };
+
+ if (handle)
+ {
+ if (_foo::err(handle))
+ return;
+ Dialog = (CTOR_FUNPTR) dlsym(handle, "GetDialog");
+ if (_foo::err(handle))
+ return;
+ Constructor = (CTOR_FUNPTR) dlsym(handle, "GetConstructor");
+ if (_foo::err(handle))
+ return;
+ Meta = (METADATA_FUNPTR) dlsym(handle, "GetMetadata");
+ if (_foo::err(handle))
+ return;
+ } else {
+ (void) _foo::err(handle);
+ }
+#endif
+
+ auto m = ptr<Metadata>(Meta());
+
+ icon = m->icon();
+ name = m->name();
+}
+
+dylib::~dylib()
+{
+#if defined(_WIN32)
+ handle->unload();
+#else
+ if (handle)
+ (void) dlclose(handle);
+#endif
+}
diff --git a/opentrack/plugin-support.h b/opentrack/plugin-support.h
new file mode 100644
index 00000000..238aeb53
--- /dev/null
+++ b/opentrack/plugin-support.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "plugin-api.hpp"
+
+#include <QWidget>
+#include <QDebug>
+#include <QString>
+#include <QLibrary>
+#include <QFrame>
+#include <QList>
+
+#include <memory>
+template<typename t> using ptr = std::shared_ptr<t>;
+
+extern "C" typedef void* (*CTOR_FUNPTR)(void);
+extern "C" typedef Metadata* (*METADATA_FUNPTR)(void);
+
+struct dylib {
+ enum Type { Filter, Tracker, Protocol };
+
+ dylib(const QString& filename, Type t);
+ ~dylib();
+ static QList<ptr<dylib>> enum_libraries();
+
+ Type type;
+ QString filename;
+
+ QIcon icon;
+ QString name;
+
+ CTOR_FUNPTR Dialog;
+ CTOR_FUNPTR Constructor;
+ METADATA_FUNPTR Meta;
+private:
+#if defined(_WIN32)
+ QLibrary* handle;
+#else
+ void* handle;
+#endif
+};
+
+struct SelectedLibraries {
+ using dylibtr = ptr<dylib>;
+ ptr<ITracker> pTracker;
+ ptr<IFilter> pFilter;
+ ptr<IProtocol> pProtocol;
+ SelectedLibraries(QFrame* frame, dylibtr t, dylibtr p, dylibtr f);
+ SelectedLibraries() : pTracker(nullptr), pFilter(nullptr), pProtocol(nullptr), correct(false) {}
+ ~SelectedLibraries();
+ bool correct;
+}; \ No newline at end of file
diff --git a/opentrack/pose.hpp b/opentrack/pose.hpp
new file mode 100644
index 00000000..41e984f5
--- /dev/null
+++ b/opentrack/pose.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <utility>
+#include <algorithm>
+#include "./quat.hpp"
+#include "./plugin-api.hpp"
+
+class Pose {
+private:
+ static constexpr double pi = 3.141592653;
+ static constexpr double d2r = pi/180.0;
+ static constexpr double r2d = 180./pi;
+
+ double axes[6];
+public:
+ Pose() : axes {0,0,0, 0,0,0 } {}
+
+ inline operator double*() { return axes; }
+ inline operator const double*() const { return axes; }
+
+ inline double& operator()(int i) { return axes[i]; }
+ inline double operator()(int i) const { return axes[i]; }
+
+ Quat quat() const
+ {
+ return Quat(axes[Yaw]*d2r, axes[Pitch]*d2r, axes[Roll]*d2r);
+ }
+
+ static Pose fromQuat(const Quat& q)
+ {
+ Pose ret;
+ q.to_euler_degrees(ret(Yaw), ret(Pitch), ret(Roll));
+ return ret;
+ }
+
+ Pose operator&(const Pose& B) const
+ {
+ const Quat q = quat() * B.quat().inv();
+ Pose ret = fromQuat(q);
+ for (int i = TX; i < TX + 3; i++)
+ ret(i) = axes[i] - B.axes[i];
+ return ret;
+ }
+};
diff --git a/opentrack/qcopyable-mutex.hpp b/opentrack/qcopyable-mutex.hpp
new file mode 100644
index 00000000..f7f36f93
--- /dev/null
+++ b/opentrack/qcopyable-mutex.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <QMutex>
+
+class MyMutex {
+private:
+ QMutex inner;
+
+public:
+ QMutex* operator->() { return &inner; }
+ QMutex* operator->() const { return &const_cast<MyMutex*>(this)->inner; }
+
+ MyMutex operator=(const MyMutex& datum)
+ {
+ auto mode =
+ datum->isRecursive()
+ ? QMutex::Recursive
+ : QMutex::NonRecursive;
+
+ return MyMutex(mode);
+ }
+
+ MyMutex(const MyMutex& datum)
+ {
+ *this = datum;
+ }
+
+ MyMutex(QMutex::RecursionMode mode = QMutex::NonRecursive) :
+ inner(mode)
+ {
+ }
+
+ QMutex* operator&()
+ {
+ return &inner;
+ }
+};
diff --git a/opentrack/quat.hpp b/opentrack/quat.hpp
new file mode 100644
index 00000000..6d777b28
--- /dev/null
+++ b/opentrack/quat.hpp
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+#pragma once
+#include <cmath>
+
+class Quat {
+private:
+ static constexpr double pi = 3.141592653;
+ static constexpr double r2d = 180./pi;
+ double a,b,c,d; // quaternion coefficients
+public:
+ Quat() : a(1.),b(0.),c(0.),d(0.) {}
+ Quat(double yaw, double pitch, double roll) { from_euler_rads(yaw, pitch, roll); }
+ Quat(double a, double b, double c, double d) : a(a),b(b),c(c),d(d) {}
+
+ Quat inv(){
+ return Quat(a,-b,-c, -d);
+ }
+
+ // conversions
+ // see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
+ void from_euler_rads(double yaw, double pitch, double roll)
+ {
+
+ const double sin_phi = sin(roll/2.);
+ const double cos_phi = cos(roll/2.);
+ const double sin_the = sin(pitch/2.);
+ const double cos_the = cos(pitch/2.);
+ const double sin_psi = sin(yaw/2.);
+ const double cos_psi = cos(yaw/2.);
+
+ 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 to_euler_rads(double& yaw, double& pitch, double& roll) const
+ {
+ roll = atan2(2.*(a*b + c*d), 1. - 2.*(b*b + c*c));
+ pitch = asin(2.*(a*c - b*d));
+ yaw = atan2(2.*(a*d + b*c), 1. - 2.*(c*c + d*d));
+ }
+
+ void to_euler_degrees(double& yaw, double& pitch, double& roll) const
+ {
+ to_euler_rads(yaw, pitch, roll);
+ yaw *= r2d;
+ pitch *= r2d;
+ roll *= r2d;
+ }
+
+ const Quat operator*(const Quat& B) const
+ {
+ const Quat& A = *this;
+ return Quat(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);
+ }
+};
diff --git a/opentrack/timer.hpp b/opentrack/timer.hpp
new file mode 100644
index 00000000..628365c9
--- /dev/null
+++ b/opentrack/timer.hpp
@@ -0,0 +1,68 @@
+#pragma once
+#include <ctime>
+#if defined (_WIN32)
+# include <windows.h>
+static inline void opentrack_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;
+}
+# define clock_gettime opentrack_clock_gettime
+#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);
+ long ret = conv(cur);
+ state = cur;
+ return ret;
+ }
+ long elapsed() {
+ struct timespec cur;
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+ return conv(cur);
+ }
+ long elapsed_ms() {
+ return elapsed() / 1000000L;
+ }
+};
diff --git a/opentrack/tracker.cpp b/opentrack/tracker.cpp
new file mode 100644
index 00000000..41db7c19
--- /dev/null
+++ b/opentrack/tracker.cpp
@@ -0,0 +1,187 @@
+/* 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 <opencv2/core/core.hpp>
+
+#include "./tracker.h"
+#include <cmath>
+#include <algorithm>
+
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+
+Tracker::Tracker(main_settings& s, Mappings &m, SelectedLibraries &libs) :
+ s(s),
+ m(m),
+ centerp(false),
+ enabledp(true),
+ should_quit(false),
+ libs(libs)
+{
+}
+
+Tracker::~Tracker()
+{
+ should_quit = true;
+ wait();
+}
+
+double Tracker::map(double pos, Mapping& axis) {
+ bool altp = (pos < 0) && axis.opts.altp;
+ axis.curve.setTrackingActive( !altp );
+ axis.curveAlt.setTrackingActive( altp );
+ auto& fc = altp ? axis.curveAlt : axis.curve;
+ double invert = axis.opts.invert ? -1 : 1;
+ return invert * (fc.getValue(pos) + axis.opts.zero);
+}
+
+static cv::Matx33d euler_to_rmat(const double* input)
+{
+ static constexpr double pi = 3.141592653;
+ const auto H = input[0] * pi / -180;
+ const auto P = input[1] * pi / -180;
+ const auto B = input[2] * 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,
+ };
+
+ return cv::Matx33d(foo);
+}
+
+void Tracker::t_compensate(const double* input, double* output, bool rz)
+{
+ const cv::Matx33d rmat = euler_to_rmat(&input[Yaw]);
+ const cv::Vec3d tvec(input);
+ const cv::Vec3d ret = rmat * tvec;
+
+ const int max = !rz ? 3 : 2;
+
+ for (int i = 0; i < max; i++)
+ output[i] = ret(i);
+}
+
+void Tracker::logic()
+{
+ libs.pTracker->data(newpose);
+
+ Pose final_raw;
+
+ if (enabledp)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ auto& axis = m(i);
+ int k = axis.opts.src;
+ if (k < 0 || k >= 6)
+ {
+ final_raw(i) = 0;
+ continue;
+ }
+ // not really raw, after axis remap -sh
+ final_raw(i) = newpose[k];
+ }
+ unstopped_raw = final_raw;
+ }
+
+ Pose filtered_pose;
+
+ if (libs.pFilter)
+ libs.pFilter->filter(final_raw, filtered_pose);
+ else
+ filtered_pose = final_raw;
+
+ if (centerp)
+ {
+ centerp = false;
+ raw_center = final_raw;
+ }
+
+ Pose raw_centered = filtered_pose & raw_center;
+
+ Pose mapped_pose_precomp;
+
+ for (int i = 0; i < 6; i++)
+ mapped_pose_precomp(i) = map(raw_centered(i), m(i));
+
+ Pose mapped_pose;
+
+ mapped_pose = mapped_pose_precomp;
+ if (s.tcomp_p)
+ t_compensate(mapped_pose_precomp, mapped_pose, s.tcomp_tz);
+
+ libs.pProtocol->pose(mapped_pose);
+
+ {
+ QMutexLocker foo(&mtx);
+ output_pose = mapped_pose;
+ raw_6dof = unstopped_raw;
+ }
+}
+
+void Tracker::run() {
+ const int sleep_ms = 3;
+
+#if defined(_WIN32)
+ (void) timeBeginPeriod(1);
+#endif
+
+ while (!should_quit)
+ {
+ t.start();
+
+ logic();
+
+ double q = sleep_ms * 1000L;
+ q -= t.elapsed();
+ q = std::max(0., q);
+ usleep((long)q);
+ }
+
+#if defined(_WIN32)
+ (void) timeEndPeriod(1);
+#endif
+
+ for (int i = 0; i < 6; i++)
+ {
+ m(i).curve.setTrackingActive(false);
+ m(i).curveAlt.setTrackingActive(false);
+ }
+}
+
+void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const {
+ QMutexLocker foo(&const_cast<Tracker&>(*this).mtx);
+ for (int i = 0; i < 6; i++)
+ {
+ raw[i] = raw_6dof(i);
+ mapped[i] = output_pose(i);
+ }
+}
+
diff --git a/opentrack/tracker.h b/opentrack/tracker.h
new file mode 100644
index 00000000..02d6bee2
--- /dev/null
+++ b/opentrack/tracker.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <atomic>
+#include <vector>
+
+#include "./timer.hpp"
+#include "./plugin-support.h"
+#include "./mappings.hpp"
+#include "./pose.hpp"
+
+#include "../qfunctionconfigurator/functionconfig.h"
+#include "./main-settings.hpp"
+#include "./options.hpp"
+
+#include <QMutex>
+#include <QThread>
+
+class Tracker : private QThread {
+ Q_OBJECT
+private:
+ QMutex mtx;
+ main_settings& s;
+ // XXX can be const-cast when functionconfig const-correct -sh 20141004
+ Mappings& m;
+
+ Timer t;
+ Pose output_pose, raw_6dof, raw_center, unstopped_raw;
+ double newpose[6];
+ std::atomic<bool> centerp;
+ std::atomic<bool> enabledp;
+ std::atomic<bool> should_quit;
+ SelectedLibraries const& libs;
+
+ double map(double pos, Mapping& axis);
+ void logic();
+
+ static void t_compensate(const double* input, double* output, bool rz);
+ void run() override;
+public:
+ Tracker(main_settings& s, Mappings& m, SelectedLibraries& libs);
+ ~Tracker();
+
+ void get_raw_and_mapped_poses(double* mapped, double* raw) const;
+ void start() { QThread::start(); }
+ void toggle_enabled() { enabledp.store(!enabledp.load()); }
+ void center() { centerp.store(!centerp.load()); }
+};