summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore12
-rw-r--r--3rdparty-notices/ARUCO-COPYING.txt35
-rw-r--r--3rdparty-notices/FACETRACKNOIR-COPYING.txt23
-rw-r--r--3rdparty-notices/GOOGLE-BREAKPAD-COPYING.txt29
-rw-r--r--3rdparty-notices/LIBOVR-COPYING.txt5
-rw-r--r--3rdparty-notices/LIBQXT-COPYING.txt40
-rw-r--r--3rdparty-notices/NFAILCLIENT-CREDIT.txt4
-rw-r--r--3rdparty-notices/OPENCV-COPYING.txt37
-rw-r--r--3rdparty-notices/QT5-COPYING.txt514
-rw-r--r--CMakeLists.txt599
-rw-r--r--FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui1790
-rw-r--r--FTNoIR_Tracker_PT/Resources/Logo_IR.pngbin0 -> 10386 bytes
-rw-r--r--FTNoIR_Tracker_PT/Resources/cap_front.pngbin0 -> 1164 bytes
-rw-r--r--FTNoIR_Tracker_PT/Resources/cap_side.pngbin0 -> 1733 bytes
-rw-r--r--FTNoIR_Tracker_PT/Resources/clip_front.pngbin0 -> 571 bytes
-rw-r--r--FTNoIR_Tracker_PT/Resources/clip_side.pngbin0 -> 2677 bytes
-rw-r--r--FTNoIR_Tracker_PT/boost-compat.h5
-rw-r--r--FTNoIR_Tracker_PT/camera.cpp351
-rw-r--r--FTNoIR_Tracker_PT/camera.h145
-rw-r--r--FTNoIR_Tracker_PT/doc/index.htm262
-rw-r--r--FTNoIR_Tracker_PT/doc/logo.pngbin0 -> 10386 bytes
-rw-r--r--FTNoIR_Tracker_PT/doc/ptrack.icobin0 -> 4286 bytes
-rw-r--r--FTNoIR_Tracker_PT/doc/settings1.pngbin0 -> 25013 bytes
-rw-r--r--FTNoIR_Tracker_PT/doc/settings2.pngbin0 -> 26841 bytes
-rw-r--r--FTNoIR_Tracker_PT/doc/settings3.pngbin0 -> 29547 bytes
-rw-r--r--FTNoIR_Tracker_PT/doc/style.css131
-rw-r--r--FTNoIR_Tracker_PT/frame_observer.cpp18
-rw-r--r--FTNoIR_Tracker_PT/frame_observer.h76
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp265
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt.h95
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt.qrc9
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp321
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h70
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.cpp42
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.h27
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h90
-rw-r--r--FTNoIR_Tracker_PT/point_extractor.cpp163
-rw-r--r--FTNoIR_Tracker_PT/point_extractor.h35
-rw-r--r--FTNoIR_Tracker_PT/point_tracker.cpp380
-rw-r--r--FTNoIR_Tracker_PT/point_tracker.h129
-rw-r--r--FTNoIR_Tracker_PT/pt_video_widget.cpp64
-rw-r--r--FTNoIR_Tracker_PT/pt_video_widget.h71
-rw-r--r--FTNoIR_Tracker_PT/trans_calib.cpp44
-rw-r--r--FTNoIR_Tracker_PT/trans_calib.h39
-rw-r--r--README.md64
-rw-r--r--TODO.txt29
-rwxr-xr-xbin/NPClient.dllbin0 -> 15872 bytes
-rwxr-xr-xbin/NPClient64.dllbin0 -> 19968 bytes
-rw-r--r--bin/TrackIR.exebin0 -> 386048 bytes
-rw-r--r--bin/cleye.config4
-rw-r--r--bin/settings/default.ini0
-rw-r--r--bin/settings/facetracknoir supported games.csv519
-rw-r--r--bin/tracker-ht/cleye.config4
-rw-r--r--bin/tracker-ht/license.txt32
-rw-r--r--bin/tracker-ht/thanks.txt5
-rw-r--r--cmake/GetGitRevisionDescription.cmake123
-rw-r--r--cmake/GetGitRevisionDescription.cmake.in38
-rw-r--r--compat/compat.cpp85
-rw-r--r--compat/compat.h49
-rw-r--r--compat/qt-bug-appeasement.cpp1
-rw-r--r--dinput/dinput8.libbin0 -> 19978 bytes
-rw-r--r--dinput/dxguid.libbin0 -> 105732 bytes
-rw-r--r--dinput/strmiids.libbin0 -> 272820 bytes
-rw-r--r--documentation/filemapping example.docbin0 -> 35840 bytes
-rw-r--r--documentation/qt 4.5 with visual studio 2.pdfbin0 -> 575691 bytes
-rw-r--r--documentation/setting up qt.pdfbin0 -> 20759 bytes
-rw-r--r--documentation/using shared memory for freetrack server.pdfbin0 -> 13514 bytes
-rw-r--r--documentation/working with qt visual studio plugin.pdfbin0 -> 314796 bytes
-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
-rw-r--r--freetrackclient/freetrackclient.cpp262
-rw-r--r--freetrackclient/freetrackclient.def6
-rw-r--r--ftnoir_csv/csv.cpp180
-rw-r--r--ftnoir_csv/csv.h45
-rw-r--r--ftnoir_filter_accela/accela-filter.qrc5
-rw-r--r--ftnoir_filter_accela/ftnoir_accela_filtercontrols.ui409
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela.cpp72
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela.h81
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela_dialog.cpp58
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela_dll.cpp7
-rw-r--r--ftnoir_filter_accela/images/filter-16-ac.pngbin0 -> 725 bytes
-rw-r--r--ftnoir_filter_base/ftnoir_filter_base.h29
-rw-r--r--ftnoir_filter_base/ftnoir_filter_base_global.h16
-rw-r--r--ftnoir_filter_ewma2/ewma-filter.qrc5
-rw-r--r--ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui478
-rw-r--r--ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp109
-rw-r--r--ftnoir_filter_ewma2/ftnoir_filter_ewma2.h98
-rw-r--r--ftnoir_filter_ewma2/ftnoir_filter_ewma2_dialog.cpp73
-rw-r--r--ftnoir_filter_ewma2/ftnoir_filter_ewma_dll.cpp31
-rw-r--r--ftnoir_filter_ewma2/images/filter-16.pngbin0 -> 642 bytes
-rw-r--r--ftnoir_filter_ewma2/images/filter-32.pngbin0 -> 1904 bytes
-rw-r--r--ftnoir_filter_kalman/ftnoir_filter_kalman.h69
-rw-r--r--ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui91
-rw-r--r--ftnoir_filter_kalman/images/filter-16-ac.pngbin0 -> 725 bytes
-rw-r--r--ftnoir_filter_kalman/kalman-filter.qrc5
-rw-r--r--ftnoir_filter_kalman/kalman.cpp136
-rw-r--r--ftnoir_posewidget/glwidget.cpp197
-rw-r--r--ftnoir_posewidget/glwidget.h97
-rw-r--r--ftnoir_posewidget/images/side1.pngbin0 -> 26449 bytes
-rw-r--r--ftnoir_posewidget/images/side6.pngbin0 -> 26493 bytes
-rw-r--r--ftnoir_posewidget/posewidget.qrc6
-rw-r--r--ftnoir_protocol_base/ftnoir_protocol_base.h57
-rw-r--r--ftnoir_protocol_base/ftnoir_protocol_base_global.h16
-rw-r--r--ftnoir_protocol_fg/fg-protocol.qrc5
-rw-r--r--ftnoir_protocol_fg/fgtypes.h27
-rw-r--r--ftnoir_protocol_fg/ftnoir_fgcontrols.ui143
-rw-r--r--ftnoir_protocol_fg/ftnoir_protocol_fg.cpp64
-rw-r--r--ftnoir_protocol_fg/ftnoir_protocol_fg.h96
-rw-r--r--ftnoir_protocol_fg/ftnoir_protocol_fg_dialog.cpp69
-rw-r--r--ftnoir_protocol_fg/ftnoir_protocol_fg_dll.cpp32
-rw-r--r--ftnoir_protocol_fg/images/flightgear.pngbin0 -> 979 bytes
-rw-r--r--ftnoir_protocol_fsuipc/fsuipc-protocol.qrc5
-rw-r--r--ftnoir_protocol_fsuipc/ftnoir_fsuipccontrols.ui134
-rw-r--r--ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp168
-rw-r--r--ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.h110
-rw-r--r--ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dialog.cpp62
-rw-r--r--ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dll.cpp31
-rw-r--r--ftnoir_protocol_fsuipc/images/fs9.pngbin0 -> 758 bytes
-rw-r--r--ftnoir_protocol_fsuipc/images/fs91.pngbin0 -> 1644 bytes
-rw-r--r--ftnoir_protocol_ft/ft-protocol.qrc5
-rw-r--r--ftnoir_protocol_ft/ftnoir_ftcontrols.ui223
-rw-r--r--ftnoir_protocol_ft/ftnoir_protocol_ft.cpp211
-rw-r--r--ftnoir_protocol_ft/ftnoir_protocol_ft.h120
-rw-r--r--ftnoir_protocol_ft/ftnoir_protocol_ft_dialog.cpp96
-rw-r--r--ftnoir_protocol_ft/ftnoir_protocol_ft_dll.cpp30
-rw-r--r--ftnoir_protocol_ft/fttypes.h86
-rw-r--r--ftnoir_protocol_ft/images/freetrack.pngbin0 -> 1773 bytes
-rw-r--r--ftnoir_protocol_ftn/ftnoir_ftncontrols.ui266
-rw-r--r--ftnoir_protocol_ftn/ftnoir_protocol_ftn.cpp55
-rw-r--r--ftnoir_protocol_ftn/ftnoir_protocol_ftn.h92
-rw-r--r--ftnoir_protocol_ftn/ftnoir_protocol_ftn_dialog.cpp62
-rw-r--r--ftnoir_protocol_ftn/ftnoir_protocol_ftn_dll.cpp31
-rw-r--r--ftnoir_protocol_libevdev/ftnoir_libevdev_controls.ui111
-rw-r--r--ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.cpp101
-rw-r--r--ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.h69
-rw-r--r--ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dialog.cpp26
-rw-r--r--ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dll.cpp16
-rw-r--r--ftnoir_protocol_libevdev/images/linux.pngbin0 -> 668 bytes
-rw-r--r--ftnoir_protocol_libevdev/libevdev-protocol.qrc5
-rw-r--r--ftnoir_protocol_mouse/ftnoir_mousecontrols.ui205
-rw-r--r--ftnoir_protocol_mouse/ftnoir_protocol_mouse.cpp67
-rw-r--r--ftnoir_protocol_mouse/ftnoir_protocol_mouse.h109
-rw-r--r--ftnoir_protocol_mouse/ftnoir_protocol_mouse_dialog.cpp69
-rw-r--r--ftnoir_protocol_mouse/ftnoir_protocol_mouse_dll.cpp31
-rw-r--r--ftnoir_protocol_mouse/images/mouse.pngbin0 -> 1169 bytes
-rw-r--r--ftnoir_protocol_mouse/win32-mouse-protocol.qrc5
-rw-r--r--ftnoir_protocol_sc/ftnoir-protocol-sc.rc4
-rw-r--r--ftnoir_protocol_sc/ftnoir_protocol_sc.cpp252
-rw-r--r--ftnoir_protocol_sc/ftnoir_protocol_sc.h157
-rw-r--r--ftnoir_protocol_sc/ftnoir_protocol_sc_dialog.cpp54
-rw-r--r--ftnoir_protocol_sc/ftnoir_protocol_sc_dll.cpp32
-rw-r--r--ftnoir_protocol_sc/ftnoir_sccontrols.ui72
-rw-r--r--ftnoir_protocol_sc/images/fsx.pngbin0 -> 813 bytes
-rw-r--r--ftnoir_protocol_sc/images/fsx1.pngbin0 -> 1920 bytes
-rw-r--r--ftnoir_protocol_sc/sc-protocol.qrc5
-rw-r--r--ftnoir_protocol_sc/scserver-acceleration.manifest13
-rw-r--r--ftnoir_protocol_sc/scserver-sp2.manifest13
-rw-r--r--ftnoir_protocol_sc/scserver.manifest13
-rw-r--r--ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.cpp35
-rw-r--r--ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.h125
-rw-r--r--ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dialog.cpp26
-rw-r--r--ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dll.cpp16
-rw-r--r--ftnoir_protocol_vjoy/ftnoir_vjoy_controls.ui113
-rw-r--r--ftnoir_protocol_vjoy/images/vjoy.pngbin0 -> 694 bytes
-rw-r--r--ftnoir_protocol_vjoy/vjoy-protocol.qrc5
-rw-r--r--ftnoir_protocol_vjoy/vjoy.def5
-rw-r--r--ftnoir_protocol_wine/ftnoir_protocol_wine.cpp76
-rw-r--r--ftnoir_protocol_wine/ftnoir_protocol_wine.h102
-rw-r--r--ftnoir_protocol_wine/ftnoir_protocol_wine_dialog.cpp39
-rw-r--r--ftnoir_protocol_wine/ftnoir_protocol_wine_dll.cpp25
-rw-r--r--ftnoir_protocol_wine/ftnoir_winecontrols.ui170
-rw-r--r--ftnoir_protocol_wine/images/wine.pngbin0 -> 376 bytes
-rw-r--r--ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx74
-rw-r--r--ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx8
-rw-r--r--ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx38
-rw-r--r--ftnoir_protocol_wine/wine-protocol.qrc5
-rw-r--r--ftnoir_protocol_wine/wine-shm.h11
-rw-r--r--ftnoir_tracker_aruco/ar_video_widget.cpp42
-rw-r--r--ftnoir_tracker_aruco/ar_video_widget.h47
-rw-r--r--ftnoir_tracker_aruco/aruco-tracker.qrc5
-rw-r--r--ftnoir_tracker_aruco/aruco-trackercontrols.ui390
-rw-r--r--ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp483
-rw-r--r--ftnoir_tracker_aruco/ftnoir_tracker_aruco.h94
-rw-r--r--ftnoir_tracker_aruco/ftnoir_tracker_aruco_dll.h19
-rw-r--r--ftnoir_tracker_aruco/images/aruco.pngbin0 -> 987 bytes
-rw-r--r--ftnoir_tracker_aruco/include/aruco.h134
-rw-r--r--ftnoir_tracker_aruco/include/arucofidmarkers.h119
-rw-r--r--ftnoir_tracker_aruco/include/board.h168
-rw-r--r--ftnoir_tracker_aruco/include/boarddetector.h139
-rw-r--r--ftnoir_tracker_aruco/include/cameraparameters.h137
-rw-r--r--ftnoir_tracker_aruco/include/cvdrawingutils.h52
-rw-r--r--ftnoir_tracker_aruco/include/exports.h46
-rw-r--r--ftnoir_tracker_aruco/include/marker.h143
-rw-r--r--ftnoir_tracker_aruco/include/markerdetector.h357
-rw-r--r--ftnoir_tracker_base/ftnoir_tracker_base.h65
-rw-r--r--ftnoir_tracker_base/ftnoir_tracker_base_global.h18
-rw-r--r--ftnoir_tracker_base/ftnoir_tracker_types.h4
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht.cpp291
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht.h78
-rw-r--r--ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h19
-rw-r--r--ftnoir_tracker_ht/headtracker-ftnoir.h24
-rw-r--r--ftnoir_tracker_ht/ht-api.h49
-rw-r--r--ftnoir_tracker_ht/ht-tracker.qrc5
-rw-r--r--ftnoir_tracker_ht/ht-trackercontrols.ui228
-rw-r--r--ftnoir_tracker_ht/ht_video_widget.cpp44
-rw-r--r--ftnoir_tracker_ht/ht_video_widget.h47
-rw-r--r--ftnoir_tracker_ht/images/ht.pngbin0 -> 2010 bytes
-rw-r--r--ftnoir_tracker_ht/stdafx.h13
-rw-r--r--ftnoir_tracker_hydra/ftnoir_hydra_clientcontrols.ui205
-rw-r--r--ftnoir_tracker_hydra/ftnoir_tracker_hydra.cpp73
-rw-r--r--ftnoir_tracker_hydra/ftnoir_tracker_hydra.h65
-rw-r--r--ftnoir_tracker_hydra/ftnoir_tracker_hydra_dialog.cpp34
-rw-r--r--ftnoir_tracker_hydra/ftnoir_tracker_hydra_dll.cpp38
-rw-r--r--ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp230
-rw-r--r--ftnoir_tracker_joystick/ftnoir_tracker_joystick.h104
-rw-r--r--ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui439
-rw-r--r--ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp66
-rw-r--r--ftnoir_tracker_joystick/ftnoir_tracker_joystick_dll.cpp28
-rw-r--r--ftnoir_tracker_rift/ftnoir_rift.qrc7
-rw-r--r--ftnoir_tracker_rift/ftnoir_rift_clientcontrols.ui296
-rw-r--r--ftnoir_tracker_rift/ftnoir_tracker_rift.cpp92
-rw-r--r--ftnoir_tracker_rift/ftnoir_tracker_rift.h83
-rw-r--r--ftnoir_tracker_rift/ftnoir_tracker_rift_dialog.cpp36
-rw-r--r--ftnoir_tracker_rift/ftnoir_tracker_rift_dll.cpp41
-rw-r--r--ftnoir_tracker_rift/images/medium.pngbin0 -> 5764 bytes
-rw-r--r--ftnoir_tracker_rift/images/rift_medium.pngbin0 -> 5764 bytes
-rw-r--r--ftnoir_tracker_rift/images/rift_small.pngbin0 -> 1212 bytes
-rw-r--r--ftnoir_tracker_rift/images/rift_tiny.pngbin0 -> 624 bytes
-rw-r--r--ftnoir_tracker_rift/images/small.pngbin0 -> 1212 bytes
-rw-r--r--ftnoir_tracker_rift/images/tiny.pngbin0 -> 624 bytes
-rw-r--r--ftnoir_tracker_udp/ftnoir_ftnclientcontrols.ui384
-rw-r--r--ftnoir_tracker_udp/ftnoir_tracker_udp.cpp83
-rw-r--r--ftnoir_tracker_udp/ftnoir_tracker_udp.h77
-rw-r--r--ftnoir_tracker_udp/ftnoir_tracker_udp_dialog.cpp58
-rw-r--r--ftnoir_tracker_udp/ftnoir_tracker_udp_dll.cpp52
-rwxr-xr-xinstall-fail-tool19
-rw-r--r--installer/opentrack-installer.iss52
-rw-r--r--opentrack-api/context.cpp112
-rw-r--r--opentrack-api/gnuc-version-script.txt12
-rw-r--r--opentrack-api/opentrack-guts.h57
-rw-r--r--opentrack-api/opentrack.h58
-rw-r--r--opentrack-api/trackers.cpp38
-rw-r--r--opentrack-version.h7
-rw-r--r--qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp111
-rw-r--r--qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h37
-rw-r--r--qfunctionconfigurator/functionconfig.cpp281
-rw-r--r--qfunctionconfigurator/functionconfig.h75
-rw-r--r--qfunctionconfigurator/qfunctionconfigurator.cpp413
-rw-r--r--qfunctionconfigurator/qfunctionconfigurator.h92
-rw-r--r--qxt-mini/QxtGlobalShortcut2
-rw-r--r--qxt-mini/plat/qxtglobalshortcut_mac.cpp265
-rw-r--r--qxt-mini/plat/qxtglobalshortcut_x11.cpp235
-rw-r--r--qxt-mini/qplatformnativeinterface.h99
-rw-r--r--qxt-mini/qxtglobal.h233
-rw-r--r--qxt-mini/qxtglobalshortcut.cpp224
-rw-r--r--qxt-mini/qxtglobalshortcut.h64
-rw-r--r--qxt-mini/qxtglobalshortcut_p.h81
-rw-r--r--x-plane-plugin/plugin.c152
-rw-r--r--x-plane-plugin/version-script.txt10
306 files changed, 28254 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..9c4f50ff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+/CMakeLists.txt.user
+*~
+/build*
+/install
+/bin/tracker-ht/headtracker-ftnoir.exe
+/bin/tracker-ht/libgcc_s_dw2-1.dll
+/bin/tracker-ht/libstdc++-6.dll
+/bin/tracker-ht/bounding-box.raw
+/bin/tracker-ht/flandmark_model.dat
+/bin/tracker-ht/head.raw
+/installer/Output
+/nbproject/
diff --git a/3rdparty-notices/ARUCO-COPYING.txt b/3rdparty-notices/ARUCO-COPYING.txt
new file mode 100644
index 00000000..24e57080
--- /dev/null
+++ b/3rdparty-notices/ARUCO-COPYING.txt
@@ -0,0 +1,35 @@
+The ARUCO Library has been developed by the Ava group of the Univeristy of Cordoba(Spain)
+Contact to Rafael Muñoz Salinas <rmsalinas@uco.es>
+
+-----------------------------------------------------------------------
+
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
diff --git a/3rdparty-notices/FACETRACKNOIR-COPYING.txt b/3rdparty-notices/FACETRACKNOIR-COPYING.txt
new file mode 100644
index 00000000..befeafb5
--- /dev/null
+++ b/3rdparty-notices/FACETRACKNOIR-COPYING.txt
@@ -0,0 +1,23 @@
+********************************************************************************
+* 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/>. *
+*********************************************************************************
diff --git a/3rdparty-notices/GOOGLE-BREAKPAD-COPYING.txt b/3rdparty-notices/GOOGLE-BREAKPAD-COPYING.txt
new file mode 100644
index 00000000..80f54ae2
--- /dev/null
+++ b/3rdparty-notices/GOOGLE-BREAKPAD-COPYING.txt
@@ -0,0 +1,29 @@
+Copyright (c) 2006, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/3rdparty-notices/LIBOVR-COPYING.txt b/3rdparty-notices/LIBOVR-COPYING.txt
new file mode 100644
index 00000000..e23c0490
--- /dev/null
+++ b/3rdparty-notices/LIBOVR-COPYING.txt
@@ -0,0 +1,5 @@
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form. \ No newline at end of file
diff --git a/3rdparty-notices/LIBQXT-COPYING.txt b/3rdparty-notices/LIBQXT-COPYING.txt
new file mode 100644
index 00000000..252b76ba
--- /dev/null
+++ b/3rdparty-notices/LIBQXT-COPYING.txt
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
diff --git a/3rdparty-notices/NFAILCLIENT-CREDIT.txt b/3rdparty-notices/NFAILCLIENT-CREDIT.txt
new file mode 100644
index 00000000..7a69b0ae
--- /dev/null
+++ b/3rdparty-notices/NFAILCLIENT-CREDIT.txt
@@ -0,0 +1,4 @@
+NFailClient is a clean-room implementation of NFail LLC NFailClient.
+
+Completed as an operation spanning the whole Europe, NFailClient was
+written by gnomes, halflings and rakshasa. \ No newline at end of file
diff --git a/3rdparty-notices/OPENCV-COPYING.txt b/3rdparty-notices/OPENCV-COPYING.txt
new file mode 100644
index 00000000..8824228d
--- /dev/null
+++ b/3rdparty-notices/OPENCV-COPYING.txt
@@ -0,0 +1,37 @@
+IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+
+ By downloading, copying, installing or using the software you agree to this license.
+ If you do not agree to this license, do not download, install,
+ copy or use the software.
+
+
+ License Agreement
+ For Open Source Computer Vision Library
+
+Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
+Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
+Third party copyrights are property of their respective owners.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * The name of the copyright holders may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as is" and
+any express or implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are disclaimed.
+In no event shall the Intel Corporation or contributors be liable for any direct,
+indirect, incidental, special, exemplary, or consequential damages
+(including, but not limited to, procurement of substitute goods or services;
+loss of use, data, or profits; or business interruption) however caused
+and on any theory of liability, whether in contract, strict liability,
+or tort (including negligence or otherwise) arising in any way out of
+the use of this software, even if advised of the possibility of such damage.
diff --git a/3rdparty-notices/QT5-COPYING.txt b/3rdparty-notices/QT5-COPYING.txt
new file mode 100644
index 00000000..b2e7909d
--- /dev/null
+++ b/3rdparty-notices/QT5-COPYING.txt
@@ -0,0 +1,514 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+
+ The Qt Toolkit is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Contact: http://www.qt-project.org/legal
+
+ You may use, distribute and copy the Qt GUI Toolkit under the terms of
+ GNU Lesser General Public License version 2.1, which is displayed below.
+
+-------------------------------------------------------------------------
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..2b2e9f99
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,599 @@
+project(opentrack)
+cmake_minimum_required(VERSION 2.8)
+
+include(CMakeParseArguments)
+
+set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/cmake/")
+include(GetGitRevisionDescription)
+include(FindPkgConfig)
+find_package(Git QUIET)
+if(GIT_FOUND)
+ git_describe(OPENTRACK__COMMIT --tags --always)
+endif()
+
+include_directories(${CMAKE_SOURCE_DIR})
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ set(CMAKE_COMPILER_IS_GNUCC TRUE)
+ set(CMAKE_COMPILER_IS_GNUCXX TRUE)
+endif()
+
+if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
+ if(MINGW)
+ set(version-script mingw)
+ else()
+ set(version-script posix)
+ endif()
+endif()
+
+if(APPLE)
+ set(apple-frameworks "-stdlib=libc++ -framework Cocoa -framework CoreFoundation -lobjc -lz -framework Carbon")
+ set(CMAKE_SHARED_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_SHARED_LINKER_FLAGS}")
+ set(CMAKE_STATIC_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_STATIC_LINKER_FLAGS}")
+ set(CMAKE_EXE_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_EXE_LINKER_FLAGS}")
+ set(CMAKE_MODULE_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_MODULE_LINKER_FLAGS}")
+ set(CMAKE_CXX_FLAGS " -stdlib=libc++ -std=c++11 ${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+endif()
+
+SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+SET(CMAKE_SKIP_INSTALL_RPATH FALSE)
+SET(CMAKE_SKIP_RPATH FALSE)
+SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}")
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+add_definitions(-DOPENTRACK_API -DIN_OPENTRACK)
+
+if(MSVC)
+ add_definitions(-DNOMINMAX)
+endif()
+
+if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
+ add_definitions(-std=c++11)
+endif()
+
+if(WIN32 AND MSVC)
+ set(SDK_GOOGLE_BREAKPAD "" CACHE PATH "google-breakpad for crash reporting")
+endif()
+
+if(MINGW)
+ set(SDK_MINGW_PREFIX "" CACHE PATH "mingw prefix")
+endif()
+
+if(SDK_GOOGLE_BREAKPAD AND WIN32)
+ add_definitions(-DOPENTRACK_BREAKPAD)
+ include_directories("${SDK_GOOGLE_BREAKPAD}/src/client/windows/handler")
+ include_directories("${SDK_GOOGLE_BREAKPAD}/src/")
+endif()
+
+if(WIN32 AND DEFINED MSVC_VERSION AND NOT ${MSVC_VERSION} LESS 1700)
+ find_path (WIN8_SDK_ROOT_DIR
+ Include/um/windows.h
+ PATHS
+ "$ENV{ProgramFiles}/Windows Kits/8.0"
+ "$ENV{ProgramFiles(x86)}/Windows Kits/8.0"
+ DOC "Windows 8 SDK root directory"
+ )
+
+ if(WIN8_SDK_ROOT_DIR)
+ SET(CMAKE_LIBRARY_PATH "${WIN8_SDK_ROOT_DIR}/Lib/win8/um/x86")
+ endif()
+endif()
+
+if(WIN32 AND DEFINED MSVC_VERSION AND NOT ${MSVC_VERSION} LESS 1700)
+ SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
+ SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
+ SET (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
+endif()
+
+if(UNIX)
+ set(SDK_ENABLE_LIBEVDEV FALSE CACHE BOOL "libevdev virtual joystick protocol support (probably Linux only)")
+endif()
+if(WIN32)
+ SET(SDK_CONSOLE_DEBUG FALSE CACHE BOOL "Console build")
+endif()
+
+IF(WIN32)
+ SET(SDK_VJOY "" CACHE PATH "VJoy SDK path")
+ENDIF()
+SET(SDK_HYDRA "" CACHE PATH "libSixense path")
+SET(SDK_HYDRA_AMD64 FALSE CACHE BOOL "whether target is amd64 (else ia-32)")
+
+SET(SDK_RIFT "" CACHE PATH "libOVR path")
+
+include_directories(${CMAKE_SOURCE_DIR})
+
+if(MINGW)
+ # qt scripts are broken
+ set(Qt5Gui_user32_LIBRARY ${SDK_MINGW_PREFIX}/mingw/lib/libuser32.a)
+ set(Qt5Gui_opengl32_LIBRARY ${SDK_MINGW_PREFIX}/mingw/lib/libopengl32.a)
+ set(Qt5Gui_glu32_LIBRARY ${SDK_MINGW_PREFIX}/mingw/lib/libglu32.a)
+ set(Qt5Gui_gdi32_LIBRARY ${SDK_MINGW_PREFIX}/mingw/lib/libgdi32.a)
+endif()
+
+find_package(OpenCV REQUIRED)
+
+find_package(Qt5 REQUIRED COMPONENTS Core Xml Network Widgets Gui ${maybe-serial-port} QUIET)
+cmake_policy(SET CMP0020 NEW)
+include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS})
+add_definitions(${Qt5Core_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Gui_DEFINITIONS} ${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS})
+
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/qfunctionconfigurator)
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/ftnoir_posewidget)
+
+set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Path to Aruco static library")
+
+SET(SDK_OPENCV_STATIC FALSE CACHE BOOL "Whether OpenCV is statically linked")
+if(WIN32)
+ set(SDK_SIMCONNECT "" CACHE PATH "Path to SimConnect SDK")
+ set(SDK_DIRECTX "" CACHE PATH "Path to DirectX SDK")
+ set(SDK_FSUIPC "" CACHE PATH "Path to FSUIPC")
+ if(SDK_DIRECTX)
+ include_directories("${SDK_DIRECTX}/Include")
+ link_directories("${SDK_DIRECTX}/Lib")
+ endif()
+endif()
+
+if(NOT WIN32)
+ set(SDK_WINE_PREFIX "" CACHE PATH "Path where Wine is installed")
+ set(SDK_WINE_NO_WRAPPER FALSE CACHE BOOL "Don't build wrapper, for instance X-Plane is native Linux app")
+endif()
+IF("${CMAKE_SYSTEM}" MATCHES "Linux" OR APPLE)
+ set(SDK_XPLANE "" CACHE PATH "Path to X-Plane SDK")
+endif()
+
+if(SDK_XPLANE)
+ INCLUDE_DIRECTORIES("${SDK_XPLANE}/CHeaders" "${SDK_XPLANE}/CHeaders/XPLM")
+endif()
+
+if(WIN32)
+ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC)
+ set(CMAKE_RC_COMPILER_INIT i686-w64-mingw32-windres)
+ SET(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
+ endif()
+ ENABLE_LANGUAGE(RC)
+endif(WIN32)
+
+if(SDK_FSUIPC AND WIN32)
+ include_directories("${SDK_FSUIPC}")
+ link_directories("${SDK_FSUIPC}")
+endif()
+
+# Qxt bundled :: sorry for this, but gentoo ebuild is broken
+if(APPLE)
+ set(qxt-mini-c qxt-mini/plat/qxtglobalshortcut_mac.cpp qxt-mini/qxtglobalshortcut.cpp)
+ include_directories("qxt-mini/")
+else()
+ if(UNIX)
+ set(qxt-mini-c qxt-mini/plat/qxtglobalshortcut_x11.cpp qxt-mini/qxtglobalshortcut.cpp)
+ include_directories("qxt-mini/")
+ endif()
+endif()
+
+# qt being broken as usual
+set(EXTRA-MOCS "${CMAKE_SOURCE_DIR}/facetracknoir/options.h")
+
+function(link_with_dinput8 n)
+ if(WIN32)
+ if(MSVC)
+ target_link_libraries(${n}
+ "${CMAKE_SOURCE_DIR}/dinput/dinput8.lib"
+ "${CMAKE_SOURCE_DIR}/dinput/dxguid.lib"
+ "${CMAKE_SOURCE_DIR}/dinput/strmiids.lib"
+ uuid)
+ else()
+ target_link_libraries(${n} dinput8 dxguid strmiids)
+ endif()
+ endif()
+endfunction()
+
+macro(opentrack_module n dir)
+ file(GLOB ${n}-c "${dir}/*.cpp" "${dir}/*.h" "${dir}/*.rc" "${dir}/*.hpp" ${EXTRA-MOCS})
+ file(GLOB ${n}-ui "${dir}/*.ui")
+ file(GLOB ${n}-rc "${dir}/*.qrc")
+ QT5_WRAP_UI(${n}-uih ${${n}-ui})
+ QT5_ADD_RESOURCES(${n}-rcc ${${n}-rc})
+endmacro()
+
+macro(opentrack_library n)
+ cmake_parse_arguments(foolib "" "LINK;COMPILE" "" ${ARGN})
+ if(NOT " ${foolib_UNPARSED_ARGUMENTS}" STREQUAL " ")
+ message(FATAL_ERROR "opentrack_library bad formals")
+ endif()
+ add_library(${n} SHARED ${${n}-c} ${${n}-uih} ${${n}-rcc})
+ target_link_libraries(${n} ${MY_QT_LIBS})
+ if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
+ SET_TARGET_PROPERTIES(${n} PROPERTIES
+ LINK_FLAGS "${foolib_LINK} -Wl,--version-script=${CMAKE_SOURCE_DIR}/facetracknoir/${version-script}-version-script.txt"
+ COMPILE_FLAGS "${foolib_COMPILE}"
+ )
+ else()
+ set_target_properties(${n} PROPERTIES LINK_FLAGS "${foolib_LINK}" COMPILE_FLAGS "${foolib_COMPILE}")
+ endif()
+ install(TARGETS ${n} RUNTIME DESTINATION . LIBRARY DESTINATION .)
+endmacro()
+
+file(GLOB opentrack-lib-c "opentrack-api/*.cpp" "facetracknoir/global-settings.cpp" "opentrack-api/*.h" "facetracknoir/global-settings.h")
+
+opentrack_module(opentrack-bin facetracknoir)
+opentrack_module(opentrack-pose-widget ftnoir_posewidget)
+opentrack_module(opentrack-spline-widget qfunctionconfigurator)
+
+# filters
+
+opentrack_module(opentrack-filter-accela ftnoir_filter_accela)
+opentrack_module(opentrack-filter-kalman ftnoir_filter_kalman)
+opentrack_module(opentrack-filter-ewma ftnoir_filter_ewma2)
+
+# protocols
+
+opentrack_module(opentrack-proto-fgfs ftnoir_protocol_fg)
+opentrack_module(opentrack-proto-fsuipc ftnoir_protocol_fsuipc)
+opentrack_module(opentrack-proto-freetrack ftnoir_protocol_ft)
+opentrack_module(opentrack-proto-udp ftnoir_protocol_ftn)
+opentrack_module(opentrack-proto-wine ftnoir_protocol_wine)
+opentrack_module(opentrack-proto-win32-mouse ftnoir_protocol_mouse)
+opentrack_module(opentrack-proto-simconnect ftnoir_protocol_sc)
+opentrack_module(opentrack-proto-vjoy ftnoir_protocol_vjoy)
+opentrack_module(opentrack-proto-libevdev ftnoir_protocol_libevdev)
+
+# trackers
+
+opentrack_module(opentrack-tracker-ht ftnoir_tracker_ht)
+opentrack_module(opentrack-tracker-aruco ftnoir_tracker_aruco)
+opentrack_module(opentrack-tracker-pt FTNoIR_Tracker_PT)
+opentrack_module(opentrack-tracker-udp ftnoir_tracker_udp)
+opentrack_module(opentrack-tracker-joystick ftnoir_tracker_joystick)
+opentrack_module(opentrack-tracker-rift ftnoir_tracker_rift)
+opentrack_module(opentrack-tracker-hydra ftnoir_tracker_hydra)
+
+file(GLOB opentrack-csv-c "ftnoir_csv/*.cpp" "ftnoir_csv/*.h")
+
+# compat lib for POSIX/win32
+
+file(GLOB opentrack-compat-c "compat/*.cpp" "compat/*.h")
+
+# x-plane plugin
+file(GLOB opentrack-xplane-plugin-c "x-plane-plugin/*.c")
+
+# freetrack
+
+file(GLOB opentrack-freetrack-c "freetrackclient/*.cpp")
+
+if(SDK_XPLANE)
+ # probably librt already included
+ add_library(opentrack-xplane-plugin SHARED ${opentrack-xplane-plugin-c})
+ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC AND NOT APPLE)
+ SET_TARGET_PROPERTIES(opentrack-xplane-plugin
+ PROPERTIES LINK_FLAGS
+ "-Wl,--version-script=${CMAKE_SOURCE_DIR}/x-plane-plugin/version-script.txt -shared -rdynamic -nodefaultlibs -undefined_warning -fPIC"
+ COMPILE_FLAGS "-Wall -O2 -pipe -fPIC -DLIN -DXPLM210"
+ LIBRARY_OUTPUT_NAME "opentrack.xpl"
+ PREFIX "" SUFFIX "")
+ endif()
+ if(APPLE)
+ SET_TARGET_PROPERTIES(opentrack-xplane-plugin PROPERTIES
+ COMPILE_FLAGS "-iframework ${SDK_XPLANE}/Libraries/Mac/ -DAPL -DXPLM210 -framework XPLM -framework XPWidgets"
+ LINK_FLAGS "-F${SDK_XPLANE}/Libraries/Mac/ -framework XPLM -framework XPWidgets")
+ endif()
+ if(UNIX AND NOT APPLE)
+ target_link_libraries(opentrack-xplane-plugin rt)
+ endif()
+endif()
+
+# some boilerplate
+
+if(QT_USE_FILE)
+ INCLUDE(${QT_USE_FILE})
+endif()
+
+add_library(opentrack-compat SHARED ${opentrack-compat-c})
+if(NOT WIN32 AND NOT APPLE)
+ target_link_libraries(opentrack-compat rt)
+endif()
+
+# hack to avoid breakage on buildbot
+set(my-qt-deps)
+if(WIN32)
+ set(my-qt-deps ws2_32)
+endif()
+
+set(MY_QT_LIBS ${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Core_LIBRARIES} ${my-qt-deps})
+
+add_library(opentrack-csv SHARED ${opentrack-csv-c})
+target_link_libraries(opentrack-csv ${MY_QT_LIBS})
+
+add_library(opentrack-pose-widget SHARED ${opentrack-pose-widget-c} ${opentrack-pose-widget-rcc})
+target_link_libraries(opentrack-pose-widget ${MY_QT_LIBS})
+add_library(opentrack-spline-widget SHARED ${opentrack-spline-widget-c})
+target_link_libraries(opentrack-spline-widget ${MY_QT_LIBS})
+
+opentrack_library(opentrack-filter-accela)
+opentrack_library(opentrack-filter-kalman)
+opentrack_library(opentrack-filter-ewma)
+
+target_link_libraries(opentrack-filter-kalman ${OpenCV_LIBS})
+
+opentrack_library(opentrack-proto-fgfs)
+
+if(SDK_VJOY)
+ include_directories(${SDK_VJOY})
+ opentrack_library(opentrack-proto-vjoy)
+ if(MSVC)
+ target_link_libraries(opentrack-proto-vjoy ${MY_QT_LIBS} "${SDK_VJOY}/VJoy.lib")
+ else()
+ target_link_libraries(opentrack-proto-vjoy ${MY_QT_LIBS} "${SDK_VJOY}/VJoy.dll")
+ endif()
+endif()
+
+if(SDK_ENABLE_LIBEVDEV)
+ opentrack_library(opentrack-proto-libevdev)
+ pkg_check_modules(libevdev REQUIRED QUIET libevdev)
+ include_directories(${libevdev_INCLUDE_DIRS})
+ target_link_libraries(opentrack-proto-libevdev ${libevdev_LIBRARIES})
+endif()
+
+if(SDK_FSUIPC)
+ if(MSVC)
+ set(link-flags "/NODEFAULTLIB:libc")
+ endif()
+ opentrack_library(opentrack-proto-fsuipc LINK "${link-flags}")
+ target_link_libraries(opentrack-proto-fsuipc "${SDK_FSUIPC}/FSUIPC_User.lib")
+endif()
+
+if(SDK_SIMCONNECT)
+ opentrack_library(opentrack-proto-simconnect)
+ include_directories("${SDK_SIMCONNECT}/inc")
+ target_link_libraries(opentrack-proto-simconnect "${SDK_SIMCONNECT}/lib/SimConnect.lib")
+endif()
+
+if(WIN32)
+ opentrack_library(opentrack-proto-freetrack)
+ target_link_libraries(opentrack-proto-freetrack opentrack-csv opentrack-compat)
+ opentrack_library(opentrack-proto-win32-mouse)
+endif()
+
+if(WIN32)
+ add_library(freetrackclient SHARED ${opentrack-freetrack-c})
+ if(CMAKE_COMPILER_IS_GNUCC)
+ set_target_properties(freetrackclient PROPERTIES LINK_FLAGS "-Wl,--enable-stdcall-fixup -fno-lto -Wl,-kill-at" PREFIX "" COMPILE_FLAGS "-fno-lto")
+ endif()
+endif()
+
+opentrack_library(opentrack-proto-udp)
+
+if(WIN32)
+ opentrack_library(opentrack-tracker-joystick)
+endif()
+
+if(SDK_WINE_PREFIX)
+ opentrack_library(opentrack-proto-wine)
+ target_link_libraries(opentrack-proto-wine opentrack-compat opentrack-csv)
+ if(NOT SDK_WINE_NO_WRAPPER)
+ set(my-rt -lrt)
+ if(APPLE)
+ set(my-rt)
+ endif()
+ add_custom_command(
+ OUTPUT opentrack-wrapper-wine.exe.so
+ DEPENDS "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx"
+ "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx"
+ "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx"
+ COMMAND "${SDK_WINE_PREFIX}/bin/wineg++" -g -O2 -m32 -o
+ opentrack-wrapper-wine.exe -I "${CMAKE_SOURCE_DIR}"
+ "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx"
+ "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx"
+ "${CMAKE_SOURCE_DIR}/ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx"
+ ${my-rt})
+ add_custom_target(wine-wrapper ALL DEPENDS opentrack-wrapper-wine.exe.so)
+ add_dependencies(wine-wrapper opentrack-compat opentrack-proto-wine)
+ endif()
+endif()
+
+opentrack_library(opentrack-tracker-ht)
+target_link_libraries(opentrack-tracker-ht opentrack-compat)
+
+if(SDK_ARUCO_LIBPATH)
+ include_directories(${CMAKE_SOURCE_DIR}/ftnoir_tracker_aruco/include)
+ opentrack_library(opentrack-tracker-aruco)
+ target_link_libraries(opentrack-tracker-aruco ${SDK_ARUCO_LIBPATH} ${OpenCV_LIBS})
+ if(WIN32 AND MSVC)
+ target_link_libraries(opentrack-tracker-aruco
+ "${CMAKE_SOURCE_DIR}/dinput/dxguid.lib"
+ "${CMAKE_SOURCE_DIR}/dinput/strmiids.lib"
+ uuid)
+ endif()
+endif()
+
+link_with_dinput8(opentrack-tracker-ht)
+link_with_dinput8(opentrack-tracker-joystick)
+
+opentrack_library(opentrack-tracker-pt)
+target_link_libraries(opentrack-tracker-pt ${OpenCV_LIBS})
+
+link_with_dinput8(opentrack-tracker-pt)
+
+opentrack_library(opentrack-tracker-udp)
+
+if(SDK_RIFT)
+ include_directories("${SDK_RIFT}/Include")
+ include_directories("${SDK_RIFT}/Src")
+ set(link-flags)
+ set(c-flags)
+ if(APPLE)
+ set(link-flags "-framework CoreFoundation -framework CoreGraphics -framework IOKit -framework Quartz")
+ set(c-flags "-fno-strict-aliasing")
+ else()
+ if(MSVC)
+ set(link-flags "/NODEFAULTLIB:LIBCMT")
+ else()
+ set(c-flags "-fno-strict-aliasing")
+ endif()
+ endif()
+ opentrack_library(opentrack-tracker-rift LINK "${link-flags}" COMPILE "${c-flags}")
+ if(MSVC)
+ target_link_libraries(opentrack-tracker-rift "${SDK_RIFT}/Lib/Win32/libovr.lib" winmm.lib setupapi.lib)
+ else()
+ if(WIN32)
+ target_link_libraries(opentrack-tracker-rift "${SDK_RIFT}/libLibOVR.a" winmm setupapi)
+ else()
+ if(NOT APPLE)
+ target_link_libraries(opentrack-tracker-rift "${SDK_RIFT}/libLibOVR.a" udev Xinerama)
+ else()
+ target_link_libraries(opentrack-tracker-rift "${SDK_RIFT}/libLibOVR.a")
+ endif()
+ endif()
+ endif()
+endif()
+
+if(SDK_HYDRA)
+ include_directories("${SDK_HYDRA}/include")
+ include_directories("${SDK_HYDRA}/include/sixense_utils")
+ opentrack_library(opentrack-tracker-hydra)
+ if(WIN32)
+ target_link_libraries(opentrack-tracker-hydra
+ "${SDK_HYDRA}/lib/win32/release_dll/sixense.lib"
+ "${SDK_HYDRA}/lib/win32/release_dll/sixense_utils.lib")
+ install(FILES "${SDK_HYDRA}/bin/win32/release_dll/sixense.dll" "${SDK_HYDRA}/bin/win32/release_dll/sixense_utils.dll" DESTINATION .)
+ else()
+ if(SDK_HYDRA_AMD64)
+ set(underscore-sixtyfour _x64)
+ else()
+ set(underscore-sixtyfour)
+ endif()
+ if(APPLE)
+ set(underscore-dll _dll)
+ set(soext dylib)
+ set(sixense-plat osx)
+ else()
+ set(underscore-dll)
+ set(soext so)
+ set(sixense-plat linux)
+ endif()
+ install(FILES
+ "${SDK_HYDRA}/lib/${sixense-plat}${underscore-sixtyfour}/release${underscore-dll}/libsixense${underscore-sixtyfour}.${soext}"
+ "${SDK_HYDRA}/lib/${sixense-plat}${underscore-sixtyfour}/release${underscore-dll}/libsixense_utils${underscore-sixtyfour}.${soext}"
+ DESTINATION .
+ )
+ target_link_libraries(opentrack-tracker-hydra "${SDK_HYDRA}/lib/${sixense-plat}${underscore-sixtyfour}/release${underscore-dll}/libsixense${underscore-sixtyfour}.${soext}" "${SDK_HYDRA}/lib/${sixense-plat}${underscore-sixtyfour}/release${underscore-dll}/libsixense_utils${underscore-sixtyfour}.${soext}")
+ endif()
+endif()
+
+if(WIN32 AND NOT SDK_CONSOLE_DEBUG)
+ set(opentrack-win32-executable WIN32)
+else()
+ set(opentrack-win32-executable "")
+endif()
+if(UNIX OR APPLE)
+ add_library(opentrack-qxt-mini SHARED ${qxt-mini-c})
+ SET_TARGET_PROPERTIES(opentrack-qxt-mini PROPERTIES COMPILE_FLAGS "-DBUILD_QXT_CORE=42 -DBUILD_QXT_WIDGETS=42 -DBUILD_QXT_GUI=42")
+ target_link_libraries(opentrack-qxt-mini ${MY_QT_LIBS})
+ if(NOT APPLE)
+ target_link_libraries(opentrack-qxt-mini X11)
+ endif()
+endif()
+add_executable(opentrack ${opentrack-win32-executable} ${opentrack-bin-c} ${opentrack-bin-uih} ${opentrack-bin-rcc})
+set_target_properties(opentrack PROPERTIES COMPILE_DEFINITIONS OPENTRACK_VERSION=\"${OPENTRACK__COMMIT}\")
+set(OPENTRACK_COMMIT_VERSION \"${OPENTRACK__COMMIT}\")
+configure_file("${CMAKE_SOURCE_DIR}/opentrack-version.h" "${CMAKE_BINARY_DIR}/opentrack-version.h" @ONLY NEWLINE_STYLE UNIX)
+
+if(APPLE)
+ SET_TARGET_PROPERTIES(opentrack-qxt-mini PROPERTIES LINK_FLAGS "-framework Carbon -framework CoreFoundation")
+endif()
+
+add_library(opentrack-api SHARED ${opentrack-lib-c})
+target_link_libraries(opentrack-api ${MY_QT_LIBS})
+if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
+ SET_TARGET_PROPERTIES(opentrack-api PROPERTIES
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/opentrack-api/gnuc-version-script.txt"
+ COMPILE_FLAGS "-fvisibility=protected -fvisibility-inlines-hidden"
+ COMPILE_DEFINITIONS IN_OPENTRACK_API
+ )
+endif()
+
+set_target_properties(opentrack PROPERTIES COMPILE_DEFINITIONS OPENTRACK_VERSION=\"${OPENTRACK__COMMIT}\")
+
+if(UNIX OR APPLE)
+ target_link_libraries(opentrack opentrack-qxt-mini)
+endif()
+target_link_libraries(opentrack ${OpenCV_LIBS})
+include_directories(${OpenCV_INCLUDE_DIRS})
+
+if(UNIX OR APPLE)
+ install(TARGETS opentrack-qxt-mini RUNTIME DESTINATION . LIBRARY DESTINATION . )
+endif()
+
+link_with_dinput8(opentrack)
+
+if(CMAKE_SYSTEM STREQUAL LINUX)
+ link_libraries(rt)
+endif()
+
+if(MSVC)
+ SET_TARGET_PROPERTIES(opentrack PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup")
+endif()
+target_link_libraries(opentrack opentrack-pose-widget opentrack-spline-widget ${MY_QT_LIBS} ${QXT_QXTCORE_LIB_RELEASE} ${QXT_QXTWIDGETS_LIB_RELEASE})
+if(NOT WIN32)
+ target_link_libraries(opentrack dl)
+ target_link_libraries(opentrack-api dl)
+endif()
+if(SDK_GOOGLE_BREAKPAD)
+ if(MSVC)
+ target_link_libraries(opentrack
+ "${SDK_GOOGLE_BREAKPAD}/src/client/windows/Release/lib/crash_generation_client.lib"
+ "${SDK_GOOGLE_BREAKPAD}/src/client/windows/Release/lib/exception_handler.lib"
+ "${SDK_GOOGLE_BREAKPAD}/src/client/windows/Release/lib/common.lib")
+ endif()
+endif()
+set_target_properties(opentrack PROPERTIES COMPILE_FLAGS -DOPENTRACK_MAIN)
+
+# make install
+
+install(FILES "${CMAKE_SOURCE_DIR}/README.md" DESTINATION .)
+
+if(SDK_XPLANE)
+ install(TARGETS opentrack-xplane-plugin RUNTIME DESTINATION . LIBRARY DESTINATION . )
+endif()
+
+if(WIN32)
+ install(DIRECTORY "${CMAKE_SOURCE_DIR}/bin/tracker-ht" DESTINATION .)
+ install(TARGETS freetrackclient RUNTIME DESTINATION . LIBRARY DESTINATION . )
+endif()
+
+install(DIRECTORY "${CMAKE_SOURCE_DIR}/3rdparty-notices" DESTINATION .)
+
+install(FILES "${CMAKE_SOURCE_DIR}/bin/NPClient.dll" "${CMAKE_SOURCE_DIR}/bin/NPClient64.dll" "${CMAKE_SOURCE_DIR}/bin/TrackIR.exe" DESTINATION .)
+install(DIRECTORY "${CMAKE_SOURCE_DIR}/bin/settings" "${CMAKE_SOURCE_DIR}/facetracknoir/clientfiles" DESTINATION .)
+
+if(NOT WIN32 AND SDK_WINE_PREFIX)
+ install(FILES "${CMAKE_BINARY_DIR}/opentrack-wrapper-wine.exe.so"
+ DESTINATION .)
+endif()
+
+install(TARGETS
+ opentrack-api
+ opentrack-compat
+ opentrack-csv
+ opentrack-pose-widget
+ opentrack-spline-widget
+ RUNTIME DESTINATION . LIBRARY DESTINATION . )
+
+install(TARGETS opentrack DESTINATION .)
+
+if(SDK_VJOY)
+ install(FILES "${SDK_VJOY}/VJoy.dll" DESTINATION .)
+endif()
+
+if(WIN32)
+ install(FILES "${CMAKE_SOURCE_DIR}/bin/cleye.config" DESTINATION .)
+endif()
+
+if(MSVC)
+ file(GLOB pdbs1 "${CMAKE_BINARY_DIR}/Release/*.pdb")
+ file(GLOB pdbs2 "${CMAKE_BINARY_DIR}/*.pdb")
+ install(FILES ${pdbs1} ${pdbs2} DESTINATION .)
+endif()
diff --git a/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui b/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui
new file mode 100644
index 00000000..0bbec7e1
--- /dev/null
+++ b/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui
@@ -0,0 +1,1790 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICPTClientControls</class>
+ <widget class="QWidget" name="UICPTClientControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>459</width>
+ <height>621</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>PointTracker Settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="ftnoir_tracker_pt.qrc">
+ <normaloff>:/Resources/Logo_IR.png</normaloff>:/Resources/Logo_IR.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>General</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="groupBox_6">
+ <property name="title">
+ <string>Tracker Thread</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_43">
+ <property name="text">
+ <string>Auto-reset time</string>
+ </property>
+ <property name="buddy">
+ <cstring>reset_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="reset_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Time until automatic reset of tracker's internal state when no valid tracking result is found</string>
+ </property>
+ <property name="suffix">
+ <string>ms</string>
+ </property>
+ <property name="maximum">
+ <number>9999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Dynamic Pose Resolution</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="dynpose_check">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPushButton" name="reset_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Reset the tracker's internal state</string>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>85</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Enable Axis</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Roll:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnableRoll</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Pitch:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnablePitch</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Yaw:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnableYaw</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="chkEnableRoll">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkEnablePitch">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkEnableYaw">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>X:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnableX</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QCheckBox" name="chkEnableX">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Y:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnableY</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QCheckBox" name="chkEnableY">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Z:</string>
+ </property>
+ <property name="buddy">
+ <cstring>chkEnableZ</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QCheckBox" name="chkEnableZ">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="2">
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Camera</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="toolTip">
+ <string>The camera device used as input</string>
+ </property>
+ <property name="title">
+ <string>Camera Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="minimumSize">
+ <size>
+ <width>55</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Device</string>
+ </property>
+ <property name="buddy">
+ <cstring>camdevice_combo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="camdevice_combo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Camera device used as input</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_8">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_36">
+ <property name="minimumSize">
+ <size>
+ <width>55</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLabel" name="label_37">
+ <property name="text">
+ <string>FPS</string>
+ </property>
+ <property name="buddy">
+ <cstring>fps_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6">
+ <widget class="QSpinBox" name="fps_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Desired capture framerate</string>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_41">
+ <property name="text">
+ <string>x</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="res_x_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Desired capture width</string>
+ </property>
+ <property name="maximum">
+ <number>2000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QSpinBox" name="res_y_spin">
+ <property name="toolTip">
+ <string>Desired capture height</string>
+ </property>
+ <property name="maximum">
+ <number>2000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_34">
+ <property name="text">
+ <string>F/W</string>
+ </property>
+ <property name="buddy">
+ <cstring>f_dspin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="f_dspin">
+ <property name="toolTip">
+ <string>The camera's focal length devided by its sensor width</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="7">
+ <spacer name="horizontalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_7"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Camera Orientation</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ <property name="buddy">
+ <cstring>campitch_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="campitch_spin">
+ <property name="contextMenuPolicy">
+ <enum>Qt::DefaultContextMenu</enum>
+ </property>
+ <property name="toolTip">
+ <string>The angle the camera is facing upwards</string>
+ </property>
+ <property name="minimum">
+ <number>-99</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ <property name="buddy">
+ <cstring>camyaw_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="camyaw_spin">
+ <property name="contextMenuPolicy">
+ <enum>Qt::DefaultContextMenu</enum>
+ </property>
+ <property name="toolTip">
+ <string>The angle the camera is facing leftwards</string>
+ </property>
+ <property name="minimum">
+ <number>-99</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>deg (positve = leftwards)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="camroll_combo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Rotation of the camera image</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>deg (positive = upwards)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>deg</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ <property name="buddy">
+ <cstring>camroll_combo</cstring>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_10">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Point Extraction</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Threshold</string>
+ </property>
+ <property name="buddy">
+ <cstring>threshold_slider</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="threshold_slider">
+ <property name="toolTip">
+ <string>Intensity threshold for point extraction</string>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="value">
+ <number>127</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_secondary">
+ <item>
+ <widget class="QLabel" name="label_secondary">
+ <property name="text">
+ <string>Hysteresis</string>
+ </property>
+ <property name="buddy">
+ <cstring>threshold_secondary_slider</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="threshold_secondary_slider">
+ <property name="toolTip">
+ <string>Per pixel hysteresis width (leave left if there is little difference between dot and non-dot, move right for increased stability against pixel noise)</string>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Min Diameter</string>
+ </property>
+ <property name="buddy">
+ <cstring>mindiam_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="mindiam_spin">
+ <property name="toolTip">
+ <string>Minimum point diameter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>px</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Max Diameter</string>
+ </property>
+ <property name="buddy">
+ <cstring>maxdiam_spin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="maxdiam_spin">
+ <property name="toolTip">
+ <string>Maximum point diameter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>px</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>Model</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_16">
+ <item>
+ <widget class="QTabWidget" name="model_tabs">
+ <property name="tabShape">
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <property name="usesScrollButtons">
+ <bool>false</bool>
+ </property>
+ <property name="documentMode">
+ <bool>false</bool>
+ </property>
+ <property name="tabsClosable">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="tab_5">
+ <attribute name="title">
+ <string>Clip</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <item>
+ <widget class="QGroupBox" name="groupBox_8">
+ <property name="title">
+ <string>Model Dimensions (mm)</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <item>
+ <widget class="QWidget" name="widget_4" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>160</height>
+ </size>
+ </property>
+ <widget class="QLabel" name="label_44">
+ <property name="geometry">
+ <rect>
+ <x>30</x>
+ <y>30</y>
+ <width>71</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/clip_side.png</pixmap>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="clip_theight_spin">
+ <property name="geometry">
+ <rect>
+ <x>100</x>
+ <y>50</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="clip_tlength_spin">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>10</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="clip_bheight_spin">
+ <property name="geometry">
+ <rect>
+ <x>100</x>
+ <y>90</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_50">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>46</width>
+ <height>13</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Side</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="clip_blength_spin">
+ <property name="geometry">
+ <rect>
+ <x>40</x>
+ <y>140</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_52">
+ <property name="geometry">
+ <rect>
+ <x>70</x>
+ <y>70</y>
+ <width>16</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_3" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>140</height>
+ </size>
+ </property>
+ <widget class="QLabel" name="label_51">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>46</width>
+ <height>13</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Front</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_45">
+ <property name="geometry">
+ <rect>
+ <x>40</x>
+ <y>30</y>
+ <width>21</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/clip_front.png</pixmap>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_53">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>70</y>
+ <width>16</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_6">
+ <attribute name="title">
+ <string>Cap</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_14">
+ <item>
+ <widget class="QGroupBox" name="groupBox_9">
+ <property name="title">
+ <string>Model Dimensions (mm)</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>140</width>
+ <height>130</height>
+ </size>
+ </property>
+ <widget class="QLabel" name="label_46">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>50</y>
+ <width>111</width>
+ <height>81</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/cap_side.png</pixmap>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="cap_height_spin">
+ <property name="geometry">
+ <rect>
+ <x>30</x>
+ <y>80</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_54">
+ <property name="geometry">
+ <rect>
+ <x>130</x>
+ <y>50</y>
+ <width>16</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_48">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>46</width>
+ <height>13</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Side</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="cap_length_spin">
+ <property name="geometry">
+ <rect>
+ <x>50</x>
+ <y>40</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_2" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>130</height>
+ </size>
+ </property>
+ <widget class="QLabel" name="label_49">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>46</width>
+ <height>13</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Front</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_55">
+ <property name="geometry">
+ <rect>
+ <x>30</x>
+ <y>50</y>
+ <width>16</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_47">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>50</y>
+ <width>81</width>
+ <height>81</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/cap_front.png</pixmap>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="cap_width_spin">
+ <property name="geometry">
+ <rect>
+ <x>50</x>
+ <y>30</y>
+ <width>46</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_7">
+ <attribute name="title">
+ <string>Custom</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_15">
+ <item>
+ <widget class="QGroupBox" name="groupBox_7">
+ <property name="title">
+ <string>Model Dimensions (mm)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_12">
+ <item>
+ <widget class="QLabel" name="label_56">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Location of the two remaining model points&lt;br/&gt;with respect to the reference point in default pose&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <item>
+ <spacer name="horizontalSpacer_14">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="3" column="2">
+ <widget class="QSpinBox" name="m1z_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_58">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QSpinBox" name="m1y_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_57">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_60">
+ <property name="text">
+ <string>M1:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="m1x_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_63">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_15">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="m2x_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_67">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_69">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QSpinBox" name="m2y_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_70">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_64">
+ <property name="text">
+ <string>M2:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QSpinBox" name="m2z_spin">
+ <property name="suffix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_16">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_10">
+ <property name="title">
+ <string>Model Position (mm)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_11">
+ <item>
+ <widget class="QLabel" name="label_59">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Translation from head center to model reference point&lt;br/&gt; in default pose&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_17">
+ <item>
+ <spacer name="horizontalSpacer_17">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="tz_spin">
+ <property name="suffix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_61">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_62">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_66">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="ty_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="tx_spin">
+ <property name="minimum">
+ <number>-999</number>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_18">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="tcalib_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Calibrate</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_19">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>About</string>
+ </attribute>
+ <widget class="QLabel" name="label_10">
+ <property name="geometry">
+ <rect>
+ <x>30</x>
+ <y>30</y>
+ <width>161</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;FTNoIR PointTracker Plugin&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;by Patrick Ruoff&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://ftnoirpt.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Manual (external)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_35">
+ <property name="geometry">
+ <rect>
+ <x>200</x>
+ <y>30</y>
+ <width>141</width>
+ <height>141</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/Logo_IR.png</pixmap>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Status</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_38">
+ <property name="text">
+ <string>Camera Info:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="caminfo_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Extracted Points:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="pointinfo_label">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QPushButton" name="btnApply">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="ok_button">
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="text">
+ <string>Ok</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancel_button">
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>reset_spin</tabstop>
+ <tabstop>chkEnableRoll</tabstop>
+ <tabstop>chkEnablePitch</tabstop>
+ <tabstop>chkEnableYaw</tabstop>
+ <tabstop>chkEnableX</tabstop>
+ <tabstop>chkEnableY</tabstop>
+ <tabstop>chkEnableZ</tabstop>
+ <tabstop>camdevice_combo</tabstop>
+ <tabstop>res_x_spin</tabstop>
+ <tabstop>res_y_spin</tabstop>
+ <tabstop>fps_spin</tabstop>
+ <tabstop>f_dspin</tabstop>
+ <tabstop>camroll_combo</tabstop>
+ <tabstop>campitch_spin</tabstop>
+ <tabstop>camyaw_spin</tabstop>
+ <tabstop>threshold_slider</tabstop>
+ <tabstop>mindiam_spin</tabstop>
+ <tabstop>maxdiam_spin</tabstop>
+ <tabstop>model_tabs</tabstop>
+ <tabstop>clip_tlength_spin</tabstop>
+ <tabstop>clip_theight_spin</tabstop>
+ <tabstop>clip_bheight_spin</tabstop>
+ <tabstop>clip_blength_spin</tabstop>
+ <tabstop>cap_length_spin</tabstop>
+ <tabstop>cap_height_spin</tabstop>
+ <tabstop>cap_width_spin</tabstop>
+ <tabstop>m1x_spin</tabstop>
+ <tabstop>m1y_spin</tabstop>
+ <tabstop>m1z_spin</tabstop>
+ <tabstop>m2x_spin</tabstop>
+ <tabstop>m2y_spin</tabstop>
+ <tabstop>m2z_spin</tabstop>
+ <tabstop>tx_spin</tabstop>
+ <tabstop>ty_spin</tabstop>
+ <tabstop>tz_spin</tabstop>
+ <tabstop>tcalib_button</tabstop>
+ <tabstop>ok_button</tabstop>
+ <tabstop>cancel_button</tabstop>
+ </tabstops>
+ <resources>
+ <include location="ftnoir_tracker_pt.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>dynpose_check</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>reset_spin</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>172</x>
+ <y>110</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>351</x>
+ <y>112</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/FTNoIR_Tracker_PT/Resources/Logo_IR.png b/FTNoIR_Tracker_PT/Resources/Logo_IR.png
new file mode 100644
index 00000000..95032a25
--- /dev/null
+++ b/FTNoIR_Tracker_PT/Resources/Logo_IR.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/Resources/cap_front.png b/FTNoIR_Tracker_PT/Resources/cap_front.png
new file mode 100644
index 00000000..14207a67
--- /dev/null
+++ b/FTNoIR_Tracker_PT/Resources/cap_front.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/Resources/cap_side.png b/FTNoIR_Tracker_PT/Resources/cap_side.png
new file mode 100644
index 00000000..5ad4ee65
--- /dev/null
+++ b/FTNoIR_Tracker_PT/Resources/cap_side.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/Resources/clip_front.png b/FTNoIR_Tracker_PT/Resources/clip_front.png
new file mode 100644
index 00000000..04880138
--- /dev/null
+++ b/FTNoIR_Tracker_PT/Resources/clip_front.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/Resources/clip_side.png b/FTNoIR_Tracker_PT/Resources/clip_side.png
new file mode 100644
index 00000000..72667ac7
--- /dev/null
+++ b/FTNoIR_Tracker_PT/Resources/clip_side.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/boost-compat.h b/FTNoIR_Tracker_PT/boost-compat.h
new file mode 100644
index 00000000..612f2c4d
--- /dev/null
+++ b/FTNoIR_Tracker_PT/boost-compat.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <memory>
+namespace boost {
+ using std::shared_ptr;
+}
diff --git a/FTNoIR_Tracker_PT/camera.cpp b/FTNoIR_Tracker_PT/camera.cpp
new file mode 100644
index 00000000..754533c5
--- /dev/null
+++ b/FTNoIR_Tracker_PT/camera.cpp
@@ -0,0 +1,351 @@
+/* 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.
+ */
+
+ #if defined(OPENTRACK_API) && defined(_WIN32)
+#include <windows.h>
+#include <dshow.h>
+#endif
+
+#include "camera.h"
+#include <string>
+#include <QDebug>
+
+using namespace cv;
+
+#if defined(OPENTRACK_API) && (defined(__unix) || defined(__linux) || defined(__APPLE__))
+#include <unistd.h>
+#endif
+
+#ifdef OPENTRACK_API
+void get_camera_device_names(std::vector<std::string>& device_names) {
+# if defined(_WIN32)
+ // 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))
+ {
+ return;
+ }
+ // Obtain a class enumerator for the video compressor category.
+ IEnumMoniker *pEnumCat = NULL;
+ hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+ if (hr == S_OK) {
+ // Enumerate the monikers.
+ IMoniker *pMoniker = NULL;
+ ULONG cFetched;
+ while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
+ // To retrieve the filter's friendly name, do the following:
+ VARIANT varName;
+ VariantInit(&varName);
+ hr = pPropBag->Read(L"FriendlyName", &varName, 0);
+ if (SUCCEEDED(hr))
+ {
+ auto wstr = std::wstring(varName.bstrVal);
+ auto str = std::string(wstr.begin(), wstr.end());
+ device_names.push_back(str);
+ }
+ VariantClear(&varName);
+
+ ////// To create an instance of the filter, do the following:
+ ////IBaseFilter *pFilter;
+ ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
+ //// (void**)&pFilter);
+ // Now add the filter to the graph.
+ //Remember to release pFilter later.
+ 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) {
+ device_names.push_back(std::string(buf));
+ }
+ }
+# endif
+}
+#else
+// ----------------------------------------------------------------------------
+void get_camera_device_names(std::vector<std::string>& device_names)
+{
+ videoInput VI;
+ VI.listDevices();
+ std::string device_name;
+ for(int index = 0; ; ++index) {
+ device_name = VI.getDeviceName(index);
+ if (device_name.empty()) break;
+ device_names.push_back(device_name);
+ }
+}
+#endif
+
+// ----------------------------------------------------------------------------
+void Camera::set_device_index(int index)
+{
+ if (desired_index != index)
+ {
+ desired_index = index;
+ _set_device_index();
+
+ // reset fps
+ dt_valid = 0;
+ dt_mean = 0;
+ active_index = index;
+ }
+}
+
+void Camera::set_f(float f)
+{
+ if (cam_desired.f != f)
+ {
+ cam_desired.f = f;
+ _set_f();
+ }
+}
+void Camera::set_fps(int fps)
+{
+ if (cam_desired.fps != fps)
+ {
+ cam_desired.fps = fps;
+ _set_fps();
+ }
+}
+
+void Camera::set_res(int x_res, int y_res)
+{
+ if (cam_desired.res_x != x_res || cam_desired.res_y != y_res)
+ {
+ cam_desired.res_x = x_res;
+ cam_desired.res_y = y_res;
+ _set_res();
+ _set_fps();
+ }
+}
+
+bool Camera::get_frame(float dt, cv::Mat* frame)
+{
+ bool new_frame = _get_frame(frame);
+ // measure fps of valid frames
+ const float dt_smoothing_const = 0.9;
+ dt_valid += dt;
+ if (new_frame)
+ {
+ dt_mean = dt_smoothing_const * dt_mean + (1.0 - dt_smoothing_const) * dt_valid;
+ cam_info.fps = 1.0 / dt_mean;
+ dt_valid = 0;
+ }
+ return new_frame;
+}
+
+// ----------------------------------------------------------------------------
+#ifdef OPENTRACK_API
+void CVCamera::start()
+{
+ cap = new VideoCapture(desired_index);
+ // extract camera info
+ if (cap->isOpened())
+ {
+ active = true;
+ active_index = desired_index;
+ cam_info.res_x = cap->get(CV_CAP_PROP_FRAME_WIDTH);
+ cam_info.res_y = cap->get(CV_CAP_PROP_FRAME_HEIGHT);
+ } else {
+ delete cap;
+ cap = nullptr;
+ }
+}
+
+void CVCamera::stop()
+{
+ if (cap)
+ {
+ cap->release();
+ delete cap;
+ }
+ active = false;
+}
+
+bool CVCamera::_get_frame(Mat* frame)
+{
+ if (cap && cap->isOpened())
+ {
+ Mat img;
+ /*
+ * XXX some Windows webcams fail to decode first
+ * frames and then some every once in a while
+ * -sh
+ */
+ for (int i = 0; i < 100 && !cap->read(img); i++)
+ ;;
+
+ if (img.empty())
+ return false;
+
+ *frame = img;
+ return true;
+ }
+ return false;
+}
+
+void CVCamera::_set_index()
+{
+ if (active) restart();
+}
+
+void CVCamera::_set_f()
+{
+ cam_info.f = cam_desired.f;
+}
+
+void CVCamera::_set_fps()
+{
+ if (cap) cap->set(CV_CAP_PROP_FPS, cam_desired.fps);
+}
+
+void CVCamera::_set_res()
+{
+ if (cap)
+ {
+ cap->set(CV_CAP_PROP_FRAME_WIDTH, cam_desired.res_x);
+ cap->set(CV_CAP_PROP_FRAME_HEIGHT, cam_desired.res_y);
+ cam_info.res_x = cap->get(CV_CAP_PROP_FRAME_WIDTH);
+ cam_info.res_y = cap->get(CV_CAP_PROP_FRAME_HEIGHT);
+ }
+}
+void CVCamera::_set_device_index()
+{
+ if (cap)
+ {
+ cap->release();
+ delete cap;
+ }
+ cap = new VideoCapture(desired_index);
+}
+
+#else
+// ----------------------------------------------------------------------------
+VICamera::VICamera() : frame_buffer(NULL)
+{
+ VI.listDevices();
+}
+
+void VICamera::start()
+{
+ if (desired_index >= 0)
+ {
+ if (cam_desired.res_x == 0 || cam_desired.res_y == 0)
+ VI.setupDevice(desired_index);
+ else
+ VI.setupDevice(desired_index, cam_desired.res_x, cam_desired.res_y);
+
+ active = true;
+ active_index = desired_index;
+
+ cam_info.res_x = VI.getWidth(active_index);
+ cam_info.res_y = VI.getHeight(active_index);
+ new_frame = cv::Mat(cam_info.res_y, cam_info.res_x, CV_8UC3);
+ // If matrix is not continuous we have to copy manually via frame_buffer
+ if (!new_frame.isContinuous()) {
+ unsigned int size = VI.getSize(active_index);
+ frame_buffer = new unsigned char[size];
+ }
+ }
+}
+
+void VICamera::stop()
+{
+ if (active)
+ {
+ VI.stopDevice(active_index);
+ }
+ if (frame_buffer)
+ {
+ delete[] frame_buffer;
+ frame_buffer = NULL;
+ }
+ active = false;
+}
+
+bool VICamera::_get_frame(Mat* frame)
+{
+ if (active && VI.isFrameNew(active_index))
+ {
+ if (new_frame.isContinuous())
+ {
+ VI.getPixels(active_index, new_frame.data, false, true);
+ }
+ else
+ {
+ // If matrix is not continuous we have to copy manually via frame_buffer
+ VI.getPixels(active_index, frame_buffer, false, true);
+ new_frame = cv::Mat(cam_info.res_y, cam_info.res_x, CV_8UC3, frame_buffer).clone();
+ }
+ *frame = new_frame;
+ return true;
+ }
+ return false;
+}
+
+void VICamera::_set_device_index()
+{
+ if (active) restart();
+}
+
+void VICamera::_set_f()
+{
+ cam_info.f = cam_desired.f;
+}
+
+void VICamera::_set_fps()
+{
+ bool was_active = active;
+ if (active) stop();
+ VI.setIdealFramerate(desired_index, cam_desired.fps);
+ if (was_active) start();
+}
+
+void VICamera::_set_res()
+{
+ if (active) restart();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+Mat FrameRotation::rotate_frame(Mat frame)
+{
+ switch (rotation)
+ {
+ case CLOCKWISE:
+ {
+ Mat dst;
+ transpose(frame, dst);
+ flip(dst, dst, 1);
+ return dst;
+ }
+
+ case COUNTER_CLOCKWISE:
+ {
+ Mat dst;
+ transpose(frame, dst);
+ flip(dst, dst, 0);
+ return dst;
+ }
+
+ default:
+ return frame;
+ }
+}
diff --git a/FTNoIR_Tracker_PT/camera.h b/FTNoIR_Tracker_PT/camera.h
new file mode 100644
index 00000000..ea68c387
--- /dev/null
+++ b/FTNoIR_Tracker_PT/camera.h
@@ -0,0 +1,145 @@
+/* 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 CAMERA_H
+#define CAMERA_H
+
+#include <opencv2/opencv.hpp>
+#ifndef OPENTRACK_API
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+# include <opencv2/highgui/highgui.hpp>
+# include <opencv2/highgui/highgui_c.h>
+#endif
+#include <string>
+
+// ----------------------------------------------------------------------------
+void get_camera_device_names(std::vector<std::string>& device_names);
+
+
+// ----------------------------------------------------------------------------
+struct CamInfo
+{
+ CamInfo() : res_x(0), res_y(0), fps(0), f(1) {}
+
+ int res_x;
+ int res_y;
+ int fps;
+ float f; // (focal length) / (sensor width)
+};
+
+// ----------------------------------------------------------------------------
+// Base class for cameras, calculates the frame rate
+class Camera
+{
+public:
+ Camera() : dt_valid(0), dt_mean(0), desired_index(0), active_index(-1), active(false) {}
+ virtual ~Camera() {}
+
+ // start/stop capturing
+ virtual void start() = 0;
+ virtual void stop() = 0;
+ void restart() { stop(); start(); }
+
+ // calls corresponding template methods and reinitializes frame rate calculation
+ void set_device_index(int index);
+ void set_f(float f);
+ void set_fps(int fps);
+ void set_res(int x_res, int y_res);
+
+ // gets a frame from the camera, dt: time since last call in seconds
+ bool get_frame(float dt, cv::Mat* frame);
+
+ // WARNING: returned references are valid as long as object
+ const CamInfo& get_info() const { return cam_info; }
+ const CamInfo& get_desired() const { return cam_desired; }
+
+protected:
+ // get a frame from the camera
+ virtual bool _get_frame(cv::Mat* frame) = 0;
+
+ // update the camera using cam_desired, write res and f to cam_info if successful
+ virtual void _set_device_index() = 0;
+ virtual void _set_f() = 0;
+ virtual void _set_fps() = 0;
+ virtual void _set_res() = 0;
+
+ float dt_valid;
+ float dt_mean;
+ int desired_index;
+ int active_index;
+ bool active;
+ CamInfo cam_info;
+ CamInfo cam_desired;
+};
+
+
+// ----------------------------------------------------------------------------
+// camera based on OpenCV's videoCapture
+#ifdef OPENTRACK_API
+class CVCamera : public Camera
+{
+public:
+ CVCamera() : cap(NULL) {}
+ ~CVCamera() { stop(); }
+
+ virtual void start();
+ virtual void stop();
+
+protected:
+ virtual bool _get_frame(cv::Mat* frame);
+ virtual void _set_index();
+ virtual void _set_f();
+ virtual void _set_fps();
+ virtual void _set_res();
+ virtual void _set_device_index();
+
+ cv::VideoCapture* cap;
+};
+#else
+// ----------------------------------------------------------------------------
+// Camera based on the videoInput library
+class VICamera : public Camera
+{
+public:
+ VICamera();
+ ~VICamera() { stop(); }
+
+ virtual void start();
+ virtual void stop();
+
+protected:
+ virtual bool _get_frame(cv::Mat* frame);
+ virtual void _set_device_index();
+ virtual void _set_f();
+ virtual void _set_fps();
+ virtual void _set_res();
+
+ videoInput VI;
+ cv::Mat new_frame;
+ unsigned char* frame_buffer;
+};
+#endif
+
+enum RotationType
+{
+ CLOCKWISE = 0,
+ ZERO = 1,
+ COUNTER_CLOCKWISE = 2
+};
+
+// ----------------------------------------------------------------------------
+class FrameRotation
+{
+public:
+ RotationType rotation;
+
+ cv::Mat rotate_frame(cv::Mat frame);
+};
+
+#endif //CAMERA_H
diff --git a/FTNoIR_Tracker_PT/doc/index.htm b/FTNoIR_Tracker_PT/doc/index.htm
new file mode 100644
index 00000000..87b7356f
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/index.htm
@@ -0,0 +1,262 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+ <title>FTNoIR PointTracker Help</title>
+
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+
+ <meta name="author"
+ content="Patrick Ruoff (C14)"/>
+ <meta name="keywords"
+ content="facetracknoir infrared point model tracker plugin"/>
+ <meta name="description"
+ content="Pointtracker plugin for FaceTrackNoIR"/>
+
+ <link rel="shortcut icon" href="ptrack.ico" type="image/vnd.microsoft.icon" />
+ <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+
+<body>
+<div id="navbar">
+<ul class="navbar">
+<li class="navbar"><a class="navbar" href="#about">About</a></li>
+<li class="navbar"><a class="navbar" href="#settings">Settings</a></li>
+<li class="navbar"><a class="navbar" href="#setup">Filter Setup</a></li>
+<li class="navbar"><a class="navbar" href="#support">Support</a></li>
+<li class="navbar"><a class="navbar" href="#changelog">ChangeLog</a></li>
+<li class="navbar"><a class="navbar" href="#build_instructions">Build Instructions</a></li>
+</ul>
+</div>
+
+<div id="content">
+<div style="text-align:center"><h1>FaceTrackNoIR PointTracker Plugin</h1><img src="logo.png" alt="PointTracker Plugin Logo" /></div>
+
+<a class="nav" id="about"></a>
+<h2>About</h2>
+<div class="indent">
+<p>
+PointTracker is a plugin for the free head tracking software <a href="http://facetracknoir.sourceforge.net">FaceTrackNoIR</a>
+which introduces the capability to track a (typically IR-) point model comprising 3 bright points to FaceTrackNoIR,
+much like the popular free tracking software <a href="http://www.free-track.net/">Freetrack</a> does.<br/>
+It was created as a stable modular alternative to Freetrack, which has some stability issues with newer systems and seems to be no longer actively developped.
+</p>
+</div>
+
+<a class="nav" id="settings"></a>
+<h2>Settings</h2>
+<div class="indent">
+<p>
+This section desribes the various settings of the PointTracker plugin in detail.
+</p>
+
+<img src="settings1.png" alt="Settings Pane 1"/>
+<dl>
+<dt>Show VideoWidget</dt><dd>Whether the video widget is updated or not. It may save some performance to turn this off when not needed</dd>
+<dt>Sleep time</dt><dd>Time the tracking thread sleeps after each processed image. It's inverse should be below the framefrate you want to achieve.
+(check the framerate in the status region when tracker is active, in case the sleep time is too high, the framerate will decrease).
+Low values will result in more CPU-load.</dd>
+<dt>Dynamic Pose Resolution</dt><dd>Whether the point correspondence and pose ambiquity is resolved using a more sophisticated dynamic algorithm (constant velocity prediction) or a simple static resolution.
+Dynamic pose resolution can capture more extreme poses but may occasionally get stuck in a wrong pose estimates so that a reset of the internal state becomes neccessary.</dd>
+<dt>Auto-reset time</dt><dd>If no valid tracking result can be found when using dynamic pose resolution, the tracker will automatically reset its internal state (used for resolving the pose ambiguity and point correspondence)
+and return to a fail-safe initialization phase that assumes a neutral pose after this time.
+Decrease this time, if you get stuck in a wrong pose too often.</dd>
+<dt>Reset</dt><dd>Manually reset the trackers internal state used for dynamic pose resolution and return to a fail-safe initialization phase that assumes a neutral pose.
+You may use this in case you get stuck in a wrong pose.</dd>
+<dt>Enable Axis ...</dt><dd>Which axis to use for FTNoIR.</dd>
+</dl>
+
+<img src="settings2.png" alt="Settings Pane 2"/>
+<dl>
+<dt>Device</dt><dd>The camera used for tracking.</dd>
+<dt>Resolution</dt><dd>The desired capture resolution. If your camera does not support the entered resolution the true output resolution may be different or even invalid.
+You may check the true capture resolution in the status area while the tracker is running. A higher resolution results in more accurate point positions and will increase the
+stability of the tracking result, as long as the signal/noise ratio is sufficiently high.</dd>
+<dt>FPS</dt><dd>The desired capture framerate. Again, if your camera does not support the entered framerate, the true caputre framerate may be different or invalid.
+You may check the true processing framerate in the status area while the tracker is running.</dd>
+<dt>F/W</dt><dd>The focal length of the camera divided by the sensor width (of course in the same units).
+In case you don't have access to your camera's specifications, you can measure this yourself by placing a plane object of known width (for example a piece of cardboard) in front of the camera until it fills the whole image width.
+Then measure the distance between the object and the camera and divide by the object width.</dd>
+<dt>VideoWidget</dt><dd>Shows a resizable stand-alone video widget that shows the same content as the integrated video widget in FTNoIR.
+Update rate is only 10 fps and may lag behind a bit. Mainly useful during calibration of the point extraction. Same as for the integrated wiget, to save resources, this widget should only be shown when needed.</dd>
+<dt>Roll Pitch Yaw...</dt><dd>The orientation of the camera relative to the reference frame.
+If these angles are setup properly, the direction of translations may not be correct.
+Roll is treated in a special way since it is implemented as a frame rotation by +/- 90 deg that is transparent to the rest of the processing pipeline.
+</dd>
+<dt>Threshold</dt><dd>The threshold for point recognition. Areas above the threshold are shown in blue in the VideoWidget.
+Since point accuracy is best if the points are as big as possible in pixels, the theshold should be chosen as low as possible (stop before the contour of the points becomes "noisy").
+If small reflections are being falsely classified as points, increasing the minimum point diameter (see below) may help.</dd>
+<dt>Min Diameter</dt><dd>Minimum diameter of blobs to be classified as a pointmodel-point.</dd>
+<dt>Max Diameter</dt><dd>Maximum diameter of blobs to be classified as a pointmodel-point.</dd>
+<dt>Status</dt><dd>The tracker's status is shown in this area while the tracker is running.
+The FPS shown here correspond to the framerate of the whole tracker processing chain and may be lower than what your camera is able to provide, when<br/>
+1. The processing gets not enough CPU time<br/>
+2. The sleep time of the tracking thread is set too high<br/></dd>
+</dl>
+
+<img src="settings3.png" alt="Settings Pane 3"/>
+<dl>
+<dt>Model Selection and Dimensions ...</dt><dd>
+First select your model type (point, clip, custom), then enter the dimensions of your model in milimeters here.<br/>
+For the custom setting, the coordinates of the two remaining model points have to be entered (reference point M0 is at (0,0,0)) in a pose where the model roughly faces the camera.
+For orientation, the coordinates for the standard Freetrack clip are (0,40,-30), (0,-70,-80) and the ones for the cap (40,-60,-100), (-40,-60,-100).<br/>
+When using a custom point-model configuration, the following restrictions should be observed:<br/>
+The plane in which the 3 points lie should never be parallel to the image plane, M0-M1 and M0-M2 should be roughly perpendicular.</dd>
+
+<dt>Model Position</dt><dd>The vector from the model to the center of the head in the model frame. Can be calibrated automatically.</dd>
+<dt>Calibrate</dt><dd>In order to automatically calibrate the model-head offset, do the following:<br/>Press the Calibrate button, then look around while not moving your shoulder. (i.e. only rotation, no translation).
+Do not stay in one pose for too long. The current translation estimate will be updated in real time. As soon as the values stabilized sufficiently, press the Calibrate button again to stop the calibration process.</dd>
+</dl>
+</div>
+
+<a class="nav" id="setup"></a>
+<h2>Filter Setup</h2>
+<div class="indent">
+<p>
+This section desribes how the FTNoIR filter work and what the recommended settings for PointTracker are.
+</p>
+<p>
+Filtering is always a tradeoff between stability, accuracy and responsiveness.
+</p>
+<p>
+The <q>Smoothing</q> filter in FTNoIR is just a simple average over the last n samples.
+Since this filter produces input lag no matter how fast the head-movements are, it is recommended to turn it off by setting samples to 1.
+</p>
+<p>
+In the filter tab, it is recommended to select <q>Accela Filter Mk2</q>.
+Accela is a non-linear filter that works as follows:<br/>
+It looks at the difference between the new raw values <i>new_val</i> from the tracker and the last filtered value <i>old_val</i>
+and maps this difference via the customizable response function <i>f</i> via:<br/>
+</p>
+<p style="text-align: center">
+<i>new_val = old_val + f(new_val - old_val) / reduction_factor</i>
+</p>
+<p>
+So by setting <i>f(x) = reduction_factor * x</i>, one will get no filtering at all.<br/>
+If you set lower values for small x, small deviations (usually noise) will get dampened.
+This results in a dynamic dead-zone around the current position.
+</p>
+<p>
+The last two points are used by accela to extrapolate for large deviations.
+So in order to get a fast unfiltered response for large deviations, the line connecting the last two points should have a slope >= <i>reduction_factor</i>.
+</p>
+<p>
+More aggressive accela settings than the default FTNoIR accela settings are recommended in order to decrease the filtering lag and fully use the potential of point tracking.<br/>
+My current settings are:
+</p>
+<pre class="indent"><code>
+[Accela]
+Reduction=20
+
+[Curves-Accela-Scaling-Rotation]
+point-count=4
+point-0-x=0.1
+point-0-y=0
+point-1-x=1.43
+point-1-y=2.45
+point-2-x=2.0
+point-2-y=5.44
+point-3-x=2.06
+point-3-y=6
+</code></pre>
+<p>
+The curve is not too different from the standard one (except that I like a small dynamic dead zone for steady aiming, that's why the curve has a slope of 0 at the beginning).<br/>
+However, the reduction factor is decreased to a value of 20 (compared to the standard value of 100). This implies that each value of the curve is effectively 5 times higher than in standard FTNoIR (see formula above), which means higher responsiveness but can also lead to jitter/shaking.<br/>
+Keep in mind that there are no <q>best filter settings</q>. Since filtering is always a compromise it's a matter of personal taste and
+playing around with the filter settings is highly recommended.
+</p>
+</div>
+
+<a class="nav" id="support"></a>
+<h2>Support</h2>
+<div class="indent">
+<p>
+For questions/feedback about the plugin, post to the <a href="https://sourceforge.net/projects/facetracknoir/forums">FTNoIR-Forum</a>.<br/>
+In case you like this plugin and would like to support the author, you may consider making a donation.
+</p>
+<div style="text-align:center">
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+<fieldset class="blind">
+<input type="hidden" name="cmd" value="_s-xclick"/>
+<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHJwYJKoZIhvcNAQcEoIIHGDCCBxQCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCa+2zPZ+6vFPqveJsBIjFLpy54m7tl0AdojRr/K5qa3QJDyRBhGwGAP2jRihkmZFE2oKlfLpkz7nrwOQY/wFEPkggO+cABxUfjcQVpIupHEtwdV0hMklLs0RmACJy802yfi1yTiCpJ4hvWN+VfUI3gOiZ9uRZ3L4iGXES7xtqJbDELMAkGBSsOAwIaBQAwgaQGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIeopHzcJ8XBOAgYCYJFyTejSplEOwF21aQ01qQOads9Z+RUVI+hlvM/pHTjimaZPKSis3poAeqv6wKn40DpLNxDnmcT+Y9KXhrV+Gy4GZCPaeNzq2vquQ2ZVN0fTr84QVmKqPkjMBGmJAHSLCcZswUddemJgoD1uyvS0kNbchvxw7gDXJnJeBRNyXXKCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTEyMDkyMzA5NTcwOFowIwYJKoZIhvcNAQkEMRYEFG/qW7uo4R4m5uFYegcZaZsTPAcUMA0GCSqGSIb3DQEBAQUABIGAGygLfrR6IQbG2xZY2OrwKkfmRwiwtnXpLBnSbnWb7XxUOMhvM6962RiKBQBGP0+XYw0S9yu8ZHx7tqz/3bcMfGjtz7PwixYx6Rm8Z29ja78aUy5FmU7fc9yAWFxLHptSliK1dJBPxdQa9J2YSDvPQPAj+AdB9sJvqJoMoxTFGM4=-----END PKCS7-----
+"/>
+<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online!"/>
+<img alt="" src="https://www.paypalobjects.com/de_DE/i/scr/pixel.gif" width="1" height="1"/>
+</fieldset>
+</form>
+</div>
+</div>
+
+<a class="nav" id="changelog"></a>
+<h2>ChangeLog</h2>
+<div class="indent">
+<h3>1.1</h3>
+<ul>
+<li>Added camera yaw and roll correction (intended for vertically mounted cameras)</li>
+<li>Improved point extraction algorithm, thanks to Michael Welter</li>
+<li>UI improvements: Select camera by device name, different VideoWidget architecture</li>
+<li>Bugfixes: Removed 99 FPS limitation</li>
+</ul>
+
+<h3>1.0</h3>
+<ul>
+<li>Added camera pitch correction</li>
+<li>Better communication with FTNoIR: output axis configuration, status report</li>
+</ul>
+
+<h3>1.0 beta</h3>
+<ul>
+<li>Switchted to videoInput library for capture. Desired capture resolution and fps can now be customized</li>
+<li>Introduced dynamic point-correspondence and POSIT-ambiguity resolution, which allows for the reconstruction of more extreme poses</li>
+<li>More convenient freetrack-like model dimension GUI</li>
+<li>Bugfixes: VideoWidget skipping frames, Timer resolution too low for accurate FPS measurement</li>
+</ul>
+</div>
+
+<a class="nav" id="build_instructions"></a>
+<h2>Build Instructions</h2>
+<div class="indent">
+<p>
+This section describes what you need to do in order to build PointTracker yourself.<br/>
+You can find the sources at the <a href="https://sourceforge.net/projects/ftnoirpt/">project site</a>
+or as part of the <a href="https://sourceforge.net/projects/facetracknoir/">FTNoIR sources</a>.
+</p>
+<p> The project was created with Visual Studio. </p>
+
+<h3>Dependencies</h3>
+<ul>
+<li>Qt 4.8.2 library</li>
+<li>Qt plugin for Visual studio</li>
+<li>OpenCV 2.4 prebuilt for Windows</li>
+<li>Boost 1.47</li>
+</ul>
+
+<h3>Details</h3>
+<div class="indent">
+<h4>Common</h4>
+<ul>
+<li>setup environment variable "QTDIR" (example value "D:\Devel\Libs\Qt\4.8.2")</li>
+<li>add "%QTDIR%\bin" to PATH</li>
+<li>setup environment variable "BOOST_DIR" (example value "D:\Devel\Libs\boost_1_47_0")</li>
+<li>setup environment variable "OPENCV_DIR" (example value "D:\Devel\Libs\opencv\build")</li>
+</ul>
+<h4>Debug</h4>
+<p>opencv linked dynamically:</p>
+<ul>
+<li>add "%OPENCV_DIR%\x86\vc9\bin" to PATH</li>
+</ul>
+<p>(in case of different Visual studio, change PATH and linker dependencies accordingly)</p>
+<h4>Release</h4>
+<p>opencv linked statically:</p>
+<ul>
+<li>custom build a statically linked version of opencv with the buil-option BUILD_WITH_STATIC_CRT set to OFF!</li>
+<li>copy resulting libaries to "%OPENCV_DIR%\x86\vc9\static_lib"</li>
+</ul>
+<p>(in case of different Visual studio, change PATH and linker dependencies accordingly)</p>
+</div>
+</div>
+
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/FTNoIR_Tracker_PT/doc/logo.png b/FTNoIR_Tracker_PT/doc/logo.png
new file mode 100644
index 00000000..95032a25
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/logo.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/doc/ptrack.ico b/FTNoIR_Tracker_PT/doc/ptrack.ico
new file mode 100644
index 00000000..c4b2aedc
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/ptrack.ico
Binary files differ
diff --git a/FTNoIR_Tracker_PT/doc/settings1.png b/FTNoIR_Tracker_PT/doc/settings1.png
new file mode 100644
index 00000000..35b84c5c
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/settings1.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/doc/settings2.png b/FTNoIR_Tracker_PT/doc/settings2.png
new file mode 100644
index 00000000..c6cfd1f3
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/settings2.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/doc/settings3.png b/FTNoIR_Tracker_PT/doc/settings3.png
new file mode 100644
index 00000000..5922403d
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/settings3.png
Binary files differ
diff --git a/FTNoIR_Tracker_PT/doc/style.css b/FTNoIR_Tracker_PT/doc/style.css
new file mode 100644
index 00000000..a8d3e333
--- /dev/null
+++ b/FTNoIR_Tracker_PT/doc/style.css
@@ -0,0 +1,131 @@
+body {
+ width: 1000px;
+ font-size: 13px;
+ color: #000000;
+ padding: 0;
+ margin: 0 auto;
+ background: #444444;
+ font-family: verdana,arial;
+}
+
+table {
+ border-width: 3px;
+ border-color: #0000FF;
+ border-style: ridge;
+ margin-top: 5px;
+ background-color: #E0E0FF;
+}
+
+table.blind {
+ border: none;
+ background-color: #E6E6E6;
+}
+
+fieldset.blind {
+ border: none;
+}
+
+h1 { font-size: 160%; }
+h2 { font-size: 140%; }
+h3 { font-size: 115%; }
+
+.indent {
+ margin-left: 25px;
+}
+
+p
+{
+ margin-left: 10px;
+}
+
+li
+{
+ margin: 10px;
+}
+
+
+dl
+{
+ /*width: 80%;*/
+ border-bottom: 1px solid #999;
+}
+
+dt
+{
+ padding-top: 5px;
+ font-weight: bold;
+ border-top: 1px solid #999;
+}
+
+dd
+{
+ padding: 5px;
+}
+
+
+hr {
+ color: #688938;
+}
+
+a:link, a:visited {
+ color: #0000BF;
+}
+a:hover {
+ color: #0000FF;
+}
+
+a.nav {
+ position: relative;
+ top: -30px;
+ display: block;
+ visibility: hidden;
+}
+
+#navbar {
+ width: 1000px;
+ height: 30px;
+ background-color:#1a1a1b;
+ position: fixed;
+ margin: 0 auto;
+ padding: 0;
+}
+
+#navbar ul
+{
+ list-style-type: none;
+ margin: 0 auto;
+ padding: 0;
+ overflow: hidden;
+}
+
+#navbar li
+{
+ margin: 0 auto;
+ padding: 5px;
+ float:left;
+}
+
+#navbar a:link,a:visited
+{
+ display:block;
+ width:150px;
+ font-weight:bold;
+ color:#e85d02;
+ text-align:center;
+ /*padding:4px;*/
+ text-decoration:none;
+ /*text-transform:uppercase;*/
+}
+
+#navbar a:hover,a:active
+{
+ color:#ffffff;
+}
+
+#content {
+ background-color:#ffffff;
+ padding: 15px;
+ padding-top: 40px;
+ padding-right: 40px;
+ margin: 0 auto;
+}
diff --git a/FTNoIR_Tracker_PT/frame_observer.cpp b/FTNoIR_Tracker_PT/frame_observer.cpp
new file mode 100644
index 00000000..281f3d57
--- /dev/null
+++ b/FTNoIR_Tracker_PT/frame_observer.cpp
@@ -0,0 +1,18 @@
+/* Copyright (c) 2013 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.
+ */
+
+#include "frame_observer.h"
+
+//-----------------------------------------------------------------------------
+FrameProvider::~FrameProvider()
+{
+ QMutexLocker lock(&observer_mutex);
+ for (std::set<FrameObserver*>::iterator iter=frame_observers.begin(); iter!=frame_observers.end(); ++iter)
+ {
+ (*iter)->on_frame_provider_destroy();
+ }
+}
diff --git a/FTNoIR_Tracker_PT/frame_observer.h b/FTNoIR_Tracker_PT/frame_observer.h
new file mode 100644
index 00000000..585a6ee7
--- /dev/null
+++ b/FTNoIR_Tracker_PT/frame_observer.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2013 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 FRAME_OBSERVER_H
+#define FRAME_OBSERVER_H
+
+#include <QMutex>
+#include <opencv2/opencv.hpp>
+#ifndef OPENTRACK_API
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+#endif
+#include <set>
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+class FrameObserver;
+
+//-----------------------------------------------------------------------------
+// Provides means to copy frame and point information if it has observers
+// Instantiate a FrameObserver to get the information
+class FrameProvider
+{
+ friend class FrameObserver;
+public:
+ ~FrameProvider();
+
+protected:
+ virtual bool get_frame_and_points(cv::Mat& frame, boost::shared_ptr< std::vector<cv::Vec2f> >& points) = 0;
+
+ bool has_observers() const { QMutexLocker lock(&observer_mutex); return !frame_observers.empty(); }
+
+private:
+ mutable QMutex observer_mutex;
+ void add_observer(FrameObserver* obs) { QMutexLocker lock(&observer_mutex); frame_observers.insert(obs); }
+ void remove_observer(FrameObserver* obs) { QMutexLocker lock(&observer_mutex); frame_observers.erase(obs); }
+ std::set<FrameObserver*> frame_observers;
+};
+
+//-----------------------------------------------------------------------------
+// Used to get frame and point information from MutexedFrameProvider
+// Destroy instance if not interested anymore since a living
+// FrameObserver instance causes MutexedFrameProvider to provide the information,
+// potentially reducing its performance
+class FrameObserver
+{
+public:
+ FrameObserver(FrameProvider* provider) : provider(provider) {
+ provider->add_observer(this);
+ }
+
+ ~FrameObserver() {
+ if (provider) provider->remove_observer(this);
+ }
+
+ bool get_frame_and_points(cv::Mat& frame, boost::shared_ptr< std::vector<cv::Vec2f> >& points) {
+ return provider ? provider->get_frame_and_points(frame, points) : false;
+ }
+
+ void on_frame_provider_destroy() {
+ provider = NULL;
+ }
+
+protected:
+ FrameProvider* provider;
+
+private:
+ FrameObserver(const FrameObserver&);
+};
+
+#endif //FRAME_OBSERVER_H
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp
new file mode 100644
index 00000000..ef72f9a2
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp
@@ -0,0 +1,265 @@
+/* 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.
+ */
+
+#include "ftnoir_tracker_pt.h"
+#include <QHBoxLayout>
+#include <cmath>
+#include <QDebug>
+#include <QFile>
+#include <QCoreApplication>
+
+using namespace std;
+using namespace cv;
+using namespace boost;
+
+//#define PT_PERF_LOG //log performance
+
+const float rad2deg = 180.0/3.14159265;
+const float deg2rad = 1.0/rad2deg;
+
+//-----------------------------------------------------------------------------
+Tracker::Tracker()
+ : mutex(QMutex::Recursive),
+ commands(0),
+ video_widget(NULL),
+ video_frame(NULL),
+ tracking_valid(false),
+ new_settings(nullptr)
+
+{
+ qDebug()<<"Tracker::Tracker";
+}
+
+Tracker::~Tracker()
+{
+ qDebug()<<"Tracker::~Tracker";
+ // terminate tracker thread
+ set_command(ABORT);
+ wait();
+ s.video_widget = false;
+ delete video_widget;
+ video_widget = NULL;
+ if (video_frame->layout()) delete video_frame->layout();
+}
+
+void Tracker::set_command(Command command)
+{
+ //QMutexLocker lock(&mutex);
+ commands |= command;
+}
+
+void Tracker::reset_command(Command command)
+{
+ //QMutexLocker lock(&mutex);
+ commands &= ~command;
+}
+
+void Tracker::run()
+{
+ qDebug()<<"Tracker:: Thread started";
+
+#ifdef PT_PERF_LOG
+ QFile log_file(QCoreApplication::applicationDirPath() + "/PointTrackerPerformance.txt");
+ if (!log_file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
+ QTextStream log_stream(&log_file);
+#endif
+
+ time.start();
+ double dt;
+ bool new_frame;
+ forever
+ {
+ if (commands & ABORT) break;
+ if (commands & PAUSE) continue;
+ commands = 0;
+ apply_inner();
+ dt = time.start() / 1000000000.;
+
+ new_frame = camera.get_frame(dt, &frame);
+
+ if (new_frame && !frame.empty())
+ {
+ QMutexLocker lock(&mutex);
+
+ frame = frame_rotation.rotate_frame(frame);
+ const std::vector<cv::Vec2f>& points = point_extractor.extract_points(frame, dt, true);
+ for (auto p : points)
+ {
+ auto p2 = cv::Point(p[0] * frame.cols + frame.cols/2, -p[1] * frame.cols + frame.rows/2);
+ cv::Scalar color(0, 255, 0);
+ cv::line(frame,
+ cv::Point(p2.x - 20, p2.y),
+ cv::Point(p2.x + 20, p2.y),
+ color,
+ 4);
+ cv::line(frame,
+ cv::Point(p2.x, p2.y - 20),
+ cv::Point(p2.x, p2.y + 20),
+ color,
+ 4);
+ }
+ tracking_valid = point_tracker.track(points, camera.get_info().f, dt);
+ video_widget->update_image(frame);
+ }
+#ifdef PT_PERF_LOG
+ log_stream<<"dt: "<<dt;
+ if (!frame.empty()) log_stream<<" fps: "<<camera.get_info().fps;
+ log_stream<<"\n";
+#endif
+ }
+
+ qDebug()<<"Tracker:: Thread stopping";
+}
+void Tracker::apply(settings& s)
+{
+ // caller guarantees object lifetime
+ new_settings = &s;
+}
+
+void Tracker::apply_inner()
+{
+ settings* tmp = new_settings.exchange(nullptr);
+ if (tmp == nullptr)
+ return;
+ auto& s = *tmp;
+ qDebug()<<"Tracker:: Applying settings";
+ camera.set_device_index(s.cam_index);
+ camera.set_res(s.cam_res_x, s.cam_res_y);
+ camera.set_fps(s.cam_fps);
+ camera.set_f(s.cam_f);
+ frame_rotation.rotation = static_cast<RotationType>(static_cast<int>(s.cam_roll));
+ point_extractor.threshold_val = s.threshold;
+ point_extractor.threshold_secondary_val = s.threshold_secondary;
+ point_extractor.min_size = s.min_point_size;
+ point_extractor.max_size = s.max_point_size;
+ {
+ cv::Vec3f M01(s.m01_x, s.m01_y, s.m01_z);
+ cv::Vec3f M02(s.m02_x, s.m02_y, s.m02_z);
+ point_tracker.point_model = boost::shared_ptr<PointModel>(new PointModel(M01, M02));
+ }
+ point_tracker.dynamic_pose_resolution = s.dyn_pose_res;
+ point_tracker.dt_reset = s.reset_time / 1000.0;
+ t_MH = cv::Vec3f(s.t_MH_x, s.t_MH_y, s.t_MH_z);
+ R_GC = Matx33f( cos(deg2rad*s.cam_yaw), 0, sin(deg2rad*s.cam_yaw),
+ 0, 1, 0,
+ -sin(deg2rad*s.cam_yaw), 0, cos(deg2rad*s.cam_yaw));
+ R_GC = R_GC * Matx33f( 1, 0, 0,
+ 0, cos(deg2rad*s.cam_pitch), sin(deg2rad*s.cam_pitch),
+ 0, -sin(deg2rad*s.cam_pitch), cos(deg2rad*s.cam_pitch));
+
+ FrameTrafo X_MH(Matx33f::eye(), t_MH);
+ X_GH_0 = R_GC * X_MH;
+
+ qDebug()<<"Tracker::apply ends";
+}
+
+void Tracker::reset()
+{
+ QMutexLocker lock(&mutex);
+ point_tracker.reset();
+}
+
+void Tracker::center()
+{
+ point_tracker.reset(); // we also do a reset here since there is no reset shortkey yet
+ QMutexLocker lock(&mutex);
+ FrameTrafo X_CM_0 = point_tracker.get_pose();
+ FrameTrafo X_MH(Matx33f::eye(), t_MH);
+ X_GH_0 = R_GC * X_CM_0 * X_MH;
+}
+
+bool Tracker::get_frame_and_points(cv::Mat& frame_copy, boost::shared_ptr< std::vector<Vec2f> >& points)
+{
+ QMutexLocker lock(&mutex);
+ if (frame.empty()) return false;
+
+ // copy the frame and points from the tracker thread
+ frame_copy = frame.clone();
+ points = boost::shared_ptr< vector<Vec2f> >(new vector<Vec2f>(point_extractor.get_points()));
+ return true;
+}
+
+void Tracker::refreshVideo()
+{
+ if (video_widget) video_widget->update_frame_and_points();
+}
+
+void Tracker::StartTracker(QFrame *parent_window)
+{
+ this->video_frame = parent_window;
+ video_frame->setAttribute(Qt::WA_NativeWindow);
+ video_frame->show();
+ video_widget = new PTVideoWidget(video_frame, this);
+ QHBoxLayout* video_layout = new QHBoxLayout(parent_window);
+ video_layout->setContentsMargins(0, 0, 0, 0);
+ video_layout->addWidget(video_widget);
+ video_frame->setLayout(video_layout);
+ video_widget->resize(video_frame->width(), video_frame->height());
+ camera.start();
+ apply(s);
+ start();
+ reset_command(PAUSE);
+}
+
+#ifndef OPENTRACK_API
+void Tracker::StopTracker(bool exit)
+{
+ set_command(PAUSE);
+}
+#endif
+
+#ifdef OPENTRACK_API
+#define THeadPoseData double
+#endif
+
+void Tracker::GetHeadPoseData(THeadPoseData *data)
+{
+ {
+ QMutexLocker lock(&mutex);
+
+ if (!tracking_valid) return;
+
+ FrameTrafo X_CM = point_tracker.get_pose();
+ FrameTrafo X_MH(Matx33f::eye(), t_MH);
+ FrameTrafo X_GH = R_GC * X_CM * X_MH;
+ Matx33f R = X_GH.R * X_GH_0.R.t();
+ Vec3f t = X_GH.t - X_GH_0.t;
+
+ // get translation(s)
+ if (s.bEnableX) data[TX] = t[0] / 10.0; // convert to cm
+ if (s.bEnableY) data[TY] = t[1] / 10.0;
+ if (s.bEnableZ) data[TZ] = t[2] / 10.0;
+
+ // translate rotation matrix from opengl (G) to roll-pitch-yaw (E) frame
+ // -z -> x, y -> z, x -> -y
+ Matx33f R_EG( 0, 0,-1,
+ -1, 0, 0,
+ 0, 1, 0);
+ R = R_EG * R * R_EG.t();
+
+ // extract rotation angles
+ float alpha, beta, gamma;
+ beta = atan2( -R(2,0), sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) );
+ alpha = atan2( R(1,0), R(0,0));
+ gamma = atan2( R(2,1), R(2,2));
+
+ if (s.bEnableYaw) data[Yaw] = rad2deg * alpha;
+ if (s.bEnablePitch) data[Pitch] = - rad2deg * beta; // FTNoIR expects a minus here
+ if (s.bEnableRoll) data[Roll] = rad2deg * gamma;
+ }
+}
+
+//-----------------------------------------------------------------------------
+#ifdef OPENTRACK_API
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+#else
+#pragma comment(linker, "/export:GetTracker=_GetTracker@0")
+FTNOIR_TRACKER_BASE_EXPORT ITrackerPtr __stdcall GetTracker()
+#endif
+{
+ return new Tracker;
+}
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.h
new file mode 100644
index 00000000..190ed76a
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.h
@@ -0,0 +1,95 @@
+/* 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 FTNOIR_TRACKER_PT_H
+#define FTNOIR_TRACKER_PT_H
+
+#ifdef OPENTRACK_API
+# include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+# include "facetracknoir/global-settings.h"
+#endif
+#include "ftnoir_tracker_pt_settings.h"
+#include "frame_observer.h"
+#include "camera.h"
+#include "point_extractor.h"
+#include "point_tracker.h"
+#include "pt_video_widget.h"
+#include "facetracknoir/timer.hpp"
+
+#include <QThread>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QTime>
+#include <opencv2/opencv.hpp>
+#include <atomic>
+#ifndef OPENTRACK_API
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+#endif
+#include <vector>
+
+//-----------------------------------------------------------------------------
+// Constantly processes the tracking chain in a separate thread
+class Tracker : public ITracker, QThread, public FrameProvider
+{
+public:
+ Tracker();
+ virtual ~Tracker();
+ virtual void StartTracker(QFrame* parent_window);
+ virtual void GetHeadPoseData(double* data);
+ virtual void refreshVideo();
+
+ void apply(settings& s);
+ void apply_inner();
+ void center();
+ void reset(); // reset the trackers internal state variables
+ void run();
+
+ void get_pose(FrameTrafo* X_CM) { QMutexLocker lock(&mutex); *X_CM = point_tracker.get_pose(); }
+ int get_n_points() { QMutexLocker lock(&mutex); return point_extractor.get_points().size(); }
+ void get_cam_info(CamInfo* info) { QMutexLocker lock(&mutex); *info = camera.get_info(); }
+
+protected:
+ // --- MutexedFrameProvider interface ---
+ virtual bool get_frame_and_points(cv::Mat& frame, boost::shared_ptr< std::vector<cv::Vec2f> >& points);
+
+ // --- thread ---
+ QMutex mutex;
+ // thread commands
+ enum Command {
+ ABORT = 1<<0,
+ PAUSE = 1<<1
+ };
+ void set_command(Command command);
+ void reset_command(Command command);
+ volatile int commands;
+
+ CVCamera camera;
+ FrameRotation frame_rotation;
+ PointExtractor point_extractor;
+ PointTracker point_tracker;
+
+ FrameTrafo X_GH_0; // for centering
+ cv::Vec3f t_MH; // translation from model frame to head frame
+ cv::Matx33f R_GC; // rotation from opengl reference frame to camera frame
+
+ // --- ui ---
+ cv::Mat frame; // the output frame for display
+
+ PTVideoWidget* video_widget;
+ QFrame* video_frame;
+ bool tracking_valid;
+
+ settings s;
+ std::atomic<settings*> new_settings;
+ Timer time;
+};
+
+#undef VideoWidget
+
+#endif // FTNOIR_TRACKER_PT_H
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt.qrc b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.qrc
new file mode 100644
index 00000000..a8f9a1af
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>Resources/cap_front.png</file>
+ <file>Resources/cap_side.png</file>
+ <file>Resources/clip_front.png</file>
+ <file>Resources/clip_side.png</file>
+ <file>Resources/Logo_IR.png</file>
+ </qresource>
+</RCC>
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp
new file mode 100644
index 00000000..c103b78c
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp
@@ -0,0 +1,321 @@
+/* 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.
+ */
+
+#include "ftnoir_tracker_pt_dialog.h"
+
+#include <QMessageBox>
+#include <QDebug>
+#include <opencv2/opencv.hpp>
+#ifndef OPENTRACK_API
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+#endif
+#include <vector>
+
+using namespace std;
+
+//-----------------------------------------------------------------------------
+TrackerDialog::TrackerDialog()
+ : tracker(NULL),
+ video_widget_dialog(NULL),
+ timer(this),
+ trans_calib_running(false)
+{
+ qDebug()<<"TrackerDialog::TrackerDialog";
+ setAttribute(Qt::WA_DeleteOnClose, false);
+
+ ui.setupUi( this );
+
+ vector<string> device_names;
+ get_camera_device_names(device_names);
+ for (vector<string>::iterator iter = device_names.begin(); iter != device_names.end(); ++iter)
+ {
+ ui.camdevice_combo->addItem(iter->c_str());
+ }
+
+ ui.camroll_combo->addItem("-90");
+ ui.camroll_combo->addItem("0");
+ ui.camroll_combo->addItem("90");
+
+ tie_setting(s.dyn_pose_res, ui.dynpose_check);
+ tie_setting(s.reset_time, ui.reset_spin);
+
+ tie_setting(s.cam_index, ui.camdevice_combo);
+ tie_setting(s.cam_f, ui.f_dspin);
+ tie_setting(s.cam_res_x, ui.res_x_spin);
+ tie_setting(s.cam_res_y, ui.res_y_spin);
+ tie_setting(s.cam_fps, ui.fps_spin);
+ tie_setting(s.cam_roll, ui.camroll_combo);
+ tie_setting(s.cam_pitch, ui.campitch_spin);
+ tie_setting(s.cam_yaw, ui.camyaw_spin);
+
+ tie_setting(s.threshold_secondary, ui.threshold_secondary_slider);
+ tie_setting(s.threshold, ui.threshold_slider);
+
+ tie_setting(s.bEnableYaw, ui.chkEnableYaw);
+ tie_setting(s.bEnablePitch, ui.chkEnablePitch);
+ tie_setting(s.bEnableRoll, ui.chkEnableRoll);
+ tie_setting(s.bEnableX, ui.chkEnableX);
+ tie_setting(s.bEnableY, ui.chkEnableY);
+ tie_setting(s.bEnableZ, ui.chkEnableZ);
+
+ tie_setting(s.min_point_size, ui.mindiam_spin);
+ tie_setting(s.max_point_size, ui.maxdiam_spin);
+
+ tie_setting(s.clip_by, ui.clip_bheight_spin);
+ tie_setting(s.clip_bz, ui.clip_blength_spin);
+ tie_setting(s.clip_ty, ui.clip_theight_spin);
+ tie_setting(s.clip_tz, ui.clip_tlength_spin);
+
+ tie_setting(s.cap_x, ui.cap_width_spin);
+ tie_setting(s.cap_y, ui.cap_height_spin);
+ tie_setting(s.cap_z, ui.cap_length_spin);
+
+ tie_setting(s.m01_x, ui.m1x_spin);
+ tie_setting(s.m01_y, ui.m1y_spin);
+ tie_setting(s.m01_z, ui.m1z_spin);
+
+ tie_setting(s.m02_x, ui.m2x_spin);
+ tie_setting(s.m02_y, ui.m2y_spin);
+ tie_setting(s.m02_z, ui.m2z_spin);
+
+ tie_setting(s.t_MH_x, ui.tx_spin);
+ tie_setting(s.t_MH_y, ui.ty_spin);
+ tie_setting(s.t_MH_z, ui.tz_spin);
+
+ connect( ui.tcalib_button,SIGNAL(toggled(bool)), this,SLOT(startstop_trans_calib(bool)) );
+ connect(ui.reset_button, SIGNAL(clicked()), this, SLOT(doReset()));
+
+ connect(ui.ok_button, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.cancel_button, SIGNAL(clicked()), this, SLOT(doCancel()));
+ connect(ui.btnApply, SIGNAL(clicked()), this, SLOT(doApply()));
+
+ ui.model_tabs->setCurrentIndex(s.active_model_panel);
+
+ connect(ui.model_tabs, SIGNAL(currentChanged(int)), this, SLOT(set_model(int)));
+ connect(&timer,SIGNAL(timeout()), this,SLOT(poll_tracker_info()));
+ timer.start(100);
+
+ connect(s.b.get(), SIGNAL(bundleChanged()), this, SLOT(do_apply_without_saving()));
+}
+
+void TrackerDialog::set_model_clip()
+{
+ s.m01_x = 0;
+ s.m01_y = static_cast<double>(s.clip_ty);
+ s.m01_z = -static_cast<double>(s.clip_tz);
+ s.m02_x = 0;
+ s.m02_y = -static_cast<double>(s.clip_by);
+ s.m02_z = -static_cast<double>(s.clip_bz);
+
+ settings_changed();
+}
+
+void TrackerDialog::set_model_cap()
+{
+ s.m01_x = -static_cast<double>(s.cap_x);
+ s.m01_y = -static_cast<double>(s.cap_y);
+ s.m01_z = -static_cast<double>(s.cap_z);
+ s.m02_x = static_cast<double>(s.cap_x);
+ s.m02_y = -static_cast<double>(s.cap_y);
+ s.m02_z = -static_cast<double>(s.cap_z);
+
+ settings_changed();
+}
+
+void TrackerDialog::set_model_custom()
+{
+ settings_changed();
+}
+
+void TrackerDialog::set_model(int val)
+{
+ s.active_model_panel = val;
+}
+
+void TrackerDialog::startstop_trans_calib(bool start)
+{
+ if (start)
+ {
+ qDebug()<<"TrackerDialog:: Starting translation calibration";
+ trans_calib.reset();
+ trans_calib_running = true;
+ }
+ else
+ {
+ qDebug()<<"TrackerDialog:: Stoppping translation calibration";
+ trans_calib_running = false;
+ {
+ auto tmp = trans_calib.get_estimate();
+ s.t_MH_x = tmp[0];
+ s.t_MH_y = tmp[1];
+ s.t_MH_z = tmp[2];
+ }
+ settings_changed();
+ }
+}
+
+void TrackerDialog::trans_calib_step()
+{
+ if (tracker)
+ {
+ FrameTrafo X_CM;
+ tracker->get_pose(&X_CM);
+ trans_calib.update(X_CM.R, X_CM.t);
+ cv::Vec3f t_MH = trans_calib.get_estimate();
+ s.t_MH_x = t_MH[0];
+ s.t_MH_y = t_MH[1];
+ s.t_MH_z = t_MH[2];
+ }
+}
+
+void TrackerDialog::settings_changed()
+{
+ if (tracker) tracker->apply(s);
+}
+
+void TrackerDialog::doCenter()
+{
+ if (tracker) tracker->center();
+}
+
+void TrackerDialog::doReset()
+{
+ if (tracker) tracker->reset();
+}
+
+void TrackerDialog::save()
+{
+ do_apply_without_saving();
+ s.b->save();
+}
+
+void TrackerDialog::doOK()
+{
+ save();
+ close();
+}
+
+void TrackerDialog::do_apply_without_saving()
+{
+ switch (s.active_model_panel) {
+ default:
+ case 0:
+ set_model_clip();
+ break;
+ case 1:
+ set_model_cap();
+ break;
+ case 2:
+ set_model_custom();
+ break;
+ }
+ if (tracker) tracker->apply(s);
+}
+
+void TrackerDialog::doApply()
+{
+ save();
+}
+
+void TrackerDialog::doCancel()
+{
+ s.b->revert();
+ close();
+}
+
+void TrackerDialog::widget_destroyed(QObject* obj)
+{
+ if (obj == video_widget_dialog) {
+ // widget was / will be already deleted by Qt
+ destroy_video_widget(false);
+ }
+}
+
+void TrackerDialog::create_video_widget()
+{
+ // this should not happen but better be sure
+ if (video_widget_dialog) destroy_video_widget();
+ if (!tracker) return;
+
+ video_widget_dialog = new VideoWidgetDialog(this, tracker);
+ video_widget_dialog->setAttribute( Qt::WA_DeleteOnClose );
+ connect( video_widget_dialog, SIGNAL(destroyed(QObject*)), this, SLOT(widget_destroyed(QObject*)) );
+ video_widget_dialog->show();
+}
+
+void TrackerDialog::destroy_video_widget(bool do_delete /*= true*/)
+{
+ if (video_widget_dialog) {
+ if (do_delete) delete video_widget_dialog;
+ video_widget_dialog = NULL;
+ }
+}
+
+void TrackerDialog::poll_tracker_info()
+{
+ if (tracker)
+ {
+ QString to_print;
+
+ // display caminfo
+ CamInfo info;
+ tracker->get_cam_info(&info);
+ to_print = QString::number(info.res_x)+"x"+QString::number(info.res_y)+" @ "+QString::number(info.fps)+" FPS";
+ ui.caminfo_label->setText(to_print);
+
+ // display pointinfo
+ int n_points = tracker->get_n_points();
+ to_print = QString::number(n_points);
+ if (n_points == 3)
+ to_print += " OK!";
+ else
+ to_print += " BAD!";
+ ui.pointinfo_label->setText(to_print);
+
+ // update calibration
+ if (trans_calib_running) trans_calib_step();
+
+ // update videowidget
+ if (video_widget_dialog) {
+ video_widget_dialog->get_video_widget()->update_frame_and_points();
+ }
+ }
+ else
+ {
+ QString to_print = "Tracker offline";
+ ui.caminfo_label->setText(to_print);
+ ui.pointinfo_label->setText(to_print);
+ }
+}
+
+void TrackerDialog::registerTracker(ITracker *t)
+{
+ qDebug()<<"TrackerDialog:: Tracker registered";
+ tracker = static_cast<Tracker*>(t);
+ if (isVisible() & s.b->modifiedp())
+ tracker->apply(s);
+ ui.tcalib_button->setEnabled(true);
+ //ui.center_button->setEnabled(true);
+ ui.reset_button->setEnabled(true);
+}
+
+void TrackerDialog::unRegisterTracker()
+{
+ qDebug()<<"TrackerDialog:: Tracker un-registered";
+ tracker = NULL;
+ destroy_video_widget();
+ ui.tcalib_button->setEnabled(false);
+ //ui.center_button->setEnabled(false);
+ ui.reset_button->setEnabled(false);
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerDialog;
+}
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h
new file mode 100644
index 00000000..0325160d
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h
@@ -0,0 +1,70 @@
+/* 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 FTNOIR_TRACKER_PT_DIALOG_H
+#define FTNOIR_TRACKER_PT_DIALOG_H
+
+#ifdef OPENTRACK_API
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#else
+#include "..\ftnoir_tracker_base\ftnoir_tracker_base.h"
+#endif
+#include "ftnoir_tracker_pt_settings.h"
+#include "ftnoir_tracker_pt.h"
+#include "trans_calib.h"
+#include "pt_video_widget.h"
+#include "ui_FTNoIR_PT_Controls.h"
+
+#include <QTimer>
+
+//-----------------------------------------------------------------------------
+// The dialog that shows up when the user presses "Settings"
+class TrackerDialog : public QWidget, Ui::UICPTClientControls, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ TrackerDialog();
+ void registerTracker(ITracker *tracker);
+ void unRegisterTracker();
+ void save();
+ void trans_calib_step();
+
+public slots:
+ void doCenter();
+ void doReset();
+ void doOK();
+ void doApply();
+ void doCancel();
+ void do_apply_without_saving();
+
+ void startstop_trans_calib(bool start);
+ void widget_destroyed(QObject* obj);
+ void create_video_widget();
+ void poll_tracker_info();
+ void set_model(int idx);
+
+protected:
+ void destroy_video_widget(bool do_delete = true);
+
+ void set_model_clip();
+ void set_model_cap();
+ void set_model_custom();
+
+ void settings_changed();
+
+ settings s;
+ Tracker* tracker;
+ VideoWidgetDialog* video_widget_dialog;
+ QTimer timer;
+
+ TranslationCalibrator trans_calib;
+ bool trans_calib_running;
+
+ Ui::UICPTClientControls ui;
+};
+
+#endif //FTNOIR_TRACKER_PT_DIALOG_H
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.cpp
new file mode 100644
index 00000000..f3fbbef7
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.cpp
@@ -0,0 +1,42 @@
+/* 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.
+ */
+
+#include "ftnoir_tracker_pt_dll.h"
+#include <QIcon>
+
+//-----------------------------------------------------------------------------
+void TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "PointTracker 1.1";
+}
+
+void TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "PointTracker";
+}
+
+void TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "Tracks a 3-point model with know geometry like Freetrack / TrackIR";
+}
+
+void TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/Resources/Logo_IR.png");
+}
+
+
+#ifdef OPENTRACK_API
+# include "facetracknoir/global-settings.h"
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+#else
+# pragma comment(linker, "/export:GetTrackerDll=_GetTrackerDll@0")
+FTNOIR_TRACKER_BASE_EXPORT ITrackerDllPtr __stdcall GetTrackerDll()
+#endif
+{
+ return new TrackerDll;
+}
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.h
new file mode 100644
index 00000000..1d30e7e5
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dll.h
@@ -0,0 +1,27 @@
+/* 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.
+ */
+
+#if defined(OPENTRACK_API)
+# include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+# include "facetracknoir/global-settings.h"
+#else
+# include "../ftnoir_tracker_base/ftnoir_tracker_base.h"
+#endif
+
+//-----------------------------------------------------------------------------
+class TrackerDll :
+#if defined(OPENTRACK_API)
+ public Metadata
+#else
+ public ITrackerDll
+#endif
+{
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h
new file mode 100644
index 00000000..109090b3
--- /dev/null
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h
@@ -0,0 +1,90 @@
+/* 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 FTNOIR_TRACKER_PT_SETTINGS_H
+#define FTNOIR_TRACKER_PT_SETTINGS_H
+
+#include <opencv2/opencv.hpp>
+#include "point_tracker.h"
+
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings
+{
+ pbundle b;
+ value<int> cam_index,
+ cam_res_x,
+ cam_res_y,
+ cam_fps,
+ cam_roll,
+ cam_pitch,
+ cam_yaw,
+ threshold,
+ threshold_secondary,
+ min_point_size,
+ max_point_size;
+ value<double> cam_f;
+
+ value<int> m01_x, m01_y, m01_z;
+ value<int> m02_x, m02_y, m02_z;
+ value<bool> dyn_pose_res, video_widget;
+
+ value<int> t_MH_x, t_MH_y, t_MH_z;
+
+ value<int> reset_time;
+
+ value<bool> bEnableYaw, bEnablePitch, bEnableRoll;
+ value<bool> bEnableX, bEnableY, bEnableZ;
+
+ value<int> clip_ty, clip_tz, clip_by, clip_bz;
+ value<int> active_model_panel, cap_x, cap_y, cap_z;
+
+ settings() :
+ b(bundle("tracker-pt")),
+ cam_index(b, "camera-index", 0),
+ cam_res_x(b, "camera-res-width", 640),
+ cam_res_y(b, "camera-res-height", 480),
+ cam_fps(b, "camera-fps", 30),
+ cam_roll(b, "camera-roll", 1),
+ cam_pitch(b, "camera-pitch", 0),
+ cam_yaw(b, "camera-yaw", 0),
+ threshold(b, "threshold-primary", 128),
+ threshold_secondary(b, "threshold-secondary", 128),
+ min_point_size(b, "min-point-size", 10),
+ max_point_size(b, "max-point-size", 50),
+ cam_f(b, "camera-focal-length", 1),
+ m01_x(b, "m_01-x", 0),
+ m01_y(b, "m_01-y", 0),
+ m01_z(b, "m_01-z", 0),
+ m02_x(b, "m_02-x", 0),
+ m02_y(b, "m_02-y", 0),
+ m02_z(b, "m_02-z", 0),
+ dyn_pose_res(b, "dynamic-pose-resolution", false),
+ video_widget(b, "video-widget", true),
+ t_MH_x(b, "model-centroid-x", 0),
+ t_MH_y(b, "model-centroid-y", 0),
+ t_MH_z(b, "model-centroid-z", 0),
+ reset_time(b, "reset-time", 2000),
+ bEnableYaw(b, "enable-yaw", true),
+ bEnablePitch(b, "enable-pitch", true),
+ bEnableRoll(b, "enable-roll", true),
+ bEnableX(b, "enable-x", true),
+ bEnableY(b, "enable-y", true),
+ bEnableZ(b, "enable-z", true),
+ clip_ty(b, "clip-ty", 0),
+ clip_tz(b, "clip-tz", 0),
+ clip_by(b, "clip-by", 0),
+ clip_bz(b, "clip-bz", 0),
+ active_model_panel(b, "active-model-panel", 0),
+ cap_x(b, "cap-x", 0),
+ cap_y(b, "cap-y", 0),
+ cap_z(b, "cap-z", 0)
+ {}
+};
+
+#endif //FTNOIR_TRACKER_PT_SETTINGS_H
diff --git a/FTNoIR_Tracker_PT/point_extractor.cpp b/FTNoIR_Tracker_PT/point_extractor.cpp
new file mode 100644
index 00000000..d9ff0a5b
--- /dev/null
+++ b/FTNoIR_Tracker_PT/point_extractor.cpp
@@ -0,0 +1,163 @@
+/* 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.
+ */
+
+#include "point_extractor.h"
+#include <QDebug>
+
+
+using namespace cv;
+using namespace std;
+
+
+PointExtractor::PointExtractor(){
+ //if (!AllocConsole()){}
+ //else SetConsoleTitle("debug");
+ //freopen("CON", "w", stdout);
+ //freopen("CON", "w", stderr);
+}
+// ----------------------------------------------------------------------------
+const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool draw_output)
+{
+ const int W = frame.cols;
+ const int H = frame.rows;
+
+ if (frame_last.cols != W || frame_last.rows != H)
+ {
+ frame_last = cv::Mat();
+ }
+
+ // clear old points
+ points.clear();
+
+ // convert to grayscale
+ Mat frame_gray;
+ cvtColor(frame, frame_gray, CV_RGB2GRAY);
+
+ int secondary = threshold_secondary_val;
+
+ // mask for everything that passes the threshold (or: the upper threshold of the hysteresis)
+ Mat frame_bin;
+ // only used if draw_output
+ Mat frame_bin_copy;
+ // mask for everything that passes
+ Mat frame_bin_low;
+ // mask for lower-threshold && combined result of last, needs to remain in scope until drawing, but is only used if secondary != 0
+ Mat frame_last_and_low;
+
+ if(secondary==0){
+ threshold(frame_gray, frame_bin, threshold_val, 255, THRESH_BINARY);
+ }else{
+ // we recombine a number of buffers, this might be slower than a single loop of per-pixel logic
+ // but it might as well be faster if openCV makes good use of SIMD
+ float t = threshold_val;
+ //float hyst = float(threshold_secondary_val)/512.;
+ //threshold(frame_gray, frame_bin, (t + ((255.-t)*hyst)), 255, THRESH_BINARY);
+ float hyst = float(threshold_secondary_val)/256.;
+ threshold(frame_gray, frame_bin, t, 255, THRESH_BINARY);
+ threshold(frame_gray, frame_bin_low,std::max(float(1), t - (t*hyst)), 255, THRESH_BINARY);
+
+ if(draw_output) frame_bin.copyTo(frame_bin_copy);
+ if(frame_last.empty()){
+ frame_bin.copyTo(frame_last);
+ }else{
+ // keep pixels from last if they are above lower threshold
+ bitwise_and(frame_last, frame_bin_low, frame_last_and_low);
+ // union of pixels >= higher threshold and pixels >= lower threshold
+ bitwise_or(frame_bin, frame_last_and_low, frame_last);
+ frame_last.copyTo(frame_bin);
+ }
+ }
+ unsigned int region_size_min = 3.14*min_size*min_size/4.0;
+ unsigned int region_size_max = 3.14*max_size*max_size/4.0;
+
+ int blob_index = 1;
+ for (int y=0; y<H; y++)
+ {
+ if (blob_index >= 255) break;
+ for (int x=0; x<W; x++)
+ {
+ if (blob_index >= 255) break;
+
+ // find connected components with floodfill
+ if (frame_bin.at<unsigned char>(y,x) != 255) continue;
+ Rect rect;
+
+ floodFill(frame_bin, Point(x,y), Scalar(blob_index), &rect, Scalar(0), Scalar(0), FLOODFILL_FIXED_RANGE);
+ blob_index++;
+
+ // calculate the size of the connected component
+ unsigned int region_size = 0;
+ for (int i=rect.y; i < (rect.y+rect.height); i++)
+ {
+ for (int j=rect.x; j < (rect.x+rect.width); j++)
+ {
+ if (frame_bin.at<unsigned char>(i,j) != blob_index-1) continue;
+ region_size++;
+ }
+ }
+
+ if (region_size < region_size_min || region_size > region_size_max) continue;
+
+ // calculate the center of mass:
+ // mx = (sum_ij j*f(frame_grey_ij)) / (sum_ij f(frame_grey_ij))
+ // my = ...
+ // f maps from [threshold,256] -> [0, 1], lower values are mapped to 0
+ float m = 0;
+ float mx = 0;
+ float my = 0;
+ for (int i=rect.y; i < (rect.y+rect.height); i++)
+ {
+ for (int j=rect.x; j < (rect.x+rect.width); j++)
+ {
+ if (frame_bin.at<unsigned char>(i,j) != blob_index-1) continue;
+ float val;
+
+ if(secondary==0){
+ val = frame_gray.at<unsigned char>(i,j);
+ val = float(val - threshold_val)/(256 - threshold_val);
+ val = val*val; // makes it more stable (less emphasis on low values, more on the peak)
+ }else{
+ //hysteresis point detection gets stability from ignoring pixel noise so we decidedly leave the actual pixel values out of the picture
+ val = frame_last.at<unsigned char>(i,j) / 256.;
+ }
+
+ m += val;
+ mx += j * val;
+ my += i * val;
+ }
+ }
+
+ // convert to centered camera coordinate system with y axis upwards
+ Vec2f c;
+ c[0] = (mx/m - W/2)/W;
+ c[1] = -(my/m - H/2)/W;
+ //qDebug()<<blob_index<<" => "<<c[0]<<" "<<c[1];
+ points.push_back(c);
+ }
+ }
+
+ // draw output image
+ if (draw_output) {
+ vector<Mat> channels;
+ if(secondary==0){
+ frame_bin.setTo(170, frame_bin);
+ channels.push_back(frame_gray + frame_bin);
+ channels.push_back(frame_gray - frame_bin);
+ channels.push_back(frame_gray - frame_bin);
+ }else{
+ frame_bin_copy.setTo(120, frame_bin_copy);
+ frame_bin_low.setTo(90, frame_bin_low);
+ channels.push_back(frame_gray + frame_bin_copy);
+ channels.push_back(frame_gray + frame_last_and_low);
+ channels.push_back(frame_gray + frame_bin_low);
+ //channels.push_back(frame_gray + frame_bin);
+ }
+ merge(channels, frame);
+ }
+
+ return points;
+}
diff --git a/FTNoIR_Tracker_PT/point_extractor.h b/FTNoIR_Tracker_PT/point_extractor.h
new file mode 100644
index 00000000..ff36f3ce
--- /dev/null
+++ b/FTNoIR_Tracker_PT/point_extractor.h
@@ -0,0 +1,35 @@
+/* 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 POINTEXTRACTOR_H
+#define POINTEXTRACTOR_H
+
+#include <opencv2/opencv.hpp>
+#include <opencv2/imgproc/imgproc_c.h>
+
+// ----------------------------------------------------------------------------
+// Extracts points from an opencv image
+class PointExtractor
+{
+public:
+ // extracts points from frame and draws some processing info into frame, if draw_output is set
+ // dt: time since last call in seconds
+ // WARNING: returned reference is valid as long as object
+ const std::vector<cv::Vec2f>& extract_points(cv::Mat frame, float dt, bool draw_output);
+ const std::vector<cv::Vec2f>& get_points() { return points; }
+ PointExtractor();
+
+ int threshold_val;
+ int threshold_secondary_val;
+ int min_size, max_size;
+
+protected:
+ std::vector<cv::Vec2f> points;
+ cv::Mat frame_last;
+};
+
+#endif //POINTEXTRACTOR_H
diff --git a/FTNoIR_Tracker_PT/point_tracker.cpp b/FTNoIR_Tracker_PT/point_tracker.cpp
new file mode 100644
index 00000000..dfefdaf8
--- /dev/null
+++ b/FTNoIR_Tracker_PT/point_tracker.cpp
@@ -0,0 +1,380 @@
+/* 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.
+ */
+
+#include "point_tracker.h"
+
+#include <vector>
+#include <algorithm>
+#include <cmath>
+
+#include <QDebug>
+
+using namespace cv;
+using namespace boost;
+using namespace std;
+
+const float PI = 3.14159265358979323846f;
+
+// ----------------------------------------------------------------------------
+static void get_row(const Matx33f& m, int i, Vec3f& v)
+{
+ v[0] = m(i,0);
+ v[1] = m(i,1);
+ v[2] = m(i,2);
+}
+
+static void set_row(Matx33f& m, int i, const Vec3f& v)
+{
+ m(i,0) = v[0];
+ m(i,1) = v[1];
+ m(i,2) = v[2];
+}
+
+// ----------------------------------------------------------------------------
+PointModel::PointModel(Vec3f M01, Vec3f M02)
+ : M01(M01),
+ M02(M02)
+{
+ // calculate u
+ u = M01.cross(M02);
+ u /= norm(u);
+
+ // calculate projection matrix on M01,M02 plane
+ float s11 = M01.dot(M01);
+ float s12 = M01.dot(M02);
+ float s22 = M02.dot(M02);
+ P = 1.0/(s11*s22-s12*s12) * Matx22f(s22, -s12,
+ -s12, s11);
+
+ // calculate d and d_order for simple freetrack-like point correspondence
+ vector<Vec2f> points;
+ points.push_back(Vec2f(0,0));
+ points.push_back(Vec2f(M01[0], M01[1]));
+ points.push_back(Vec2f(M02[0], M02[1]));
+ // fit line to orthographically projected points
+ // ERROR: yields wrong results with colinear points?!
+ /*
+ Vec4f line;
+ fitLine(points, line, CV_DIST_L2, 0, 0.01, 0.01);
+ d[0] = line[0]; d[1] = line[1];
+ */
+ // TODO: fix this
+ d = Vec2f(M01[0]-M02[0], M01[1]-M02[1]);
+
+ // sort model points
+ get_d_order(points, d_order);
+}
+
+#ifdef OPENTRACK_API
+static bool d_vals_sort(const pair<float,int> a, const pair<float,int> b)
+{
+ return a.first < b.first;
+}
+#endif
+
+void PointModel::get_d_order(const std::vector<cv::Vec2f>& points, int d_order[]) const
+{
+ // get sort indices with respect to d scalar product
+ vector< pair<float,int> > d_vals;
+ for (int i = 0; i<points.size(); ++i)
+ d_vals.push_back(pair<float, int>(d.dot(points[i]), i));
+
+ struct
+ {
+ bool operator()(const pair<float, int>& a, const pair<float, int>& b) { return a.first < b.first; }
+ } comp;
+ std::sort(d_vals.begin(),
+ d_vals.end(),
+#ifdef OPENTRACK_API
+ d_vals_sort
+#else
+ comp
+#endif
+ );
+
+ for (int i = 0; i<points.size(); ++i)
+ d_order[i] = d_vals[i].second;
+}
+
+
+// ----------------------------------------------------------------------------
+PointTracker::PointTracker()
+ : init_phase(true),
+ dt_valid(0),
+ dt_reset(1),
+ v_t(0,0,0),
+ v_r(0,0,0),
+ dynamic_pose_resolution(true)
+{
+ X_CM.t[2] = 1000; // default position: 1 m away from cam;
+}
+
+void PointTracker::reset()
+{
+ // enter init phase and reset velocities
+ init_phase = true;
+ dt_valid = 0;
+ reset_velocities();
+}
+
+void PointTracker::reset_velocities()
+{
+ v_t = Vec3f(0,0,0);
+ v_r = Vec3f(0,0,0);
+}
+
+
+bool PointTracker::track(const vector<Vec2f>& points, float f, float dt)
+{
+ if (!dynamic_pose_resolution) init_phase = true;
+
+ dt_valid += dt;
+ // if there was no valid tracking result for too long, do a reset
+ if (dt_valid > dt_reset)
+ {
+ //qDebug()<<"dt_valid "<<dt_valid<<" > dt_reset "<<dt_reset;
+ reset();
+ }
+
+ bool no_model =
+#ifdef OPENTRACK_API
+ point_model.get() == NULL;
+#else
+ !point_model;
+#endif
+
+ // if there is a pointtracking problem, reset the velocities
+ if (no_model || points.size() != PointModel::N_POINTS)
+ {
+ //qDebug()<<"Wrong number of points!";
+ reset_velocities();
+ return false;
+ }
+
+ X_CM_old = X_CM; // backup old transformation for velocity calculation
+
+ if (!init_phase)
+ predict(dt_valid);
+
+ // if there is a point correspondence problem something has gone wrong, do a reset
+ if (!find_correspondences(points, f))
+ {
+ //qDebug()<<"Error in finding point correspondences!";
+ X_CM = X_CM_old; // undo prediction
+ reset();
+ return false;
+ }
+
+ int n_iter = POSIT(f);
+ //qDebug()<<"Number of POSIT iterations: "<<n_iter;
+
+ if (!init_phase)
+ update_velocities(dt_valid);
+
+ // we have a valid tracking result, leave init phase and reset time since valid result
+ init_phase = false;
+ dt_valid = 0;
+ return true;
+}
+
+void PointTracker::predict(float dt)
+{
+ // predict with constant velocity
+ Matx33f R;
+ Rodrigues(dt*v_r, R);
+ X_CM.R = R*X_CM.R;
+ X_CM.t += dt * v_t;
+}
+
+void PointTracker::update_velocities(float dt)
+{
+ // update velocities
+ Rodrigues(X_CM.R*X_CM_old.R.t(), v_r);
+ v_r /= dt;
+ v_t = (X_CM.t - X_CM_old.t)/dt;
+}
+
+bool PointTracker::find_correspondences(const vector<Vec2f>& points, float f)
+{
+ if (init_phase) {
+ // We do a simple freetrack-like sorting in the init phase...
+ // sort points
+ int point_d_order[PointModel::N_POINTS];
+ point_model->get_d_order(points, point_d_order);
+
+ // set correspondences
+ for (int i=0; i<PointModel::N_POINTS; ++i)
+ {
+ p[point_model->d_order[i]] = points[point_d_order[i]];
+ }
+ }
+ else {
+ // ... otherwise we look at the distance to the projection of the expected model points
+ // project model points under current pose
+ p_exp[0] = project(Vec3f(0,0,0), f);
+ p_exp[1] = project(point_model->M01, f);
+ p_exp[2] = project(point_model->M02, f);
+
+ // set correspondences by minimum distance to projected model point
+ bool point_taken[PointModel::N_POINTS];
+ for (int i=0; i<PointModel::N_POINTS; ++i)
+ point_taken[i] = false;
+
+ float min_sdist = 0;
+ int min_idx = 0;
+
+ for (int i=0; i<PointModel::N_POINTS; ++i)
+ {
+ // find closest point to projected model point i
+ for (int j=0; j<PointModel::N_POINTS; ++j)
+ {
+ Vec2f d = p_exp[i]-points[j];
+ float sdist = d.dot(d);
+ if (sdist < min_sdist || j==0)
+ {
+ min_idx = j;
+ min_sdist = sdist;
+ }
+ }
+ // if one point is closest to more than one model point, abort
+ if (point_taken[min_idx]) return false;
+ point_taken[min_idx] = true;
+ p[i] = points[min_idx];
+ }
+ }
+ return true;
+}
+
+
+
+int PointTracker::POSIT(float f)
+{
+ // POSIT algorithm for coplanar points as presented in
+ // [Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis: "Iterative Pose Estimation Using Coplanar Feature Points"]
+ // we use the same notation as in the paper here
+
+ // The expected rotation used for resolving the ambiguity in POSIT:
+ // In every iteration step the rotation closer to R_expected is taken
+ Matx33f R_expected;
+ if (init_phase)
+ R_expected = Matx33f::eye(); // in the init phase, we want to be close to the default pose = no rotation
+ else
+ R_expected = X_CM.R; // later we want to be close to the last (predicted) rotation
+
+ // initial pose = last (predicted) pose
+ Vec3f k;
+ get_row(R_expected, 2, k);
+ float Z0 = init_phase ? 1000 : X_CM.t[2];
+
+ float old_epsilon_1 = 0;
+ float old_epsilon_2 = 0;
+ float epsilon_1 = 1;
+ float epsilon_2 = 1;
+
+ Vec3f I0, J0;
+ Vec2f I0_coeff, J0_coeff;
+
+ Vec3f I_1, J_1, I_2, J_2;
+ Matx33f R_1, R_2;
+ Matx33f* R_current;
+
+ const int MAX_ITER = 100;
+ const float EPS_THRESHOLD = 1e-4;
+
+ int i=1;
+ for (; i<MAX_ITER; ++i)
+ {
+ epsilon_1 = k.dot(point_model->M01)/Z0;
+ epsilon_2 = k.dot(point_model->M02)/Z0;
+
+ // vector of scalar products <I0, M0i> and <J0, M0i>
+ Vec2f I0_M0i(p[1][0]*(1.0 + epsilon_1) - p[0][0],
+ p[2][0]*(1.0 + epsilon_2) - p[0][0]);
+ Vec2f J0_M0i(p[1][1]*(1.0 + epsilon_1) - p[0][1],
+ p[2][1]*(1.0 + epsilon_2) - p[0][1]);
+
+ // construct projection of I, J onto M0i plane: I0 and J0
+ I0_coeff = point_model->P * I0_M0i;
+ J0_coeff = point_model->P * J0_M0i;
+ I0 = I0_coeff[0]*point_model->M01 + I0_coeff[1]*point_model->M02;
+ J0 = J0_coeff[0]*point_model->M01 + J0_coeff[1]*point_model->M02;
+
+ // calculate u component of I, J
+ float II0 = I0.dot(I0);
+ float IJ0 = I0.dot(J0);
+ float JJ0 = J0.dot(J0);
+ float rho, theta;
+ if (JJ0 == II0) {
+ rho = sqrt(abs(2*IJ0));
+ theta = -PI/4;
+ if (IJ0<0) theta *= -1;
+ }
+ else {
+ rho = sqrt(sqrt( (JJ0-II0)*(JJ0-II0) + 4*IJ0*IJ0 ));
+ theta = atan( -2*IJ0 / (JJ0-II0) );
+ if (JJ0 - II0 < 0) theta += PI;
+ theta /= 2;
+ }
+
+ // construct the two solutions
+ I_1 = I0 + rho*cos(theta)*point_model->u;
+ I_2 = I0 - rho*cos(theta)*point_model->u;
+
+ J_1 = J0 + rho*sin(theta)*point_model->u;
+ J_2 = J0 - rho*sin(theta)*point_model->u;
+
+ float norm_const = 1.0/norm(I_1); // all have the same norm
+
+ // create rotation matrices
+ I_1 *= norm_const; J_1 *= norm_const;
+ I_2 *= norm_const; J_2 *= norm_const;
+
+ set_row(R_1, 0, I_1);
+ set_row(R_1, 1, J_1);
+ set_row(R_1, 2, I_1.cross(J_1));
+
+ set_row(R_2, 0, I_2);
+ set_row(R_2, 1, J_2);
+ set_row(R_2, 2, I_2.cross(J_2));
+
+ // the single translation solution
+ Z0 = norm_const * f;
+
+ // pick the rotation solution closer to the expected one
+ // in simple metric d(A,B) = || I - A * B^T ||
+ float R_1_deviation = norm(Matx33f::eye() - R_expected * R_1.t());
+ float R_2_deviation = norm(Matx33f::eye() - R_expected * R_2.t());
+
+ if (R_1_deviation < R_2_deviation)
+ R_current = &R_1;
+ else
+ R_current = &R_2;
+
+ get_row(*R_current, 2, k);
+
+ // check for convergence condition
+ if (abs(epsilon_1 - old_epsilon_1) + abs(epsilon_2 - old_epsilon_2) < EPS_THRESHOLD)
+ break;
+ old_epsilon_1 = epsilon_1;
+ old_epsilon_2 = epsilon_2;
+ }
+
+ // apply results
+ X_CM.R = *R_current;
+ X_CM.t[0] = p[0][0] * Z0/f;
+ X_CM.t[1] = p[0][1] * Z0/f;
+ X_CM.t[2] = Z0;
+
+ return i;
+
+ //Rodrigues(X_CM.R, r);
+ //qDebug()<<"iter: "<<i;
+ //qDebug()<<"t: "<<X_CM.t[0]<<' '<<X_CM.t[1]<<' '<<X_CM.t[2];
+ //Vec3f r;
+ //
+ //qDebug()<<"r: "<<r[0]<<' '<<r[1]<<' '<<r[2]<<'\n';
+}
diff --git a/FTNoIR_Tracker_PT/point_tracker.h b/FTNoIR_Tracker_PT/point_tracker.h
new file mode 100644
index 00000000..11034100
--- /dev/null
+++ b/FTNoIR_Tracker_PT/point_tracker.h
@@ -0,0 +1,129 @@
+/* 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 POINTTRACKER_H
+#define POINTTRACKER_H
+
+#include <opencv2/opencv.hpp>
+#ifndef OPENTRACK_API
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+#endif
+#include <list>
+
+// ----------------------------------------------------------------------------
+// Affine frame trafo
+class FrameTrafo
+{
+public:
+ FrameTrafo() : R(cv::Matx33f::eye()), t(0,0,0) {}
+ FrameTrafo(const cv::Matx33f& R, const cv::Vec3f& t) : R(R),t(t) {}
+
+ cv::Matx33f R;
+ cv::Vec3f t;
+};
+
+inline FrameTrafo operator*(const FrameTrafo& X, const FrameTrafo& Y)
+{
+ return FrameTrafo(X.R*Y.R, X.R*Y.t + X.t);
+}
+
+inline FrameTrafo operator*(const cv::Matx33f& X, const FrameTrafo& Y)
+{
+ return FrameTrafo(X*Y.R, X*Y.t);
+}
+
+inline FrameTrafo operator*(const FrameTrafo& X, const cv::Matx33f& Y)
+{
+ return FrameTrafo(X.R*Y, X.t);
+}
+
+inline cv::Vec3f operator*(const FrameTrafo& X, const cv::Vec3f& v)
+{
+ return X.R*v + X.t;
+}
+
+
+// ----------------------------------------------------------------------------
+// Describes a 3-point model
+// nomenclature as in
+// [Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis: "Iterative Pose Estimation Using Coplanar Feature Points"]
+class PointModel
+{
+ friend class PointTracker;
+public:
+ static const int N_POINTS = 3;
+
+ PointModel(cv::Vec3f M01, cv::Vec3f M02);
+
+ const cv::Vec3f& get_M01() const { return M01; };
+ const cv::Vec3f& get_M02() const { return M02; };
+
+protected:
+ cv::Vec3f M01; // M01 in model frame
+ cv::Vec3f M02; // M02 in model frame
+
+ cv::Vec3f u; // unit vector perpendicular to M01,M02-plane
+
+ cv::Matx22f P;
+
+ cv::Vec2f d; // discriminant vector for point correspondence
+ int d_order[3]; // sorting of projected model points with respect to d scalar product
+
+ void get_d_order(const std::vector<cv::Vec2f>& points, int d_order[]) const;
+};
+
+// ----------------------------------------------------------------------------
+// Tracks a 3-point model
+// implementing the POSIT algorithm for coplanar points as presented in
+// [Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis: "Iterative Pose Estimation Using Coplanar Feature Points"]
+class PointTracker
+{
+public:
+ PointTracker();
+
+ // track the pose using the set of normalized point coordinates (x pos in range -0.5:0.5)
+ // f : (focal length)/(sensor width)
+ // dt : time since last call
+ bool track(const std::vector<cv::Vec2f>& points, float f, float dt);
+ boost::shared_ptr<PointModel> point_model;
+
+ bool dynamic_pose_resolution;
+ float dt_reset;
+
+ FrameTrafo get_pose() const { return X_CM; }
+ void reset();
+
+protected:
+ inline cv::Vec2f project(const cv::Vec3f& v_M, float f)
+ {
+ cv::Vec3f v_C = X_CM * v_M;
+ return cv::Vec2f(f*v_C[0]/v_C[2], f*v_C[1]/v_C[2]);
+ }
+
+ bool find_correspondences(const std::vector<cv::Vec2f>& points, float f);
+
+ cv::Vec2f p[PointModel::N_POINTS]; // the points in model order
+ cv::Vec2f p_exp[PointModel::N_POINTS]; // the expected point positions
+
+ void predict(float dt);
+ void update_velocities(float dt);
+ void reset_velocities();
+
+
+ int POSIT(float f); // The POSIT algorithm, returns the number of iterations
+
+ bool init_phase;
+ float dt_valid; // time since last valid tracking result
+ cv::Vec3f v_t; // velocities
+ cv::Vec3f v_r;
+ FrameTrafo X_CM; // trafo from model to camera
+ FrameTrafo X_CM_old;
+};
+
+#endif //POINTTRACKER_H
diff --git a/FTNoIR_Tracker_PT/pt_video_widget.cpp b/FTNoIR_Tracker_PT/pt_video_widget.cpp
new file mode 100644
index 00000000..02817cbf
--- /dev/null
+++ b/FTNoIR_Tracker_PT/pt_video_widget.cpp
@@ -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.
+ *
+ * 20130312, WVR: Add 7 lines to resizeGL after resize_frame. This should lower CPU-load.
+ */
+
+#include "pt_video_widget.h"
+
+#include <QDebug>
+#include <QHBoxLayout>
+
+using namespace cv;
+using namespace std;
+
+void PTVideoWidget::update_image(const cv::Mat& frame)
+{
+ QMutexLocker foo(&mtx);
+ _frame = frame.clone();
+ freshp = true;
+}
+
+// ----------------------------------------------------------------------------
+VideoWidgetDialog::VideoWidgetDialog(QWidget *parent, FrameProvider* provider)
+ : QDialog(parent),
+ video_widget(NULL)
+{
+ const int VIDEO_FRAME_WIDTH = 640;
+ const int VIDEO_FRAME_HEIGHT = 480;
+
+ video_widget = new PTVideoWidget(this, provider);
+
+ QHBoxLayout* layout = new QHBoxLayout();
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(video_widget);
+ if (this->layout()) delete this->layout();
+ setLayout(layout);
+ resize(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT);
+}
+
+void PTVideoWidget::update_and_repaint()
+{
+ QMutexLocker foo(&mtx);
+ if (_frame.empty() || !freshp)
+ return;
+ freshp = false;
+ QImage qframe = QImage(_frame.cols, _frame.rows, QImage::Format_RGB888);
+ uchar* data = qframe.bits();
+ const int pitch = qframe.bytesPerLine();
+ for (int y = 0; y < _frame.rows; y++)
+ for (int x = 0; x < _frame.cols; x++)
+ {
+ const auto& elt = _frame.at<Vec3b>(y, x);
+ const cv::Scalar elt2 = static_cast<cv::Scalar>(elt);
+ data[y * pitch + x * 3 + 0] = elt2.val[2];
+ data[y * pitch + x * 3 + 1] = elt2.val[1];
+ data[y * pitch + x * 3 + 2] = elt2.val[0];
+ }
+ qframe = qframe.scaled(size(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
+ texture = qframe;
+ update();
+}
diff --git a/FTNoIR_Tracker_PT/pt_video_widget.h b/FTNoIR_Tracker_PT/pt_video_widget.h
new file mode 100644
index 00000000..25d593c3
--- /dev/null
+++ b/FTNoIR_Tracker_PT/pt_video_widget.h
@@ -0,0 +1,71 @@
+/* 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 "frame_observer.h"
+#include <QObject>
+#include <QTime>
+#include <QDialog>
+#include <opencv2/opencv.hpp>
+#ifndef OPENTRACK_API
+# include <QGLWidget>
+# include <boost/shared_ptr.hpp>
+#else
+# include "FTNoIR_Tracker_PT/boost-compat.h"
+# if defined(_WIN32)
+# include <dshow.h>
+# endif
+#endif
+#include <QPainter>
+#include <QPaintEvent>
+#include <QTimer>
+
+class PTVideoWidget : public QWidget, public FrameObserver
+{
+ Q_OBJECT
+
+public:
+ PTVideoWidget(QWidget *parent, FrameProvider* provider) :
+ QWidget(parent),
+ /* to avoid linker errors */ FrameObserver(provider),
+ freshp(false)
+ {
+ connect(&timer, SIGNAL(timeout()), this, SLOT(update_and_repaint()));
+ timer.start(40);
+ }
+ void update_image(const cv::Mat &frame);
+ void update_frame_and_points() {}
+protected slots:
+ void paintEvent( QPaintEvent* e ) {
+ QMutexLocker foo(&mtx);
+ QPainter painter(this);
+ painter.drawImage(e->rect(), texture);
+ }
+ void update_and_repaint();
+private:
+ QMutex mtx;
+ QImage texture;
+ QTimer timer;
+ cv::Mat _frame;
+ bool freshp;
+};
+
+// ----------------------------------------------------------------------------
+// A VideoWidget embedded in a dialog frame
+class VideoWidgetDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ VideoWidgetDialog(QWidget *parent, FrameProvider* provider);
+ virtual ~VideoWidgetDialog() {}
+
+ PTVideoWidget* get_video_widget() { return video_widget; }
+
+private:
+ PTVideoWidget* video_widget;
+};
diff --git a/FTNoIR_Tracker_PT/trans_calib.cpp b/FTNoIR_Tracker_PT/trans_calib.cpp
new file mode 100644
index 00000000..9b75a1b6
--- /dev/null
+++ b/FTNoIR_Tracker_PT/trans_calib.cpp
@@ -0,0 +1,44 @@
+/* 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.
+ */
+
+#include "trans_calib.h"
+
+using namespace cv;
+
+//-----------------------------------------------------------------------------
+TranslationCalibrator::TranslationCalibrator()
+{
+ reset();
+}
+
+void TranslationCalibrator::reset()
+{
+ P = Matx66f::zeros();
+ y = Vec6f(0,0,0, 0,0,0);
+}
+
+void TranslationCalibrator::update(const Matx33f& R_CM_k, const Vec3f& t_CM_k)
+{
+ Matx<float, 6,3> H_k_T = Matx<float, 6,3>::zeros();
+ for (int i=0; i<3; ++i) {
+ for (int j=0; j<3; ++j) {
+ H_k_T(i,j) = R_CM_k(j,i);
+ }
+ }
+ for (int i=0; i<3; ++i)
+ {
+ H_k_T(3+i,i) = 1.0;
+ }
+ P += H_k_T * H_k_T.t();
+ y += H_k_T * t_CM_k;
+}
+
+Vec3f TranslationCalibrator::get_estimate()
+{
+ Vec6f x = P.inv() * y;
+ return Vec3f(-x[0], -x[1], -x[2]);
+} \ No newline at end of file
diff --git a/FTNoIR_Tracker_PT/trans_calib.h b/FTNoIR_Tracker_PT/trans_calib.h
new file mode 100644
index 00000000..f2521690
--- /dev/null
+++ b/FTNoIR_Tracker_PT/trans_calib.h
@@ -0,0 +1,39 @@
+/* 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 TRANSCALIB_H
+#define TRANSCALIB_H
+
+#include <opencv2/opencv.hpp>
+
+//-----------------------------------------------------------------------------
+// Calibrates the translation from head to model = t_MH
+// by recursive least squares /
+// kalman filter in information form with identity noise covariance
+// measurement equation when head position = t_CH is fixed:
+// (R_CM_k , Id)*(-t_MH, t_CH) = t_CM_k
+
+class TranslationCalibrator
+{
+public:
+ TranslationCalibrator();
+
+ // reset the calibration process
+ void reset();
+
+ // update the current estimate
+ void update(const cv::Matx33f& R_CM_k, const cv::Vec3f& t_CM_k);
+
+ // get the current estimate for t_MH
+ cv::Vec3f get_estimate();
+
+protected:
+ cv::Matx66f P; // normalized precision matrix = inverse covariance
+ cv::Vec6f y; // P*(-t_MH, t_CH)
+};
+
+#endif //TRANSCALIB_H \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..04704cfe
--- /dev/null
+++ b/README.md
@@ -0,0 +1,64 @@
+=======
+Windows binary builds are available at <https://www.dropbox.com/sh/544fbhsokdpy3n7/AAAKwl6BluqwT9Xn2slyp0dCa>
+
+Source code access available at <http://github.com/opentrack/opentrack/>
+
+--
+
+**opentrack** is an application dedicated to tracking user's head
+movements and relaying them to games and flight simulation software.
+
+Not to be confused with railway planning software <<http://opentrack.ch>>
+
+See our wiki at <<https://github.com/opentrack/opentrack/wiki>>
+
+# Tracking sources
+
+- PointTracker by Patrick Ruoff, freetrack-like light sources
+- Oculus Rift
+- AR marker support via the ArUco library <https://github.com/rmsalinas/aruco>
+- HT tracker <https://github.com/sthalik/headtracker>
+- Razer Hydra
+- Relaying via UDP from a different computer
+- Joystick analog axes (Windows only)
+
+# Output
+
+- FlightGear Nasal script
+- FSUIPC for Microsoft Flight Simulator (Windows)
+- SimConnect for newer Microsoft Flight Simulator (Windows)
+- freetrack emulation (Windows)
+- Relaying udp to another computer
+- Joystick support via freedesktop.org libevdev (Linux)
+- Joystick support via VJoy (Windows)
+- Wine freetrack glue protocol (Linux, OSX)
+- Tablet-like coordinate output (Windows)
+
+# Configuration
+
+**opentrack** offers output shaping, filtering, is buildable on
+MS Windows, MacOSX and GNU/Linux.
+
+Don't be afraid to submit an issue/feature request if the need arises.
+
+# Credits
+
+- Stanisław Halik
+- Chris Thompson (aka mm0zct)
+- Donovan Baarda
+- Ryan Spicer (OSX tester, contributor)
+- Patrick Ruoff (PT tracker)
+- FuraX49 (hatire arduino tracker)
+- Ulf Schreiber (PT tracker)
+- uglyDwarf (high CON)
+- George Trigonakis (tester)
+- Wim Vriend (historically)
+- Ron Hendriks (historically)
+
+# Licensing information
+
+The code originally licensed under GPLv3, new code is required to be
+compatible with it unless resides in separate address space.
+
+It's recommended to submit new code under ISC license, it's a shorter
+boilerplate header than MIT/X11 or new BSD.
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 00000000..e9eb232a
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,29 @@
+20131023 sh
+ Low-hanging fruit: Go through all forms and replace ok/cancel with
+ QButtonBox which works better with layouts.
+20131020 sh
+ Add unit testing by means of batch execution, protocol/filter that does
+ nothing, add separate executables for readers of specific protocols,
+ and run continuous integration every time commit happens that day.
+
+ Add statically-typed settings trees, convert result to qsettings-enabled
+ ini files. Use metadata props in order to get class name for ini section.
+ Required here are also arrays of settings. Use QList<T> for template
+ specialization.
+20131019 mm0zct
+ Ship more then one profile for configuring the curves etc.
+ There are two main user bases, HMD and traditional monitor+webcam users,
+ each wants a drastically different curve profile (HMD is 1:1 on all axes)
+ Also re:boost, I'd rather avoid extra library dependences if possible.
+
+ Rift tracker could do with positional estimation using intertial sensors.
+ Rift could do with a return-yaw-to-centre hotkey that's not the global all-axis option.
+ Hydra is really just a hack just now, could be improved a lot.
+
+ Add per-tracker hotkey support
+20131011 sh
+ low-hanging fruit: default saving profiles to a directory in user home,
+ not into global stuffies
+
+ as for build system, low-hanging fruit is writing functions/macrology
+ for all the repetition out there.
diff --git a/bin/NPClient.dll b/bin/NPClient.dll
new file mode 100755
index 00000000..bf971c03
--- /dev/null
+++ b/bin/NPClient.dll
Binary files differ
diff --git a/bin/NPClient64.dll b/bin/NPClient64.dll
new file mode 100755
index 00000000..fd3164e5
--- /dev/null
+++ b/bin/NPClient64.dll
Binary files differ
diff --git a/bin/TrackIR.exe b/bin/TrackIR.exe
new file mode 100644
index 00000000..ce05c028
--- /dev/null
+++ b/bin/TrackIR.exe
Binary files differ
diff --git a/bin/cleye.config b/bin/cleye.config
new file mode 100644
index 00000000..55a478ab
--- /dev/null
+++ b/bin/cleye.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cleye>
+ <item name="mode" value="advanced" />
+</cleye> \ No newline at end of file
diff --git a/bin/settings/default.ini b/bin/settings/default.ini
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/bin/settings/default.ini
diff --git a/bin/settings/facetracknoir supported games.csv b/bin/settings/facetracknoir supported games.csv
new file mode 100644
index 00000000..91b9e6e6
--- /dev/null
+++ b/bin/settings/facetracknoir supported games.csv
@@ -0,0 +1,519 @@
+No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID
+1;18 Wheels of Steel: Haulin';FreeTrack20;V160;V;doc-uk;13601;000121F172F35116A02100
+2;1944 D-Day;FreeTrack20;V160;;;15701;00022E542A6A0575F05200
+497;2KMarinNEXT;FreeTrack20;V170;;;3425;0D614F6A0820D1EA8EE800
+3;3D Instructor;FreeTrack20;V160;;;20490;00034B0FC367116611B100
+4;3D Interactive;FreeTrack20;V160;;;20425;0004C6371D77135815A600
+505;3D Interieur Visualisation;FreeTrack20;V160;;;20815;01F9EDEE75DAC2968D9A00
+5;3d Nav;FreeTrack20;V160;;;20235;000579EC0E26932651EA00
+6;3D-Fahrschule Driving Simulator;FreeTrack20;V160;;;20020;000601075146E0E9625B00
+7;3ds Max;FreeTrack20;V160;;;8601;00077A32A1A7523A4DE700
+8;Aaaaa!;FreeTrack20;V160;;;1775;00085485D831DA62CA7E00
+9;Accessible Computer Games;FreeTrack20;V160;;;4301;000916BC07A2F9B35B6300
+10;Aces High II;FreeTrack20;V160;V;bash1;2701;000A4B798B7221A72E5800
+11;ADS;FreeTrack20;V160;;;20690;000BB25377780C8A894400
+12;Aerofly FS;FreeTrack20;V160;V;V4Friend;2025;000C3797169752CAB39A00
+13;Affineon;FreeTrack20;V160;;;20405;000D662B0374339635A000
+14;Air Battles: Sky Defender;FreeTrack20;V160;;;7003;000E7C822098630A2ED700
+15;Air Phobia;FreeTrack20;V160;;;20100;000F0108339B3E78525B00
+16;Air Traffic Control Tower;FreeTrack20;V160;;;3601;0010432C28637A8EF97400
+17;AirBook;FreeTrack20;V160;;;15201;0011D615A9C8430A201A00
+18;America's Army;FreeTrack20;V160;;;4101;00120BCF5753D98399E200
+19;America's Army 3;FreeTrack20;V170;;;14203;00133F3206BCEA252BB700
+20;ANAG 3D;FreeTrack20;V160;;;20250;0014E2D1D7780A72166300
+21;Apache: Air Assault;FreeTrack20;V160;V;paleta77;1875;001591D997A2D912AAA200
+22;Aprisoft Gartenplaner;FreeTrack20;V160;;;20014;0016F2F27A5762FA937A00
+23;Aprisoft Traumhaus Designer;FreeTrack20;V160;;;20013;00174655E792BB825B9300
+498;ARI;FreeTrack20;V160;;;20795;01F23FB9A61044E206E200
+24;ArmA;FreeTrack20;V160;V;EmBeES;10601;0018F2F27A57631A40F200
+25;ArmA 2;FreeTrack20;V170;V;V4Friend, Ronski;7502;0019EB3616B3A44F05B900
+26;ArmA 2 Operation Arrowhead;FreeTrack20;V160;V;vn88holden;0;001ABC224B7783DAF0D500
+27;ArmA 3;FreeTrack20;V170;;;7503;001BB69411ABD4E2B39900
+28;Armored Assault - Gold Edition;FreeTrack20;V170;;;1303;001CA7F7CAA00814ECA700
+29;Arvoch Alliance;FreeTrack20;V160;;;14908;001D318F8773BAE29A9300
+30;Arvoch Conflict;FreeTrack20;V160;;;14901;001E5C1FDE722DAE2BA900
+31;Auditory Displays for the blind;FreeTrack20;V160;;;20035;001F0FC9FF862F9D34BA00
+486;Autodesk;FreeTrack20;V160;;;20755;01E6953FA7F759AF33AC00
+32;AV Core Technology;FreeTrack20;V160;;;20016;00204655E791166FEB5300
+33;AVRS;FreeTrack20;V160;;;20340;0021231517525830F41D00
+482;B.O.M.B.;FreeTrack20;V160;;;3325;01E248ECCB81191AD49F00
+34;BAE Systems HILAS cockpit;FreeTrack20;V160;;;20019;00220D9C887FD88E98B300
+35;Battle of Britain (iENT);FreeTrack20;V170;;;1305;00230BA119195B701AED00
+36;Battle of Britain II - Wings of Victory;FreeTrack20;V160;V;Normalguy;7001;002452135882CA6419E200
+37;Battlecruiser;FreeTrack20;V160;;;6101;00257C822098728A442900
+38;Battlefield 2;FreeTrack20;V170;;;9601;0026B1560A2A7327659200
+39;Battlefield 2 Trauma Studios;FreeTrack20;V160;;;6501;002720C7529B733AE2EA00
+40;Battlefield 2142;FreeTrack20;V170;;;9602;00281C739AD3817FD43300
+41;Battleground Europe : World War II Online;FreeTrack20;V160;;;3301;0029F07957792CAD3DAF00
+42;Battleground Europe : WWII Online;FreeTrack20;V160;;;11201;002A985017330A844A1200
+43;Beckman Institute UIUC;FreeTrack20;V160;;;20012;002BB59DEFA8322A926A00
+44;Beyond The Red Line;FreeTrack20;V160;;;13301;002CE7A7FAA27424BA2A00
+45;BF2 (Unsupported Demo);FreeTrack20;V160;;;9651;002DC6371D86156AEB6000
+46;Big Scale Racing;FreeTrack20;V160;;;8301;002E4655E7A24ADDB99200
+47;Birds of Prey;FreeTrack20;V160;;;1601;002FBF8C4EAA57730B3200
+48;Birds of Steel;FreeTrack20;V160;;;1878;0030D7690F607233A21E00
+49;Boeing;FreeTrack20;V160;;;20351;0031044872BA630A033E00
+50;Boeing Demo;FreeTrack20;V160;;;20350;003228C57B369A37B13600
+51;Bomber Squadron;FreeTrack20;V160;;;1850;00338481733B9E32A83A00
+52;Brain-IT Group;FreeTrack20;V160;;;20050;0034E482F5772C9330B100
+53;Brothers In Arms;FreeTrack20;V160;;;13501;003503FB872CA8439D1F00
+82;C-Spine;FreeTrack20;V160;;;4901;00525FD692C188FB8B2D00
+54;C.A.R.S.;FreeTrack20;V170;;;2825;003698EFE2EB6EE8E11300
+55;C.U.B.;FreeTrack20;V160;;;20505;0037848174F986FD85F500
+56;CameraGripTools;FreeTrack20;V160;;;20150;003870A8B761FA524A4200
+57;Caterpillar Simulators;FreeTrack20;V160;;;23080;0039F52E4221742FA63100
+58;CCG Metamedia;FreeTrack20;V160;;;20009;003A6FA8A8DD86FE8BDB00
+59;CEWIT Immersive Cabin;FreeTrack20;V160;;;20370;003B90B5780C90078DEE00
+60;Chopper;FreeTrack20;V160;;;3150;003CC166C8632A32EA2300
+61;Clearbox;FreeTrack20;V160;;;20565;003D5C1FDE74279D1DB800
+62;CNKFsoft;FreeTrack20;V160;;;20495;003ED7690F6073187B0000
+487;Cognitics;FreeTrack20;V160;;;20760;01E715A96D19515BFC1500
+63;Colin McRae DiRT 2;FreeTrack20;V170;V;V4Friend;8107;003F7815C5379491C73300
+64;Colin McRae Rally 04;FreeTrack20;V160;;;8103;0040D13C2A843DA526A610
+65;Comanche;FreeTrack20;V160;;;2275;00414097C36A12BA32BA00
+66;Combat Flight Simulator 3;FreeTrack20;V160;V;V4Friend;2304;00420EC5F763AB21CA7310
+67;Combat Helo;FreeTrack20;V160;;;2001;004329DFA863FAE1EA7300
+515;Combined Arms;FreeTrack20;V160;;;1309;0203387EDB1A5AFEADB600
+68;Commandos Strike Force;FreeTrack20;V160;;;8801;0044C1BBF892DB037A7200
+69;Concept RS;FreeTrack20;V160;;;10701;004537971697736A72DA00
+70;Condor: The Competitive Soaring Simulator;FreeTrack20;V160;V;MicheleF;5901;004670A8B762DA623A1300
+71;Conflict Taiwan;FreeTrack20;V160;;;1950;0047D7690F6073399E2000
+72;Corys;FreeTrack20;V160;;;20550;0048157D78738A342A8D00
+73;Counter Strike;FreeTrack20;V160;;;12501;004953FE1E862AB73DA600
+74;Crane Simulator;FreeTrack20;V160;;;20048;004A6B62C6A7931972DA00
+75;Crashday;FreeTrack20;V160;;;5601;004BD6E585F893D933BA00
+76;Creanex Training Simulator;FreeTrack20;V160;;;20205;004CF478D277A40A929A00
+77;Creative Labs headset;FreeTrack20;V160;;;20018;004D348B1743E961FA6300
+78;Cross Racing Championship;FreeTrack20;V160;;;4801;004E20C763AB232B5E8900
+79;Crysis Mod;FreeTrack20;V160;;;1625;004F90B57839B231A24200
+80;Crystal Growth Simulation;FreeTrack20;V160;;;20029;005026F79742FB241A6200
+81;Crytek;FreeTrack20;V170;;;2775;0051D5F1EBBAD81C714600
+83;D2x-XL;FreeTrack20;V160;;;16001;00532E20237BEDABEB8A00
+84;Dark Horizons Lore;FreeTrack20;V160;;;7901;005487801A852AA7385200
+85;Dawn of Aces - Gold Edition;FreeTrack20;V170;;;1304;0055A9ED7700814190DE00
+86;Dawn of Aces II;FreeTrack20;V160;;;1302;00566925967C2BB0285500
+513;Dawn of Aces/Red Baron;FreeTrack20;V160;;;1307;0201916C14F32918D9B800
+87;DBS WalkAndFollow;FreeTrack20;V160;;;2525;005770A8B77008BDE89200
+88;DCS: Black Shark;FreeTrack20;V170;V;EmBeES;1006;0058F688FC9B0556868F00
+89;DCS: A-10C Warthog (32 and 64 bit);FreeTrack20;V170;V;fabri91, Shadow;1003;0059B6DCD15F5A572F6500
+90;Dead Reckoning;FreeTrack20;V160;;;10401;005A4C2F6752F912D66200
+91;Delivery;FreeTrack20;V160;;;15901;005BF3E85B1A8534AE3400
+471;Demon Core;FreeTrack20;V160;;;3225;01D7D1DC6052A165A7AC00
+92;DiRT;FreeTrack20;V160;;;8104;005C72456F7523881F1800
+93;DiRT 3;FreeTrack20;V170;;;8108;005DABA016ED2DFBDF6F00
+94;Door3;FreeTrack20;V160;;;20665;005ED13C2A853DA82F6B00
+95;Down In Flames;FreeTrack20;V160;;;1025;005F29DFA873FB82A66000
+96;Driver Test;FreeTrack20;V160;;;20265;00609343487C3FAA429D00
+97;Driver's Republic;FreeTrack20;V160;;;14001;0061D24C3F8A3F9C33AB00
+98;DriveSim;FreeTrack20;V160;;;5001;0062DD7CC2B47830A14400
+99;Driving Simulator;FreeTrack20;V160;;;13001;00630AC67539AC3F9F3A00
+100;dSphere TBA;FreeTrack20;V160;;;2425;0064A2AE4981FB5389A300
+101;DTS;FreeTrack20;V160;;;20625;006505B37C12868B3E8500
+102;Dungeons and Dragons Online;FreeTrack20;V170;;;1575;0066BD4D4DDC6BB6818800
+103;DY Demo;FreeTrack20;V160;;;20345;006727D5047A15540F9D00
+104;EADS Testing;FreeTrack20;V160;;;20303;00680288709760D5512900
+504;EAFIT;FreeTrack20;V160;;;20810;01F8D2675ABC163E99B000
+105;Eagle Lander 3D;FreeTrack20;V160;;;11901;00697686E5A7B209C27900
+106;EasyVR;FreeTrack20;V160;;;20220;006A68292872FAC4588100
+107;ECA-Sindel;FreeTrack20;V160;;;20590;006BC5BEE479FD75FB8500
+508;Elite: Dangerous;FreeTrack20;V170;;;3475;0D93A9485EECA12E18BE00
+481;Embers of Caerus;FreeTrack20;V170;;;3275;0CCB4134258D7E10119E00
+108;EMS Simulations;FreeTrack20;V160;;;20390;006CA59E687D1589DE8500
+109;Enemy Engaged 2;FreeTrack20;V160;;;2102;006D010873EA635AEDC800
+110;Enemy Engaged: Commanche vs Hokum;FreeTrack20;V160;;;2101;006E157D789379636AED00
+111;Envision TE;FreeTrack20;V160;;;20320;006FD13C2A863CAF26AB00
+112;EON Reality;FreeTrack20;V160;;;20410;0070B6C6142771F82EE900
+113;eSigma;FreeTrack20;V160;;;20700;0071950F9AA60DAD2E9F00
+114;Euro Truck Simulator;FreeTrack20;V160;V;mamsa;13602;00721B4BFF8B3EA3296600
+115;EVE Online;FreeTrack20;V160;;;11801;0073F47CC377C2388DC900
+116;Eventology;FreeTrack20;V160;;;12201;0074849F8B469C2BAC3600
+117;EVOC-101 Training;FreeTrack20;V160;;;20038;00756925967D2088FC6200
+118;Evochron Alliance 2.0;FreeTrack20;V160;;;14904;00767A32A1A8735A022900
+119;Evochron Legends;FreeTrack20;V160;;;14906;0077D6E585F8B41A12BA00
+120;Evochron Mercenary;FreeTrack20;V160;V;Scavenger4711 ;14907;00783752FBC7B42B221A00
+121;Evochron Renegades;FreeTrack20;V160;;;14905;0079621E7763FB421A1300
+122;EZCA;FreeTrack20;V160;V;dennison ;2475;007A6DBB52F7B147510601
+125;F-22 Total Air War;FreeTrack20;V160;;;1750;007D20C79F475F0621DB00
+126;F-35 PTA;FreeTrack20;V160;;;20185;007E26F7977E96C0352100
+148;F-Trainer;FreeTrack20;V160;;;20615;0094C6371D8AFB8C3E9900
+123;F1 2011/2012;FreeTrack20;V170;;;8109;007B265ECD74893EA12900
+124;F1 Challenge 99-02;FreeTrack20;V160;;;4401;007CA15F278FB660C9D210
+127;FAAC;FreeTrack20;V160;;;20500;007F0F00F489118300C000
+128;Farmer Simulator 2009;FreeTrack20;V160;;;1725;0080926ED8A1EB628AB300
+129;Faros Driving Simulator;FreeTrack20;V160;;;8001;0081B1C6647A1EA839A800
+130;Faubert Lab Car Simulator;FreeTrack20;V160;;;20480;00822E542A7F2DB61F9700
+131;Ferrari Virtual Academy;FreeTrack20;V160;V;sosna1983 ;0;0083C0AF8C24B62D974000
+132;FIFA 09;FreeTrack20;V170;;;9002;0084288979C65B0DAA0D00
+133;Fighter Ace;FreeTrack20;V160;;;8401;0085E2D1D77D259837B700
+134;Fighter Ops;FreeTrack20;V160;;;9301;00860FC9FF8B23A033BA00
+135;Fighter Squadron: Screamin Demons Over Europe;FreeTrack20;V160;;;1675;00872E20237D259A27A600
+136;First Eagles, Wings Over Europe, Strike Fighters;FreeTrack20;V160;;;1101;0088311857638A73BB7E00
+469;Flight Gear;FreeTrack20;V160;;;12901;01D5D896C66B030606E000
+466;Flight Simulator 2002;FreeTrack20;V160;;;2302;01D21F7650E254B4514B00
+467;Flight Simulator 2004;FreeTrack20;V160;;;2303;01D3E91BAAB385B2362E00
+468;Flight Simulator X;FreeTrack20;V160;;;2305;01D41C31F42FAF32603600
+137;FlightGear;FlightGear;V110;V;V4Friend;0;0089054E8839A02FAB2F00
+138;Flyboys / Warbirds;FreeTrack20;V160;;;1301;008ABB5BF72C8934AB2B00
+139;Flyer;FreeTrack20;V160;;;1650;008BD1C9F27826AE2CA500
+140;Flying Tigers;FreeTrack20;V160;;;7002;008C91D997F29A930B0200
+141;FlyingGuns;FreeTrack20;V160;;;14801;008D7A32A1A882BAA28A00
+142;Ford Racing 3;FreeTrack20;V160;;;6901;008E27D5047C2BA62F5800
+143;Free Falcon 4.0;FreeTrack20;V160;;;1901;008F157D78A3B962E55000
+144;Free Falcon 4.0: Allied Force;FreeTrack20;V160;;;8901;0090656DD6793CA9325900
+145;Free Falcon 5;FreeTrack20;V160;V;V4Friend;1902;00911038C3AAB32590E900
+146;FreeSpace2;FreeTrack20;V160;;;13302;0092C8CDF8C2EA63496300
+147;FTAlpha;FreeTrack20;V160;;;20270;00936C69677F1D772AA900
+149;Full Out;FreeTrack20;V160;;;1701;009527D5047C31A0375800
+150;Future Pinball;FreeTrack20;V160;V;V4Friend;13101;00969343487E42B541AA00
+151;Game VR;FreeTrack20;V160;;;20325;009756BA018030B0355500
+152;GameLab;FreeTrack20;V160;;;20555;00985485D891DB23092200
+472;Games Farm;FreeTrack20;V160;;;3250;01D875C0981E627E5C7600
+153;Gamma;FreeTrack20;V160;;;1050;009921007D1B9D38A76F00
+503;Garry's Mod;FreeTrack20;V160;;;12503;01F74EEB71FA7F37856900
+483;Generic Robotics;FreeTrack20;V160;;;20750;01E3FE7B4F05872A415400
+475;Glider Sim;FreeTrack20;V160;;;20720;01DB42130C4B0620871C00
+154;Global Ground Support Deicing Simulation;FreeTrack20;V160;;;20330;009ACFE709237A3AA11F00
+155;GosNIIAS Sim Trainer;FreeTrack20;V160;;;20355;009BB78B06EC8A2DB81800
+156;Gothic 3;FreeTrack20;V160;;;14401;009C37971697B36AD32A00
+157;Grand Prix Legends;FreeTrack20;V160;;;6701;009D521358D3D933B96E01
+158;Grand Theft Auto: San Andreas;FreeTrack20;V160;;;7808;009E5F3D95777D3BA23700
+159;GRID;FreeTrack20;V160;V;LeapoEclipse ;8105;009FD8175F8D1D7C0D3800
+160;GSE Power Plant Simulation;FreeTrack20;V160;;;20044;00A0432C28C217BEF93300
+161;GT Legends;FreeTrack20;V160;V;TrickyDee ;3902;00A116BC08020581CA8200
+162;GTR;FreeTrack20;V160;;;3901;00A2FD6BFE39A881B85B00
+163;GTR2 EVO;FreeTrack20;V160;V;zild1221 ;0;00A34B0FC37B2198F96300
+164;Gun Commander;FreeTrack20;V160;;;2675;00A4E2D1D77E319FEE8600
+165;Half Life 2;FreeTrack20;V160;;;7806;00A556BA018130AF365500
+166;Halo;FreeTrack20;V160;;;3801;00A656BA018130AF3F9C00
+502;hapTEL;FreeTrack20;V160;;;20805;01F63ADE510A20446B6A00
+167;Hardware Control Simulator, Railway Electronics;FreeTrack20;V160;;;20705;00A7F3E85B1A8930B42F00
+168;Harrier Attack II;FreeTrack20;V160;;;1175;00A8F2F27A57D20A940900
+499;Harry's Hard Choices Interactive;FreeTrack20;V160;;;20800;01F33A21BAE3DB6D48A000
+169;HAWX;FreeTrack20;V160;V;EmBeES ;0;00A9D615A9C8B088717000
+170;Herissons (Paris France);FreeTrack20;V160;;;20001;00AAEA1CBED8C20B430B00
+171;HOBI;FreeTrack20;V160;;;20335;00ABB253777F1779168900
+172;HoverAssault;FreeTrack20;V160;;;5303;00AC521358E3AA832A4000
+173;HPC;FreeTrack20;V160;;;20600;00AD0FC9FF8D0A7CEF9500
+174;HPG Tracking;FreeTrack20;V160;;;20255;00AE0288A1879D98930900
+175;HTW;FreeTrack20;V160;;;20630;00AF26F797911901C22C00
+176;Huawei Software;FreeTrack20;V160;;;20660;00B04655E8030A733AB300
+177;HueSpace;FreeTrack20;V160;;;20510;00B1231517C44960FB6300
+178;Hyper Vision;FreeTrack20;V160;;;15101;00B2DB242B657D43B32300
+179;iAmFootball;FreeTrack20;V160;;;1250;00B3B59DEFAAAFDB301A00
+180;id Research;FreeTrack20;V170;;;2950;00B4E723B873D7FC87D700
+181;IESA;FreeTrack20;V160;;;20037;00B5157D78D0E840AD7100
+182;IL-2 Cliffs of Dover;FreeTrack20;V160;V;V4Friend;0;00B6282282095F00521000
+183;IL-2 Forgotten Battles, ACE, Pacific Fighters;FreeTrack20;V160;V;Glenn;1001;00B722FD79137100621100
+484;IL-2 Sturmovik: Battle of Stalingrad;FreeTrack20;V170;;;1008;03F04D5368D1FD2DF2AE00
+184;Illumina;FreeTrack20;V160;;;1350;00B8054E8B39A33DB02400
+185;ILP;FreeTrack20;V160;;;20310;00B9F07957801789C9A600
+186;Imagine 3D ATC Tower Simulation;FreeTrack20;V160;;;20032;00BAEF7A8F4B8812B95300
+187;In Touch Technologies Inc.;FreeTrack20;V160;;;20006;00BB84817A3A5124B23D00
+188;Inglobe;FreeTrack20;V160;;;20360;00BCE7A7FAA27B2DA82700
+189;Innovatec Simulator;FreeTrack20;V160;;;20036;00BD22FD7935B23DB82C00
+190;Insurgency;FreeTrack20;V160;;;9401;00BEB6C61427B3EA744B00
+191;Intific;FreeTrack20;V160;;;20655;00BF2BCA747F2AA8309D00
+192;iRacing;FreeTrack20;V160;V;vn88holden ;14101;00C0103AF1AA730A236900
+193;IREQ Robotic Camera Control;FreeTrack20;V160;;;20027;00C10448E0E8618521EB00
+194;ISIC;FreeTrack20;V160;;;20680;00C2DEE3582F8F217E0B00
+506;ITCL;FreeTrack20;V160;;;20820;01FAD19412681DC9CC6100
+195;IVD Online;FreeTrack20;V160;;;20535;00C390B57E1D7DDD883D00
+196;J.J. Keller and Associates;FreeTrack20;V160;;;20002;00C4E482F57FE77CF46300
+197;Janes F18;FreeTrack20;V160;;;9001;00C55F3D9577802AAF2E00
+198;Javal Ent.;FreeTrack20;V160;;;2650;00C622FD7A28BA2FAEEA00
+199;JC Demo;FreeTrack20;V160;;;20470;00C70AC67B0A630D9B3900
+200;Jet Thunder;FreeTrack20;V160;;;4501;00C82100801FA4EA9A3500
+201;Jetfighter V;FreeTrack20;V160;;;2501;00C9F52E42217B33A63200
+202;JFIST;FreeTrack20;V160;;;20560;00CAC932727C0F8D208600
+203;John Deere Vehicle Simulator;FreeTrack20;V160;;;20175;00CB29DFA8D3FA92A66000
+204;Jump to Lightspeed;FreeTrack20;V160;;;6001;00CCC166C8D3FA12E52400
+205;Jumpgate;FreeTrack20;V160;;;15001;00CD26F797B32A63E99200
+206;Jumpgate Evolution;FreeTrack20;V170;;;15002;00CE5936D4F93DCE0CE700
+207;KAF Keymapper;FreeTrack20;V160;;;3101;00CF934348830E87EB8300
+476;KAI FLight Simulator;FreeTrack20;V160;;;20725;01DCC522132A5C01EDE500
+208;Key Macro View;FreeTrack20;V160;;;12001;00D016BC08431B1EF90100
+209;kiwi.vg;FreeTrack20;V160;;;20395;00D1409A430AB33633E900
+210;L-3;FreeTrack20;V160;;;20465;00D2F885EFA8DE67878B00
+211;L-3 Communications;FreeTrack20;V160;;;20051;00D3D6E585F92F765E7800
+212;Lionhead MGS Game Research;FreeTrack20;V160;;;15301;00D4C1BBF9227B238AE200
+213;Live For Speed;FreeTrack20;V160;V;zild1221 ;7101;00D528228526A833521300
+214;Live for Speed S2;FreeTrack20;V160;;;11601;00D63752FBC8235B923500
+218;Lock-On: Modern Air Combat;FreeTrack20;V160;;;1002;00DAAC156D903C993B6200
+215;Lockheed Martin Littoral Combat Ship simulator;FreeTrack20;V160;;;20025;00D771ED5E802A9827A000
+216;Lockheed Martin Prepar3D;SimConnect;V130;V;elelbe ;0;00D8D615A9C8F36932AA00
+217;LockOn: Flaming Cliffs 2;FreeTrack20;V170;V;EmBeES ;1005;00D9AFB0D279F3A6F72200
+219;London NHS;FreeTrack20;V160;;;20640;00DB2E2023832BA123A100
+220;Lore (Dark Horizons);FreeTrack20;V160;;;5302;00DCF478D27833DB62D500
+221;Love;FreeTrack20;V160;;;3125;00DD71ED5E802AAB214800
+222;Lucid Engine;FreeTrack20;V160;;;10501;00DE71ED5E803098259C00
+514;M4 Tank Brigade;FreeTrack20;V160;;;1308;02024ED5A3A26741604300
+223;M4 Tank Platoon;FreeTrack20;V160;;;1306;00DFDCC441A8E036320900
+224;Mach 1;FreeTrack20;V160;;;2575;00E0231518130942466000
+225;ManuVAR;FreeTrack20;V160;;;20455;00E153FE1E901CB0448800
+226;MaqSIM4;FreeTrack20;V160;;;20026;00E28F54A207D29B611700
+227;Mech Tactical Sim;FreeTrack20;V160;;;9501;00E3F3E85B1A8E34A53300
+516;MechWarrior Online;FreeTrack20;V170;;;3026;0BD2E0DCBC723836CD2400
+228;Mechwarrior Online;FreeTrack20;V170;;;3025;00E414954226DB5D8CE200
+229;Medical Image Visualization;FreeTrack20;V160;;;20295;00E59343488532A5359B00
+230;Meggitt Defense Systems;FreeTrack20;V160;;;20045;00E6926ED9122AB22AF300
+231;MetaVR;FreeTrack20;V160;;;20545;00E7C5BEE48120A8308800
+232;Meteoroid Maze;FreeTrack20;V160;;;13003;00E8F079578430AD2EB200
+233;MeVEA;FreeTrack20;V160;;;2250;00E9D24C3F933289028700
+234;Micro Flight;FreeTrack20;V160;V;V4Friend;5101;00EABA89D078439932EA00
+235;Microsoft ESP;FreeTrack20;V160;;;2306;00EB0AC67E30A63BA53F00
+236;Microsoft Flight;FreeTrack20;V170;V;V4Friend;2307;00ECAFBC54959539C51500
+237;Microsoft Flight Simulator 3;FreeTrack20;V160;V;V4Friend;0;00ED5909437D36963EA810
+238;Microsoft FS2002/2004;FSUIPC;V130;V;V4Friend;0;00EE44958F34992CB43A00
+239;Microsoft FSX;SimConnect;V130;V;V4Friend;0;00EFF478D278437A73AA00
+240;Microsoft Train Simulator;FreeTrack20;V160;;;7805;00F00449225A439A13FB00
+494;MiddleVR;FreeTrack20;V160;;;20785;01EE1227DF43CA90C26100
+241;Mig Alley;FreeTrack20;V160;;;1501;00F17686E5A83289CDA700
+242;Mimik Vehicle Simulator;FreeTrack20;V160;;;20585;00F2AB1D58A8E35A531A00
+243;Miner Wars;FreeTrack20;V160;;;2550;00F3C5BEE48124A234A400
+244;MKMapper Keymapper;FreeTrack20;V160;;;2201;00F487801A8E14822EA200
+245;ModelSim;FreeTrack20;V160;;;20540;00F544958F3A9A1FB11A00
+246;Morgan State University;FreeTrack20;V160;;;20010;00F65909437D3CA5339A00
+247;Motor Company;FreeTrack20;V160;;;11501;00F70288F37A629A7DD700
+248;Motorsport Simulators;FreeTrack20;V160;;;4201;00F8F52E42217E3DA63B00
+249;Mouse Emulation;FreeTrack20;V160;;;8501;00F9950F9A8E29B93A9700
+250;MS World Tour Kart 2004;FreeTrack20;V160;;;6601;00FA9F819CA27F10591600
+251;MSN Virtual Earth;FreeTrack20;V160;;;2309;00FBF47CC37842091DC900
+252;mTBI Balance;FreeTrack20;V160;;;20420;00FC72456F9E0E78145800
+253;NASA Crew Exploration Vehicle;FreeTrack20;V160;;;20017;00FD028900985FA5501A00
+254;NASCAR Heat;FreeTrack20;V160;;;2602;00FE311857E10880B84100
+255;NASCAR Racing 2003 season;FreeTrack20;V160;;;7804;00FFDE661F6D920B87FC10
+256;NASCAR SimRacing;FreeTrack20;V160;;;6201;01004098708870D761A500
+257;NecroVisioN;FreeTrack20;V160;;;15601;0101621E77F2EA830A8100
+258;NetKar Pro;FreeTrack20;V160;V;sosna1983 ;10901;01028C3958334A5169A300
+259;Nitro Stunt Racing;FreeTrack20;V160;;;13901;0103C0AF9428B82DA5ED00
+260;North Eastern University;FreeTrack20;V160;;;20003;0104A2AE4823BB7449DE00
+261;Novint;FreeTrack20;V160;;;2725;01051C76F8137B823A4300
+262;NVH Sound Simulator;FreeTrack20;V160;;;20695;010655A2AA93158DDE9500
+479;OHJCH;FreeTrack20;V160;;;20740;01DF992E6C0DA43A703600
+263;OMSI Bus Simulator;FreeTrack20;V160;V;Thiago ;2225;0107F47CC37861A9606600
+264;Onadime - Realtime Animation;FreeTrack20;V160;;;20049;010853FE1E9229A3339B00
+265;Open Falcon 4.5;FreeTrack20;V160;V;muplex ;0;0109B1C664832D9B385500
+266;Operation Air Assault 2;FreeTrack20;V160;;;5401;010ABB5BF72C9238973B00
+267;Operation Flashpoint 2 (inactive);FreeTrack20;V160;;;7601;010BD1C9F2812A9A399400
+268;Operation Flashpoint(r): Dragon Rising(tm);FreeTrack20;V170;;;8106;010C3328780E1322676100
+269;OptiMetrics;FreeTrack20;V160;;;20011;010D55A2AA942FB9288F00
+270;OpusFSX;FreeTrack20;V160;;;3175;010E1C76F8238B72D7C100
+271;Orbiter 2005 Plug-In;FreeTrack20;V160;;;10301;010F87801A903B9736A600
+272;Orbiter 2006;FreeTrack20;V160;V;Ripley;10303;0110D24C3F953F9526BA00
+273;Orbiter 2006 Plug-In;FreeTrack20;V160;;;10302;0111054E913F9931B72000
+274;Oryx Vehicle Simulators;FreeTrack20;V160;;;20028;0112C8CDF952EBA4763100
+275;Outer Conflict: Frontiers;FreeTrack20;V160;;;1900;01136C6967883EAA23AB00
+276;Over Flanders Fields;FreeTrack20;V160;V;Summelar ;2325;0114A59E68873E9B315210
+277;PanoPro;FreeTrack20;V160;;;20525;0115B6C61428231A23E900
+278;Paraworld;FreeTrack20;V160;;;14501;0116656DD6832BB62EB000
+279;Phoenix R/C;FreeTrack20;V160;;;3075;0117B6C61428238A334B00
+280;Pivot Maritime;FreeTrack20;V160;;;20365;01187686E5A8628AB2AA00
+281;PlanetSide 2;FreeTrack20;V170;;;6002;0119BB34B53FDC5C5AAF00
+474;PlanetSide 2 M.E.;FreeTrack20;V160;;;6003;01DA321EF31DA48C159A00
+282;Pointman;FreeTrack20;V160;;;7525;011ACFE70923833D9B2B00
+283;Power Driving Simulator;FreeTrack20;V160;;;20675;011BB78B06EC932DBC2F00
+470;Prepar3D;FreeTrack20;V160;;;20440;01D6DA768CE8AA43D8DC00
+284;Priston Tale;FreeTrack20;V160;;;9701;011C849F9642A030AC3600
+285;Project Reality (BF2 Mod);FreeTrack20;V160;;;9201;011D3C2C1C922CB239A800
+286;Project Torque (Invictus);FreeTrack20;V160;;;4802;011E612F28540B52797100
+287;Project X;FreeTrack20;V160;;;20022;011FE9D85E4842DA439900
+288;Quanser;FreeTrack20;V160;;;20435;0120950F9A922FA535A500
+289;QuantaDyn;FreeTrack20;V160;;;20575;012199BB5C9433A739B600
+290;Quest 3D Xframe Plugin;FreeTrack20;V160;;;20043;0122FD6BFE39A923C98200
+291;Quest3D;FreeTrack20;V160;;;1425;01234B0FC38542AB3BB700
+292;Quest3D - CINEACT integration;FreeTrack20;V160;;;20040;0124A15F28340AB3CA9000
+303;R-concept X (AdrenalinStorm);FreeTrack20;V160;;;9101;012F72456F83E6993AA600
+293;R.U.D.O. Tools;FreeTrack20;V160;;;20039;0125612F287FB9BEA76E00
+294;RACE (the WTCC game);FreeTrack20;V160;;;3904;01263C2C1C94FA86146300
+295;RACE 07, RACE, GTR 2;FreeTrack20;V160;V;zild1221, TrickyDee ;3903;0127C0AF9800870056FD00
+296;RaceOn;FreeTrack20;V160;V;zild1221 ;0;0128621E7832AA82388200
+297;Racer;FreeTrack20;V160;;;2401;01296D2D103862E932EA00
+298;Racer S;FreeTrack20;V160;;;20030;012AEA1CBED961CA52CB00
+490;RaceRoom Racing Experience;FreeTrack20;V160;;;3905;01EA9124F663AF03BBC500
+299;Rail Simulator 1;FreeTrack20;V160;;;1550;012BB253778929A0395900
+300;Railway Work Simulator;FreeTrack20;V160;;;20225;012C91D998B1E9933B9100
+301;RailWorks 2;FreeTrack20;V160;;;2750;012D3919C4972D9D299000
+507;RailWorks 5 (TS2014);FreeTrack20;V160;;;1551;01FB8B5232CB1502C95A00
+302;Raydon Driving Simulator;FreeTrack20;V160;;;3701;012E5485D941DBE2FB5300
+304;Real Time Visual;FreeTrack20;V160;;;9901;0130B1C664862297365500
+305;RealFlight;FreeTrack20;V160;;;4001;0131AC6E87892E993A8B00
+306;Reality Manager;FreeTrack20;V160;;;20046;0132DB242B65872FA42A00
+307;RealWorld;FreeTrack20;V160;;;20215;0133B59DEFA9322A727800
+308;Red Baron 3D;FreeTrack20;V160;;;1125;0134C6371D96349CEB7A00
+309;Red Orchestra: Heroes of Stalingrad;FreeTrack20;V170;;;12602;013521F9121056CD997300
+310;Red Orchestra: Ostfront;FreeTrack20;V170;;;12601;01369A9A2BE97252BB9000
+311;Redline Monitor;FreeTrack20;V160;;;20210;01373752FBC8831A72AA00
+312;REFLEX XTR;FreeTrack20;V160;;;2150;0138054E94127D14881300
+313;Remote Presence;FreeTrack20;V160;;;20710;01399BC3BDA93229D2BB00
+314;rFactor;FreeTrack20;V160;V;V4Friend;7401;013AEF7A8F4B8AA0495200
+315;rFactor Pro;FreeTrack20;V170;;;7403;013B175198D74BE8E89600
+316;rFactor2;FreeTrack20;V170;;;7402;013CBFF3A86D34DA24C000
+317;Richard Burns Rally;FreeTrack20;V160;V;zild1221;3401;013D42868B339A31A73C10
+318;RidingStar;FreeTrack20;V160;;;1475;013E3919C497359826A700
+319;Rig N Roll;FreeTrack20;V160;;;9801;013FD1C9F284239CE68100
+320;Rio Tinto Resource Development;FreeTrack20;V160;;;20033;01401039831B5EC8D31900
+321;Rise of Flight;FreeTrack20;V160;V;Seborgarsen ;11401;0141AA71218335A51F5100
+322;Rise: The Vieneo Province;FreeTrack20;V160;;;6301;0142DEE3582F9837A82D00
+323;Rising Conflicts;FreeTrack20;V160;;;12101;014384818335A439B12F00
+492;RITS;FreeTrack20;V160;;;20775;01ECEE745B83DD3A0DBE00
+324;Road Legends;FreeTrack20;V160;;;1876;0144379716986369A2E600
+325;Rogue Space;FreeTrack20;V160;;;3050;014588F5872C9C30ABE900
+326;Rogue Warrior;FreeTrack20;V160;;;14202;0146432C2973D9D45A8E00
+327;ROV Sim;FreeTrack20;V160;;;20530;0147FA04BD27891D9ADD00
+328;Rowan's Battle Of Britain;FreeTrack20;V160;;;1502;014821008829A72CB4F300
+329;RSD Demo;FreeTrack20;V160;;;2050;0149BF8C4EAA5871A85D00
+330;RTMS Crane Sim;FreeTrack20;V160;;;20400;014AC93272841D91205200
+331;RTT DeltaGen Plugin;FreeTrack20;V160;;;20195;014B90B5871B8DDD7D3400
+511;Sail;FreeTrack20;V160;;;3575;01FF2916D764159099F800
+332;Sail Simulator 5;FreeTrack20;V160;;;1975;014CE2D1D78A1D9A3B6300
+333;Sailors of the Sky;FreeTrack20;V160;;;10201;014D17BE02961FAF3AB100
+334;Santa Cruz Watermill;FreeTrack20;V160;;;20290;014E88F5881EA32FA7E900
+335;SCANeR Studio Plugin;FreeTrack20;V160;;;20445;014FA2AE4870F861E9A100
+336;SEGA Demo;FreeTrack20;V160;;;2075;015016BC08C117F1163000
+337;Seven-G;FreeTrack20;V160;;;2350;01510F00F49635B822A200
+338;SFSPC;FreeTrack20;V160;;;2801;0152F885EFA9400981B700
+339;Ship Simulator 2006;FreeTrack20;V160;;;5002;015329DFA9638AA2C66100
+340;Ship Simulator 2008;FreeTrack20;V160;V;djj3ff ;5003;01545213599339B3D52100
+341;Shiphandling Simulator;FreeTrack20;V160;;;20570;01553FD0FC9D8B37AD3700
+342;Shooting Simulator;FreeTrack20;V160;;;20047;0156F2F27A58827A63DA00
+343;SIA Games;FreeTrack20;V160;;;20015;0157926ED970685DA8D200
+344;Silent Wings;FreeTrack20;V160;;;8701;0158D615A9C96309C24A00
+345;SilverHat;FreeTrack20;V160;;;12401;0159D8175F99349F3F9600
+346;Simax Simulator;FreeTrack20;V160;;;20021;015AAC156D9736A331AD00
+347;Simball 4D;FreeTrack20;V160;;;20315;015BD6E585F99349F2AA00
+348;Simbionix;FreeTrack20;V160;;;20485;015CE7A7FAA28528AE1D00
+518;SimCraft;FreeTrack20;V160;;;3650;02065BA013963203CB1900
+349;SimCreator;FreeTrack20;V160;;;20520;015D3752FBC8935B001A00
+350;Simlog Personal Simulator;FreeTrack20;V160;;;20635;015E42868C33A435B53100
+351;SimQuest immersion system;FreeTrack20;V160;;;20023;015F6C69678C32A30FAE00
+352;SimSol;FreeTrack20;V160;;;20034;01602315187389E0FB5300
+491;Simumak;FreeTrack20;V160;;;20770;01EB119629980784DCE600
+510;Sir, You Are Being Hunted;FreeTrack20;V160;;;3550;01FEDAB345FA104ED4A100
+353;Sirocco Racing;FreeTrack20;V160;;;5801;01612BCA748925A6369A00
+354;Sky God: Military Freefall;FreeTrack20;V170;;;1526;01622C0104385FD1C44400
+355;Soft fx Demo;FreeTrack20;V160;;;2125;01632E20238A2B99335200
+356;Source Engine (Half-Life 2);FreeTrack20;V160;;;12502;0164E482F58829A739A600
+357;Space Shuttle Mission 2007;FreeTrack20;V160;V;purewhitewings ;1225;0165662B03863D912F9700
+517;Spaze;FreeTrack20;V160;;;3625;020573F9D3ED8C43B8DC00
+358;SRI;FreeTrack20;V160;;;20620;01663FD0FC9D8B218D8100
+359;Stanford University;FreeTrack20;V160;;;20004;0167CE35BAF9933A62AA00
+500;Star Citizen;FreeTrack20;V170;;;3450;0D7AF4CE4E343EC6B4A200
+360;Starshatter;FreeTrack20;V160;;;6401;0168F079578A3F9A3BB600
+361;Steel Beasts 2;FreeTrack20;V160;;;11703;0169DCC441A9443A831A00
+362;Steel Beasts Pro;FreeTrack20;V160;;;11701;016A8F54A20833CAA23900
+363;Steel Beasts Pro Personal;FreeTrack20;V160;;;11702;016BF3E85B1A9443A73000
+364;Stoked Rider;FreeTrack20;V160;;;14701;016CC166C963EA32997300
+365;Storm of War: the Battle of Britain;FreeTrack20;V170;;;11001;016D8F54A68B71F25D0200
+495;STRAFT;FreeTrack20;V160;;;3375;01EF1329E014ADF98D6B00
+366;Stride;FreeTrack20;V170;;;2310;016EC784B21E4B21984400
+367;STS Driving Simulator;FreeTrack20;V160;;;20300;016FA59E688B1C89DE7600
+368;Sunaerosys;FreeTrack20;V160;;;20475;01700449831AF28973EB00
+369;Superkarting;FreeTrack20;V160;;;14903;0171DE661F6D973FA41F00
+370;SuperX Research;FreeTrack20;V160;;;15401;0172BA89D078A45A021A00
+371;SWRI Demo;FreeTrack20;V160;;;20230;01730AC6841E9512561000
+372;Syncon Robot Control;FreeTrack20;V160;;;20415;0174C1BBF9937B12DB5200
+373;Synergy Simulation;FreeTrack20;V160;;;20595;0175926ED9736B220B8200
+374;T and TS Corp.;FreeTrack20;V160;;;20005;017617BE0297DDA73CA600
+375;Take On Helicopters;FreeTrack20;V170;;;7504;0177513CB40C0623B96A00
+376;Target: Korea;FreeTrack20;V160;;;1201;0178F52E4221852FA43300
+377;Target: Rabaul;FreeTrack20;V160;;;1202;0179E9D85E4881CA736900
+378;Targeting Demo;FreeTrack20;V160;;;13002;017AAB1D58A952DAA2F900
+379;TD Demo;FreeTrack20;V160;;;1925;017BC5BEE488FE54139700
+489;TeachLive;FreeTrack20;V160;;;20765;01E905245B5AC5484FC100
+380;Telepresence;FreeTrack20;V160;;;20450;017CF478D278B33B02DA00
+381;Tenstar Simulator;FreeTrack20;V160;;;20605;017D318F88A2EA62DBA200
+382;Test Drive Unlimited;FreeTrack20;V160;;;13201;017E6F2699559730A84000
+509;Tetrahedral;FreeTrack20;V160;;;3525;01FDCFF1C56D6F757E8200
+501;The Crew;FreeTrack20;V170;;;1009;03F1F534E94F4834D9B100
+488;The Gallery;FreeTrack20;V160;;;3350;01E8CE152EDE5FFD267700
+383;The Sky Gods;FreeTrack20;V170;;;1525;017FDFF782DCCEEA27C200
+384;theHunter;FreeTrack20;V170;;;2375;0180BFF3A86D34DA24C000
+385;thriXXX HII 3D;FreeTrack20;V160;;;13403;0181FA04BD27AB36B62700
+386;thriXXX Jenna;FreeTrack20;V160;;;13401;01829F819CA2A625AB2800
+387;ThriXXX Technology;FreeTrack20;V160;;;13402;0183F47CC378B35B526900
+388;tir2joy;FreeTrack20;V160;;;2202;018487801AB532A7FE9C00
+389;TIRF4;FreeTrack20;V160;;;2203;0185311858418870E77000
+390;TNO;FreeTrack20;V160;;;20285;01861AAF6870A853D64900
+391;ToCA Race Driver 2;FreeTrack20;V160;;;8102;0187FFE9E886F286297910
+392;Toca Race Driver 3;FreeTrack20;V160;;;8101;01884098D36972B551A900
+393;Tom Clancy's H.A.W.X.;FreeTrack20;V170;V;EmBeES ;1004;0189AA4D48E85966FBAA00
+394;Tom Clancy's H.A.W.X. 2;FreeTrack20;V170;;;1007;018A1B7583521C12406F00
+395;Torque Game Engine;FreeTrack20;V160;;;5301;018BC0AF9A2EB62CAB3300
+396;Total War;FreeTrack20;V160;;;1375;018C72456F8529AA2CA400
+397;Tower Crane Simulator;FreeTrack20;V160;;;20670;018D03FB9929B034A7D900
+398;Tower I;FreeTrack20;V160;;;1325;018E662B03873CA731A400
+399;TPL Testing;FreeTrack20;V160;;;20260;018F849F9A2083DC8C2C00
+400;trackd;FreeTrack20;V160;;;20580;019053FE1EB72DA3329D00
+401;TrackMapper2;FreeTrack20;V160;;;2175;0191CE35BAF9A31A61FA00
+402;TrainMaster;FreeTrack20;V160;;;20042;01925C1FDE852D9925B400
+403;Trainz;FreeTrack20;V160;;;4701;0193D1C9F2862C9630A100
+404;TraumTek;FreeTrack20;V160;;;20650;0194A2AE4883EA645A2100
+405;Turismo Carretera;FreeTrack20;V160;;;15801;01956F2699559740A73500
+406;UK Rally Champion;FreeTrack20;V160;;;5701;019679EC0E28B196522A00
+407;ULAN;FreeTrack20;V160;;;20645;01974ADE68E16780D97A00
+473;Underwater Navigation;FreeTrack20;V160;;;20715;01D9FE2B94FB03772DE200
+408;Unity 3D Plugin;FreeTrack20;V160;;;20430;0198D24C3F9B3B9C31BF00
+409;Untitled 10tacle;FreeTrack20;V160;;;13701;0199FFE9E886F28728AA00
+410;Untitled 3D People;FreeTrack20;V160;;;15501;019A6925968D38AD23A900
+411;Untitled Apportmedia;FreeTrack20;V160;;;6801;019B3C2C1C9728B738B700
+412;Untitled Combat Flight Sim;FreeTrack20;V160;;;13801;019CA59E688D36AA28A600
+413;Untitled Deep Silver;FreeTrack20;V160;;;14601;019D656DD68838B836AD00
+414;Untitled G5 Software;FreeTrack20;V160;;;7301;019E318F88B37AC23BA300
+415;Untitled Microsoft;FreeTrack20;V160;;;2308;019FA15F28739BA32A9300
+416;Untitled Research;FreeTrack20;V160;;;1075;01A07686E5A8B2DA924A00
+417;Untitled Ron E;FreeTrack20;V160;;;14902;01A14ADE68E38AB28B9300
+418;Untitled Sega of America;FreeTrack20;V160;;;12801;01A27A32A1A972DA528A00
+419;Untitled Simbin;FreeTrack20;V160;;;3902;01A36C69678E37AA27AD00
+420;Untitled WorkShield;FreeTrack20;V160;;;4601;01A442868E38AB32BA3600
+421;Untitled ZootFly;FreeTrack20;V160;;;12701;01A5348B1863AA527A6300
+422;UQO;FreeTrack20;V160;;;23085;01A67C822099A187FE3600
+423;UVEG Machinery Simulator;FreeTrack20;V160;;;20024;01A73118585257A0F63100
+424;VAR;FreeTrack20;V160;;;20460;01A8950F9A97FA96FD2E00
+425;VBS2;FreeTrack20;V160;;;7501;01A999BB5C990099FCBF00
+485;VBS2 2.0;FreeTrack20;V170;;;7505;1D51EB3616B3A44F05B900
+426;Vehicle Simulator (Quality Simulations);FreeTrack20;V160;;;5102;01AA0FC9FF9B1FA134A900
+478;Vehicle Simulator Direction;FreeTrack20;V160;;;20735;01DEB88CF58E570F1CEF00
+427;Vestibular Ocular Reflex;FreeTrack20;V160;;;20008;01AB3919C49B31A731A200
+428;Viper Arena;FreeTrack20;V160;;;3501;01AC6F2699559934A53100
+429;VIRTools Engine;FreeTrack20;V160;;;10801;01AD1AAF68905881EB5300
+430;Virtools Plugin;FreeTrack20;V160;;;20200;01AE03FB9B23AB43A42900
+431;Virtual Driving;FreeTrack20;V160;;;20245;01AF99BB5C9927B83FB700
+432;Virtual Heroes;FreeTrack20;V160;;;20375;01B0621E78732B732AE200
+433;Virtual RC Racing;FreeTrack20;V160;;;8201;01B16D2D1038A36A23DA00
+434;Virtual Sailor;FreeTrack20;V160;;;5201;01B25F3D95778C32B33D00
+435;Virtual Shopper;FreeTrack20;V160;;;20610;01B3B78B06EC9927B73E00
+436;Virtual Supermarket;FreeTrack20;V160;;;20280;01B4849F9C39A931AD2800
+496;VirtualiTeach;FreeTrack20;V160;;;20790;01F062AE1CAB33B52D0700
+480;Vizard;FreeTrack20;V160;;;20745;01E0C1F20A79D7C0561300
+437;VizRD;FreeTrack20;V160;;;20031;01B55485D9825BF1D8AB00
+438;Void War;FreeTrack20;V160;;;10001;01B65C1FDE872AA1206600
+477;VRPlayer;FreeTrack20;V160;;;20730;01DD6787E57245201D5000
+439;VT08;FreeTrack20;V160;;;1275;01B7B78B06EC9912750200
+440;VVVV Plugin;FreeTrack20;V160;;;20075;01B8B1C6648A138C205500
+441;VW Testing;FreeTrack20;V160;;;20275;01B9AC6E878D205822AA00
+442;Walkabout3d;FreeTrack20;V160;;;20125;01BA2822901E9E39932F00
+443;Wallbusters;FreeTrack20;V160;;;1450;01BB88F58C1EA127A83F00
+444;War Thunder;FreeTrack20;V160;;;0;01BC17BE029A1FB8ED9600
+445;WAVES;FreeTrack20;V160;;;20515;01BD05B38FFE89008B8B00
+446;Welding Simulator;FreeTrack20;V160;;;2625;01BE348B188319D229B300
+447;West Racing;FreeTrack20;V160;;;1801;01BF91D999022A33B62000
+448;Whirlwind of Vietnam;FreeTrack20;V160;;;11101;01C0054E9935A03AAF3200
+449;Wikid FJ;FreeTrack20;V160;;;20685;01C19BC3BDA98269B25A00
+450;Wings of Prey;FreeTrack20;V160;V;V4Friend;1877;01C25909438736A133AC00
+451;Wings of War;FreeTrack20;V160;;;7807;01C34B0FC38B36B42FB610
+512;Wings: Over Flanders Fields;FreeTrack20;V160;;;2326;02008AE39AAA4624B33C00
+452;World Racing 2;FreeTrack20;V160;;;7201;01C41AAF68A2BA836AAE00
+453;WWII Battle Tanks: T-34 vs. Tiger;FreeTrack20;V160;;;11102;01C5432C29C257F1963100
+454;X Motor Racing;FreeTrack20;V160;;;1150;01C669259690E98629A900
+458;X-Cockpit for X-Plane;FreeTrack20;V160;;;2901;01CA1C76F8BF4852999300
+459;X-Plane (32 and 64 bit);FreeTrack20;V160;V;EmBeES ;7701;01CB3FD0FC9D90FB943300
+460;X-Plane 6DOF Plugin;FreeTrack20;V160;;;10101;01CCA15F28AF7963596300
+461;X-Plane Pilot View (32 and 64 bit);FreeTrack20;V160;V;V4Friend;11301;01CD79EC0E28EFA953CA00
+462;X-Tower for X-Plane;FreeTrack20;V160;;;3001;01CE05B390EA872AAF3200
+455;X1Alpha;FreeTrack20;V160;;;1825;01C76D2D1038CFD7135A00
+456;X2: The Threat;FreeTrack20;V160;;;5501;01C81039EF980EC8D30900
+457;X3: Albion Prelude and Reunion;FreeTrack20;V170;V;V4Friend;5502;01C97E101A708F8524E300
+463;Xyphoid;FreeTrack20;V160;;;14301;01CF656DD68B43B435A800
+464;zedasoft F-35 Demo;FreeTrack20;V160;;;20041;01D02100B01F942CB93C00
+493;ZKT;FreeTrack20;V160;;;20780;01ED2BAA723F3C097EE800
+465;Zombie AA Research;FreeTrack20;V160;;;14201;01D1AA71218B3B9F1C9A00
diff --git a/bin/tracker-ht/cleye.config b/bin/tracker-ht/cleye.config
new file mode 100644
index 00000000..bfb37b47
--- /dev/null
+++ b/bin/tracker-ht/cleye.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cleye>
+ <item name="mode" value="advanced" />
+</cleye> \ No newline at end of file
diff --git a/bin/tracker-ht/license.txt b/bin/tracker-ht/license.txt
new file mode 100644
index 00000000..a1a778e6
--- /dev/null
+++ b/bin/tracker-ht/license.txt
@@ -0,0 +1,32 @@
+Copyright (c) 2012-2013 Stanisław 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.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+------------------------------------------------------------------------
+
+Credit for the head mesh:
+
+Jed Frechette submitted the mesh as a pull request.
+I cannot thank him enough!
+
+------------------------------------------------------------------------
+
+The project links against the flandmark library, written by
+Michal Uřičář and Vojtěch Franc. The library is GPL3-licensed.
+
+More info at: http://cmp.felk.cvut.cz/~uricamic/flandmark
+Github repo: https://github.com/uricamic/flandmark
+
+------------------------------------------------------------------------
+
+The projects uses OpenCV, see http://code.opencv.org for more info. \ No newline at end of file
diff --git a/bin/tracker-ht/thanks.txt b/bin/tracker-ht/thanks.txt
new file mode 100644
index 00000000..42612dfa
--- /dev/null
+++ b/bin/tracker-ht/thanks.txt
@@ -0,0 +1,5 @@
+The following people deserve thanks for their work on libheadtracker:
+
+The awesome head mesh, made using a 3D scanner was made by
+Jed Frechette. I cannot thank him enough, given how much it
+helped improve the program. \ No newline at end of file
diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake
new file mode 100644
index 00000000..e16d8c37
--- /dev/null
+++ b/cmake/GetGitRevisionDescription.cmake
@@ -0,0 +1,123 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+# git_describe(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+ return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+ set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}")
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
+ set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+ get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+ if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+ # We have reached the root directory, we are not in git
+ set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ endwhile()
+ set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+ if(NOT EXISTS "${GIT_DATA}")
+ file(MAKE_DIRECTORY "${GIT_DATA}")
+ endif()
+
+ if(NOT EXISTS "${GIT_DIR}/HEAD")
+ return()
+ endif()
+ set(HEAD_FILE "${GIT_DATA}/HEAD")
+ configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+ configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+ "${GIT_DATA}/grabRef.cmake"
+ @ONLY)
+ include("${GIT_DATA}/grabRef.cmake")
+
+ set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+ set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+ if(NOT GIT_FOUND)
+ find_package(Git QUIET)
+ endif()
+ get_git_head_revision(refspec hash)
+ if(NOT GIT_FOUND)
+ set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ if(NOT hash)
+ set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+
+ # TODO sanitize
+ #if((${ARGN}" MATCHES "&&") OR
+ # (ARGN MATCHES "||") OR
+ # (ARGN MATCHES "\\;"))
+ # message("Please report the following error to the project!")
+ # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+ #endif()
+
+ #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+ execute_process(COMMAND
+ "${GIT_EXECUTABLE}"
+ describe
+ ${hash}
+ ${ARGN}
+ WORKING_DIRECTORY
+ "${CMAKE_SOURCE_DIR}"
+ RESULT_VARIABLE
+ res
+ OUTPUT_VARIABLE
+ out
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(NOT res EQUAL 0)
+ set(out "${out}-${res}-NOTFOUND")
+ endif()
+
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+ git_describe(out ${ARGN})
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in
new file mode 100644
index 00000000..888ce13a
--- /dev/null
+++ b/cmake/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,38 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+ # named branch
+ string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+ if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+ configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
+ configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ set(HEAD_HASH "${HEAD_REF}")
+ endif()
+else()
+ # detached HEAD
+ configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+ file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+ string(STRIP "${HEAD_HASH}" HEAD_HASH)
+endif()
diff --git a/compat/compat.cpp b/compat/compat.cpp
new file mode 100644
index 00000000..7b695617
--- /dev/null
+++ b/compat/compat.cpp
@@ -0,0 +1,85 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+#define IN_FTNOIR_COMPAT
+#include "compat.h"
+#include <string.h>
+
+#if defined(_WIN32)
+
+PortableLockedShm::PortableLockedShm(const char* shmName, const char* mutexName, int mapSize)
+{
+ hMutex = CreateMutexA(NULL, false, mutexName);
+ hMapFile = CreateFileMappingA(
+ INVALID_HANDLE_VALUE,
+ NULL,
+ PAGE_READWRITE,
+ 0,
+ mapSize,
+ shmName);
+ mem = MapViewOfFile(hMapFile,
+ FILE_MAP_WRITE,
+ 0,
+ 0,
+ mapSize);
+}
+
+PortableLockedShm::~PortableLockedShm()
+{
+ UnmapViewOfFile(mem);
+ CloseHandle(hMapFile);
+ CloseHandle(hMutex);
+}
+
+void PortableLockedShm::lock()
+{
+ (void) WaitForSingleObject(hMutex, INFINITE);
+}
+
+void PortableLockedShm::unlock()
+{
+ (void) ReleaseMutex(hMutex);
+}
+
+#else
+PortableLockedShm::PortableLockedShm(const char *shmName, const char* /*mutexName*/, int mapSize) : size(mapSize)
+{
+ char filename[512] = {0};
+ strcpy(filename, "/");
+ strcat(filename, shmName);
+ fd = shm_open(filename, O_RDWR | O_CREAT, 0600);
+ (void) ftruncate(fd, mapSize);
+ mem = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0);
+}
+
+PortableLockedShm::~PortableLockedShm()
+{
+ //(void) shm_unlink(shm_filename);
+
+ (void) munmap(mem, size);
+ (void) close(fd);
+}
+
+void PortableLockedShm::lock()
+{
+ flock(fd, LOCK_EX);
+}
+
+void PortableLockedShm::unlock()
+{
+ flock(fd, LOCK_UN);
+}
+
+#endif
+
+bool PortableLockedShm::success()
+{
+#ifndef _WIN32
+ return (void*) mem != (void*) -1;
+#else
+ return (void*) mem != NULL;
+#endif
+}
diff --git a/compat/compat.h b/compat/compat.h
new file mode 100644
index 00000000..0e488752
--- /dev/null
+++ b/compat/compat.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+#pragma once
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+
+#if !defined(OPENTRACK_COMPAT_BUNDLED)
+# if defined(IN_FTNOIR_COMPAT) && defined(_WIN32)
+# define COMPAT_EXPORT __declspec(dllexport)
+# elif defined(_WIN32)
+# define COMPAT_EXPORT __declspec(dllimport)
+# else
+# define COMPAT_EXPORT __attribute__ ((visibility ("default")))
+# endif
+#else
+# define COMPAT_EXPORT
+#endif
+
+class COMPAT_EXPORT PortableLockedShm {
+public:
+ PortableLockedShm(const char *shmName, const char *mutexName, int mapSize);
+ ~PortableLockedShm();
+ void lock();
+ void unlock();
+ bool success();
+ void* mem;
+private:
+#if defined(_WIN32)
+ HANDLE hMutex, hMapFile;
+#else
+ int fd, size;
+ //char shm_filename[NAME_MAX];
+#endif
+};
diff --git a/compat/qt-bug-appeasement.cpp b/compat/qt-bug-appeasement.cpp
new file mode 100644
index 00000000..9a86ac0a
--- /dev/null
+++ b/compat/qt-bug-appeasement.cpp
@@ -0,0 +1 @@
+#include "facetracknoir/qt-moc.h"
diff --git a/dinput/dinput8.lib b/dinput/dinput8.lib
new file mode 100644
index 00000000..3fad62fb
--- /dev/null
+++ b/dinput/dinput8.lib
Binary files differ
diff --git a/dinput/dxguid.lib b/dinput/dxguid.lib
new file mode 100644
index 00000000..8397d134
--- /dev/null
+++ b/dinput/dxguid.lib
Binary files differ
diff --git a/dinput/strmiids.lib b/dinput/strmiids.lib
new file mode 100644
index 00000000..8d921239
--- /dev/null
+++ b/dinput/strmiids.lib
Binary files differ
diff --git a/documentation/filemapping example.doc b/documentation/filemapping example.doc
new file mode 100644
index 00000000..0940508c
--- /dev/null
+++ b/documentation/filemapping example.doc
Binary files differ
diff --git a/documentation/qt 4.5 with visual studio 2.pdf b/documentation/qt 4.5 with visual studio 2.pdf
new file mode 100644
index 00000000..c23e3ac9
--- /dev/null
+++ b/documentation/qt 4.5 with visual studio 2.pdf
Binary files differ
diff --git a/documentation/setting up qt.pdf b/documentation/setting up qt.pdf
new file mode 100644
index 00000000..addaee9a
--- /dev/null
+++ b/documentation/setting up qt.pdf
Binary files differ
diff --git a/documentation/using shared memory for freetrack server.pdf b/documentation/using shared memory for freetrack server.pdf
new file mode 100644
index 00000000..980a0e55
--- /dev/null
+++ b/documentation/using shared memory for freetrack server.pdf
Binary files differ
diff --git a/documentation/working with qt visual studio plugin.pdf b/documentation/working with qt visual studio plugin.pdf
new file mode 100644
index 00000000..5d70447e
--- /dev/null
+++ b/documentation/working with qt visual studio plugin.pdf
Binary files differ
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
diff --git a/freetrackclient/freetrackclient.cpp b/freetrackclient/freetrackclient.cpp
new file mode 100644
index 00000000..395f017d
--- /dev/null
+++ b/freetrackclient/freetrackclient.cpp
@@ -0,0 +1,262 @@
+/***********************************************************************************
+ * * FTTypes FTTypes contains the specific type definitions for the *
+ * * FreeTrack protocol. *
+ * * It was loosely translated from FTTypes.pas *
+ * * which was created by the FreeTrack-team. *
+ * * *
+ * * Wim Vriend (Developing) *
+ * * Ron Hendriks (Testing and Research) *
+ * * *
+ * * Homepage <http://facetracknoir.sourceforge.net/home/default.htm> *
+ * * *
+ * * 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. *
+ * * *
+ * * *
+ * * The FreeTrackClient sources were translated from the original Delphi sources *
+ * * created by the FreeTrack developers. *
+ */
+#define NP_AXIS_MAX 16383
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+//#include <tchar.h>
+
+#include "ftnoir_protocol_ft/fttypes.h"
+
+#define FT_EXPORT(t) extern "C" __declspec(dllexport) t __stdcall
+
+//
+// Functions to create/open the file-mapping
+// and to destroy it again.
+//
+static float scale2AnalogLimits( float x, float min_x, float max_x );
+static float getDegreesFromRads ( float rads );
+FT_EXPORT(bool) FTCreateMapping(void);
+
+#if 0
+static FILE *debug_stream = fopen("c:\\FreeTrackClient.log", "a");
+#define dbg_report(...) if (debug_stream) { fprintf(debug_stream, __VA_ARGS__); fflush(debug_stream); }
+#else
+#define dbg_report(...)
+#endif
+
+//
+// Handles to 'handle' the memory mapping
+//
+static HANDLE hFTMemMap = 0;
+static FTMemMap *pMemData = 0;
+static HANDLE hFTMutex = 0;
+static const char* dllVersion = "1.0.0.0";
+static const char* dllProvider = "FreeTrack";
+
+static unsigned short gameid = 0;
+
+//
+// DllMain gets called, when the DLL is (un)loaded or a process attaches.
+//
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+#ifdef WIN64
+ dbg_report("\n= WIN64 =========================================================================================\n");
+#else
+ dbg_report("\n= WIN32 =========================================================================================\n");
+#endif
+ dbg_report("DllMain: (0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
+ dbg_report("DllMain: Attach request\n");
+ DisableThreadLibraryCalls(hinstDLL);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ dbg_report("DllMain: Detach\n");
+ dbg_report("DllMain: (0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
+ dbg_report("==========================================================================================\n");
+ break;
+ }
+ return TRUE;
+}
+
+/******************************************************************
+ * FTGetData (FreeTrackClient.1)
+ */
+
+#pragma comment(linker, "/export:FTGetData@4=FTGetData")
+FT_EXPORT(bool) FTGetData(PFreetrackData data)
+{
+ static int prevDataID = 0;
+ static int dlyTrackingOff = 0;
+
+
+// dbg_report("NP_GetData called.");
+ if (FTCreateMapping() == false) return false;
+
+ if (hFTMutex && WaitForSingleObject(hFTMutex, 5) == WAIT_OBJECT_0) {
+ if (pMemData) {
+
+ //
+ // When FaceTrackNoIR does not update frames (any more), don't update the data.
+ //
+ if (prevDataID != pMemData->data.DataID) {
+ memcpy(data, &pMemData->data, sizeof(TFreeTrackData));
+ dlyTrackingOff = 0;
+ }
+ else {
+ dlyTrackingOff++;
+ if (dlyTrackingOff > 20) {
+ dlyTrackingOff = 100;
+ }
+ }
+ prevDataID = pMemData->data.DataID;
+
+ //
+ // Limit the range of DataID
+ //
+ if (pMemData->data.DataID > 1000) {
+ pMemData->data.DataID = 0;
+ }
+ data->DataID = pMemData->data.DataID;
+
+ //
+ // Send the ID to FaceTrackNoIR, so it can display the game-name.
+ // This could be a FreeTrack-specific ID
+ //
+ pMemData->GameID = gameid;
+ }
+ ReleaseMutex(hFTMutex);
+ }
+ return true;
+}
+
+/******************************************************************
+ * FTReportName (FreeTrackClient.2)
+ */
+#pragma comment(linker, "/export:FTReportName@4=FTReportName")
+//
+// For some mysterious reason, the previously existing function FTReportID has been changed to FTReportName, but with an integer as argument.
+// The Delphi-code from the FreeTrack repo suggest a char * as argument, so it cost me an afternoon to figure it out (and keep ArmA2 from crashing).
+// Thanks guys!
+//
+FT_EXPORT(void) FTReportName( int name )
+{
+ dbg_report("FTReportName request (ID = %d).\n", name);
+ gameid = name; // They might have really passed the name here... but they didn't!
+ return;
+}
+
+/******************************************************************
+ * FTGetDllVersion (FreeTrackClient.3)
+ */
+#pragma comment(linker, "/export:FTGetDllVersion@0=FTGetDllVersion")
+FT_EXPORT(const char*) FTGetDllVersion(void)
+{
+ dbg_report("FTGetDllVersion request.\n");
+
+ return dllVersion;
+}
+
+/******************************************************************
+ * FTProvider (FreeTrackClient.4)
+ */
+#pragma comment(linker, "/export:FTProvider@0=FTProvider")
+FT_EXPORT(const char*) FTProvider(void)
+{
+ dbg_report("FTProvider request.\n");
+
+ return dllProvider;
+}
+
+//
+// Create a memory-mapping to the Freetrack data.
+// It contains the tracking data, a handle to the main-window and the program-name of the Game!
+//
+//
+FT_EXPORT(bool) FTCreateMapping(void)
+{
+ //
+ // Memory-mapping already exists!
+ //
+ if ( pMemData != NULL ) {
+ return true;
+ }
+
+ dbg_report("FTCreateMapping request (pMemData == NULL).\n");
+
+ //
+ // A FileMapping is used to create 'shared memory' between the FTClient and the FTServer.
+ //
+ // Try to create a FileMapping to the Shared Memory. This is done to check if it's already there (what
+ // may mean the face-tracker program is already running).
+ //
+ // If one already exists: close it and open the file-mapping to the existing one.
+ //
+ hFTMemMap = CreateFileMappingA( INVALID_HANDLE_VALUE , 00 , PAGE_READWRITE , 0 ,
+ sizeof( FTMemMap ),
+ (LPCSTR) FT_MM_DATA );
+
+ if ( ( hFTMemMap != 0 ) && ( GetLastError() == ERROR_ALREADY_EXISTS ) ) {
+ dbg_report("FTCreateMapping: Mapping already exists.\n");
+ CloseHandle( hFTMemMap );
+ hFTMemMap = 0;
+ }
+
+ //
+ // Create a new FileMapping, Read/Write access
+ //
+ hFTMemMap = OpenFileMappingA( FILE_MAP_WRITE , false , (LPCSTR) FT_MM_DATA );
+ if ( ( hFTMemMap != 0 ) ) {
+ dbg_report("FTCreateMapping: Mapping opened.\n");
+ pMemData = (FTMemMap *) MapViewOfFile(hFTMemMap, FILE_MAP_WRITE, 0, 0, sizeof( FTMemMap ) );
+ hFTMutex = CreateMutexA(NULL, false, FREETRACK_MUTEX);
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+
+//
+// Destory the FileMapping to the shared memory
+//
+FT_EXPORT(void) FTDestroyMapping(void)
+{
+ if ( pMemData != NULL ) {
+ UnmapViewOfFile ( pMemData );
+ }
+
+ CloseHandle( hFTMutex );
+ CloseHandle( hFTMemMap );
+ pMemData = 0;
+ hFTMemMap = 0;
+}
+
+//
+// 4 convenience
+//
+static float getDegreesFromRads ( float rads ) {
+ return (rads * 57.295781f);
+}
+
+//
+// Scale the measured value to the TIR values
+//
+static float scale2AnalogLimits( float x, float min_x, float max_x ) {
+double y;
+double local_x;
+
+ local_x = x;
+ if (local_x > max_x) {
+ local_x = max_x;
+ }
+ if (local_x < min_x) {
+ local_x = min_x;
+ }
+ y = ( NP_AXIS_MAX * local_x ) / max_x;
+
+ return (float) y;
+}
diff --git a/freetrackclient/freetrackclient.def b/freetrackclient/freetrackclient.def
new file mode 100644
index 00000000..155648dd
--- /dev/null
+++ b/freetrackclient/freetrackclient.def
@@ -0,0 +1,6 @@
+LIBRARY freetrackclient.dll
+EXPORTS
+FTGetData@4 = FTGetData
+FTReportName@4 = FTReportName
+FTGetDllVersion@0 = FTGetDllVersion
+FTProvider@0 = FTProvider
diff --git a/ftnoir_csv/csv.cpp b/ftnoir_csv/csv.cpp
new file mode 100644
index 00000000..66823d24
--- /dev/null
+++ b/ftnoir_csv/csv.cpp
@@ -0,0 +1,180 @@
+/********************************************************************************
+* 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) 2013 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/>. *
+* *
+********************************************************************************/
+#define INSIDE_CSV
+#include "csv.h"
+#include <QTextDecoder>
+#include <QDebug>
+#include <QFile>
+#include <QCoreApplication>
+
+CSV::CSV(QIODevice * device)
+{
+ m_device = device;
+ m_codec = QTextCodec::codecForLocale();
+ m_pos = 0;
+ m_rx = QRegExp("((?:(?:[^;\\n]*;?)|(?:\"[^\"]*\";?))*)\\n");
+}
+CSV::CSV(QString &string){
+ m_device = NULL;
+ m_codec = QTextCodec::codecForLocale();
+ m_string = string;
+ m_pos = 0;
+ m_rx = QRegExp("((?:(?:[^;\\n]*;?)|(?:\"[^\"]*\";?))*)\\n");
+}
+
+CSV::~CSV()
+{
+ //delete m_codec;
+}
+
+
+void CSV::setCodec(const char* codecName){
+ //delete m_codec;
+ m_codec = QTextCodec::codecForName(codecName);
+}
+
+QString CSV::readLine(){
+ QString line;
+
+ if(m_string.isNull()){
+ //READ DATA FROM DEVICE
+ if(m_device && m_device->isReadable()){
+ QTextDecoder dec(m_codec);
+ m_string = dec.toUnicode(m_device->readAll());
+ }else{
+ return QString();
+ }
+ }
+
+ //PARSE
+ if((m_pos = m_rx.indexIn(m_string,m_pos)) != -1) {
+ line = m_rx.cap(1);
+ m_pos += m_rx.matchedLength();
+ }
+ return line;
+
+}
+QStringList CSV::parseLine(){
+ return parseLine(readLine());
+}
+QStringList CSV::parseLine(QString line){
+ QStringList list;
+ int pos2 = 0;
+ QRegExp rx2("(?:\"([^\"]*)\";?)|(?:([^;]*);?)");
+ if(line.size()<1){
+ list << "";
+ }else while (line.size()>pos2 && (pos2 = rx2.indexIn(line, pos2)) != -1) {
+ QString col;
+ if(rx2.cap(1).size()>0)
+ col = rx2.cap(1);
+ else if(rx2.cap(2).size()>0)
+ col = rx2.cap(2);
+
+ list << col;
+
+ if(col.size())
+ pos2 += rx2.matchedLength();
+ else
+ pos2++;
+ }
+ return list;
+}
+
+void CSV::getGameData( const int id, unsigned char* table, QString& gamename)
+{
+ QString gameID = QString::number(id);
+
+ /* zero table first, in case unknown game is connecting */
+ memset(table, 0, 8);
+ QStringList gameLine;
+ qDebug() << "getGameData, ID = " << gameID;
+
+ //
+ // Open the supported games list, to get the Name.
+ //
+ QFile file(QCoreApplication::applicationDirPath() + "/settings/facetracknoir supported games.csv");
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
+ return;
+ }
+ CSV csv(&file);
+ gameLine = csv.parseLine();
+
+ while (gameLine.count() > 2) {
+ //qDebug() << "Column 0: " << gameLine.at(0); // No.
+ //qDebug() << "Column 1: " << gameLine.at(1); // Game Name
+ //qDebug() << "Column 2: " << gameLine.at(2); // Game Protocol
+ //qDebug() << "Column 3: " << gameLine.at(3); // Supported since version
+ //qDebug() << "Column 4: " << gameLine.at(4); // Verified
+ //qDebug() << "Column 5: " << gameLine.at(5); // By
+ //qDebug() << "Column 6: " << gameLine.at(6); // International ID
+ //qDebug() << "Column 7: " << gameLine.at(7); // FaceTrackNoIR ID
+
+ //
+ // If the gameID was found, fill the shared memory
+ //
+ if (gameLine.count() > 6) {
+ if (gameLine.at(6).compare( gameID, Qt::CaseInsensitive ) == 0) {
+ QByteArray id = gameLine.at(7).toLatin1();
+ unsigned int tmp[8];
+ unsigned int fuzz[3];
+ if (gameLine.at(3) == QString("V160"))
+ {
+ qDebug() << "no table";
+ }
+ else if (sscanf(id.constData(),
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ fuzz + 2,
+ fuzz + 0,
+ tmp + 3,
+ tmp + 2,
+ tmp + 1,
+ tmp + 0,
+ tmp + 7,
+ tmp + 6,
+ tmp + 5,
+ tmp + 4,
+ fuzz + 1) != 11)
+ {
+ qDebug() << "scanf failed" << fuzz[0] << fuzz[1] << fuzz[2];
+ }
+ else
+ for (int i = 0; i < 8; i++)
+ table[i] = tmp[i];
+ qDebug() << gameID << "game-id" << gameLine.at(7);
+ gamename = gameLine.at(1);
+ file.close();
+ return;
+ }
+ }
+
+ gameLine = csv.parseLine();
+ }
+
+ //
+ // If the gameID was NOT found, fill only the name "Unknown game connected"
+ //
+ qDebug() << "Unknown game connected" << gameID;
+ file.close();
+}
diff --git a/ftnoir_csv/csv.h b/ftnoir_csv/csv.h
new file mode 100644
index 00000000..bfbece58
--- /dev/null
+++ b/ftnoir_csv/csv.h
@@ -0,0 +1,45 @@
+/*dummy CSV reader for QT4*/
+/*version 0.1*/
+/*11.1.2009*/
+#ifndef CSV_H
+#define CSV_H
+
+//#include "myclass_api.h"
+
+#include <QObject>
+#include <QStringList>
+#include <QIODevice>
+#include <QTextCodec>
+#include <QRegExp>
+#include <QtGlobal>
+
+#if defined(INSIDE_CSV)
+# define CSV_API Q_DECL_EXPORT
+#else
+# define CSV_API Q_DECL_IMPORT
+#endif
+
+class CSV_API CSV
+{
+ /*Q_OBJECT*/
+
+public:
+ ~CSV();
+
+ QString readLine();
+ QStringList parseLine();
+ static QStringList parseLine(QString line);
+
+ void setCodec(const char* codecName);
+ static void getGameData(const int gameID, unsigned char* table, QString& gamename);
+private:
+ QIODevice *m_device;
+ QTextCodec *m_codec;
+ QString m_string;
+ int m_pos;
+ QRegExp m_rx;
+ CSV(QIODevice * device);
+ CSV(QString &string);
+};
+
+#endif // CSV_H
diff --git a/ftnoir_filter_accela/accela-filter.qrc b/ftnoir_filter_accela/accela-filter.qrc
new file mode 100644
index 00000000..9a7d75fa
--- /dev/null
+++ b/ftnoir_filter_accela/accela-filter.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/filter-16-ac.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_filter_accela/ftnoir_accela_filtercontrols.ui b/ftnoir_filter_accela/ftnoir_accela_filtercontrols.ui
new file mode 100644
index 00000000..c544263d
--- /dev/null
+++ b/ftnoir_filter_accela/ftnoir_accela_filtercontrols.ui
@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AccelaUICFilterControls</class>
+ <widget class="QWidget" name="AccelaUICFilterControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>261</width>
+ <height>330</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <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>6667</width>
+ <height>6666</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Accela filter settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../ftnoir_filter_ewma2/ewma-filter.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>7</number>
+ </property>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Translation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="rotation_alpha">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="correctionMode">
+ <enum>QAbstractSpinBox::CorrectToPreviousValue</enum>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>65535.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rotation</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Exponent</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Order #3</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="translation_alpha">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>65535.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Order #2</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QDoubleSpinBox" name="rot_deadzone">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>0.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.050000000000000</double>
+ </property>
+ <property name="value">
+ <double>0.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="lblSensYaw_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">color:#0;
+background:none;</string>
+ </property>
+ <property name="text">
+ <string>Rotation deadband</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0" colspan="2">
+ <widget class="QLabel" name="label_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>7</pointsize>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;Accela by &lt;a href=&quot;https://github.com/sthalik&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Stanisław Halik&lt;/span&gt;&lt;/a&gt;&lt;br/&gt;Help from &lt;a href=&quot;https://github.com/dbaarda&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Donovan Baarda&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;2012-2013&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QDoubleSpinBox" name="expt">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>0.050000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.050000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QDoubleSpinBox" name="order_3rd">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="correctionMode">
+ <enum>QAbstractSpinBox::CorrectToPreviousValue</enum>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>65535.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDoubleSpinBox" name="order_2nd">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="correctionMode">
+ <enum>QAbstractSpinBox::CorrectToPreviousValue</enum>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>65535.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Translation deadband</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QDoubleSpinBox" name="trans_deadzone">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>0.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.050000000000000</double>
+ </property>
+ <property name="value">
+ <double>0.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>rotation_alpha</tabstop>
+ <tabstop>translation_alpha</tabstop>
+ <tabstop>order_2nd</tabstop>
+ <tabstop>order_3rd</tabstop>
+ <tabstop>rot_deadzone</tabstop>
+ <tabstop>trans_deadzone</tabstop>
+ <tabstop>expt</tabstop>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../ftnoir_filter_ewma2/ewma-filter.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.cpp b/ftnoir_filter_accela/ftnoir_filter_accela.cpp
new file mode 100644
index 00000000..b07290e2
--- /dev/null
+++ b/ftnoir_filter_accela/ftnoir_filter_accela.cpp
@@ -0,0 +1,72 @@
+/* Copyright (c) 2012-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.
+ */
+#include "ftnoir_filter_accela/ftnoir_filter_accela.h"
+#include <algorithm>
+#include <cmath>
+#include <QDebug>
+#include <QMutexLocker>
+#include "facetracknoir/global-settings.h"
+using namespace std;
+
+FTNoIR_Filter::FTNoIR_Filter() : first_run(true)
+{
+}
+
+static inline double parabola(const double a, const double x, const double dz, const double expt)
+{
+ const double sign = x > 0 ? 1 : -1;
+ const double a1 = 1./a;
+ return a1 * pow(std::max<double>(fabs(x) - dz, 0), expt) * sign;
+}
+
+void FTNoIR_Filter::FilterHeadPoseData(const double* target_camera_position,
+ double *new_camera_position)
+{
+ if (first_run)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ last_output[j][i] = target_camera_position[i];
+ l.write(target_camera_position, target_camera_position, new_camera_position);
+ }
+
+ first_run = false;
+ return;
+ }
+
+ if (!l.idempotentp(target_camera_position))
+ {
+ for (int i=0;i<6;i++)
+ {
+ const double vec = target_camera_position[i] - last_output[0][i];
+ const double vec2 = target_camera_position[i] - last_output[1][i];
+ const double vec3 = target_camera_position[i] - last_output[2][i];
+ const int sign = vec < 0 ? -1 : 1;
+ const double a = i >= 3 ? s.rotation_alpha : s.translation_alpha;
+ const double a2 = a * s.second_order_alpha;
+ const double a3 = a * s.third_order_alpha;
+ const double deadzone = i >= 3 ? s.rot_deadzone : s.trans_deadzone;
+ const double velocity =
+ parabola(a, vec, deadzone, s.expt) +
+ parabola(a2, vec2, deadzone, s.expt) +
+ parabola(a3, vec3, deadzone, s.expt);
+ const double result = last_output[0][i] + velocity;
+ const bool done = sign > 0 ? result >= target_camera_position[i] : result <= target_camera_position[i];
+ last_output[2][i] = last_output[1][i];
+ last_output[1][i] = last_output[0][i];
+ last_output[0][i] = done ? target_camera_position[i] : result;
+ }
+ }
+
+ l.write(target_camera_position, last_output[0], new_camera_position);
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilter* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Filter;
+}
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.h b/ftnoir_filter_accela/ftnoir_filter_accela.h
new file mode 100644
index 00000000..2187fd01
--- /dev/null
+++ b/ftnoir_filter_accela/ftnoir_filter_accela.h
@@ -0,0 +1,81 @@
+#pragma once
+#include "ftnoir_filter_base/ftnoir_filter_base.h"
+#include "ui_ftnoir_accela_filtercontrols.h"
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/lerp.hpp"
+#include <QMutex>
+
+#define ACCELA_SMOOTHING_ROTATION 60.0
+#define ACCELA_SMOOTHING_TRANSLATION 40.0
+#define ACCELA_SECOND_ORDER_ALPHA 100.0
+#define ACCELA_THIRD_ORDER_ALPHA 180.0
+
+#include <facetracknoir/options.h>
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<double> rotation_alpha,
+ translation_alpha,
+ second_order_alpha,
+ third_order_alpha,
+ rot_deadzone,
+ trans_deadzone,
+ expt;
+ settings() :
+ b(bundle("Accela")),
+ rotation_alpha(b, "rotation-alpha", ACCELA_SMOOTHING_ROTATION),
+ translation_alpha(b, "translation-alpha", ACCELA_SMOOTHING_TRANSLATION),
+ second_order_alpha(b, "second-order-alpha", ACCELA_SECOND_ORDER_ALPHA),
+ third_order_alpha(b, "third-order-alpha", ACCELA_THIRD_ORDER_ALPHA),
+ rot_deadzone(b, "rotation-deadband", 0),
+ trans_deadzone(b, "translation-deadband", 0),
+ expt(b, "exponent", 2)
+ {}
+};
+
+class FTNoIR_Filter : public IFilter
+{
+public:
+ FTNoIR_Filter();
+ void FilterHeadPoseData(const double* target_camera_position, double *new_camera_position);
+ void reset() {
+ first_run = true;
+ }
+ void receiveSettings() {
+ s.b->reload();
+ }
+private:
+ lerp l;
+ settings s;
+ bool first_run;
+ double last_output[3][6];
+};
+
+class FilterControls: public QWidget, public IFilterDialog
+{
+ Q_OBJECT
+public:
+ FilterControls();
+ void registerFilter(IFilter* filter);
+ void unregisterFilter();
+private:
+ Ui::AccelaUICFilterControls ui;
+ void discard();
+ void save();
+ FTNoIR_Filter* accela_filter;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_FilterDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("Accela Filter Mk4"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("Accela Mk4"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Accela filter Mk4"); }
+
+ void getIcon(QIcon *icon){ *icon = QIcon(":/images/filter-16.png"); }
+};
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela_dialog.cpp b/ftnoir_filter_accela/ftnoir_filter_accela_dialog.cpp
new file mode 100644
index 00000000..ca321891
--- /dev/null
+++ b/ftnoir_filter_accela/ftnoir_filter_accela_dialog.cpp
@@ -0,0 +1,58 @@
+#include "ftnoir_filter_accela/ftnoir_filter_accela.h"
+#include <cmath>
+#include <QDebug>
+#include <algorithm>
+#include <QDoubleSpinBox>
+#include "facetracknoir/global-settings.h"
+
+FilterControls::FilterControls() :
+ accela_filter(nullptr)
+{
+ ui.setupUi( this );
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.rotation_alpha, ui.rotation_alpha);
+ tie_setting(s.translation_alpha, ui.translation_alpha);
+ tie_setting(s.second_order_alpha, ui.order_2nd);
+ tie_setting(s.third_order_alpha, ui.order_3rd);
+ tie_setting(s.rot_deadzone, ui.rot_deadzone);
+ tie_setting(s.trans_deadzone, ui.trans_deadzone);
+ tie_setting(s.expt, ui.expt);
+}
+
+void FilterControls::registerFilter(IFilter* filter)
+{
+ accela_filter = (FTNoIR_Filter*) filter;
+}
+
+void FilterControls::unregisterFilter()
+{
+ accela_filter = NULL;
+}
+
+void FilterControls::doOK() {
+ save();
+ this->close();
+}
+
+void FilterControls::doCancel() {
+ discard();
+ close();
+}
+
+void FilterControls::discard()
+{
+ s.b->revert();
+}
+
+void FilterControls::save() {
+ s.b->save();
+ if (accela_filter)
+ accela_filter->receiveSettings();
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilterDialog* CALLING_CONVENTION GetDialog()
+{
+ return new FilterControls;
+}
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela_dll.cpp b/ftnoir_filter_accela/ftnoir_filter_accela_dll.cpp
new file mode 100644
index 00000000..a024e789
--- /dev/null
+++ b/ftnoir_filter_accela/ftnoir_filter_accela_dll.cpp
@@ -0,0 +1,7 @@
+#include "ftnoir_filter_accela.h"
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_FilterDll;
+}
diff --git a/ftnoir_filter_accela/images/filter-16-ac.png b/ftnoir_filter_accela/images/filter-16-ac.png
new file mode 100644
index 00000000..d263db2d
--- /dev/null
+++ b/ftnoir_filter_accela/images/filter-16-ac.png
Binary files differ
diff --git a/ftnoir_filter_base/ftnoir_filter_base.h b/ftnoir_filter_base/ftnoir_filter_base.h
new file mode 100644
index 00000000..fbb0441d
--- /dev/null
+++ b/ftnoir_filter_base/ftnoir_filter_base.h
@@ -0,0 +1,29 @@
+#ifndef FTNOIR_FILTER_BASE_H
+#define FTNOIR_FILTER_BASE_H
+
+#include "ftnoir_filter_base_global.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include <QString>
+#include <QList>
+#include <QFile>
+#include <QCoreApplication>
+#include <QMessageBox>
+#include <QSettings>
+
+struct IFilter
+{
+ virtual ~IFilter() = 0;
+ virtual void FilterHeadPoseData(const double *target_camera_position, double *new_camera_position) = 0;
+ virtual void reset() = 0;
+};
+
+inline IFilter::~IFilter() { }
+
+struct IFilterDialog
+{
+ virtual ~IFilterDialog() {}
+ virtual void registerFilter(IFilter* tracker) = 0;
+ virtual void unregisterFilter() = 0;
+};
+
+#endif // FTNOIR_FILTER_BASE_H
diff --git a/ftnoir_filter_base/ftnoir_filter_base_global.h b/ftnoir_filter_base/ftnoir_filter_base_global.h
new file mode 100644
index 00000000..a1a13315
--- /dev/null
+++ b/ftnoir_filter_base/ftnoir_filter_base_global.h
@@ -0,0 +1,16 @@
+#ifndef FTNOIR_FILTER_BASE_GLOBAL_H
+#define FTNOIR_FILTER_BASE_GLOBAL_H
+
+#include <QtGlobal>
+
+#ifndef OPENTRACK_MAIN
+# if !defined(_MSC_VER)
+# define FTNOIR_FILTER_BASE_EXPORT __attribute__ ((visibility ("default")))
+# else
+# define FTNOIR_FILTER_BASE_EXPORT Q_DECL_EXPORT
+#endif
+#else
+# define FTNOIR_FILTER_BASE_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // FTNOIR_FILTER_BASE_GLOBAL_H
diff --git a/ftnoir_filter_ewma2/ewma-filter.qrc b/ftnoir_filter_ewma2/ewma-filter.qrc
new file mode 100644
index 00000000..e64ec35a
--- /dev/null
+++ b/ftnoir_filter_ewma2/ewma-filter.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/filter-16.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui b/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui
new file mode 100644
index 00000000..1a798a45
--- /dev/null
+++ b/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui
@@ -0,0 +1,478 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFilterControls</class>
+ <widget class="QWidget" name="UICFilterControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>404</width>
+ <height>380</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>380</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>EWMA2 filter settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="ewma-filter.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="geometry">
+ <rect>
+ <x>204</x>
+ <y>351</y>
+ <width>164</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_4">
+ <property name="geometry">
+ <rect>
+ <x>4</x>
+ <y>143</y>
+ <width>396</width>
+ <height>204</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: rgb(214, 214, 214);
+border-color: rgb(0, 0, 0);</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Max:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the way the filter responds to slow movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Pow:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the filters 'readiness' to respond to speed changes;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value = &lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-weight:600;&quot;&gt;faster&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt; response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="lblInvert1_6">
+ <property name="geometry">
+ <rect>
+ <x>5</x>
+ <y>5</y>
+ <width>27</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ <widget class="QSlider" name="maxSmooth">
+ <property name="geometry">
+ <rect>
+ <x>47</x>
+ <y>32</y>
+ <width>84</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="spinMaxSmooth">
+ <property name="geometry">
+ <rect>
+ <x>135</x>
+ <y>32</y>
+ <width>52</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>120</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="spinPowCurve">
+ <property name="geometry">
+ <rect>
+ <x>135</x>
+ <y>59</y>
+ <width>52</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ <widget class="QSlider" name="powCurve">
+ <property name="geometry">
+ <rect>
+ <x>47</x>
+ <y>59</y>
+ <width>84</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" name="lblInvert1_8">
+ <property name="geometry">
+ <rect>
+ <x>5</x>
+ <y>59</y>
+ <width>38</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Curve</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="lblInvert1_7">
+ <property name="geometry">
+ <rect>
+ <x>5</x>
+ <y>32</y>
+ <width>32</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ <widget class="QSlider" name="minSmooth">
+ <property name="geometry">
+ <rect>
+ <x>47</x>
+ <y>5</y>
+ <width>84</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="spinMinSmooth">
+ <property name="geometry">
+ <rect>
+ <x>135</x>
+ <y>5</y>
+ <width>52</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>120</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ </widget>
+ </widget>
+ <resources>
+ <include location="ewma-filter.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>minSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinMinSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>199</x>
+ <y>22</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>337</x>
+ <y>23</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>spinMinSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>minSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>330</x>
+ <y>12</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>185</x>
+ <y>17</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>maxSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinMaxSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>181</x>
+ <y>48</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>335</x>
+ <y>54</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>spinMaxSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>maxSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>324</x>
+ <y>42</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>259</x>
+ <y>43</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>powCurve</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinPowCurve</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>145</x>
+ <y>74</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>339</x>
+ <y>78</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>spinPowCurve</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>powCurve</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>330</x>
+ <y>69</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>176</x>
+ <y>76</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp
new file mode 100644
index 00000000..d3cb2c32
--- /dev/null
+++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp
@@ -0,0 +1,109 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_filter_ewma2.h"
+#include "math.h"
+#include <QDebug>
+#include <QWidget>
+#include "facetracknoir/global-settings.h"
+#include <algorithm>
+#include <QMutexLocker>
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// EWMA Filter: Exponentially Weighted Moving Average filter with dynamic smoothing parameter
+//
+// This filter tries to adjust the amount of filtering to minimize lag when
+// moving, and minimize noise when still. It uses the delta filtered over the
+// last 3 frames (0.1secs) compared to the delta's average noise variance over
+// the last 3600 frames (~2mins) to try and detect movement vs noise. As the
+// delta increases from 0->3 stdevs of the noise, the filtering scales down
+// from maxSmooth->minSmooth at a rate controlled by the powCurve setting.
+//
+// Written by Donovan Baarda
+//
+///////////////////////////////////////////////////////////////////////////////
+
+FTNoIR_Filter::FTNoIR_Filter() :
+ first_run(true),
+ // Deltas are smoothed over the last 3 frames (0.1sec at 30fps).
+ delta_smoothing(1.0/3.0),
+ // Noise is smoothed over the last 3600 frames (~2mins at 30fps).
+ noise_smoothing(1.0/3600.0)
+{
+}
+
+void FTNoIR_Filter::receiveSettings()
+{
+ s.b->reload();
+}
+
+void FTNoIR_Filter::FilterHeadPoseData(const double *target_camera_position,
+ double *new_camera_position)
+{
+ double new_delta, new_noise, norm_noise;
+ double alpha;
+ double pos[6];
+
+ //On the first run, initialize to output=target and return.
+ if (first_run==true) {
+ for (int i=0;i<6;i++) {
+ delta[i] = 0.0;
+ noise[i] = 0.0;
+ l.write(target_camera_position, target_camera_position, new_camera_position);
+ }
+ first_run=false;
+ return;
+ }
+
+ if (!l.idempotentp(target_camera_position))
+ {
+ double cur[6];
+ l.get_state(cur);
+ // Calculate the new camera position.
+ for (int i=0;i<6;i++) {
+ // Calculate the current and smoothed delta.
+ new_delta = target_camera_position[i]-cur[i];
+ delta[i] = delta_smoothing*new_delta + (1.0-delta_smoothing)*delta[i];
+ // Calculate the current and smoothed noise variance.
+ new_noise = delta[i]*delta[i];
+ noise[i] = noise_smoothing*new_noise + (1.0-noise_smoothing)*noise[i];
+ // Normalise the noise between 0->1 for 0->9 variances (0->3 stddevs).
+ norm_noise = std::min<double>(new_noise/(9.0*noise[i]), 1.0);
+ // Calculate the alpha from the normalized noise.
+ // TODO(abo): change kSmoothingScaleCurve to a float where 1.0 is sqrt(norm_noise).
+ alpha = 1.0/(s.kMinSmoothing+(1.0-pow(norm_noise,s.kSmoothingScaleCurve/20.0))*(s.kMaxSmoothing-s.kMinSmoothing));
+ // Update the current camera position to the new position.
+ double value = alpha*target_camera_position[i] + (1.0-alpha)*cur[i];
+ pos[i] = value;
+ }
+ }
+
+ l.write(target_camera_position, pos, new_camera_position);
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilter* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Filter;
+}
diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h
new file mode 100644
index 00000000..ea25a135
--- /dev/null
+++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h
@@ -0,0 +1,98 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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/>. *
+* *
+********************************************************************************/
+#pragma once
+#ifndef INCLUDED_FTN_FILTER_H
+#define INCLUDED_FTN_FILTER_H
+
+#include "ftnoir_filter_base/ftnoir_filter_base.h"
+#include "facetracknoir/global-settings.h"
+#include "ui_ftnoir_ewma_filtercontrols.h"
+#include <QWidget>
+#include <QMutex>
+#include "facetracknoir/options.h"
+#include "facetracknoir/lerp.hpp"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ // these are sadly sliders for now due to int/double mismatch -sh
+ value<int> kMinSmoothing, kMaxSmoothing, kSmoothingScaleCurve;
+ settings() :
+ b(bundle("ewma-filter")),
+ kMinSmoothing(b, "min-smoothing", 15),
+ kMaxSmoothing(b, "max-smoothing", 50),
+ kSmoothingScaleCurve(b, "smoothing-scale-curve", 10)
+ {}
+};
+
+
+class FTNoIR_Filter : public IFilter
+{
+public:
+ FTNoIR_Filter();
+ void reset() {}
+ void FilterHeadPoseData(const double *target_camera_position,
+ double *new_camera_position);
+ void receiveSettings();
+private:
+ bool first_run;
+ double delta_smoothing;
+ double noise_smoothing;
+ double delta[6];
+ double noise[6];
+ settings s;
+ lerp l;
+};
+
+class FilterControls: public QWidget, public IFilterDialog
+{
+ Q_OBJECT
+public:
+ FilterControls();
+ void registerFilter(IFilter* flt);
+ void unregisterFilter();
+
+private:
+ Ui::UICFilterControls ui;
+ void save();
+ settings s;
+ FTNoIR_Filter* pFilter;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_FilterDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("EWMA Filter Mk3"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("EWMA"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Exponentially Weighted Moving Average filter with dynamic smoothing parameter"); }
+ void getIcon(QIcon *icon){ *icon = QIcon(":/images/filter-16.png"); }
+};
+
+#endif //INCLUDED_FTN_FILTER_H
+//END
diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2_dialog.cpp b/ftnoir_filter_ewma2/ftnoir_filter_ewma2_dialog.cpp
new file mode 100644
index 00000000..fb02354e
--- /dev/null
+++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2_dialog.cpp
@@ -0,0 +1,73 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_filter_ewma2.h"
+#include <cmath>
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+#include "ui_ftnoir_ewma_filtercontrols.h"
+
+FilterControls::FilterControls() :
+ QWidget(), pFilter(NULL)
+{
+ ui.setupUi( this );
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.kMaxSmoothing, ui.maxSmooth);
+ tie_setting(s.kMinSmoothing, ui.minSmooth);
+ tie_setting(s.kSmoothingScaleCurve, ui.powCurve);
+}
+
+void FilterControls::registerFilter(IFilter* flt)
+{
+ pFilter = (FTNoIR_Filter*) flt;
+}
+
+void FilterControls::unregisterFilter()
+{
+ pFilter = NULL;
+}
+
+void FilterControls::doOK() {
+ save();
+ this->close();
+}
+
+void FilterControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+void FilterControls::save() {
+ s.b->save();
+ if (pFilter)
+ pFilter->receiveSettings();
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilterDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new FilterControls;
+}
diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma_dll.cpp b/ftnoir_filter_ewma2/ftnoir_filter_ewma_dll.cpp
new file mode 100644
index 00000000..6ef7768e
--- /dev/null
+++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma_dll.cpp
@@ -0,0 +1,31 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_filter_ewma2.h"
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_FilterDll;
+}
diff --git a/ftnoir_filter_ewma2/images/filter-16.png b/ftnoir_filter_ewma2/images/filter-16.png
new file mode 100644
index 00000000..ecde6a10
--- /dev/null
+++ b/ftnoir_filter_ewma2/images/filter-16.png
Binary files differ
diff --git a/ftnoir_filter_ewma2/images/filter-32.png b/ftnoir_filter_ewma2/images/filter-32.png
new file mode 100644
index 00000000..12b02caf
--- /dev/null
+++ b/ftnoir_filter_ewma2/images/filter-32.png
Binary files differ
diff --git a/ftnoir_filter_kalman/ftnoir_filter_kalman.h b/ftnoir_filter_kalman/ftnoir_filter_kalman.h
new file mode 100644
index 00000000..f2a1b4ec
--- /dev/null
+++ b/ftnoir_filter_kalman/ftnoir_filter_kalman.h
@@ -0,0 +1,69 @@
+#pragma once
+/* Copyright (c) 2013 Stanisław 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.
+ */
+#ifndef INCLUDED_FTN_FILTER_H
+#define INCLUDED_FTN_FILTER_H
+
+#undef FTNOIR_TRACKER_BASE_LIB
+#define FTNOIR_TRACKER_BASE_EXPORT Q_DECL_IMPORT
+
+#include "ftnoir_filter_base/ftnoir_filter_base.h"
+#include "ui_ftnoir_kalman_filtercontrols.h"
+#include "facetracknoir/global-settings.h"
+#include <opencv2/opencv.hpp>
+#include <vector>
+#include <QString>
+#include <QIcon>
+#include <QWidget>
+#include <QElapsedTimer>
+#include <QObject>
+#include "facetracknoir/options.h"
+using namespace options;
+
+class FTNOIR_FILTER_BASE_EXPORT FTNoIR_Filter : public IFilter
+{
+public:
+ FTNoIR_Filter();
+ void reset() virt_override;
+ void FilterHeadPoseData(const double *target_camera_position,
+ double *new_camera_position) virt_override;
+ cv::KalmanFilter kalman;
+ double prev_position[6];
+ double prev2_filter_pos[6];
+ double prev_filter_pos[6];
+ QElapsedTimer timer;
+ qint64 timedelta;
+};
+
+class FTNOIR_FILTER_BASE_EXPORT FTNoIR_FilterDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); }
+ void getIcon(QIcon *icon){ *icon = QIcon(":/images/filter-16.png"); }
+};
+
+class FTNOIR_FILTER_BASE_EXPORT FilterControls: public QWidget, public IFilterDialog
+{
+ Q_OBJECT
+public:
+ FilterControls() {
+ ui.setupUi(this);
+ connect(ui.btnOk, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+ show();
+ }
+ Ui::KalmanUICFilterControls ui;
+ virtual void registerFilter(IFilter*) virt_override {}
+ virtual void unregisterFilter() virt_override {}
+public slots:
+ void doOK();
+ void doCancel();
+};
+
+#endif
diff --git a/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui b/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui
new file mode 100644
index 00000000..e0c849c9
--- /dev/null
+++ b/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>KalmanUICFilterControls</class>
+ <widget class="QWidget" name="KalmanUICFilterControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>334</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Kalman settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../ftnoir_filter_ewma2/ewma-filter.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <widget class="QPushButton" name="btnOk">
+ <property name="geometry">
+ <rect>
+ <x>173</x>
+ <y>70</y>
+ <width>73</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="geometry">
+ <rect>
+ <x>250</x>
+ <y>70</y>
+ <width>73</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </widget>
+ <resources>
+ <include location="../ftnoir_filter_ewma2/ewma-filter.qrc"/>
+ </resources>
+ <connections/>
+ <designerdata>
+ <property name="gridDeltaX">
+ <number>10</number>
+ </property>
+ <property name="gridDeltaY">
+ <number>10</number>
+ </property>
+ <property name="gridSnapX">
+ <bool>false</bool>
+ </property>
+ <property name="gridSnapY">
+ <bool>false</bool>
+ </property>
+ <property name="gridVisible">
+ <bool>true</bool>
+ </property>
+ </designerdata>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_filter_kalman/images/filter-16-ac.png b/ftnoir_filter_kalman/images/filter-16-ac.png
new file mode 100644
index 00000000..d263db2d
--- /dev/null
+++ b/ftnoir_filter_kalman/images/filter-16-ac.png
Binary files differ
diff --git a/ftnoir_filter_kalman/kalman-filter.qrc b/ftnoir_filter_kalman/kalman-filter.qrc
new file mode 100644
index 00000000..9a7d75fa
--- /dev/null
+++ b/ftnoir_filter_kalman/kalman-filter.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/filter-16-ac.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_filter_kalman/kalman.cpp b/ftnoir_filter_kalman/kalman.cpp
new file mode 100644
index 00000000..bef6ddad
--- /dev/null
+++ b/ftnoir_filter_kalman/kalman.cpp
@@ -0,0 +1,136 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+#include "ftnoir_filter_kalman.h"
+#include "facetracknoir/global-settings.h"
+#include <QDebug>
+#include <math.h>
+
+FTNoIR_Filter::FTNoIR_Filter() {
+ reset();
+}
+
+// the following was written by Donovan Baarda <abo@minkirri.apana.org.au>
+// https://sourceforge.net/p/facetracknoir/discussion/1150909/thread/418615e1/?limit=25#af75/084b
+void FTNoIR_Filter::reset() {
+ const double accel_variance = 1e-3;
+ const double noise_variance = 5e2;
+ kalman.init(12, 6, 0, CV_64F);
+ kalman.transitionMatrix = (cv::Mat_<double>(12, 12) <<
+ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+ double a = 0.25 * accel_variance;
+ double b = 0.5 * accel_variance;
+ double c = 1.0 * accel_variance;
+ kalman.processNoiseCov = (cv::Mat_<double>(12, 12) <<
+ a, 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0,
+ 0, a, 0, 0, 0, 0, 0, b, 0, 0, 0, 0,
+ 0, 0, a, 0, 0, 0, 0, 0, b, 0, 0, 0,
+ 0, 0, 0, a, 0, 0, 0, 0, 0, b, 0, 0,
+ 0, 0, 0, 0, a, 0, 0, 0, 0, 0, b, 0,
+ 0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, b,
+ b, 0, 0, 0, 0, 0, c, 0, 0, 0, 0, 0,
+ 0, b, 0, 0, 0, 0, 0, c, 0, 0, 0, 0,
+ 0, 0, b, 0, 0, 0, 0, 0, c, 0, 0, 0,
+ 0, 0, 0, b, 0, 0, 0, 0, 0, c, 0, 0,
+ 0, 0, 0, 0, b, 0, 0, 0, 0, 0, c, 0,
+ 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0, c);
+ cv::setIdentity(kalman.measurementMatrix);
+ cv::setIdentity(kalman.measurementNoiseCov, cv::Scalar::all(noise_variance));
+ cv::setIdentity(kalman.errorCovPost, cv::Scalar::all(accel_variance * 1e4));
+ for (int i = 0; i < 6; i++)
+ {
+ prev_position[i] = 0;
+ prev2_filter_pos[i] = 0;
+ prev_filter_pos[i] = 0;
+ timedelta = 1;
+ timer.invalidate();
+ }
+}
+
+template<typename T>
+static inline T clamp(const T min, const T max, const T value)
+{
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+}
+
+void FTNoIR_Filter::FilterHeadPoseData(const double* target_camera_position,
+ double *new_camera_position)
+{
+ bool new_target = false;
+
+ for (int i = 0; i < 6; i++)
+ if (prev_position[i] != target_camera_position[i])
+ {
+ new_target = true;
+ break;
+ }
+
+ if (new_target) {
+ cv::Mat output = kalman.predict();
+ cv::Mat measurement(6, 1, CV_64F);
+ for (int i = 0; i < 3; i++) {
+ measurement.at<double>(i) = target_camera_position[i+3];
+ measurement.at<double>(i+3) = target_camera_position[i];
+ }
+ kalman.correct(measurement);
+ for (int i = 0; i < 6; i++)
+ {
+ prev_position[i] = target_camera_position[i];
+ }
+ if (timer.isValid())
+ timedelta = timer.elapsed();
+ else
+ timedelta = 1;
+ for (int i = 0; i < 6; i++)
+ {
+ prev2_filter_pos[i] = prev_filter_pos[i];
+ prev_filter_pos[i] = new_camera_position[i] = output.at<double>((i + 3) % 6);
+ }
+ timer.start();
+ } else {
+ auto d = timer.isValid() ? timer.elapsed() : 1;
+ auto c = clamp(0.0, 1.0, d / (double) timedelta);
+ for (int i = 0; i < 6; i++)
+ new_camera_position[i] = prev2_filter_pos[i] + (prev_filter_pos[i] - prev2_filter_pos[i]) * c;
+ }
+}
+
+void FilterControls::doOK() {
+ close();
+}
+
+void FilterControls::doCancel() {
+ close();
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_FilterDll;
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilter* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Filter;
+}
+
+extern "C" FTNOIR_FILTER_BASE_EXPORT IFilterDialog* CALLING_CONVENTION GetDialog() {
+ return new FilterControls;
+}
diff --git a/ftnoir_posewidget/glwidget.cpp b/ftnoir_posewidget/glwidget.cpp
new file mode 100644
index 00000000..26803385
--- /dev/null
+++ b/ftnoir_posewidget/glwidget.cpp
@@ -0,0 +1,197 @@
+/* Copyright (c) 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.
+ */
+
+#include <QtGui>
+#include "glwidget.h"
+#include <QWidget>
+#include <cmath>
+#include <algorithm>
+
+GLWidget::GLWidget(QWidget *parent) : QWidget(parent)
+{
+ front = QImage(QString(":/images/side1.png"));
+ back = QImage(QString(":/images/side6.png"));
+ rotateBy(0, 0, 0);
+}
+
+GLWidget::~GLWidget()
+{
+}
+
+void GLWidget::paintEvent ( QPaintEvent * event ) {
+ QWidget::paintEvent(event);
+ QPainter p(this);
+ project_quad_texture();
+ p.drawImage(event->rect(), texture);
+}
+
+void GLWidget::rotateBy(double xAngle, double yAngle, double zAngle)
+{
+
+ double ch = cos(xAngle / 57.295781);
+ double sh = sin(xAngle / 57.295781);
+ double ca = cos(yAngle / 57.295781);
+ double sa = sin(yAngle / 57.295781);
+ double cb = cos(zAngle / 57.295781);
+ double sb = sin(zAngle / 57.295781);
+
+ matrix[0 * 3 + 0] = ch * ca;
+ matrix[0 * 3 + 1]= sh*sb - ch*sa*cb;
+ matrix[0 * 3 + 2]= ch*sa*sb + sh*cb;
+ matrix[1 * 3 + 0]= sa;
+ matrix[1 * 3 + 1]= ca*cb;
+ matrix[1 * 3 + 2]= -ca*sb;
+ matrix[2 * 3 + 0]= -sh*ca;
+ matrix[2 * 3 + 1]= sh*sa*cb + ch*sb;
+ matrix[2 * 3 + 2]= -sh*sa*sb + ch*cb;
+ update();
+}
+
+class Triangle {
+public:
+ Triangle(const Vec2f& p1,
+ const Vec2f& p2,
+ const Vec2f& p3)
+ {
+ origin = p1;
+ v0 = Vec2f(p3.x - p1.x, p3.y - p1.y);
+ v1 = Vec2f(p2.x - p1.x, p2.y - p1.y);
+ dot00 = dot(v0, v0);
+ dot01 = dot(v0, v1);
+ dot11 = dot(v1, v1);
+ invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+ }
+ bool barycentric_coords(const Vec2f& px, Vec2f& uv) const
+ {
+ Vec2f v2(px.x - origin.x, px.y - origin.y);
+ double dot12 = dot(v1, v2);
+ double dot02 = dot(v0, v2);
+ double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+ uv.x = u;
+ uv.y = v;
+ return (u >= 0) && (v >= 0) && (u + v <= 1);
+ }
+
+private:
+ double dot00, dot01, dot11, invDenom;
+ Vec2f v0, v1, origin;
+ double dot(const Vec2f& p1, const Vec2f& p2) const {
+ return p1.x * p2.x + p1.y * p2.y;
+ }
+};
+
+static __inline Vec3f cross(const Vec3f& p1, const Vec3f& p2)
+{
+ return Vec3f(p1.y * p2.z - p2.y * p1.z,
+ p2.x * p1.z - p1.x * p2.z,
+ p1.x * p2.y - p1.y * p2.x);
+}
+
+static __inline Vec3f normal(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3)
+{
+ Vec3f u(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);
+ Vec3f v(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z);
+
+ Vec3f tmp = cross(u, v);
+
+ double i = 1./sqrt(tmp.x * tmp.x + tmp.y * tmp.y + tmp.z * tmp.z);
+
+ return Vec3f(i * tmp.x, i * tmp.y, i * tmp.z);
+}
+
+void GLWidget::project_quad_texture() {
+ const int sx = width(), sy = height();
+ Point pt[4];
+ static Vec3f corners[] = {
+ Vec3f(0, 0, 0),
+ Vec3f(sx-1, 0, 0),
+ Vec3f(0, sy-1, 0),
+ Vec3f(sx-1, sy-1, 0)
+ };
+
+ for (int i = 0; i < 4; i++) {
+ pt[i] = project(Vec3f(corners[i].x - sx/2, corners[i].y - sy/2, 0));
+ pt[i].x += sx/2;
+ pt[i].y += sy/2;
+ }
+
+ Vec3f normal1(0, 0, 1);
+ Vec3f normal2;
+ {
+ Vec3f foo[3];
+ for (int i = 0; i < 3; i++)
+ foo[i] = project2(corners[i]);
+ normal2 = normal(foo[0], foo[1], foo[2]);
+ }
+
+ double dir = normal1.x * normal2.x + normal1.y * normal2.y + normal1.z * normal2.z;
+
+ QImage& tex = dir < 0 ? back : front;
+
+ int ow = tex.width(), oh = tex.height();
+
+ Vec2f p2[4];
+
+ for (int i = 0; i < 4; i++)
+ p2[i] = Vec2f(pt[i].x, pt[i].y);
+ QImage texture(QSize(sx, sy), QImage::Format_RGB888);
+ QColor bgColor = palette().color(QPalette::Current, QPalette::Window);
+ texture.fill(bgColor);
+
+ const Vec2f projected[2][3] = { { p2[0], p2[1], p2[2] }, { p2[3], p2[1], p2[2] } };
+ const Vec2f origs[2][3] = {
+ { Vec2f(0, 0), Vec2f(ow-1, 0), Vec2f(0, oh-1) },
+ { Vec2f(ow-1, oh-1), Vec2f(ow-1, 0), Vec2f(0, oh-1) }
+ };
+ const Triangle triangles[2] = {
+ Triangle(projected[0][0], projected[0][1], projected[0][2]),
+ Triangle(projected[1][0], projected[1][1], projected[1][2])
+ };
+
+ int orig_pitch = tex.bytesPerLine();
+ int dest_pitch = texture.bytesPerLine();
+
+ const unsigned char* orig = tex.bits();
+ unsigned char* dest = texture.bits();
+
+ int orig_depth = tex.depth() / 8;
+ int dest_depth = texture.depth() / 8;
+
+ /* image breakage? */
+ if (orig_depth < 3)
+ return;
+
+ for (int y = 0; y < sy; y++)
+ for (int x = 0; x < sx; x++) {
+ Vec2f pos;
+ pos.x = x;
+ pos.y = y;
+ for (int i = 0; i < 2; i++) {
+ Vec2f coords;
+ if (triangles[i].barycentric_coords(pos, coords))
+ {
+ int px = origs[i][0].x
+ + coords.x * (origs[i][2].x - origs[i][0].x)
+ + coords.y * (origs[i][1].x - origs[i][0].x);
+ int py = origs[i][0].y
+ + coords.x * (origs[i][2].y - origs[i][0].y)
+ + coords.y * (origs[i][1].y - origs[i][0].y);
+ int r = orig[py * orig_pitch + px * orig_depth + 2];
+ int g = orig[py * orig_pitch + px * orig_depth + 1];
+ int b = orig[py * orig_pitch + px * orig_depth + 0];
+
+ dest[y * dest_pitch + x * dest_depth + 0] = r;
+ dest[y * dest_pitch + x * dest_depth + 1] = g;
+ dest[y * dest_pitch + x * dest_depth + 2] = b;
+
+ break;
+ }
+ }
+ }
+ this->texture = texture;
+}
diff --git a/ftnoir_posewidget/glwidget.h b/ftnoir_posewidget/glwidget.h
new file mode 100644
index 00000000..c4b2e09d
--- /dev/null
+++ b/ftnoir_posewidget/glwidget.h
@@ -0,0 +1,97 @@
+/* Copyright (c) 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.
+ */
+
+#ifndef GLWIDGET_H
+#define GLWIDGET_H
+
+#include <QtGui>
+#include <QPixmap>
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+
+struct Point {
+ Point(int x, int y) :
+ x(x), y(y)
+ {
+ }
+ Point() :
+ x(0), y(0)
+ {
+ }
+ int x, y;
+};
+
+struct Vec3f {
+ double x, y, z;
+ Vec3f(double x, double y, double z) :
+ x(x), y(y), z(z)
+ {
+ }
+ Vec3f() :
+ x(0), y(0), z(0)
+ {
+ }
+};
+
+struct Vec2f {
+ double x, y;
+ Vec2f(double x, double y) :
+ x(x), y(y)
+ {
+ }
+ Vec2f() :
+ x(0), y(0)
+ {
+ }
+};
+
+class FTNOIR_TRACKER_BASE_EXPORT GLWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ GLWidget(QWidget *parent);
+ ~GLWidget();
+ void rotateBy(double xAngle, double yAngle, double zAngle);
+
+protected:
+ void paintEvent ( QPaintEvent * event );
+
+private:
+ Point project(const Vec3f& point) {
+ Point rect;
+
+ rect.x = point.x * matrix[0]
+ + point.y * matrix[1]
+ + point.z * matrix[2];
+ rect.y = point.x * matrix[3]
+ + point.y * matrix[4]
+ + point.z * matrix[5];
+
+ return rect;
+ }
+ Vec3f project2(const Vec3f& point) {
+ Vec3f rect;
+
+ rect.x = point.x * matrix[0]
+ + point.y * matrix[1]
+ + point.z * matrix[2];
+ rect.y = point.x * matrix[3]
+ + point.y * matrix[4]
+ + point.z * matrix[5];
+ rect.z = point.x * matrix[6]
+ + point.y * matrix[7]
+ + point.z * matrix[8];
+ return rect;
+ }
+ void project_quad_texture();
+ double matrix[9];
+ QImage front;
+ QImage back;
+ QImage texture;
+};
+
+#endif
diff --git a/ftnoir_posewidget/images/side1.png b/ftnoir_posewidget/images/side1.png
new file mode 100644
index 00000000..d7467943
--- /dev/null
+++ b/ftnoir_posewidget/images/side1.png
Binary files differ
diff --git a/ftnoir_posewidget/images/side6.png b/ftnoir_posewidget/images/side6.png
new file mode 100644
index 00000000..eaa80d7e
--- /dev/null
+++ b/ftnoir_posewidget/images/side6.png
Binary files differ
diff --git a/ftnoir_posewidget/posewidget.qrc b/ftnoir_posewidget/posewidget.qrc
new file mode 100644
index 00000000..e799432f
--- /dev/null
+++ b/ftnoir_posewidget/posewidget.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/side1.png</file>
+ <file>images/side6.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_base/ftnoir_protocol_base.h b/ftnoir_protocol_base/ftnoir_protocol_base.h
new file mode 100644
index 00000000..d2f85ec0
--- /dev/null
+++ b/ftnoir_protocol_base/ftnoir_protocol_base.h
@@ -0,0 +1,57 @@
+/********************************************************************************
+* 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/>. *
+* This class implements a tracker-base *
+*********************************************************************************/
+/*
+ Modifications (last one on top):
+
+ 20121115 - WVR: Added RegisterProtocol() and unRegisterProtocol() to Dialog Class
+ 20110415 - WVR: Added overloaded operator - and -=
+*/
+
+#ifndef FTNOIR_PROTOCOL_BASE_H
+#define FTNOIR_PROTOCOL_BASE_H
+
+#include "ftnoir_protocol_base_global.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_types.h"
+#include <QWidget>
+#include <QFrame>
+
+struct IProtocol
+{
+ virtual ~IProtocol() = 0;
+ virtual bool checkServerInstallationOK() = 0;
+ virtual void sendHeadposeToGame( const double* headpose ) = 0;
+ virtual QString getGameName() = 0;
+};
+
+inline IProtocol::~IProtocol() { }
+
+struct IProtocolDialog
+{
+ virtual ~IProtocolDialog() {}
+ virtual void registerProtocol(IProtocol *protocol) = 0;
+ virtual void unRegisterProtocol() = 0;
+};
+
+#endif // FTNOIR_PROTOCOL_BASE_H
diff --git a/ftnoir_protocol_base/ftnoir_protocol_base_global.h b/ftnoir_protocol_base/ftnoir_protocol_base_global.h
new file mode 100644
index 00000000..06386c7f
--- /dev/null
+++ b/ftnoir_protocol_base/ftnoir_protocol_base_global.h
@@ -0,0 +1,16 @@
+#ifndef FTNOIR_PROTOCOL_BASE_GLOBAL_H
+#define FTNOIR_PROTOCOL_BASE_GLOBAL_H
+
+#include <QtGlobal>
+
+#ifndef OPENTRACK_MAIN
+# if !defined(_MSC_VER)
+# define FTNOIR_PROTOCOL_BASE_EXPORT __attribute__ ((visibility ("default")))
+# else
+# define FTNOIR_PROTOCOL_BASE_EXPORT Q_DECL_EXPORT
+#endif
+#else
+# define FTNOIR_PROTOCOL_BASE_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // FTNOIR_PROTOCOL_BASE_GLOBAL_H
diff --git a/ftnoir_protocol_fg/fg-protocol.qrc b/ftnoir_protocol_fg/fg-protocol.qrc
new file mode 100644
index 00000000..1c685437
--- /dev/null
+++ b/ftnoir_protocol_fg/fg-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/flightgear.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_fg/fgtypes.h b/ftnoir_protocol_fg/fgtypes.h
new file mode 100644
index 00000000..0f29be3d
--- /dev/null
+++ b/ftnoir_protocol_fg/fgtypes.h
@@ -0,0 +1,27 @@
+/********************************************************************************
+* 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 *
+* *
+* Type definitions for the FlightGear server. *
+********************************************************************************/
+#pragma once
+#ifndef INCLUDED_FGTYPES_H
+#define INCLUDED_FGTYPES_H
+
+//
+// x,y,z position in metres, heading, pitch and roll in degrees...
+//
+#pragma pack(push, 2)
+struct TFlightGearData {
+ double x, y, z, h, p, r;
+ int status;
+};
+#pragma pack(pop)
+
+#endif//INCLUDED_FGTYPES_H
diff --git a/ftnoir_protocol_fg/ftnoir_fgcontrols.ui b/ftnoir_protocol_fg/ftnoir_fgcontrols.ui
new file mode 100644
index 00000000..a4092c05
--- /dev/null
+++ b/ftnoir_protocol_fg/ftnoir_fgcontrols.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFGControls</class>
+ <widget class="QWidget" name="UICFGControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>415</width>
+ <height>112</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>FlightGear protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../ftnoir_filter_ewma2/ewma-filter.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.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="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>IP-address remote PC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="spinIPFirstNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSpinBox" name="spinIPSecondNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QSpinBox" name="spinIPThirdNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QSpinBox" name="spinIPFourthNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Port-number</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinPortNumber">
+ <property name="minimum">
+ <number>1000</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" colspan="3">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>spinIPFirstNibble</tabstop>
+ <tabstop>spinIPSecondNibble</tabstop>
+ <tabstop>spinIPThirdNibble</tabstop>
+ <tabstop>spinIPFourthNibble</tabstop>
+ <tabstop>spinPortNumber</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../ftnoir_filter_ewma2/ewma-filter.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_fg/ftnoir_protocol_fg.cpp b/ftnoir_protocol_fg/ftnoir_protocol_fg.cpp
new file mode 100644
index 00000000..0ef6b50f
--- /dev/null
+++ b/ftnoir_protocol_fg/ftnoir_protocol_fg.cpp
@@ -0,0 +1,64 @@
+/********************************************************************************
+* 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/>. *
+* *
+* FGServer FGServer is the Class, that communicates headpose-data *
+* to FlightGear, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#include "ftnoir_protocol_fg.h"
+#include "facetracknoir/global-settings.h"
+#include <ftnoir_tracker_base/ftnoir_tracker_types.h>
+
+// For Todd and Arda Kutlu
+
+void FTNoIR_Protocol::reloadSettings()
+{
+ s.b->reload();
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double* headpose) {
+ FlightData.x = headpose[TX] * 1e-2;
+ FlightData.y = headpose[TY] * 1e-2;
+ FlightData.z = headpose[TZ] * 1e-2;
+ FlightData.p = headpose[Pitch];
+ FlightData.h = headpose[Yaw];
+ FlightData.r = headpose[Roll];
+ FlightData.status = 1;
+ QHostAddress destIP(QString("%1.%2.%3.%4").arg(
+ QString::number(static_cast<int>(s.ip1)),
+ QString::number(static_cast<int>(s.ip2)),
+ QString::number(static_cast<int>(s.ip3)),
+ QString::number(static_cast<int>(s.ip4))));
+ int destPort = s.port;
+ (void) outSocket.writeDatagram(reinterpret_cast<const char*>(&FlightData), sizeof(FlightData), destIP, static_cast<quint16>(destPort));
+}
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ return outSocket.bind(QHostAddress::Any, 0, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_fg/ftnoir_protocol_fg.h b/ftnoir_protocol_fg/ftnoir_protocol_fg.h
new file mode 100644
index 00000000..dca1f245
--- /dev/null
+++ b/ftnoir_protocol_fg/ftnoir_protocol_fg.h
@@ -0,0 +1,96 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2013 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/>. *
+* *
+* FGServer FGServer is the Class, that communicates headpose-data *
+* to FlightGear, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#pragma once
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_fgcontrols.h"
+#include "fgtypes.h"
+#include <QThread>
+#include <QUdpSocket>
+#include <QMessageBox>
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> ip1, ip2, ip3, ip4;
+ value<int> port;
+ settings() :
+ b(bundle("flightgear-proto")),
+ ip1(b, "ip1", 192),
+ ip2(b, "ip2", 168),
+ ip3(b, "ip3", 0),
+ ip4(b, "ip4", 2),
+ port(b, "port", 5542)
+ {}
+};
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame(const double *headpose);
+ QString getGameName() {
+ return "FlightGear";
+ }
+ void reloadSettings();
+private:
+ settings s;
+ TFlightGearData FlightData;
+ QUdpSocket outSocket;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class FGControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ FGControls();
+ void registerProtocol(IProtocol *protocol) {
+ theProtocol = (FTNoIR_Protocol *) protocol; // Accept the pointer to the Protocol
+ }
+ void unRegisterProtocol() {
+ theProtocol = NULL; // Reset the pointer
+ }
+private:
+ Ui::UICFGControls ui;
+ FTNoIR_Protocol *theProtocol;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("FlightGear"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("FlightGear"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("FlightGear UDP protocol"); }
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/flightgear.png"); }
+};
diff --git a/ftnoir_protocol_fg/ftnoir_protocol_fg_dialog.cpp b/ftnoir_protocol_fg/ftnoir_protocol_fg_dialog.cpp
new file mode 100644
index 00000000..1c3e5ef8
--- /dev/null
+++ b/ftnoir_protocol_fg/ftnoir_protocol_fg_dialog.cpp
@@ -0,0 +1,69 @@
+/********************************************************************************
+* 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/>. *
+* *
+* FGServer FGServer is the Class, that communicates headpose-data *
+* to FlightGear, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#include "ftnoir_protocol_fg.h"
+#include <QObject>
+#include <QFile>
+#include "facetracknoir/global-settings.h"
+
+//*******************************************************************************************************
+// FaceTrackNoIR Client Settings-dialog.
+//*******************************************************************************************************
+
+//
+// Constructor for server-settings-dialog
+//
+FGControls::FGControls() : theProtocol(nullptr)
+{
+ ui.setupUi( this );
+
+ tie_setting(s.ip1, ui.spinIPFirstNibble);
+ tie_setting(s.ip2, ui.spinIPSecondNibble);
+ tie_setting(s.ip3, ui.spinIPThirdNibble);
+ tie_setting(s.ip4, ui.spinIPFourthNibble);
+ tie_setting(s.port, ui.spinPortNumber);
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+}
+
+void FGControls::doOK() {
+ s.b->save();
+ this->close();
+ if (theProtocol)
+ theProtocol->reloadSettings();
+}
+
+void FGControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new FGControls;
+}
diff --git a/ftnoir_protocol_fg/ftnoir_protocol_fg_dll.cpp b/ftnoir_protocol_fg/ftnoir_protocol_fg_dll.cpp
new file mode 100644
index 00000000..3125f136
--- /dev/null
+++ b/ftnoir_protocol_fg/ftnoir_protocol_fg_dll.cpp
@@ -0,0 +1,32 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_fg.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_fg/images/flightgear.png b/ftnoir_protocol_fg/images/flightgear.png
new file mode 100644
index 00000000..2a546642
--- /dev/null
+++ b/ftnoir_protocol_fg/images/flightgear.png
Binary files differ
diff --git a/ftnoir_protocol_fsuipc/fsuipc-protocol.qrc b/ftnoir_protocol_fsuipc/fsuipc-protocol.qrc
new file mode 100644
index 00000000..34756f84
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/fsuipc-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/fs9.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_fsuipc/ftnoir_fsuipccontrols.ui b/ftnoir_protocol_fsuipc/ftnoir_fsuipccontrols.ui
new file mode 100644
index 00000000..6cb066bd
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/ftnoir_fsuipccontrols.ui
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFSUIPCControls</class>
+ <widget class="QWidget" name="UICFSUIPCControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>512</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>FSUIPC settings FaceTrackNoIR</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="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="txtLocationOfDLL">
+ <property name="minimumSize">
+ <size>
+ <width>230</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Location of FSUIPC.dll</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="text">
+ <string>Location of FSUIPC.dll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>The DLL should be located in the Modules/ directory of MS FS 2004</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="btnFindDLL">
+ <property name="maximumSize">
+ <size>
+ <width>35</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp
new file mode 100644
index 00000000..632d502a
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp
@@ -0,0 +1,168 @@
+/********************************************************************************
+* 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-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/>. *
+* *
+* FSUIPCServer FSUIPCServer is the Class, that communicates headpose-data *
+* to games, using the FSUIPC.dll. *
+********************************************************************************/
+#include "ftnoir_protocol_fsuipc.h"
+#include "facetracknoir/global-settings.h"
+
+/** constructor **/
+FTNoIR_Protocol::FTNoIR_Protocol()
+{
+ prevPosX = 0.0f;
+ prevPosY = 0.0f;
+ prevPosZ = 0.0f;
+ prevRotX = 0.0f;
+ prevRotY = 0.0f;
+ prevRotZ = 0.0f;
+}
+
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ FSUIPCLib.unload();
+}
+
+//
+// Scale the measured value to the Joystick values
+//
+int FTNoIR_Protocol::scale2AnalogLimits( float x, float min_x, float max_x ) {
+double y;
+double local_x;
+
+ local_x = x;
+ if (local_x > max_x) {
+ local_x = max_x;
+ }
+ if (local_x < min_x) {
+ local_x = min_x;
+ }
+ y = ( 16383 * local_x ) / max_x;
+
+ return (int) y;
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double *headpose ) {
+ DWORD result;
+ TFSState pitch;
+ TFSState yaw;
+ TFSState roll;
+ WORD FSZoom;
+
+ float virtPosX;
+ float virtPosY;
+ float virtPosZ;
+
+ float virtRotX;
+ float virtRotY;
+ float virtRotZ;
+
+// qDebug() << "FSUIPCServer::run() says: started!";
+
+ virtRotX = -headpose[Pitch]; // degrees
+ virtRotY = headpose[Yaw];
+ virtRotZ = headpose[Roll];
+
+ virtPosX = 0.0f; // cm, X and Y are not working for FS2002/2004!
+ virtPosY = 0.0f;
+ virtPosZ = headpose[TZ];
+
+ //
+ // Init. the FSUIPC offsets (derived from Free-track...)
+ //
+ pitch.Control = 66503;
+ yaw.Control = 66504;
+ roll.Control = 66505;
+
+ //
+ // Only do this when the data has changed. This way, the HAT-switch can be used when tracking is OFF.
+ //
+ if ((prevPosX != virtPosX) || (prevPosY != virtPosY) || (prevPosZ != virtPosZ) ||
+ (prevRotX != virtRotX) || (prevRotY != virtRotY) || (prevRotZ != virtRotZ)) {
+ //
+ // Open the connection
+ //
+ FSUIPC_Open(SIM_ANY, &result);
+
+ //
+ // Check the FS-version
+ //
+ if (((result == FSUIPC_ERR_OK) || (result == FSUIPC_ERR_OPEN)) &&
+ ((FSUIPC_FS_Version == SIM_FS2K2) || (FSUIPC_FS_Version == SIM_FS2K4))) {
+// qDebug() << "FSUIPCServer::run() says: FSUIPC opened succesfully";
+ //
+ // Write the 4! DOF-data to FS. Only rotations and zoom are possible.
+ //
+ pitch.Value = scale2AnalogLimits(virtRotX, -180, 180);
+ FSUIPC_Write(0x3110, 8, &pitch, &result);
+
+ yaw.Value = scale2AnalogLimits(virtRotY, -180, 180);
+ FSUIPC_Write(0x3110, 8, &yaw, &result);
+
+ roll.Value = scale2AnalogLimits(virtRotZ, -180, 180);
+ FSUIPC_Write(0x3110, 8, &roll, &result);
+
+ FSZoom = (WORD) (64/50) * virtPosZ + 64;
+ FSUIPC_Write(0x832E, 2, &FSZoom, &result);
+
+ //
+ // Write the data, in one go!
+ //
+ FSUIPC_Process(&result);
+ if (result == FSUIPC_ERR_SENDMSG) {
+ FSUIPC_Close(); //timeout (1 second) so assume FS closed
+ }
+ }
+ }
+
+ prevPosX = virtPosX;
+ prevPosY = virtPosY;
+ prevPosZ = virtPosZ;
+ prevRotX = virtRotX;
+ prevRotY = virtRotY;
+ prevRotZ = virtRotZ;
+}
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ qDebug() << "checkServerInstallationOK says: Starting Function";
+
+ //
+ // Load the DLL.
+ //
+ FSUIPCLib.setFileName( s.LocationOfDLL );
+ if (FSUIPCLib.load() != true) {
+ qDebug() << "checkServerInstallationOK says: Error loading FSUIPC DLL";
+ return false;
+ }
+ else {
+ qDebug() << "checkServerInstallationOK says: FSUIPC DLL loaded.";
+ }
+
+ return true;
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT FTNoIR_Protocol* CALLING_CONVENTION GetConstructor(void)
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.h b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.h
new file mode 100644
index 00000000..ff8d3b7f
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.h
@@ -0,0 +1,110 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010-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/>. *
+* *
+* FSUIPCServer FSUIPCServer is the Class, that communicates headpose-data *
+* to games, using the FSUIPC.dll. *
+********************************************************************************/
+#pragma once
+#ifndef INCLUDED_FSUIPCSERVER_H
+#define INCLUDED_FSUIPCSERVER_H
+
+#include <windows.h>
+#include <stdlib.h>
+#include "FSUIPC_User.h"
+#include "facetracknoir/global-settings.h"
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_fsuipccontrols.h"
+#include <QMessageBox>
+#include <QSettings>
+#include <QLibrary>
+#include <QProcess>
+#include <QDebug>
+#include <QFile>
+#include <QFileDialog>
+#include "facetracknoir/options.h"
+using namespace options;
+
+#define FSUIPC_FILENAME "C:\\Program Files\\Microsoft Games\\Flight Simulator 9\\Modules\\FSUIPC.dll"
+
+struct settings {
+ pbundle b;
+ value<QString> LocationOfDLL;
+ settings() :
+ b(bundle("proto-fsuipc")),
+ LocationOfDLL(b, "dll-location", FSUIPC_FILENAME)
+ {}
+};
+
+#pragma pack(push,1) // All fields in structure must be byte aligned.
+typedef struct
+{
+ int Control; // Control identifier
+ int Value; // Value of DOF
+} TFSState;
+#pragma pack(pop)
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol() virt_override;
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame(const double* headpose);
+ QString getGameName() {
+ return "Microsoft Flight Simulator X";
+ }
+private:
+ QLibrary FSUIPCLib;
+ double prevPosX, prevPosY, prevPosZ, prevRotX, prevRotY, prevRotZ;
+ static int scale2AnalogLimits( float x, float min_x, float max_x );
+ settings s;
+};
+
+class FSUIPCControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ FSUIPCControls();
+ void registerProtocol(IProtocol *) {}
+ void unRegisterProtocol() {}
+private:
+ Ui::UICFSUIPCControls ui;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+ void getLocationOfDLL();
+};
+
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("FS2002/FS2004"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("FSUIPC"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Microsoft FS2004 protocol"); }
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/fs9.png"); }
+};
+
+
+#endif//INCLUDED_FSUIPCSERVER_H
+//END
diff --git a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dialog.cpp b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dialog.cpp
new file mode 100644
index 00000000..d97cff99
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dialog.cpp
@@ -0,0 +1,62 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_fsuipc.h"
+#include "facetracknoir/global-settings.h"
+
+FSUIPCControls::FSUIPCControls() :
+ QWidget()
+{
+ ui.setupUi( this );
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+ connect(ui.btnFindDLL, SIGNAL(clicked()), this, SLOT(getLocationOfDLL()));
+
+ tie_setting(s.LocationOfDLL, ui.txtLocationOfDLL);
+}
+
+void FSUIPCControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void FSUIPCControls::doCancel() {
+ s.b->revert();
+ close();
+}
+
+void FSUIPCControls::getLocationOfDLL()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Locate file"),
+ ui.txtLocationOfDLL->text(),
+ tr("FSUIPC DLL file (FSUIPC*.dll);;All Files (*)"));
+ if (!fileName.isEmpty()) {
+ s.LocationOfDLL = fileName;
+ }
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog(void)
+{
+ return new FSUIPCControls;
+}
diff --git a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dll.cpp b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dll.cpp
new file mode 100644
index 00000000..57b174c5
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc_dll.cpp
@@ -0,0 +1,31 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_fsuipc.h"
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata(void)
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_fsuipc/images/fs9.png b/ftnoir_protocol_fsuipc/images/fs9.png
new file mode 100644
index 00000000..914c9dcb
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/images/fs9.png
Binary files differ
diff --git a/ftnoir_protocol_fsuipc/images/fs91.png b/ftnoir_protocol_fsuipc/images/fs91.png
new file mode 100644
index 00000000..f9540d1d
--- /dev/null
+++ b/ftnoir_protocol_fsuipc/images/fs91.png
Binary files differ
diff --git a/ftnoir_protocol_ft/ft-protocol.qrc b/ftnoir_protocol_ft/ft-protocol.qrc
new file mode 100644
index 00000000..c04959f0
--- /dev/null
+++ b/ftnoir_protocol_ft/ft-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/freetrack.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_ft/ftnoir_ftcontrols.ui b/ftnoir_protocol_ft/ftnoir_ftcontrols.ui
new file mode 100644
index 00000000..941aaff0
--- /dev/null
+++ b/ftnoir_protocol_ft/ftnoir_ftcontrols.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFTControls</class>
+ <widget class="QWidget" name="UICFTControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>489</width>
+ <height>402</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="windowTitle">
+ <string>freetrack protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="ft-protocol.qrc">
+ <normaloff>:/images/freetrack.png</normaloff>:/images/freetrack.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>TIRViews</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="chkTIRViews">
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="text">
+ <string>Memory hacks</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Only for very old and buggy old games such as CFS3.</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>TrackIR.exe</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_3">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="chkStartDummy">
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="text">
+ <string>Using EZCA</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>FSX-specific EZCA protocol hacks</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Select interface</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QComboBox" name="cbxSelectInterface"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Disable one of the protocols if game is confused by presence of both at the same time.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Repair NPClient location</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignTop</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QPushButton" name="bntLocateNPClient">
+ <property name="text">
+ <string>Locate DLL</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Replace the registry entry if you want to use other software with the NPClient protocol and it doesn't work automatically.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="ft-protocol.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp b/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp
new file mode 100644
index 00000000..281af6a0
--- /dev/null
+++ b/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp
@@ -0,0 +1,211 @@
+/********************************************************************************
+* 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) 2013 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/>. *
+* *
+* FTServer FTServer is the Class, that communicates headpose-data *
+* to games, using the FreeTrackClient.dll. *
+********************************************************************************/
+#include "ftnoir_protocol_ft.h"
+#include "ftnoir_csv/csv.h"
+
+FTNoIR_Protocol::FTNoIR_Protocol() :
+ shm(FT_MM_DATA, FREETRACK_MUTEX, sizeof(FTMemMap))
+{
+ pMemData = (FTMemMap*) shm.mem;
+ ProgramName = "";
+ intGameID = 0;
+ viewsStart = 0;
+ viewsStop = 0;
+}
+
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ if (viewsStop != NULL) {
+ viewsStop();
+ FTIRViewsLib.unload();
+ }
+ dummyTrackIR.terminate();
+ dummyTrackIR.kill();
+ dummyTrackIR.waitForFinished(50);
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double* headpose) {
+ float virtPosX;
+ float virtPosY;
+ float virtPosZ;
+
+ float virtRotX;
+ float virtRotY;
+ float virtRotZ;
+
+ float headPosX;
+ float headPosY;
+ float headPosZ;
+
+ float headRotX;
+ float headRotY;
+ float headRotZ;
+ headRotX = virtRotX = getRadsFromDegrees(headpose[Pitch]) * (s.useDummyExe ? 2.0 : 1.0);
+ headRotY = virtRotY = getRadsFromDegrees(headpose[Yaw]);
+ headRotZ = virtRotZ = getRadsFromDegrees(headpose[Roll]);
+ headPosX = virtPosX = headpose[TX] * 10;
+ headPosY = virtPosY = headpose[TY] * 10;
+ headPosZ = virtPosZ = headpose[TZ] * 10;
+
+ shm.lock();
+
+ pMemData->data.RawX = headPosX;
+ pMemData->data.RawY = headPosY;
+ pMemData->data.RawZ = headPosZ;
+ pMemData->data.RawPitch = headRotX;
+ pMemData->data.RawYaw = headRotY;
+ pMemData->data.RawRoll = headRotZ;
+
+ //
+ //
+ pMemData->data.X = virtPosX;
+ pMemData->data.Y = virtPosY;
+ pMemData->data.Z = virtPosZ;
+ pMemData->data.Pitch = virtRotX;
+ pMemData->data.Yaw = virtRotY;
+ pMemData->data.Roll = virtRotZ;
+
+ //
+ // Leave some values 0 yet...
+ //
+ pMemData->data.X1 = pMemData->data.DataID + 10;
+ pMemData->data.X2 = 0;
+ pMemData->data.X3 = 0;
+ pMemData->data.X4 = 0;
+ pMemData->data.Y1 = 0;
+ pMemData->data.Y2 = 0;
+ pMemData->data.Y3 = 0;
+ pMemData->data.Y4 = 0;
+
+ if (intGameID != pMemData->GameID)
+ {
+ QString gamename;
+ CSV::getGameData(pMemData->GameID, pMemData->table, gamename);
+ pMemData->GameID2 = pMemData->GameID;
+ intGameID = pMemData->GameID;
+ QMutexLocker foo(&this->game_name_mutex);
+ connected_game = gamename;
+ }
+
+ pMemData->data.DataID += 1;
+ shm.unlock();
+}
+
+void FTNoIR_Protocol::start_tirviews() {
+ QString aFileName = QCoreApplication::applicationDirPath() + "/TIRViews.dll";
+ if ( QFile::exists( aFileName )) {
+ FTIRViewsLib.setFileName(aFileName);
+ FTIRViewsLib.load();
+
+ viewsStart = (importTIRViewsStart) FTIRViewsLib.resolve("TIRViewsStart");
+ if (viewsStart == NULL) {
+ qDebug() << "FTServer::run() says: TIRViewsStart function not found in DLL!";
+ }
+ else {
+ qDebug() << "FTServer::run() says: TIRViewsStart executed!";
+ viewsStart();
+ }
+
+ //
+ // Load the Stop function from TIRViews.dll. Call it when terminating the thread.
+ //
+ viewsStop = (importTIRViewsStop) FTIRViewsLib.resolve("TIRViewsStop");
+ if (viewsStop == NULL) {
+ qDebug() << "FTServer::run() says: TIRViewsStop function not found in DLL!";
+ }
+ }
+}
+
+void FTNoIR_Protocol::start_dummy() {
+
+
+ QString program = QCoreApplication::applicationDirPath() + "/TrackIR.exe";
+ dummyTrackIR.setProgram("\"" + program + "\"");
+ dummyTrackIR.start();
+
+ qDebug() << "FTServer::run() says: TrackIR.exe executed!" << program;
+}
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ QSettings settings("Freetrack", "FreetrackClient"); // Registry settings (in HK_USER)
+ QSettings settingsTIR("NaturalPoint", "NATURALPOINT\\NPClient Location"); // Registry settings (in HK_USER)
+ QString aLocation; // Location of Client DLL
+
+
+ if (!shm.success())
+ return false;
+
+ qDebug() << "checkServerInstallationOK says: Starting Function";
+
+ //
+ // Write the path in the registry (for FreeTrack and FreeTrack20), for the game(s).
+ //
+ aLocation = QCoreApplication::applicationDirPath() + "/";
+
+ qDebug() << "checkServerInstallationOK says: used interface = " << s.intUsedInterface;
+ switch (s.intUsedInterface) {
+ case 0: // Use both interfaces
+ settings.setValue( "Path" , aLocation );
+ settingsTIR.setValue( "Path" , aLocation );
+ break;
+ case 1: // Use FreeTrack, disable TrackIR
+ settings.setValue( "Path" , aLocation );
+ settingsTIR.setValue( "Path" , "" );
+ break;
+ case 2: // Use TrackIR, disable FreeTrack
+ settings.setValue( "Path" , "" );
+ settingsTIR.setValue( "Path" , aLocation );
+ break;
+ default:
+ // should never be reached
+ break;
+ }
+
+ //
+ // TIRViews must be started first, or the NPClient DLL will never be loaded.
+ //
+ if (s.useTIRViews) {
+ start_tirviews();
+ }
+
+ // more games need the dummy executable than previously thought
+ start_dummy();
+
+ pMemData->data.DataID = 1;
+ pMemData->data.CamWidth = 100;
+ pMemData->data.CamHeight = 250;
+ pMemData->GameID2 = 0;
+ memset(pMemData->table, 0, 8);
+
+ return true;
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_ft/ftnoir_protocol_ft.h b/ftnoir_protocol_ft/ftnoir_protocol_ft.h
new file mode 100644
index 00000000..0af9ff07
--- /dev/null
+++ b/ftnoir_protocol_ft/ftnoir_protocol_ft.h
@@ -0,0 +1,120 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of 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/>. *
+* *
+* FTServer FTServer is the Class, that communicates headpose-data *
+* to games, using the FreeTrackClient.dll. *
+********************************************************************************/
+#pragma once
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_ftcontrols.h"
+#include "facetracknoir/global-settings.h"
+#include "fttypes.h"
+#include <QMessageBox>
+#include <QSettings>
+#include <QLibrary>
+#include <QProcess>
+#include <QDebug>
+#include <QFile>
+#include <QString>
+#include <windows.h>
+#include <QMutex>
+#include <QMutexLocker>
+#include "compat/compat.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> intUsedInterface;
+ value<bool> useTIRViews, useDummyExe;
+ settings() :
+ b(bundle("proto-freetrack")),
+ intUsedInterface(b, "used-interfaces", 0),
+ useTIRViews(b, "use-memory-hacks", false),
+ useDummyExe(b, "ezca-mode", false)
+ {}
+};
+
+//typedef char *(WINAPI *importProvider)(void);
+typedef void (WINAPI *importTIRViewsStart)(void);
+typedef void (WINAPI *importTIRViewsStop)(void);
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol();
+ bool checkServerInstallationOK( );
+ void sendHeadposeToGame( const double *headpose );
+ QString getGameName() {
+ QMutexLocker foo(&game_name_mutex);
+ return connected_game;
+ }
+private:
+ importTIRViewsStart viewsStart; // Functions inside TIRViews.dll
+ importTIRViewsStop viewsStop;
+
+ FTMemMap *pMemData;
+ QString game_name;
+ PortableLockedShm shm;
+
+ // Private properties
+ QString ProgramName;
+ QLibrary FTIRViewsLib;
+ QProcess dummyTrackIR;
+ static inline double getRadsFromDegrees ( double degrees )
+ {
+ return degrees * 0.017453;
+ }
+ int intGameID;
+ void start_tirviews();
+ void start_dummy();
+ QString connected_game;
+ QMutex game_name_mutex;
+ settings s;
+};
+
+class FTControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ explicit FTControls();
+ void registerProtocol(IProtocol *) {}
+ void unRegisterProtocol() {}
+private:
+ Ui::UICFTControls ui;
+ settings s;
+private slots:
+ void selectDLL();
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("FreeTrack 2.0"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("FreeTrack 2.0"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Enhanced FreeTrack protocol"); }
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/freetrack.png"); }
+};
diff --git a/ftnoir_protocol_ft/ftnoir_protocol_ft_dialog.cpp b/ftnoir_protocol_ft/ftnoir_protocol_ft_dialog.cpp
new file mode 100644
index 00000000..5ce903b7
--- /dev/null
+++ b/ftnoir_protocol_ft/ftnoir_protocol_ft_dialog.cpp
@@ -0,0 +1,96 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_ft.h"
+#include <QDebug>
+#include <QFileDialog>
+
+//*******************************************************************************************************
+// FaceTrackNoIR Client Settings-dialog.
+//*******************************************************************************************************
+
+//
+// Constructor for server-settings-dialog
+//
+FTControls::FTControls() :
+ QWidget()
+{
+ QString aFileName; // File Path and Name
+
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ connect(ui.bntLocateNPClient, SIGNAL(clicked()), this, SLOT(selectDLL()));
+
+ tie_setting(s.useDummyExe, ui.chkStartDummy);
+ tie_setting(s.useTIRViews, ui.chkTIRViews);
+
+ ui.cbxSelectInterface->addItem("Enable both");
+ ui.cbxSelectInterface->addItem("Use FreeTrack, hide TrackIR");
+ ui.cbxSelectInterface->addItem("Use TrackIR, hide FreeTrack");
+
+ tie_setting(s.intUsedInterface, ui.cbxSelectInterface);
+
+ aFileName = QCoreApplication::applicationDirPath() + "/TIRViews.dll";
+ if ( !QFile::exists( aFileName ) ) {
+ ui.chkTIRViews->setChecked( false );
+ ui.chkTIRViews->setEnabled ( false );
+ }
+ else {
+ ui.chkTIRViews->setEnabled ( true );
+ }
+}
+
+void FTControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void FTControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+void FTControls::selectDLL() {
+ QString fileName = QFileDialog::getOpenFileName( this, tr("Select the desired NPClient DLL"), QCoreApplication::applicationDirPath() + "/NPClient.dll", tr("Dll file (*.dll);;All Files (*)"));
+
+ //
+ // Write the location of the file in the required Registry-key.
+ //
+ if (! fileName.isEmpty() ) {
+ if (fileName.endsWith("NPClient.dll", Qt::CaseInsensitive) ) {
+ QSettings settingsTIR("NaturalPoint", "NATURALPOINT\\NPClient Location"); // Registry settings (in HK_USER)
+ QString aLocation = fileName.left(fileName.length() - 12); // Location of Client DLL
+
+ settingsTIR.setValue( "Path" , aLocation );
+ }
+ }
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new FTControls;
+}
diff --git a/ftnoir_protocol_ft/ftnoir_protocol_ft_dll.cpp b/ftnoir_protocol_ft/ftnoir_protocol_ft_dll.cpp
new file mode 100644
index 00000000..38f11211
--- /dev/null
+++ b/ftnoir_protocol_ft/ftnoir_protocol_ft_dll.cpp
@@ -0,0 +1,30 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_ft.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_ft/fttypes.h b/ftnoir_protocol_ft/fttypes.h
new file mode 100644
index 00000000..ced844dc
--- /dev/null
+++ b/ftnoir_protocol_ft/fttypes.h
@@ -0,0 +1,86 @@
+/************************************************************************************
+ * * FTTypes FTTypes contains the specific type definitions for the *
+ * * FreeTrack protocol. *
+ * * It was loosely translated from FTTypes.pas *
+ * * which was created by the FreeTrack-team. *
+ * * *
+ * * Wim Vriend (Developing) *
+ * * Ron Hendriks (Testing and Research) *
+ * * *
+ * * Homepage <http://facetracknoir.sourceforge.net/home/default.htm> *
+ * * *
+ * * 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. *
+ * * *
+ * * The FTTypes sources were translated from the original Delphi sources *
+ * * created by the FreeTrack developers. *
+ */
+#ifndef INCLUDED_FTTYPES_H
+#define INCLUDED_FTTYPES_H
+
+#if !defined(_WIN32)
+# include <inttypes.h>
+typedef int32_t my_32bit_int;
+# define WINAPI
+#else
+# include <windows.h>
+typedef __int32 my_32bit_int;
+#endif
+
+//#include "Registry.h"
+
+// static const char* FT_CLIENT_LOCATION = "Software\\Freetrack\\FreetrackClient";
+//#define FT_CLIENT_FILENAME "FreeTrackClient.Dll"
+#define FT_MM_DATA "FT_SharedMem"
+//#define FREETRACK "Freetrack"
+#define FREETRACK_MUTEX "FT_Mutext"
+
+struct TFreeTrackData {
+ int DataID;
+ int CamWidth;
+ int CamHeight;
+ // virtual pose
+ float Yaw; // positive yaw to the left
+ float Pitch; // positive pitch up
+ float Roll; // positive roll to the left
+ float X;
+ float Y;
+ float Z;
+ // raw pose with no smoothing, sensitivity, response curve etc.
+ float RawYaw;
+ float RawPitch;
+ float RawRoll;
+ float RawX;
+ float RawY;
+ float RawZ;
+ // raw points, sorted by Y, origin top left corner
+ float X1;
+ float Y1;
+ float X2;
+ float Y2;
+ float X3;
+ float Y3;
+ float X4;
+ float Y4;
+};
+typedef TFreeTrackData * PFreetrackData;
+
+struct FTMemMap {
+ TFreeTrackData data;
+ my_32bit_int GameID;
+ unsigned char table[8];
+ my_32bit_int GameID2;
+};
+typedef FTMemMap * PFTMemMap;
+
+//extern bool (*FTGetData) (PFreetrackData data);
+// DLL function signatures
+// These match those given in FTTypes.pas
+// WINAPI is macro for __stdcall defined somewhere in the depths of windows.h
+typedef bool (WINAPI *importGetData)(TFreeTrackData * data);
+typedef char *(WINAPI *importGetDllVersion)(void);
+typedef void (WINAPI *importReportID)(int name);
+typedef char *(WINAPI *importProvider)(void);
+
+#endif//INCLUDED_FTTYPES_H
diff --git a/ftnoir_protocol_ft/images/freetrack.png b/ftnoir_protocol_ft/images/freetrack.png
new file mode 100644
index 00000000..c184dc88
--- /dev/null
+++ b/ftnoir_protocol_ft/images/freetrack.png
Binary files differ
diff --git a/ftnoir_protocol_ftn/ftnoir_ftncontrols.ui b/ftnoir_protocol_ftn/ftnoir_ftncontrols.ui
new file mode 100644
index 00000000..48679f3c
--- /dev/null
+++ b/ftnoir_protocol_ftn/ftnoir_ftncontrols.ui
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFTNControls</class>
+ <widget class="QWidget" name="UICFTNControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>411</width>
+ <height>169</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>UDP protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../facetracknoir/main-facetracknoir.qrc">
+ <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="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="4">
+ <widget class="QSpinBox" name="spinIPFourthNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinIPFirstNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="spinIPSecondNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QSpinBox" name="spinIPThirdNibble">
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>IP-address remote PC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Port-number</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="spinPortNumber">
+ <property name="minimum">
+ <number>1000</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Enter IP-address and port-number for the remote PC.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Remember: you may have to change firewall-settings too!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>spinIPFirstNibble</tabstop>
+ <tabstop>spinIPSecondNibble</tabstop>
+ <tabstop>spinIPThirdNibble</tabstop>
+ <tabstop>spinIPFourthNibble</tabstop>
+ <tabstop>spinPortNumber</tabstop>
+ <tabstop>btnOK</tabstop>
+ <tabstop>btnCancel</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../facetracknoir/main-facetracknoir.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_ftn/ftnoir_protocol_ftn.cpp b/ftnoir_protocol_ftn/ftnoir_protocol_ftn.cpp
new file mode 100644
index 00000000..e93a751e
--- /dev/null
+++ b/ftnoir_protocol_ftn/ftnoir_protocol_ftn.cpp
@@ -0,0 +1,55 @@
+/********************************************************************************
+* 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-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/>. *
+* *
+* FTNServer FTNServer is the Class, that communicates headpose-data *
+* to another FaceTrackNoIR program, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#include "ftnoir_protocol_ftn.h"
+#include <QFile>
+#include "facetracknoir/global-settings.h"
+
+/** constructor **/
+FTNoIR_Protocol::FTNoIR_Protocol()
+{
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double *headpose) {
+ int destPort = s.port;
+ QHostAddress destIP(QString("%1.%2.%3.%4").arg(
+ QString::number(static_cast<int>(s.ip1)),
+ QString::number(static_cast<int>(s.ip2)),
+ QString::number(static_cast<int>(s.ip3)),
+ QString::number(static_cast<int>(s.ip4))));
+ outSocket.writeDatagram((const char *) headpose, sizeof( double[6] ), destIP, destPort);
+}
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ return outSocket.bind(QHostAddress::Any, 0, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_ftn/ftnoir_protocol_ftn.h b/ftnoir_protocol_ftn/ftnoir_protocol_ftn.h
new file mode 100644
index 00000000..99e6c6a1
--- /dev/null
+++ b/ftnoir_protocol_ftn/ftnoir_protocol_ftn.h
@@ -0,0 +1,92 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010-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/>. *
+* *
+* FTNServer FTNServer is the Class, that communicates headpose-data *
+* to FlightGear, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#pragma once
+
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_ftnoir_ftncontrols.h"
+#include <QThread>
+#include <QUdpSocket>
+#include <QMessageBox>
+#include <math.h>
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> ip1, ip2, ip3, ip4, port;
+ settings() :
+ b(bundle("udp-proto")),
+ ip1(b, "ip1", 192),
+ ip2(b, "ip2", 168),
+ ip3(b, "ip3", 0),
+ ip4(b, "ip4", 2),
+ port(b, "port", 4242)
+ {}
+};
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame(const double *headpose);
+ QString getGameName() {
+ return "UDP Tracker";
+ }
+private:
+ QUdpSocket outSocket;
+ settings s;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class FTNControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ FTNControls();
+ void registerProtocol(IProtocol *) {}
+ void unRegisterProtocol() {}
+private:
+ Ui::UICFTNControls ui;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("UDP"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("UDP"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("opentrack UDP protocol"); }
+
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/facetracknoir.png"); }
+};
diff --git a/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dialog.cpp b/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dialog.cpp
new file mode 100644
index 00000000..37db314f
--- /dev/null
+++ b/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dialog.cpp
@@ -0,0 +1,62 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_ftn.h"
+#include "facetracknoir/global-settings.h"
+
+FTNControls::FTNControls() :
+ QWidget()
+{
+ ui.setupUi( this );
+
+ tie_setting(s.ip1, ui.spinIPFirstNibble);
+ tie_setting(s.ip2, ui.spinIPSecondNibble);
+ tie_setting(s.ip3, ui.spinIPThirdNibble);
+ tie_setting(s.ip4, ui.spinIPFourthNibble);
+ tie_setting(s.port, ui.spinPortNumber);
+
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+}
+
+//
+// OK clicked on server-dialog
+//
+void FTNControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+//
+// Cancel clicked on server-dialog
+//
+void FTNControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new FTNControls;
+}
diff --git a/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dll.cpp b/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dll.cpp
new file mode 100644
index 00000000..99689432
--- /dev/null
+++ b/ftnoir_protocol_ftn/ftnoir_protocol_ftn_dll.cpp
@@ -0,0 +1,31 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_ftn.h"
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_libevdev/ftnoir_libevdev_controls.ui b/ftnoir_protocol_libevdev/ftnoir_libevdev_controls.ui
new file mode 100644
index 00000000..d2b86445
--- /dev/null
+++ b/ftnoir_protocol_libevdev/ftnoir_libevdev_controls.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICLibevdevControls</class>
+ <widget class="QWidget" name="UICLibevdevControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>228</width>
+ <height>69</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>VJoy</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>:/images/vjoy.png</normaloff>:/images/vjoy.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Make sure rw for /dev/input/uinput!</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>btnOK</tabstop>
+ <tabstop>btnCancel</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.cpp b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.cpp
new file mode 100644
index 00000000..70fde395
--- /dev/null
+++ b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.cpp
@@ -0,0 +1,101 @@
+#include "ftnoir_protocol_libevdev.h"
+#include "facetracknoir/global-settings.h"
+//#include "ftnoir_tracker_base/ftnoir_tracker_types.h"
+#include <cstdio>
+#include <algorithm>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define CHECK_LIBEVDEV(expr) if ((error = (expr)) != 0) goto error;
+
+static const int max_input = 65535;
+static const int mid_input = 32767;
+static const int min_input = 0;
+
+FTNoIR_Protocol::FTNoIR_Protocol() : dev(NULL), uidev(NULL)
+{
+ int error = 0;
+
+ dev = libevdev_new();
+
+ if (!dev)
+ goto error;
+
+ CHECK_LIBEVDEV(libevdev_enable_property(dev, INPUT_PROP_BUTTONPAD));
+
+ libevdev_set_name(dev, "opentrack headpose");
+
+ struct input_absinfo absinfo;
+
+ absinfo.minimum = min_input;
+ absinfo.maximum = max_input;
+ absinfo.resolution = 1;
+ absinfo.value = mid_input;
+ absinfo.flat = 1;
+ absinfo.fuzz = 0;
+
+ CHECK_LIBEVDEV(libevdev_enable_event_type(dev, EV_ABS));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_X, &absinfo));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_Y, &absinfo));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_Z, &absinfo));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_RX, &absinfo));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_RY, &absinfo));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_ABS, ABS_RZ, &absinfo));
+
+ /* do not remove next 3 lines or udev scripts won't assign 0664 permissions -sh */
+ CHECK_LIBEVDEV(libevdev_enable_event_type(dev, EV_KEY));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_KEY, BTN_JOYSTICK, NULL));
+ CHECK_LIBEVDEV(libevdev_enable_event_code(dev, EV_KEY, BTN_TRIGGER, NULL));
+
+ CHECK_LIBEVDEV(libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev));
+
+ return;
+error:
+ if (uidev)
+ libevdev_uinput_destroy(uidev);
+ if (dev)
+ libevdev_free(dev);
+ if (error)
+ fprintf(stderr, "libevdev error: %d\n", error);
+ uidev = NULL;
+ dev = NULL;
+}
+
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ if (uidev)
+ libevdev_uinput_destroy(uidev);
+ if (dev)
+ libevdev_free(dev);
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double* headpose) {
+ static const int axes[] = {
+ /* translation goes first */
+ ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ
+ };
+
+ static const int max_value[] = {
+ 100,
+ 100,
+ 100,
+ 180,
+ 90,
+ 180
+ };
+
+ for (int i = 0; i < 6; i++)
+ {
+ int value = headpose[i] * mid_input / max_value[i] + mid_input;
+ int normalized = std::max(std::min(max_input, value), min_input);
+ (void) libevdev_uinput_write_event(uidev, EV_ABS, axes[i], normalized);
+ }
+
+ (void) libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.h b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.h
new file mode 100644
index 00000000..66f53547
--- /dev/null
+++ b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 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.
+ */
+#pragma once
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_libevdev_controls.h"
+
+#include <QMessageBox>
+#include "facetracknoir/global-settings.h"
+
+extern "C" {
+# include <libevdev/libevdev.h>
+# include <libevdev/libevdev-uinput.h>
+}
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol();
+ bool checkServerInstallationOK() {
+ return dev != NULL;
+ }
+ void sendHeadposeToGame(const double *headpose);
+ QString getGameName() {
+ return "Virtual joystick for Linux";
+ }
+private:
+ struct libevdev* dev;
+ struct libevdev_uinput* uidev;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class LibevdevControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+
+ explicit LibevdevControls();
+ void registerProtocol(IProtocol *) {}
+ void unRegisterProtocol() {}
+
+private:
+ Ui::UICLibevdevControls ui;
+ void save();
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Protocol DLL. Functions used to get general info on the Protocol
+//*******************************************************************************************************
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ FTNoIR_ProtocolDll();
+ ~FTNoIR_ProtocolDll();
+
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("libevdev"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("libevdev"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("libevdev"); }
+
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/linux.png"); }
+};
diff --git a/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dialog.cpp b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dialog.cpp
new file mode 100644
index 00000000..bb54c354
--- /dev/null
+++ b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dialog.cpp
@@ -0,0 +1,26 @@
+#include "ftnoir_protocol_libevdev.h"
+#include "facetracknoir/global-settings.h"
+
+LibevdevControls::LibevdevControls() : QWidget()
+{
+ ui.setupUi( this );
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+}
+
+void LibevdevControls::doOK() {
+ save();
+ this->close();
+}
+
+void LibevdevControls::doCancel() {
+ this->close();
+}
+
+void LibevdevControls::save() {
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new LibevdevControls;
+}
diff --git a/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dll.cpp b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dll.cpp
new file mode 100644
index 00000000..79a22d5e
--- /dev/null
+++ b/ftnoir_protocol_libevdev/ftnoir_protocol_libevdev_dll.cpp
@@ -0,0 +1,16 @@
+#include "ftnoir_protocol_libevdev.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+FTNoIR_ProtocolDll::FTNoIR_ProtocolDll() {
+}
+
+FTNoIR_ProtocolDll::~FTNoIR_ProtocolDll()
+{
+
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_libevdev/images/linux.png b/ftnoir_protocol_libevdev/images/linux.png
new file mode 100644
index 00000000..8836c0e2
--- /dev/null
+++ b/ftnoir_protocol_libevdev/images/linux.png
Binary files differ
diff --git a/ftnoir_protocol_libevdev/libevdev-protocol.qrc b/ftnoir_protocol_libevdev/libevdev-protocol.qrc
new file mode 100644
index 00000000..70bb415f
--- /dev/null
+++ b/ftnoir_protocol_libevdev/libevdev-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/linux.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_mouse/ftnoir_mousecontrols.ui b/ftnoir_protocol_mouse/ftnoir_mousecontrols.ui
new file mode 100644
index 00000000..2705fff7
--- /dev/null
+++ b/ftnoir_protocol_mouse/ftnoir_mousecontrols.ui
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICMOUSEControls</class>
+ <widget class="QWidget" name="UICMOUSEControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>257</width>
+ <height>114</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Mouse protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="win32-mouse-protocol.qrc">
+ <normaloff>:/images/mouse.png</normaloff>:/images/mouse.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QComboBox" name="cbxSelectMouse_Y">
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Select Number</string>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::InsertAlphabetically</enum>
+ </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>Map mouse Y to:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel2_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Map mouse X to:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cbxSelectMouse_X">
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Select Number</string>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::InsertAlphabetically</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="win32-mouse-protocol.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_mouse/ftnoir_protocol_mouse.cpp b/ftnoir_protocol_mouse/ftnoir_protocol_mouse.cpp
new file mode 100644
index 00000000..cc8aa11e
--- /dev/null
+++ b/ftnoir_protocol_mouse/ftnoir_protocol_mouse.cpp
@@ -0,0 +1,67 @@
+/********************************************************************************
+* 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-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/>. *
+* *
+* FTNoIR_Protocol_Mouse The Class, that communicates headpose-data by *
+* generating Mouse commands. *
+* Many games (like FPS's) support Mouse-look features, *
+* but no face-tracking. *
+********************************************************************************/
+#include "ftnoir_protocol_mouse.h"
+#include "facetracknoir/global-settings.h"
+
+void FTNoIR_Protocol::sendHeadposeToGame(const double *headpose ) {
+ double fMouse_X = 0;
+ double fMouse_Y = 0;
+
+ int Mouse_X = s.Mouse_X;
+ int Mouse_Y = s.Mouse_Y;
+
+ if (Mouse_X > 0 && Mouse_X <= 6)
+ fMouse_X = headpose[Mouse_X-1] / (Mouse_X < 3 ? 100 : 180);
+
+ if (Mouse_Y > 0 && Mouse_Y <= 6)
+ fMouse_Y = headpose[Mouse_Y-1] / (Mouse_Y < 3 ? 100 : 180);
+
+ RECT desktop;
+ const HWND hDesktop = GetDesktopWindow();
+ if (hDesktop != NULL && GetWindowRect(hDesktop, &desktop)) {
+ fMouse_X *= desktop.right;
+ fMouse_Y *= desktop.bottom;
+ SetCursorPos(fMouse_X + desktop.right/2, fMouse_Y + desktop.bottom/2);
+ }
+}
+
+void FTNoIR_Protocol::reload()
+{
+ s.b->reload();
+}
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ return true;
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_mouse/ftnoir_protocol_mouse.h b/ftnoir_protocol_mouse/ftnoir_protocol_mouse.h
new file mode 100644
index 00000000..01f283d3
--- /dev/null
+++ b/ftnoir_protocol_mouse/ftnoir_protocol_mouse.h
@@ -0,0 +1,109 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010-2011 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* http://facetracknoir.sourceforge.net/home/default.htm *
+* *
+* 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/>. *
+* *
+* FTNoIR_Protocol_Mouse The Class, that communicates headpose-data by *
+* generating Mouse commands. *
+* Many games (like FPS's) support Mouse-look features, *
+* but no face-tracking. *
+********************************************************************************/
+#pragma once
+#ifndef INCLUDED_MOUSESERVER_H
+#define INCLUDED_MOUSESERVER_H
+
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_mousecontrols.h"
+#include <QMessageBox>
+#include <QSettings>
+#include <QLibrary>
+#include <QProcess>
+#include <QDebug>
+#include <QFile>
+#include <windows.h>
+#include <winuser.h>
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> Mouse_X, Mouse_Y;
+ settings() :
+ b(bundle("mouse-proto")),
+ Mouse_X(b, "mouse-x", 0),
+ Mouse_Y(b, "mouse-y", 0)
+ {}
+};
+
+#define MOUSE_AXIS_MIN 0
+#define MOUSE_AXIS_MAX 65535
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol() {}
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame( const double *headpose);
+ QString getGameName() {
+ return "Mouse tracker";
+ }
+ void reload();
+private:
+ struct settings s;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class MOUSEControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ MOUSEControls();
+ void registerProtocol(IProtocol *protocol) {
+ _proto = (FTNoIR_Protocol *) protocol;
+ }
+ void unRegisterProtocol() {
+ _proto = NULL;
+ }
+private:
+ Ui::UICMOUSEControls ui;
+ settings s;
+ FTNoIR_Protocol* _proto;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Protocol DLL. Functions used to get general info on the Protocol
+//*******************************************************************************************************
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("Mouse Look"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("Mouse Look"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Mouse Look protocol"); }
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/mouse.png"); }
+};
+
+
+#endif//INCLUDED_MOUSESERVER_H
+//END
diff --git a/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dialog.cpp b/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dialog.cpp
new file mode 100644
index 00000000..22b7024c
--- /dev/null
+++ b/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dialog.cpp
@@ -0,0 +1,69 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_mouse.h"
+#include "facetracknoir/global-settings.h"
+
+MOUSEControls::MOUSEControls() : _proto(nullptr)
+{
+ ui.setupUi( this );
+ ui.cbxSelectMouse_X->addItem("None");
+ ui.cbxSelectMouse_X->addItem("X");
+ ui.cbxSelectMouse_X->addItem("Y");
+ ui.cbxSelectMouse_X->addItem("Z");
+ ui.cbxSelectMouse_X->addItem("Yaw");
+ ui.cbxSelectMouse_X->addItem("Pitch");
+ ui.cbxSelectMouse_X->addItem("Roll");
+
+ ui.cbxSelectMouse_Y->addItem("None");
+ ui.cbxSelectMouse_Y->addItem("X");
+ ui.cbxSelectMouse_Y->addItem("Y");
+ ui.cbxSelectMouse_Y->addItem("Z");
+ ui.cbxSelectMouse_Y->addItem("Yaw");
+ ui.cbxSelectMouse_Y->addItem("Pitch");
+ ui.cbxSelectMouse_Y->addItem("Roll");
+
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+
+ tie_setting(s.Mouse_X, ui.cbxSelectMouse_X);
+ tie_setting(s.Mouse_Y, ui.cbxSelectMouse_Y);
+}
+
+void MOUSEControls::doOK() {
+ s.b->save();
+ if (_proto)
+ _proto->reload();
+ this->close();
+}
+
+void MOUSEControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new MOUSEControls;
+}
diff --git a/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dll.cpp b/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dll.cpp
new file mode 100644
index 00000000..54f6b307
--- /dev/null
+++ b/ftnoir_protocol_mouse/ftnoir_protocol_mouse_dll.cpp
@@ -0,0 +1,31 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_mouse.h"
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_mouse/images/mouse.png b/ftnoir_protocol_mouse/images/mouse.png
new file mode 100644
index 00000000..c6f9ea26
--- /dev/null
+++ b/ftnoir_protocol_mouse/images/mouse.png
Binary files differ
diff --git a/ftnoir_protocol_mouse/win32-mouse-protocol.qrc b/ftnoir_protocol_mouse/win32-mouse-protocol.qrc
new file mode 100644
index 00000000..ed6a71be
--- /dev/null
+++ b/ftnoir_protocol_mouse/win32-mouse-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/mouse.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_sc/ftnoir-protocol-sc.rc b/ftnoir_protocol_sc/ftnoir-protocol-sc.rc
new file mode 100644
index 00000000..80b6c12c
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir-protocol-sc.rc
@@ -0,0 +1,4 @@
+#include <winuser.h>
+142 RT_MANIFEST scserver.manifest
+143 RT_MANIFEST scserver-sp2.manifest
+144 RT_MANIFEST scserver-acceleration.manifest \ No newline at end of file
diff --git a/ftnoir_protocol_sc/ftnoir_protocol_sc.cpp b/ftnoir_protocol_sc/ftnoir_protocol_sc.cpp
new file mode 100644
index 00000000..2714e980
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir_protocol_sc.cpp
@@ -0,0 +1,252 @@
+/********************************************************************************
+* 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-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/>. *
+* *
+* FTNoIR_Protocol: the Class, that communicates headpose-data *
+* to games, using the SimConnect.dll. *
+* SimConnect.dll is a so called 'side-by-side' assembly, so it *
+* must be treated as such... *
+********************************************************************************/
+#include "ftnoir_protocol_sc.h"
+#include "facetracknoir/global-settings.h"
+
+importSimConnect_CameraSetRelative6DOF FTNoIR_Protocol::simconnect_set6DOF;
+HANDLE FTNoIR_Protocol::hSimConnect = 0; // Handle to SimConnect
+
+float FTNoIR_Protocol::virtSCPosX = 0.0f; // Headpose
+float FTNoIR_Protocol::virtSCPosY = 0.0f;
+float FTNoIR_Protocol::virtSCPosZ = 0.0f;
+
+float FTNoIR_Protocol::virtSCRotX = 0.0f;
+float FTNoIR_Protocol::virtSCRotY = 0.0f;
+float FTNoIR_Protocol::virtSCRotZ = 0.0f;
+
+float FTNoIR_Protocol::prevSCPosX = 0.0f; // previous Headpose
+float FTNoIR_Protocol::prevSCPosY = 0.0f;
+float FTNoIR_Protocol::prevSCPosZ = 0.0f;
+
+float FTNoIR_Protocol::prevSCRotX = 0.0f;
+float FTNoIR_Protocol::prevSCRotY = 0.0f;
+float FTNoIR_Protocol::prevSCRotZ = 0.0f;
+
+static QLibrary SCClientLib;
+
+FTNoIR_Protocol::FTNoIR_Protocol()
+{
+ blnSimConnectActive = false;
+ hSimConnect = 0;
+}
+
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ qDebug() << "~FTNoIR_Protocol says: inside" << FTNoIR_Protocol::hSimConnect;
+
+ if (hSimConnect != 0) {
+ qDebug() << "~FTNoIR_Protocol says: before simconnect_close";
+ if (SUCCEEDED( simconnect_close( FTNoIR_Protocol::hSimConnect ) ) ) {
+ qDebug() << "~FTNoIR_Protocol says: close SUCCEEDED";
+ }
+ }
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame( const double *headpose ) {
+ virtSCRotX = -headpose[Pitch]; // degrees
+ virtSCRotY = -headpose[Yaw];
+ virtSCRotZ = headpose[Roll];
+
+ virtSCPosX = headpose[TX]/100.f; // cm to meters
+ virtSCPosY = headpose[TY]/100.f;
+ virtSCPosZ = -headpose[TZ]/100.f;
+
+ if (!blnSimConnectActive) {
+ if (SUCCEEDED(simconnect_open(&hSimConnect, "FaceTrackNoIR", NULL, 0, 0, 0))) {
+ HRESULT hr;
+
+ simconnect_subscribetosystemevent(hSimConnect, EVENT_PING, "Frame");
+
+ hr = simconnect_mapclienteventtosimevent(hSimConnect, EVENT_INIT, "");
+ hr = simconnect_addclienteventtonotificationgroup(hSimConnect, GROUP0, EVENT_INIT, false);
+ hr = simconnect_setnotificationgrouppriority(hSimConnect, GROUP0, SIMCONNECT_GROUP_PRIORITY_HIGHEST);
+ blnSimConnectActive = true;
+ }
+ }
+ else
+ (void) (simconnect_calldispatch(hSimConnect, processNextSimconnectEvent, NULL));
+}
+
+class ActivationContext {
+public:
+ ActivationContext(const int resid) {
+ hactctx = INVALID_HANDLE_VALUE;
+ actctx_cookie = 0;
+ ACTCTXA actx = {0};
+ actx.cbSize = sizeof(ACTCTXA);
+ actx.lpResourceName = MAKEINTRESOURCEA(resid);
+ actx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
+#ifdef _MSC_VER
+# define PREFIX ""
+#else
+# define PREFIX "lib"
+#endif
+ QString path = QCoreApplication::applicationDirPath() + "/" PREFIX "opentrack-proto-simconnect.dll";
+ QByteArray name = QFile::encodeName(path);
+ actx.lpSource = name.constData();
+ hactctx = CreateActCtxA(&actx);
+ actctx_cookie = 0;
+ if (hactctx != INVALID_HANDLE_VALUE) {
+ if (!ActivateActCtx(hactctx, &actctx_cookie)) {
+ qDebug() << "SC: can't set win32 activation context" << GetLastError();
+ ReleaseActCtx(hactctx);
+ hactctx = INVALID_HANDLE_VALUE;
+ }
+ } else {
+ qDebug() << "SC: can't create win32 activation context";
+ }
+ }
+ ~ActivationContext() {
+ if (hactctx != INVALID_HANDLE_VALUE)
+ {
+ DeactivateActCtx(0, actctx_cookie);
+ ReleaseActCtx(hactctx);
+ }
+ }
+private:
+ ULONG_PTR actctx_cookie;
+ HANDLE hactctx;
+};
+
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ if (!SCClientLib.isLoaded())
+ {
+ ActivationContext ctx(142 + static_cast<int>(s.sxs_manifest));
+
+ SCClientLib.setFileName("SimConnect.dll");
+ if (!SCClientLib.load()) {
+ qDebug() << "SC load" << SCClientLib.errorString();
+ return false;
+ }
+ }
+
+ //
+ // Get the functions from the DLL.
+ //
+ simconnect_open = (importSimConnect_Open) SCClientLib.resolve("SimConnect_Open");
+ if (simconnect_open == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_Open function not found in DLL!";
+ return false;
+ }
+ simconnect_set6DOF = (importSimConnect_CameraSetRelative6DOF) SCClientLib.resolve("SimConnect_CameraSetRelative6DOF");
+ if (simconnect_set6DOF == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_CameraSetRelative6DOF function not found in DLL!";
+ return false;
+ }
+ simconnect_close = (importSimConnect_Close) SCClientLib.resolve("SimConnect_Close");
+ if (simconnect_close == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_Close function not found in DLL!";
+ return false;
+ }
+
+ //return true;
+
+ simconnect_calldispatch = (importSimConnect_CallDispatch) SCClientLib.resolve("SimConnect_CallDispatch");
+ if (simconnect_calldispatch == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_CallDispatch function not found in DLL!";
+ return false;
+ }
+
+ simconnect_subscribetosystemevent = (importSimConnect_SubscribeToSystemEvent) SCClientLib.resolve("SimConnect_SubscribeToSystemEvent");
+ if (simconnect_subscribetosystemevent == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_SubscribeToSystemEvent function not found in DLL!";
+ return false;
+ }
+
+ simconnect_mapclienteventtosimevent = (importSimConnect_MapClientEventToSimEvent) SCClientLib.resolve("SimConnect_MapClientEventToSimEvent");
+ if (simconnect_subscribetosystemevent == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_MapClientEventToSimEvent function not found in DLL!";
+ return false;
+ }
+
+ simconnect_addclienteventtonotificationgroup = (importSimConnect_AddClientEventToNotificationGroup) SCClientLib.resolve("SimConnect_AddClientEventToNotificationGroup");
+ if (simconnect_subscribetosystemevent == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_AddClientEventToNotificationGroup function not found in DLL!";
+ return false;
+ }
+
+ simconnect_setnotificationgrouppriority = (importSimConnect_SetNotificationGroupPriority) SCClientLib.resolve("SimConnect_SetNotificationGroupPriority");
+ if (simconnect_subscribetosystemevent == NULL) {
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect_SetNotificationGroupPriority function not found in DLL!";
+ return false;
+ }
+
+ qDebug() << "FTNoIR_Protocol::checkServerInstallationOK() says: SimConnect functions resolved in DLL!";
+
+ return true;
+}
+
+void CALLBACK FTNoIR_Protocol::processNextSimconnectEvent(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext)
+{
+ switch(pData->dwID)
+ {
+ default:
+ break;
+ case SIMCONNECT_RECV_ID_EVENT_FRAME:
+ {
+ if ((prevSCPosX != virtSCPosX) || (prevSCPosY != virtSCPosY) || (prevSCPosZ != virtSCPosZ) ||
+ (prevSCRotX != virtSCRotX) || (prevSCRotY != virtSCRotY) || (prevSCRotZ != virtSCRotZ)) {
+ (void) simconnect_set6DOF(hSimConnect, virtSCPosX, virtSCPosY, virtSCPosZ, virtSCRotX, virtSCRotZ, virtSCRotY);
+ }
+ prevSCPosX = virtSCPosX;
+ prevSCPosY = virtSCPosY;
+ prevSCPosZ = virtSCPosZ;
+ prevSCRotX = virtSCRotX;
+ prevSCRotY = virtSCRotY;
+ prevSCRotZ = virtSCRotZ;
+ }
+ case SIMCONNECT_RECV_ID_EXCEPTION:
+ {
+ SIMCONNECT_RECV_EXCEPTION *except = (SIMCONNECT_RECV_EXCEPTION*)pData;
+
+ switch (except->dwException)
+ {
+ case SIMCONNECT_EXCEPTION_ERROR:
+ qDebug() << "Camera error";
+ break;
+
+ default:
+ qDebug() << "Exception";
+ break;
+ }
+ break;
+ }
+
+ case SIMCONNECT_RECV_ID_QUIT:
+ {
+ break;
+ }
+ }
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_sc/ftnoir_protocol_sc.h b/ftnoir_protocol_sc/ftnoir_protocol_sc.h
new file mode 100644
index 00000000..a13c0097
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir_protocol_sc.h
@@ -0,0 +1,157 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2010-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/>. *
+* *
+* SCServer SCServer is the Class, that communicates headpose-data *
+* to games, using the SimConnect.dll. *
+* SimConnect.dll is a so called 'side-by-side' assembly, so it *
+* must be treated as such... *
+********************************************************************************/
+#pragma once
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0502
+#include "facetracknoir/global-settings.h"
+//
+// Prevent the SimConnect manifest from being merged in the application-manifest
+// This is necessary to run FaceTrackNoIR on a PC without FSX
+//
+#define SIMCONNECT_H_NOMANIFEST
+#include <windows.h>
+#include <SimConnect.h>
+
+#include <ftnoir_protocol_base/ftnoir_protocol_base.h>
+#include <ui_ftnoir_sccontrols.h>
+#include <QMessageBox>
+#include <QSettings>
+#include <QLibrary>
+#include <QProcess>
+#include <QDebug>
+#include <QFile>
+#include "facetracknoir/options.h"
+using namespace options;
+
+typedef HRESULT (WINAPI *importSimConnect_Open)(HANDLE * phSimConnect, LPCSTR szName, HWND hWnd, DWORD UserEventWin32, HANDLE hEventHandle, DWORD ConfigIndex);
+typedef HRESULT (WINAPI *importSimConnect_Close)(HANDLE hSimConnect);
+typedef HRESULT (WINAPI *importSimConnect_CameraSetRelative6DOF)(HANDLE hSimConnect, float fDeltaX, float fDeltaY, float fDeltaZ, float fPitchDeg, float fBankDeg, float fHeadingDeg);
+typedef HRESULT (WINAPI *importSimConnect_CallDispatch)(HANDLE hSimConnect, DispatchProc pfcnDispatch, void * pContext);
+typedef HRESULT (WINAPI *importSimConnect_SubscribeToSystemEvent)(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID, const char * SystemEventName);
+typedef HRESULT (WINAPI *importSimConnect_MapClientEventToSimEvent)(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID, const char * EventName);
+typedef HRESULT (WINAPI *importSimConnect_AddClientEventToNotificationGroup)(HANDLE hSimConnect, SIMCONNECT_NOTIFICATION_GROUP_ID GroupID, SIMCONNECT_CLIENT_EVENT_ID EventID, BOOL bMaskable);
+typedef HRESULT (WINAPI *importSimConnect_SetNotificationGroupPriority)(HANDLE hSimConnect, SIMCONNECT_NOTIFICATION_GROUP_ID GroupID, DWORD uPriority);
+
+#define SC_CLIENT_FILENAME "SimConnect.dll"
+
+enum GROUP_ID
+{
+ GROUP0=0,
+};
+
+enum EVENT_ID
+{
+ EVENT_PING=0,
+ EVENT_INIT,
+};
+
+enum INPUT_ID
+{
+ INPUT0=0,
+};
+
+struct settings {
+ pbundle b;
+ value<int> sxs_manifest;
+ settings() :
+ b(bundle("proto-simconnect")),
+ sxs_manifest(b, "sxs-manifest-version", 0)
+ {}
+};
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol();
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame(const double* headpose);
+ QString getGameName() {
+ return "FS2004/FSX";
+ }
+private:
+ static float virtSCPosX;
+ static float virtSCPosY;
+ static float virtSCPosZ;
+
+ static float virtSCRotX;
+ static float virtSCRotY;
+ static float virtSCRotZ;
+
+ static float prevSCPosX;
+ static float prevSCPosY;
+ static float prevSCPosZ;
+
+ static float prevSCRotX;
+ static float prevSCRotY;
+ static float prevSCRotZ;
+
+ bool blnSimConnectActive;
+
+ importSimConnect_Open simconnect_open; // SimConnect function(s) in DLL
+ importSimConnect_Close simconnect_close;
+ static importSimConnect_CameraSetRelative6DOF simconnect_set6DOF;
+ importSimConnect_CallDispatch simconnect_calldispatch;
+ importSimConnect_SubscribeToSystemEvent simconnect_subscribetosystemevent;
+ importSimConnect_MapClientEventToSimEvent simconnect_mapclienteventtosimevent;
+ importSimConnect_AddClientEventToNotificationGroup simconnect_addclienteventtonotificationgroup;
+ importSimConnect_SetNotificationGroupPriority simconnect_setnotificationgrouppriority;
+
+ static HANDLE hSimConnect; // Handle to SimConnect
+ static void CALLBACK processNextSimconnectEvent(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext);
+ settings s;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class SCControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ SCControls();
+ void registerProtocol(IProtocol *protocol) {}
+ void unRegisterProtocol() {}
+private:
+ Ui::UICSCControls ui;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Protocol DLL. Functions used to get general info on the Protocol
+//*******************************************************************************************************
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("FSX SimConnect"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("SimConnect"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Microsoft SimConnect protocol"); }
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/fsx.png"); }
+};
diff --git a/ftnoir_protocol_sc/ftnoir_protocol_sc_dialog.cpp b/ftnoir_protocol_sc/ftnoir_protocol_sc_dialog.cpp
new file mode 100644
index 00000000..c7428d77
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir_protocol_sc_dialog.cpp
@@ -0,0 +1,54 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_sc.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+SCControls::SCControls() :
+QWidget()
+{
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.sxs_manifest, ui.comboBox);
+}
+
+void SCControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void SCControls::doCancel() {
+ s.b->revert();
+ close();
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new SCControls;
+}
diff --git a/ftnoir_protocol_sc/ftnoir_protocol_sc_dll.cpp b/ftnoir_protocol_sc/ftnoir_protocol_sc_dll.cpp
new file mode 100644
index 00000000..0a52fa96
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir_protocol_sc_dll.cpp
@@ -0,0 +1,32 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_protocol_sc.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_sc/ftnoir_sccontrols.ui b/ftnoir_protocol_sc/ftnoir_sccontrols.ui
new file mode 100644
index 00000000..87dc8d86
--- /dev/null
+++ b/ftnoir_protocol_sc/ftnoir_sccontrols.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICSCControls</class>
+ <widget class="QWidget" name="UICSCControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>290</width>
+ <height>79</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>SimConnect settings FaceTrackNoIR</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="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>FSX version</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="comboBox">
+ <item>
+ <property name="text">
+ <string>SP1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>SP2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Acceleration</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_sc/images/fsx.png b/ftnoir_protocol_sc/images/fsx.png
new file mode 100644
index 00000000..16b072a1
--- /dev/null
+++ b/ftnoir_protocol_sc/images/fsx.png
Binary files differ
diff --git a/ftnoir_protocol_sc/images/fsx1.png b/ftnoir_protocol_sc/images/fsx1.png
new file mode 100644
index 00000000..a1f0f188
--- /dev/null
+++ b/ftnoir_protocol_sc/images/fsx1.png
Binary files differ
diff --git a/ftnoir_protocol_sc/sc-protocol.qrc b/ftnoir_protocol_sc/sc-protocol.qrc
new file mode 100644
index 00000000..127d5180
--- /dev/null
+++ b/ftnoir_protocol_sc/sc-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/fsx.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_sc/scserver-acceleration.manifest b/ftnoir_protocol_sc/scserver-acceleration.manifest
new file mode 100644
index 00000000..06459587
--- /dev/null
+++ b/ftnoir_protocol_sc/scserver-acceleration.manifest
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.FlightSimulator.SimConnect ' version='10.0.61242.0' processorArchitecture='x86' publicKeyToken='67c7c14424d61b5b' />
+ </dependentAssembly>
+ </dependency>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/ftnoir_protocol_sc/scserver-sp2.manifest b/ftnoir_protocol_sc/scserver-sp2.manifest
new file mode 100644
index 00000000..19b123ba
--- /dev/null
+++ b/ftnoir_protocol_sc/scserver-sp2.manifest
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.FlightSimulator.SimConnect ' version='10.0.60905.0' processorArchitecture='x86' publicKeyToken='67c7c14424d61b5b' />
+ </dependentAssembly>
+ </dependency>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/ftnoir_protocol_sc/scserver.manifest b/ftnoir_protocol_sc/scserver.manifest
new file mode 100644
index 00000000..60311d6e
--- /dev/null
+++ b/ftnoir_protocol_sc/scserver.manifest
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.FlightSimulator.SimConnect' version='10.0.61259.0' processorArchitecture='x86' publicKeyToken='67c7c14424d61b5b' />
+ </dependentAssembly>
+ </dependency>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.cpp b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.cpp
new file mode 100644
index 00000000..a3a5cb5f
--- /dev/null
+++ b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.cpp
@@ -0,0 +1,35 @@
+#include "ftnoir_protocol_vjoy.h"
+#include "facetracknoir/global-settings.h"
+#include <ftnoir_tracker_base/ftnoir_tracker_types.h>
+
+/** constructor **/
+FTNoIR_Protocol::FTNoIR_Protocol()
+{
+ VJoy_Initialize("", "");
+}
+
+/** destructor **/
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ VJoy_Shutdown();
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame( const double *headpose ) {
+ JOYSTICK_STATE state[2] = { 0 };
+
+ state[0].POV = (4 << 12) | (4 << 8) | (4 << 4) | 4;
+
+ state[0].XAxis = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[Yaw] * VJOY_AXIS_MAX / 180.0));
+ state[0].YAxis = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[Pitch] * VJOY_AXIS_MAX / 180.0));
+ state[0].ZAxis = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[Roll] * VJOY_AXIS_MAX / 180.0));
+ state[0].XRotation = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[TX] * VJOY_AXIS_MAX / 100.0));
+ state[0].YRotation = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[TY] * VJOY_AXIS_MAX / 100.0));
+ state[0].ZRotation = std::min<int>(VJOY_AXIS_MAX, std::max<int>(VJOY_AXIS_MIN, headpose[TZ] * VJOY_AXIS_MAX / 100.0));
+
+ VJoy_UpdateJoyState(0, state);
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocol* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.h b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.h
new file mode 100644
index 00000000..873b4e3c
--- /dev/null
+++ b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy.h
@@ -0,0 +1,125 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2013 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/>. *
+* *
+* FGServer FGServer is the Class, that communicates headpose-data *
+* to FlightGear, using UDP. *
+* It is based on the (Linux) example made by Melchior FRANZ. *
+********************************************************************************/
+#pragma once
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ui_ftnoir_vjoy_controls.h"
+#include <QThread>
+#include <QUdpSocket>
+#include <QMessageBox>
+#include <QSettings>
+#include <math.h>
+#include "facetracknoir/global-settings.h"
+#include <windows.h>
+
+#define FT_PROGRAMID "FT_ProgramID"
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol();
+ bool checkServerInstallationOK() {
+ return true;
+ }
+ void sendHeadposeToGame( const double *headpose );
+ QString getGameName() {
+ return "Virtual joystick";
+ }
+private:
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class VJoyControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+
+ explicit VJoyControls();
+ void registerProtocol(IProtocol *l) {}
+ void unRegisterProtocol() {}
+
+private:
+ Ui::UICVJoyControls ui;
+ void save();
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Protocol DLL. Functions used to get general info on the Protocol
+//*******************************************************************************************************
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ FTNoIR_ProtocolDll();
+ ~FTNoIR_ProtocolDll();
+
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("VJoy"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("VJoy"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("VJoy"); }
+
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/vjoy.png"); }
+};
+
+#define VJOY_AXIS_MIN -32768
+#define VJOY_AXIS_NIL 0
+#define VJOY_AXIS_MAX 32767
+
+#include <pshpack1.h>
+
+typedef struct _JOYSTICK_STATE
+{
+ UCHAR ReportId; // Report Id
+ SHORT XAxis; // X Axis
+ SHORT YAxis; // Y Axis
+ SHORT ZAxis; // Z Axis
+ SHORT XRotation; // X Rotation
+ SHORT YRotation; // Y Rotation
+ SHORT ZRotation; // Z Rotation
+ SHORT Slider; // Slider
+ SHORT Dial; // Dial
+ USHORT POV; // POV
+ UINT32 Buttons; // 32 Buttons
+} JOYSTICK_STATE, * PJOYSTICK_STATE;
+
+#include <poppack.h>
+
+#undef EXTERN_C
+#if _MSC_VER
+# define EXTERN_C
+#else
+# define EXTERN_C extern "C"
+#endif
+#if _MSC_VER
+# pragma comment(linker, "/implib:vjoy.def")
+#endif
+EXTERN_C BOOL __stdcall VJoy_Initialize(PCHAR name, PCHAR serial);
+EXTERN_C VOID __stdcall VJoy_Shutdown();
+EXTERN_C BOOL __stdcall VJoy_UpdateJoyState(int id, PJOYSTICK_STATE pJoyState);
diff --git a/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dialog.cpp b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dialog.cpp
new file mode 100644
index 00000000..febb7b18
--- /dev/null
+++ b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dialog.cpp
@@ -0,0 +1,26 @@
+#include "ftnoir_protocol_vjoy.h"
+#include "facetracknoir/global-settings.h"
+
+VJoyControls::VJoyControls() : QWidget()
+{
+ ui.setupUi( this );
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+}
+
+void VJoyControls::doOK() {
+ save();
+ this->close();
+}
+
+void VJoyControls::doCancel() {
+ this->close();
+}
+
+void VJoyControls::save() {
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT IProtocolDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new VJoyControls;
+}
diff --git a/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dll.cpp b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dll.cpp
new file mode 100644
index 00000000..5cb5ef05
--- /dev/null
+++ b/ftnoir_protocol_vjoy/ftnoir_protocol_vjoy_dll.cpp
@@ -0,0 +1,16 @@
+#include "ftnoir_protocol_vjoy.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+FTNoIR_ProtocolDll::FTNoIR_ProtocolDll() {
+}
+
+FTNoIR_ProtocolDll::~FTNoIR_ProtocolDll()
+{
+
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_vjoy/ftnoir_vjoy_controls.ui b/ftnoir_protocol_vjoy/ftnoir_vjoy_controls.ui
new file mode 100644
index 00000000..2214b887
--- /dev/null
+++ b/ftnoir_protocol_vjoy/ftnoir_vjoy_controls.ui
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICVJoyControls</class>
+ <widget class="QWidget" name="UICVJoyControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>228</width>
+ <height>69</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>VJoy</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="vjoy-protocol.qrc">
+ <normaloff>:/images/vjoy.png</normaloff>:/images/vjoy.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>No settings necessary</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>btnOK</tabstop>
+ <tabstop>btnCancel</tabstop>
+ </tabstops>
+ <resources>
+ <include location="vjoy-protocol.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_vjoy/images/vjoy.png b/ftnoir_protocol_vjoy/images/vjoy.png
new file mode 100644
index 00000000..8eb14be8
--- /dev/null
+++ b/ftnoir_protocol_vjoy/images/vjoy.png
Binary files differ
diff --git a/ftnoir_protocol_vjoy/vjoy-protocol.qrc b/ftnoir_protocol_vjoy/vjoy-protocol.qrc
new file mode 100644
index 00000000..7b3741f1
--- /dev/null
+++ b/ftnoir_protocol_vjoy/vjoy-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/vjoy.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_vjoy/vjoy.def b/ftnoir_protocol_vjoy/vjoy.def
new file mode 100644
index 00000000..aea590a4
--- /dev/null
+++ b/ftnoir_protocol_vjoy/vjoy.def
@@ -0,0 +1,5 @@
+LIBRARY vjoy.dll
+IMPORTS
+VJoy_Initialize = _VJoy_Initialize
+VJoy_Shutdown = _VJoy_Shutdown
+VJoy_UpdateJoyState = _VJoy_UpdateJoyState
diff --git a/ftnoir_protocol_wine/ftnoir_protocol_wine.cpp b/ftnoir_protocol_wine/ftnoir_protocol_wine.cpp
new file mode 100644
index 00000000..58ffe974
--- /dev/null
+++ b/ftnoir_protocol_wine/ftnoir_protocol_wine.cpp
@@ -0,0 +1,76 @@
+#include "ftnoir_protocol_wine.h"
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h> /* For mode constants */
+#include <fcntl.h> /* For O_* constants */
+
+/** constructor **/
+FTNoIR_Protocol::FTNoIR_Protocol() : lck_shm(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM)), shm(NULL), gameid(0)
+{
+ if (lck_shm.success()) {
+ shm = (WineSHM*) lck_shm.mem;
+ memset(shm, 0, sizeof(*shm));
+ }
+ wrapper.start("wine", QStringList() << (QCoreApplication::applicationDirPath() + "/opentrack-wrapper-wine.exe.so"));
+}
+
+/** destructor **/
+FTNoIR_Protocol::~FTNoIR_Protocol()
+{
+ if (shm) {
+ shm->stop = true;
+ wrapper.waitForFinished(100);
+ }
+ wrapper.terminate();
+ if (!wrapper.waitForFinished(100))
+ {
+ wrapper.kill();
+ wrapper.waitForFinished(42);
+ }
+ //shm_unlink("/" WINE_SHM_NAME);
+}
+
+void FTNoIR_Protocol::sendHeadposeToGame( const double *headpose ) {
+ if (shm)
+ {
+ lck_shm.lock();
+ for (int i = 3; i < 6; i++)
+ shm->data[i] = headpose[i] / 57.295781;
+ for (int i = 0; i < 3; i++)
+ shm->data[i] = headpose[i] * 10;
+ if (shm->gameid != gameid)
+ {
+ QString gamename;
+ QMutexLocker foo(&game_name_mutex);
+ /* only EZCA for FSX requires dummy process, and FSX doesn't work on Linux */
+ /* memory-hacks DLL can't be loaded into a Linux process, either */
+ CSV::getGameData(shm->gameid, shm->table, gamename);
+ gameid = shm->gameid2 = shm->gameid;
+ connected_game = gamename;
+ }
+ lck_shm.unlock();
+ }
+}
+
+//
+// Check if the Client DLL exists and load it (to test it), if so.
+// Returns 'true' if all seems OK.
+//
+bool FTNoIR_Protocol::checkServerInstallationOK()
+{
+ return lck_shm.success();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory function that creates instances if the Protocol object.
+
+// Export both decorated and undecorated names.
+// GetProtocol - Undecorated name, which can be easily used with GetProcAddress
+// Win32 API function.
+// _GetProtocol@0 - Common name decoration for __stdcall functions in C language.
+//#pragma comment(linker, "/export:GetProtocol=_GetProtocol@0")
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT void* CALLING_CONVENTION GetConstructor()
+{
+ return (IProtocol*) new FTNoIR_Protocol;
+}
diff --git a/ftnoir_protocol_wine/ftnoir_protocol_wine.h b/ftnoir_protocol_wine/ftnoir_protocol_wine.h
new file mode 100644
index 00000000..50d2bc0c
--- /dev/null
+++ b/ftnoir_protocol_wine/ftnoir_protocol_wine.h
@@ -0,0 +1,102 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of 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/>. *
+* *
+* FTServer FTServer is the Class, that communicates headpose-data *
+* to games, using the FreeTrackClient.dll. *
+********************************************************************************/
+#pragma once
+#ifndef INCLUDED_FTSERVER_H
+#define INCLUDED_FTSERVER_H
+
+#include "ftnoir_protocol_base/ftnoir_protocol_base.h"
+#include "ftnoir_protocol_ft/fttypes.h"
+#include "ftnoir_csv/csv.h"
+#include "ui_ftnoir_winecontrols.h"
+#include <QMessageBox>
+#include <QLibrary>
+#include <QProcess>
+#include <QDebug>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QFile>
+#include "facetracknoir/global-settings.h"
+#include "compat/compat.h"
+#include "ftnoir_protocol_wine/wine-shm.h"
+
+class FTNoIR_Protocol : public IProtocol
+{
+public:
+ FTNoIR_Protocol();
+ virtual ~FTNoIR_Protocol();
+
+ bool checkServerInstallationOK();
+ void sendHeadposeToGame(const double* headpose);
+ QString getGameName() {
+ QMutexLocker foo(&game_name_mutex);
+ return connected_game;
+ }
+private:
+ PortableLockedShm lck_shm;
+ WineSHM* shm;
+ QProcess wrapper;
+ int gameid;
+ QString connected_game;
+ QMutex game_name_mutex;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class FTControls: public QWidget, public IProtocolDialog
+{
+ Q_OBJECT
+public:
+ FTControls();
+ void registerProtocol(IProtocol *) {}
+ void unRegisterProtocol() {}
+
+private:
+ Ui::UICFTControls ui;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Protocol DLL. Functions used to get general info on the Protocol
+//*******************************************************************************************************
+class FTNoIR_ProtocolDll : public Metadata
+{
+public:
+ FTNoIR_ProtocolDll();
+ ~FTNoIR_ProtocolDll();
+
+ void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("Wine"); }
+ void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("Wine"); }
+ void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Wine glue wrapper"); }
+
+ void getIcon(QIcon *icon) { *icon = QIcon(":/images/wine.png"); }
+};
+
+
+#endif//INCLUDED_FTSERVER_H
+//END
diff --git a/ftnoir_protocol_wine/ftnoir_protocol_wine_dialog.cpp b/ftnoir_protocol_wine/ftnoir_protocol_wine_dialog.cpp
new file mode 100644
index 00000000..ecbc2137
--- /dev/null
+++ b/ftnoir_protocol_wine/ftnoir_protocol_wine_dialog.cpp
@@ -0,0 +1,39 @@
+#include "ftnoir_protocol_wine.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+//*******************************************************************************************************
+// FaceTrackNoIR Client Settings-dialog.
+//*******************************************************************************************************
+
+//
+// Constructor for server-settings-dialog
+//
+FTControls::FTControls() : QWidget()
+{
+ ui.setupUi( this );
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+}
+
+//
+// Destructor for server-dialog
+
+//
+// OK clicked on server-dialog
+//
+void FTControls::doOK() {
+ this->close();
+}
+
+//
+// Cancel clicked on server-dialog
+//
+void FTControls::doCancel() {
+ this->close();
+}
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT void* CALLING_CONVENTION GetDialog( )
+{
+ return (IProtocolDialog*) new FTControls;
+}
diff --git a/ftnoir_protocol_wine/ftnoir_protocol_wine_dll.cpp b/ftnoir_protocol_wine/ftnoir_protocol_wine_dll.cpp
new file mode 100644
index 00000000..dd7f17a6
--- /dev/null
+++ b/ftnoir_protocol_wine/ftnoir_protocol_wine_dll.cpp
@@ -0,0 +1,25 @@
+#include "ftnoir_protocol_wine.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+FTNoIR_ProtocolDll::FTNoIR_ProtocolDll() {
+}
+
+FTNoIR_ProtocolDll::~FTNoIR_ProtocolDll()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory function that creates instances if the Protocol object.
+
+// Export both decorated and undecorated names.
+// GetProtocolDll - Undecorated name, which can be easily used with GetProcAddress
+// Win32 API function.
+// _GetProtocolDll@0 - Common name decoration for __stdcall functions in C language.
+//#pragma comment(linker, "/export:GetProtocolDll=_GetProtocolDll@0")
+
+extern "C" FTNOIR_PROTOCOL_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_ProtocolDll;
+}
diff --git a/ftnoir_protocol_wine/ftnoir_winecontrols.ui b/ftnoir_protocol_wine/ftnoir_winecontrols.ui
new file mode 100644
index 00000000..9356c448
--- /dev/null
+++ b/ftnoir_protocol_wine/ftnoir_winecontrols.ui
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFTControls</class>
+ <widget class="QWidget" name="UICFTControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>409</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>FreeTrack settings FaceTrackNoIR</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/freetrack.png</normaloff>images/freetrack.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>There are no settings necessary for the Wine protocol.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_protocol_wine/images/wine.png b/ftnoir_protocol_wine/images/wine.png
new file mode 100644
index 00000000..bcf3a012
--- /dev/null
+++ b/ftnoir_protocol_wine/images/wine.png
Binary files differ
diff --git a/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx b/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx
new file mode 100644
index 00000000..6e512b6e
--- /dev/null
+++ b/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx
@@ -0,0 +1,74 @@
+#include <errno.h>
+#include <stdio.h>
+#include "ftnoir_protocol_ft/fttypes.h"
+#include "ftnoir_protocol_wine/wine-shm.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_types.h"
+
+void create_registry_key(void);
+
+class ShmPosix {
+public:
+ ShmPosix(const char *shmName, const char *mutexName, int mapSize);
+ ~ShmPosix();
+ void lock();
+ void unlock();
+ bool success();
+ void* mem;
+private:
+ int fd, size;
+};
+
+class ShmWine {
+public:
+ ShmWine(const char *shmName, const char *mutexName, int mapSize);
+ ~ShmWine();
+ void lock();
+ void unlock();
+ bool success();
+ void* mem;
+private:
+ void *hMutex, *hMapFile;
+};
+#include <windows.h>
+
+int main(void)
+{
+ ShmPosix lck_posix(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM));
+ ShmWine lck_wine("FT_SharedMem", "FT_Mutext", sizeof(FTMemMap));
+ if(!lck_posix.success()) {
+ printf("Can't open posix map: %d\n", errno);
+ return 1;
+ }
+ if(!lck_wine.success()) {
+ printf("Can't open Wine map\n");
+ return 1;
+ }
+ WineSHM* shm_posix = (WineSHM*) lck_posix.mem;
+ FTMemMap* shm_wine = (FTMemMap*) lck_wine.mem;
+ TFreeTrackData* data = &shm_wine->data;
+ create_registry_key();
+ while (1) {
+ (void) Sleep(10);
+ lck_posix.lock();
+ if (shm_posix->stop) {
+ lck_posix.unlock();
+ break;
+ }
+ lck_wine.lock();
+ data->Yaw = shm_posix->data[Yaw];
+ data->Pitch = shm_posix->data[Pitch];
+ data->Roll = shm_posix->data[Roll];
+ data->X = shm_posix->data[TX];
+ data->Y = shm_posix->data[TY];
+ data->Z = shm_posix->data[TZ];
+ data->DataID++;
+ data->CamWidth = 250;
+ data->CamHeight = 100;
+ shm_wine->GameID2 = shm_posix->gameid2;
+ shm_posix->gameid = shm_wine->GameID;
+ for (int i = 0; i < 8; i++)
+ shm_wine->table[i] = shm_posix->table[i];
+ lck_wine.unlock();
+ lck_posix.unlock();
+ }
+}
diff --git a/ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx b/ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx
new file mode 100644
index 00000000..010c4440
--- /dev/null
+++ b/ftnoir_protocol_wine/opentrack-wrapper-wine-posix.cxx
@@ -0,0 +1,8 @@
+#define OPENTRACK_COMPAT_BUNDLED
+#ifdef _WIN32
+# undef _WIN32
+#endif
+
+#define PortableLockedShm ShmPosix
+#include "compat/compat.h"
+#include "compat/compat.cpp"
diff --git a/ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx b/ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx
new file mode 100644
index 00000000..e7102600
--- /dev/null
+++ b/ftnoir_protocol_wine/opentrack-wrapper-wine-windows.cxx
@@ -0,0 +1,38 @@
+#define OPENTRACK_COMPAT_BUNDLED
+
+#ifndef __WIN32
+#define __WIN32
+#endif
+
+#define PortableLockedShm ShmWine
+
+#include "ftnoir_protocol_ft/fttypes.h"
+#include "compat/compat.h"
+#include "compat/compat.cpp"
+#include <string.h>
+
+void create_registry_key(void) {
+ char dir[8192];
+
+ if (GetCurrentDirectoryA(8192, dir) < 8190)
+ {
+ HKEY hkpath;
+ if (RegCreateKeyExA(HKEY_CURRENT_USER,
+ "Software\\NaturalPoint\\NATURALPOINT\\NPClient Location",
+ 0,
+ NULL,
+ 0,
+ KEY_ALL_ACCESS,
+ NULL,
+ &hkpath,
+ NULL) == ERROR_SUCCESS)
+ {
+ for (int i = 0; dir[i]; i++)
+ if (dir[i] == '\\')
+ dir[i] = '/';
+ strcat(dir, "/");
+ (void) RegSetValueExA(hkpath, "Path", 0, REG_SZ, (BYTE*) dir, strlen(dir) + 1);
+ RegCloseKey(hkpath);
+ }
+ }
+}
diff --git a/ftnoir_protocol_wine/wine-protocol.qrc b/ftnoir_protocol_wine/wine-protocol.qrc
new file mode 100644
index 00000000..af81caea
--- /dev/null
+++ b/ftnoir_protocol_wine/wine-protocol.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/wine.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_protocol_wine/wine-shm.h b/ftnoir_protocol_wine/wine-shm.h
new file mode 100644
index 00000000..ddbda8b5
--- /dev/null
+++ b/ftnoir_protocol_wine/wine-shm.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#define WINE_SHM_NAME "facetracknoir-wine-shm"
+#define WINE_MTX_NAME "facetracknoir-wine-mtx"
+
+struct WineSHM {
+ double data[6];
+ int gameid, gameid2;
+ unsigned char table[8];
+ bool stop;
+};
diff --git a/ftnoir_tracker_aruco/ar_video_widget.cpp b/ftnoir_tracker_aruco/ar_video_widget.cpp
new file mode 100644
index 00000000..9a089213
--- /dev/null
+++ b/ftnoir_tracker_aruco/ar_video_widget.cpp
@@ -0,0 +1,42 @@
+/* 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.
+ */
+
+#include "ar_video_widget.h"
+
+#include <QDebug>
+
+using namespace std;
+
+void ArucoVideoWidget::update_image(const cv::Mat& frame)
+{
+ QMutexLocker foo(&mtx);
+ _frame = frame.clone();
+}
+
+void ArucoVideoWidget::update_and_repaint()
+{
+ QMutexLocker foo(&mtx);
+ if (_frame.cols*_frame.rows <= 0)
+ return;
+ QImage qframe = QImage(_frame.cols, _frame.rows, QImage::Format_RGB888);
+ uchar* data = qframe.bits();
+ const int pitch = qframe.bytesPerLine();
+ for (int y = 0; y < _frame.rows; y++)
+ {
+ for (int x = 0; x < _frame.cols; x++)
+ {
+ const auto& elt = _frame.at<cv::Vec3b>(y, x);
+ const cv::Scalar elt2 = static_cast<cv::Scalar>(elt);
+ data[y * pitch + x * 3 + 0] = elt2.val[2];
+ data[y * pitch + x * 3 + 1] = elt2.val[1];
+ data[y * pitch + x * 3 + 2] = elt2.val[0];
+ }
+ }
+ auto qframe2 = qframe.scaled(size(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
+ texture = qframe2;
+ update();
+}
diff --git a/ftnoir_tracker_aruco/ar_video_widget.h b/ftnoir_tracker_aruco/ar_video_widget.h
new file mode 100644
index 00000000..e2cf4d9f
--- /dev/null
+++ b/ftnoir_tracker_aruco/ar_video_widget.h
@@ -0,0 +1,47 @@
+/* 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 VIDEOWIDGET_H
+#define VIDEOWIDGET_H
+
+#include <QTimer>
+#include <QWidget>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QLabel>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QTimer>
+#include <opencv/cv.hpp>
+
+// ----------------------------------------------------------------------------
+class ArucoVideoWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ArucoVideoWidget(QWidget *parent) : QWidget(parent) {
+ connect(&timer, SIGNAL(timeout()), this, SLOT(update_and_repaint()));
+ timer.start(60);
+ }
+ void update_image(const cv::Mat& frame);
+protected slots:
+ void paintEvent( QPaintEvent* e ) {
+ QMutexLocker foo(&mtx);
+ QPainter painter(this);
+ painter.drawImage(e->rect(), texture);
+ }
+ void update_and_repaint();
+
+private:
+ QMutex mtx;
+ QImage texture;
+ QTimer timer;
+ cv::Mat _frame;
+};
+
+#endif // VIDEOWIDGET_H
diff --git a/ftnoir_tracker_aruco/aruco-tracker.qrc b/ftnoir_tracker_aruco/aruco-tracker.qrc
new file mode 100644
index 00000000..70859876
--- /dev/null
+++ b/ftnoir_tracker_aruco/aruco-tracker.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/aruco.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_tracker_aruco/aruco-trackercontrols.ui b/ftnoir_tracker_aruco/aruco-trackercontrols.ui
new file mode 100644
index 00000000..1d5a4241
--- /dev/null
+++ b/ftnoir_tracker_aruco/aruco-trackercontrols.ui
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>615</width>
+ <height>326</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>6666</width>
+ <height>6666</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>HT tracker settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <item row="9" column="1" rowspan="3">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Head position</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="cx">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <double>-10000.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>10000.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDoubleSpinBox" name="cz">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <double>-10000.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>10000.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="cy">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <double>-10000.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>10000.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="3" rowspan="3" colspan="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Enable axes</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="horizontalSpacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="rx">
+ <property name="text">
+ <string>RX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="tx">
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="ry">
+ <property name="text">
+ <string>RY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="ty">
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="rz">
+ <property name="text">
+ <string>RZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="tz">
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Frames per second</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <widget class="QComboBox" name="cameraName"/>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QComboBox" name="cameraFPS">
+ <item>
+ <property name="text">
+ <string notr="true">Default</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>60</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>120</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>180</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QCheckBox" name="red_only">
+ <property name="text">
+ <string>Recommended!</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Camera name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <widget class="QDoubleSpinBox" name="cameraFOV">
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="minimum">
+ <double>35.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>52.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2" rowspan="5" colspan="3">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The ARUCO Library has been developed by the Ava group of the Univeristy of Cordoba, Spain&lt;/p&gt;&lt;p&gt;Rafael Muñoz Salinas &amp;lt;&lt;a href=&quot;mailto:rmsalinas@uco.es&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;rmsalinas@uco.es&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;&lt;a href=&quot;https://github.com/rmsalinas/aruco&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;https://github.com/rmsalinas/aruco&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Red channel only</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QComboBox" name="resolution">
+ <item>
+ <property name="text">
+ <string>640x480</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320x240</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320x200</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Default (not recommended!)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Horizontal FOV</string>
+ </property>
+ </widget>
+ </item>
+ <item row="11" column="4">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QDoubleSpinBox" name="marker_pitch">
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Marker pitch</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>cameraFOV</tabstop>
+ <tabstop>cameraFPS</tabstop>
+ <tabstop>cameraName</tabstop>
+ <tabstop>resolution</tabstop>
+ <tabstop>red_only</tabstop>
+ <tabstop>marker_pitch</tabstop>
+ <tabstop>cx</tabstop>
+ <tabstop>cy</tabstop>
+ <tabstop>cz</tabstop>
+ <tabstop>rx</tabstop>
+ <tabstop>ry</tabstop>
+ <tabstop>rz</tabstop>
+ <tabstop>tx</tabstop>
+ <tabstop>ty</tabstop>
+ <tabstop>tz</tabstop>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <designerdata>
+ <property name="gridDeltaX">
+ <number>10</number>
+ </property>
+ <property name="gridDeltaY">
+ <number>10</number>
+ </property>
+ <property name="gridSnapX">
+ <bool>false</bool>
+ </property>
+ <property name="gridSnapY">
+ <bool>false</bool>
+ </property>
+ <property name="gridVisible">
+ <bool>true</bool>
+ </property>
+ </designerdata>
+</ui>
diff --git a/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp
new file mode 100644
index 00000000..ae7ca0b5
--- /dev/null
+++ b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp
@@ -0,0 +1,483 @@
+/* Copyright (c) 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.
+ */
+
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ftnoir_tracker_aruco.h"
+#include "ui_aruco-trackercontrols.h"
+#include "facetracknoir/global-settings.h"
+#include <cmath>
+#include <QMutexLocker>
+#include <aruco.h>
+#include <opencv2/opencv.hpp>
+#include <opencv/highgui.h>
+#include <vector>
+#include <cstdio>
+
+#if defined(_WIN32)
+# undef NOMINMAX
+# define NOMINMAX
+# define NO_DSHOW_STRSAFE
+# include <dshow.h>
+#else
+# include <unistd.h>
+#endif
+
+// delicious copypasta
+static QList<QString> get_camera_names(void) {
+ QList<QString> ret;
+#if defined(_WIN32)
+ // 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))
+ {
+ return ret;
+ }
+ // Obtain a class enumerator for the video compressor category.
+ IEnumMoniker *pEnumCat = NULL;
+ hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+ if (hr == S_OK) {
+ // Enumerate the monikers.
+ IMoniker *pMoniker = NULL;
+ ULONG cFetched;
+ while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
+ // To retrieve the filter's friendly name, do the following:
+ VARIANT varName;
+ VariantInit(&varName);
+ hr = pPropBag->Read(L"FriendlyName", &varName, 0);
+ if (SUCCEEDED(hr))
+ {
+ // Display the name in your UI somehow.
+ QString str((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
+ ret.append(str);
+ }
+ VariantClear(&varName);
+
+ ////// To create an instance of the filter, do the following:
+ ////IBaseFilter *pFilter;
+ ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
+ //// (void**)&pFilter);
+ // Now add the filter to the graph.
+ //Remember to release pFilter later.
+ 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) {
+ ret.append(buf);
+ } else {
+ continue;
+ }
+ }
+#endif
+ return ret;
+}
+
+typedef struct {
+ int width;
+ int height;
+} resolution_tuple;
+
+static resolution_tuple resolution_choices[] = {
+ { 640, 480 },
+ { 320, 240 },
+ { 320, 200 },
+ { 0, 0 }
+};
+
+Tracker::Tracker() : stop(false), layout(nullptr), videoWidget(nullptr)
+{
+}
+
+Tracker::~Tracker()
+{
+ stop = true;
+ wait();
+ if (videoWidget)
+ delete videoWidget;
+ if(layout)
+ delete layout;
+ qDebug() << "releasing camera, brace for impact";
+ camera.release();
+ qDebug() << "all done!";
+}
+
+void Tracker::StartTracker(QFrame* videoframe)
+{
+ videoframe->show();
+ videoWidget = new ArucoVideoWidget(videoframe);
+ QHBoxLayout* layout = new QHBoxLayout();
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(videoWidget);
+ if (videoframe->layout())
+ delete videoframe->layout();
+ videoframe->setLayout(layout);
+ videoWidget->show();
+ start();
+ for (int i = 0; i < 6; i++)
+ pose[i] = 0;
+ this->layout = layout;
+}
+
+#define HT_PI 3.1415926535
+
+void Tracker::run()
+{
+ int res = s.resolution;
+ if (res < 0 || res >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple)))
+ res = 0;
+ resolution_tuple r = resolution_choices[res];
+ int fps;
+ switch (static_cast<int>(s.force_fps))
+ {
+ default:
+ case 0:
+ fps = 0;
+ break;
+ case 1:
+ fps = 30;
+ break;
+ case 2:
+ fps = 60;
+ break;
+ case 3:
+ fps = 120;
+ break;
+ case 4:
+ fps = 180;
+ break;
+ }
+ camera = cv::VideoCapture(s.camera_index);
+ if (r.width)
+ {
+ camera.set(CV_CAP_PROP_FRAME_WIDTH, r.width);
+ camera.set(CV_CAP_PROP_FRAME_HEIGHT, r.height);
+ }
+ if (fps)
+ camera.set(CV_CAP_PROP_FPS, fps);
+
+ aruco::MarkerDetector detector;
+ detector.setDesiredSpeed(3);
+
+ cv::Rect last_roi(65535, 65535, 0, 0);
+
+ cv::Mat color, color_, grayscale, rvec, tvec;
+
+ const double stateful_coeff = 0.88;
+
+ if (!camera.isOpened())
+ {
+ fprintf(stderr, "aruco tracker: can't open camera\n");
+ return;
+ }
+
+ auto freq = cv::getTickFrequency();
+ auto last_time = cv::getTickCount();
+ int cur_fps = 0;
+ int last_fps = 0;
+ cv::Point2f last_centroid;
+ bool first = true;
+
+ while (!stop)
+ {
+ if (!camera.read(color_))
+ continue;
+ auto tm = cv::getTickCount();
+ color_.copyTo(color);
+ if (s.red_only)
+ {
+ cv::Mat channel[3];
+ cv::split(color, channel);
+ grayscale = channel[2];
+ } else
+ cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY);
+
+ const int scale = frame.cols > 480 ? 2 : 1;
+ detector.setThresholdParams(scale > 1 ? 11 : 7, 4);
+
+ const float focal_length_w = 0.5 * grayscale.cols / tan(0.5 * s.fov * HT_PI / 180);
+ const float focal_length_h = 0.5 * grayscale.rows / tan(0.5 * s.fov * grayscale.rows / grayscale.cols * HT_PI / 180.0);
+ cv::Mat intrinsics = cv::Mat::eye(3, 3, CV_32FC1);
+ intrinsics.at<float> (0, 0) = focal_length_w;
+ intrinsics.at<float> (1, 1) = focal_length_h;
+ intrinsics.at<float> (0, 2) = grayscale.cols/2;
+ intrinsics.at<float> (1, 2) = grayscale.rows/2;
+
+ cv::Mat dist_coeffs = cv::Mat::zeros(5, 1, CV_32FC1);
+
+ std::vector< aruco::Marker > markers;
+
+ const double size_min = 0.04;
+ const double size_max = 0.38;
+
+ if (last_roi.width > 0 &&
+ (detector.detect(grayscale(last_roi), markers, cv::Mat(), cv::Mat(), -1, false),
+ markers.size() == 1 && markers[0].size() == 4))
+ {
+ detector.setMinMaxSize(std::max(0.01, size_min * grayscale.cols / last_roi.width),
+ std::min(1.0, size_max * grayscale.cols / last_roi.width));
+ auto& m = markers.at(0);
+ for (int i = 0; i < 4; i++)
+ {
+ auto& p = m.at(i);
+ p.x += last_roi.x;
+ p.y += last_roi.y;
+ }
+ }
+ else
+ {
+ detector.setMinMaxSize(size_min, size_max);
+ detector.detect(grayscale, markers, cv::Mat(), cv::Mat(), -1, false);
+ }
+
+ if (markers.size() == 1 && markers[0].size() == 4) {
+ const auto& m = markers.at(0);
+ for (int i = 0; i < 4; i++)
+ cv::line(color, m[i], m[(i+1)%4], cv::Scalar(0, 0, 255), scale, 8);
+ }
+
+ auto time = cv::getTickCount();
+
+ if ((long) (time / freq) != (long) (last_time / freq))
+ {
+ last_fps = cur_fps;
+ cur_fps = 0;
+ last_time = time;
+ }
+
+ cur_fps++;
+
+ char buf[128];
+
+ frame = color.clone();
+
+ ::sprintf(buf, "Hz: %d", last_fps);
+ cv::putText(frame, buf, cv::Point(10, 32), cv::FONT_HERSHEY_PLAIN, scale, cv::Scalar(0, 255, 0), scale);
+ ::sprintf(buf, "Jiffies: %ld", (long) (10000 * (time - tm) / freq));
+ cv::putText(frame, buf, cv::Point(10, 54), cv::FONT_HERSHEY_PLAIN, scale, cv::Scalar(80, 255, 0), scale);
+
+ if (markers.size() == 1 && markers[0].size() == 4) {
+ const auto& m = markers.at(0);
+ const float size = 40;
+
+ const double p = s.marker_pitch;
+ const double sq = sin(p * HT_PI / 180);
+ const double cq = cos(p * HT_PI / 180);
+
+ cv::Mat obj_points(4,3,CV_32FC1);
+ obj_points.at<float>(1,0)=-size + s.headpos_x;
+ obj_points.at<float>(1,1)=-size * cq + s.headpos_y;
+ obj_points.at<float>(1,2)=-size * sq + s.headpos_z;
+ obj_points.at<float>(2,0)=size + s.headpos_x;
+ obj_points.at<float>(2,1)=-size * cq + s.headpos_y;
+ obj_points.at<float>(2,2)=-size * sq + s.headpos_z;
+ obj_points.at<float>(3,0)=size + s.headpos_x;
+ obj_points.at<float>(3,1)=size * cq + s.headpos_y;
+ obj_points.at<float>(3,2)=size * sq + s.headpos_z;
+ obj_points.at<float>(0,0)=-size + s.headpos_x;
+ obj_points.at<float>(0,1)=size * cq + s.headpos_y;
+ obj_points.at<float>(0,2)=size * sq + s.headpos_z;
+
+ last_roi = cv::Rect(65535, 65535, 0, 0);
+
+ for (int i = 0; i < 4; i++)
+ {
+ auto foo = m.at(i);
+ last_roi.x = std::min<int>(foo.x, last_roi.x);
+ last_roi.y = std::min<int>(foo.y, last_roi.y);
+ last_roi.width = std::max<int>(foo.x, last_roi.width);
+ last_roi.height = std::max<int>(foo.y, last_roi.height);
+ }
+ {
+ last_roi.width -= last_roi.x;
+ last_roi.height -= last_roi.y;
+ last_roi.x -= last_roi.width * stateful_coeff;
+ last_roi.y -= last_roi.height * stateful_coeff;
+ last_roi.width *= stateful_coeff * 3;
+ last_roi.height *= stateful_coeff * 3;
+ last_roi.x = std::max<int>(0, last_roi.x);
+ last_roi.y = std::max<int>(0, last_roi.y);
+ last_roi.width = std::min<int>(grayscale.cols - last_roi.x, last_roi.width);
+ last_roi.height = std::min<int>(grayscale.rows - last_roi.y, last_roi.height);
+ }
+
+ cv::solvePnP(obj_points, m, intrinsics, dist_coeffs, rvec, tvec, !first, cv::ITERATIVE);
+ first = false;
+ cv::Mat rotation_matrix = cv::Mat::zeros(3, 3, CV_64FC1);
+ cv::Mat junk1(3, 3, CV_64FC1), junk2(3, 3, CV_64FC1);
+ cv::Rodrigues(rvec, rotation_matrix);
+
+ {
+ cv::Vec3d euler = cv::RQDecomp3x3(rotation_matrix, junk1, junk2);
+
+ if (fabs(euler[0]) + fabs(s.marker_pitch) > 60)
+ {
+ first = true;
+ qDebug() << "reset levmarq due to pitch breakage";
+ }
+
+ QMutexLocker lck(&mtx);
+
+ for (int i = 0; i < 3; i++)
+ pose[i] = tvec.at<double>(i);
+
+ pose[Yaw] = euler[1];
+ pose[Pitch] = -euler[0];
+ pose[Roll] = euler[2];
+ }
+
+ std::vector<cv::Point2f> repr2;
+ std::vector<cv::Point3f> centroid;
+ centroid.push_back(cv::Point3f(0, 0, 0));
+ cv::projectPoints(centroid, rvec, tvec, intrinsics, dist_coeffs, repr2);
+
+ {
+ auto s = cv::Scalar(255, 0, 255);
+ cv::circle(frame, repr2.at(0), 4, s, -1);
+ }
+
+ last_centroid = repr2[0];
+ }
+ else
+ {
+ last_roi = cv::Rect(65535, 65535, 0, 0);
+ first = true;
+ }
+
+ if (frame.rows > 0)
+ videoWidget->update_image(frame);
+ }
+}
+
+void Tracker::GetHeadPoseData(double *data)
+{
+ QMutexLocker lck(&mtx);
+
+ if (s.eyaw)
+ data[Yaw] = pose[Yaw];
+ if (s.epitch)
+ data[Pitch] = pose[Pitch];
+ if (s.eroll)
+ data[Roll] = pose[Roll];
+ if (s.ex)
+ data[TX] = pose[TX] * .1;
+ if (s.ey)
+ data[TY] = pose[TY] * .1;
+ if (s.ez)
+ data[TZ] = pose[TZ] * .1;
+}
+
+class TrackerDll : public Metadata
+{
+ // ITrackerDll interface
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
+
+//-----------------------------------------------------------------------------
+void TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "aruco";
+}
+
+void TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "aruco";
+}
+
+void TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "";
+}
+
+void TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/aruco.png");
+}
+
+
+//-----------------------------------------------------------------------------
+//#pragma comment(linker, "/export:GetTrackerDll=_GetTrackerDll@0")
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new TrackerDll;
+}
+
+//#pragma comment(linker, "/export:GetTracker=_GetTracker@0")
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new Tracker;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory function that creates instances if the Tracker-settings dialog object.
+
+// Export both decorated and undecorated names.
+// GetTrackerDialog - Undecorated name, which can be easily used with GetProcAddress
+// Win32 API function.
+// _GetTrackerDialog@0 - Common name decoration for __stdcall functions in C language.
+//#pragma comment(linker, "/export:GetTrackerDialog=_GetTrackerDialog@0")
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
+
+TrackerControls::TrackerControls()
+{
+ tracker = nullptr;
+ ui.setupUi(this);
+ setAttribute(Qt::WA_NativeWindow, true);
+ tie_setting(s.camera_index, ui.cameraName);
+ tie_setting(s.resolution, ui.resolution);
+ tie_setting(s.force_fps, ui.cameraFPS);
+ tie_setting(s.fov, ui.cameraFOV);
+ tie_setting(s.eyaw, ui.rx);
+ tie_setting(s.epitch, ui.ry);
+ tie_setting(s.eroll, ui.rz);
+ tie_setting(s.ex, ui.tx);
+ tie_setting(s.ey, ui.ty);
+ tie_setting(s.ez, ui.tz);
+ tie_setting(s.headpos_x, ui.cx);
+ tie_setting(s.headpos_y, ui.cy);
+ tie_setting(s.headpos_z, ui.cz);
+ tie_setting(s.red_only, ui.red_only);
+ tie_setting(s.marker_pitch, ui.marker_pitch);
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ ui.cameraName->addItems(get_camera_names());
+}
+
+void TrackerControls::doOK()
+{
+ s.b->save();
+ if (tracker)
+ tracker->reload();
+ this->close();
+}
+
+void TrackerControls::doCancel()
+{
+ s.b->revert();
+ this->close();
+}
diff --git a/ftnoir_tracker_aruco/ftnoir_tracker_aruco.h b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.h
new file mode 100644
index 00000000..4cab84b5
--- /dev/null
+++ b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.h
@@ -0,0 +1,94 @@
+/* Copyright (c) 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.
+ */
+
+#ifndef FTNOIR_TRACKER_HT_H
+#define FTNOIR_TRACKER_HT_H
+
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_aruco-trackercontrols.h"
+#include "ar_video_widget.h"
+#include <QObject>
+#include <QThread>
+#include <QMutex>
+#include <QHBoxLayout>
+#include <QDialog>
+#include <opencv2/opencv.hpp>
+#include <opencv/highgui.h>
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<double> fov, headpos_x, headpos_y, headpos_z;
+ value<int> camera_index, force_fps, resolution;
+ value<bool> red_only;
+ value<bool> eyaw, epitch, eroll, ex, ey, ez;
+ value<double> marker_pitch;
+ settings() :
+ b(bundle("aruco-tracker")),
+ fov(b, "field-of-view", 56),
+ headpos_x(b, "headpos-x", 0),
+ headpos_y(b, "headpos-y", 0),
+ headpos_z(b, "headpos-z", 0),
+ camera_index(b, "camera-index", 0),
+ force_fps(b, "force-fps", 0),
+ resolution(b, "force-resolution", 0),
+ red_only(b, "red-only", false),
+ eyaw(b, "enable-y", true),
+ epitch(b, "enable-p", true),
+ eroll(b, "enable-r", true),
+ ex(b, "enable-x", true),
+ ey(b, "enable-y", true),
+ ez(b, "enable-z", true),
+ marker_pitch(b, "marker-pitch", 0)
+ {}
+};
+
+class Tracker : protected QThread, public ITracker
+{
+ Q_OBJECT
+public:
+ Tracker();
+ virtual ~Tracker();
+ void StartTracker(QFrame* frame);
+ void GetHeadPoseData(double *data);
+ void run();
+ void reload() { s.b->reload(); }
+private:
+ QMutex mtx;
+ volatile bool stop;
+ QHBoxLayout* layout;
+ ArucoVideoWidget* videoWidget;
+ settings s;
+ double pose[6];
+ cv::Mat frame;
+ cv::VideoCapture camera;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class TrackerControls : public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ TrackerControls();
+ void registerTracker(ITracker * x) {
+ tracker = dynamic_cast<Tracker*>(x);
+ }
+ void unRegisterTracker() {
+ tracker = nullptr;
+ }
+private:
+ Ui::Form ui;
+ Tracker* tracker;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+#endif
+
diff --git a/ftnoir_tracker_aruco/ftnoir_tracker_aruco_dll.h b/ftnoir_tracker_aruco/ftnoir_tracker_aruco_dll.h
new file mode 100644
index 00000000..ffdc5262
--- /dev/null
+++ b/ftnoir_tracker_aruco/ftnoir_tracker_aruco_dll.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "facetracknoir/global-settings.h"
+
+//-----------------------------------------------------------------------------
+class TrackerDll : public Metadata
+{
+ // ITrackerDll interface
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
diff --git a/ftnoir_tracker_aruco/images/aruco.png b/ftnoir_tracker_aruco/images/aruco.png
new file mode 100644
index 00000000..491c39e6
--- /dev/null
+++ b/ftnoir_tracker_aruco/images/aruco.png
Binary files differ
diff --git a/ftnoir_tracker_aruco/include/aruco.h b/ftnoir_tracker_aruco/include/aruco.h
new file mode 100644
index 00000000..569b95fb
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/aruco.h
@@ -0,0 +1,134 @@
+/**
+
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+
+
+
+ \mainpage ArUco: Augmented Reality library from the University of Cordoba
+
+
+ArUco is a minimal C++ library for detection of Augmented Reality markers based on OpenCv exclusively.
+
+It is an educational project to show student how to detect augmented reality markers and it is provided under BSD license.
+
+
+\section INTRODUCTION INTRODUCTION
+
+The library relies on the use of coded markers. Each marker has an unique code indicated by the black and white colors in it. The libary detect borders, and analyzes into the rectangular regions which of them are likely to be markers. Then, a decoding is performed and if the code is valid, it is considered that the rectangle is a marker.
+
+The codification included into the marker is a slighly modified version of the Hamming Code. It has a total a 25 bits didived in 5 rows of 5 bits each. So, we have 5 words of 5 bits. Each word, contains only 2 bits of real information, the rest is for and error detection/correction (error correction is yet to be done). As a conclusion, a marker contains 10 bits of real information wich allows 1024 different markers.
+
+
+\section BOARDS BOARDS
+
+Aruco allows the possibility to employ board. Boards are markers composed by an array of markers arranged in a known order. The advantages of using boards instead of simple markers are:
+ - More robusteness. The misdetection of several markers of the board is not a problem as long as a minimum set of them are detected.
+ - More precision. Since there are a larger number of corners, camera pose estimation becomes more precise.
+
+
+\section APPLICATIONS APPLICATIONS
+
+The library comes with five applications that will help you to learn how to use the library:
+ - aruco_create_marker: which creates marker and saves it in a jpg file you can print.
+ - aruco_simple : simple test aplication that detects the markers in a image
+ - aruco_test: this is the main application for detection. It reads images either from the camera of from a video and detect markers. Additionally, if you provide the intrinsics of the camera(obtained by OpenCv calibration) and the size of the marker in meters, the library calculates the marker intrinsics so that you can easily create your AR applications.
+ - aruco_test_gl: shows how to use the library AR applications using OpenGL for rendering
+ - aruco_create_board: application that helps you to create a board
+ - aruco_simple_board: simple test aplication that detects a board of markers in a image
+ - aruco_test_board: application that detects boards
+ - aruco_test_board_gl: application that detects boards and uses OpenGL to draw
+
+\section LIBRARY LIBRARY DESCRIPTION:
+
+The ArUco library contents are divided in two main directories. The src directory, which contains the library itself. And the utils directory which contains the applications.
+
+The library main classes are:
+ - aruco::CameraParameters: represent the information of the camera that captures the images. Here you must set the calibration info.
+ - aruco::Marker: which represent a marker detected in the image
+ - aruco::MarkerDetector: that is in charge of deteting the markers in a image Detection is done by simple calling the member funcion ArMarkerDetector::detect(). Additionally, the classes contain members to create the required matrices for rendering using OpenGL. See aruco_test_gl for details
+ - aruco::BoardConfiguration: A board is an array of markers in a known order. BoardConfiguracion is the class that defines a board by indicating the id of its markers. In addition, it has informacion about the distance between the markers so that extrinsica camera computations can be done.
+ - aruco::Board: This class defines a board detected in a image. The board has the extrinsic camera parameters as public atributes. In addition, it has a method that allows obtain the matrix for getting its position in OpenGL (see aruco_test_board_gl for details).
+ - aruco::BoardDetector : This is the class in charge of detecting a board in a image. You must pass to it the set of markers detected by ArMarkerDetector and the BoardConfiguracion of the board you want to detect. This class will do the rest for you, even calculating the camera extrinsics.
+
+
+\section COMPILING COMPILING THE LIBRARY:
+\subsection Linux
+Go to the aruco library and do
+\verbatim
+>mkdir build
+>cd build
+>cmake ..
+>make
+>make install (optional)
+\endverbatim
+
+NOTE ON OPENGL: The library supports eaily the integration with OpenGL. In order to compile with support for OpenGL, you just have installed in your system the develop packages for GL and glut (or freeglut).
+
+\subsection WINDOWS
+
+The library has been compiled using MinGW and codeblocks. Below I describe the best way to compile it that I know. If you know better, please let me know.
+ - step 1) codeblocks
+ -# Download codeblocks. I recommend to download the version 10.5 with mingw included (codeblocks-10.05mingw-setup.exe)
+ -# Install and set the PATH variable so that the codeblock/mingw/bin directory is included. In my case c:/codeblocks/mingw/bin. This will allow cmake to find the compiler.
+ -# The codeblock program will not find the mingw path by deafult. So, run codeblocks and go to setting->Compuiler debugger and set the correct path to the MinGW dir.
+ - step 2) cmake
+ -# Download and install the last version of cmake.
+ - step 3) OpenCv
+ -# Download the source code and compile it using cmake and codeblocks. Note: install the library in C:\ if you want it to be easily detected by cmake afterwards
+ - step 4) aruco
+ -# Download and decompress.
+ -# Open cmake gui application and set the path to the main library directory and also set a path where the project is going to be built.
+ -# Generate the codeblock project.
+ -# Open the project with codeblock and compile then, install. The programs will be probably generated into the bin directory
+
+OpenGL: by default, the mingw version installed has not the glut library. So, the opengl programs are not compiled. If you want to compile with OpenGL support, you must install glut, or prefereably freeglut.
+Thus,
+ - Download the library (http://www.martinpayne.me.uk/software/development/GLUT/freeglut-MinGW.zip) for mingw.
+ - Decompress in a directory X.
+ - Then, rerun cmake setting the variable GLU_PATH to that directory (>cmake .. -DGLUT_PATH="C:\X")
+ - Finally, recompile and test. Indeed, you should move the freeglut.dll to the directory with the binaries or to any other place in the PATH.
+
+
+CONCLUSION: Move to Linux, things are simpler :P
+
+
+\section Testing
+
+For testing the applications, the library provides videos and the corresponding camera parameters of these videos. Into the directories you will find information on how to run the examples.
+
+\section Final Notes
+
+ - REQUIREMENTS: OpenCv >= 2.1.0. and OpenGL for (aruco_test_gl and aruco_test_board_gl)
+ - CONTACT: Rafael Munoz-Salinas: rmsalinas@uco.es
+ - This libary is free software and come with no guaratee!
+
+*/
+
+#include "markerdetector.h"
+#include "boarddetector.h"
+#include "cvdrawingutils.h"
+
diff --git a/ftnoir_tracker_aruco/include/arucofidmarkers.h b/ftnoir_tracker_aruco/include/arucofidmarkers.h
new file mode 100644
index 00000000..7dad4672
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/arucofidmarkers.h
@@ -0,0 +1,119 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+
+#ifndef ArucoFiducicalMarkerDetector_H
+#define ArucoFiducicalMarkerDetector_H
+#include <opencv2/core/core.hpp>
+#include "exports.h"
+#include "marker.h"
+#include "board.h"
+namespace aruco {
+
+class ARUCO_EXPORTS FiducidalMarkers {
+public:
+ /**
+ * \brief Creates an ar marker with the id specified using a modified version of the hamming code.
+ * There are two type of markers: a) These of 10 bits b) these of 3 bits. The latter are employed for applications
+ * that need few marker but they must be small. The two type of markers are distinguished by their ids. While the first type
+ * of markers have ids in the interval [0-1023], the second type ids in the interval [2000-2006].
+ *
+ *
+ * 10 bits markers
+ * -----------------------
+ * There are a total of 5 rows of 5 cols. Each row encodes a total of 2 bits, so there are 2^10 bits:(0-1023).
+ *
+ * The least significative bytes are first (from left-up to to right-bottom)
+ *
+ * Example: the id = 110 (decimal) is be represented in binary as : 00 01 10 11 10.
+ *
+ * Then, it will generate the following marker:
+ *
+ * -# 1st row encodes 00: 1 0 0 0 0 : hex 0x10
+ * -# 2nd row encodes 01: 1 0 1 1 1 : hex 0x17
+ * -# 3nd row encodes 10: 0 1 0 0 1 : hex 0x09
+ * -# 4th row encodes 11: 0 1 1 1 0 : hex 0x0e
+ * -# 5th row encodes 10: 0 1 0 0 1 : hex 0x09
+ *
+ * Note that : The first bit, is the inverse of the hamming parity. This avoids the 0 0 0 0 0 to be valid
+ * These marker are detected by the function getFiduciadlMarker_Aruco_Type1
+ */
+ static cv::Mat createMarkerImage(int id,int size) throw (cv::Exception);
+
+ /** Detection of fiducidal aruco markers (10 bits)
+ * @param in input image with the patch that contains the possible marker
+ * @param nRotations number of 90deg rotations in clockwise direction needed to set the marker in correct position
+ * @return -1 if the image passed is a not a valid marker, and its id in case it really is a marker
+ */
+ static int detect(const cv::Mat &in,int &nRotations);
+
+ /**Similar to createMarkerImage. Instead of returning a visible image, returns a 8UC1 matrix of 0s and 1s with the marker info
+ */
+ static cv::Mat getMarkerMat(int id) throw (cv::Exception);
+
+
+ /**Creates a printable image of a board
+ * @param gridSize grid layout (numer of sqaures in x and Y)
+ * @param MarkerSize size of markers sides in pixels
+ * @param MarkerDistance distance between the markers
+ * @param TInfo output
+ * @param excludedIds set of ids excluded from the board
+ */
+ static cv::Mat createBoardImage( cv::Size gridSize,int MarkerSize,int MarkerDistance, BoardConfiguration& TInfo ,vector<int> *excludedIds=NULL ) throw (cv::Exception);
+
+
+ /**Creates a printable image of a board in chessboard_like manner
+ * @param gridSize grid layout (numer of sqaures in x and Y)
+ * @param MarkerSize size of markers sides in pixels
+ * @param TInfo output
+ * @param setDataCentered indicates if the center is set at the center of the board. Otherwise it is the left-upper corner
+ *
+ */
+ static cv::Mat createBoardImage_ChessBoard( cv::Size gridSize,int MarkerSize, BoardConfiguration& TInfo ,bool setDataCentered=true ,vector<int> *excludedIds=NULL) throw (cv::Exception);
+
+ /**Creates a printable image of a board in a frame fashion
+ * @param gridSize grid layout (numer of sqaures in x and Y)
+ * @param MarkerSize size of markers sides in pixels
+ * @param MarkerDistance distance between the markers
+ * @param TInfo output
+ * @param setDataCentered indicates if the center is set at the center of the board. Otherwise it is the left-upper corner
+ *
+ */
+ static cv::Mat createBoardImage_Frame( cv::Size gridSize,int MarkerSize,int MarkerDistance, BoardConfiguration& TInfo ,bool setDataCentered=true,vector<int> *excludedIds=NULL ) throw (cv::Exception);
+
+private:
+
+ static vector<int> getListOfValidMarkersIds_random(int nMarkers,vector<int> *excluded) throw (cv::Exception);
+ static cv::Mat rotate(const cv::Mat & in);
+ static int hammDistMarker(cv::Mat bits);
+ static int analyzeMarkerImage(cv::Mat &grey,int &nRotations);
+ static bool correctHammMarker(cv::Mat &bits);
+};
+
+}
+
+#endif
diff --git a/ftnoir_tracker_aruco/include/board.h b/ftnoir_tracker_aruco/include/board.h
new file mode 100644
index 00000000..c1d79292
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/board.h
@@ -0,0 +1,168 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _Aruco_board_h
+#define _Aruco_board_h
+#include <opencv2/opencv.hpp>
+#include <string>
+#include <vector>
+#include "exports.h"
+#include "marker.h"
+using namespace std;
+namespace aruco {
+/**
+ * 3d representation of a marker
+ */
+struct ARUCO_EXPORTS MarkerInfo:public vector<cv::Point3f> {
+ MarkerInfo() {}
+ MarkerInfo(int _id) {id=_id; }
+ MarkerInfo(const MarkerInfo&MI): vector<cv::Point3f>(MI){id=MI.id; }
+ MarkerInfo & operator=(const MarkerInfo&MI){
+ vector<cv::Point3f> ::operator=(MI);
+ id=MI.id;
+ return *this;
+ }
+ int id;//maker id
+};
+
+/**\brief This class defines a board with several markers.
+ * A Board contains several markers so that they are more robustly detected.
+ *
+ * In general, a board is a set of markers. So BoardConfiguration is only a list
+ * of the id of the markers along with the position of their corners.
+ *
+ * The position of the corners can be specified either in pixels (in a non-specific size) or in meters.
+ * The first is the typical case in which you generate the image of board and the print it. Since you do not know in advance the real
+ * size of the markers, their corners are specified in pixels, and then, the translation to meters can be made once you know the real size.
+ *
+ * On the other hand, you may want to have the information of your boards in meters. The BoardConfiguration allows you to do so.
+ *
+ * The point is in the mInfoType variable. It can be either PIX or METERS according to your needs.
+ *
+*/
+
+
+class ARUCO_EXPORTS BoardConfiguration: public vector<MarkerInfo>
+{
+ friend class Board;
+public:
+ enum MarkerInfoType {NONE=-1,PIX=0,METERS=1};//indicates if the data in MakersInfo is expressed in meters or in pixels so as to do conversion internally
+ //variable indicates if the data in MakersInfo is expressed in meters or in pixels so as to do conversion internally
+ int mInfoType;
+ /**
+ */
+ BoardConfiguration();
+
+ /**
+ */
+ BoardConfiguration(const BoardConfiguration &T);
+
+ /**
+ */
+ BoardConfiguration & operator=(const BoardConfiguration &T);
+ /**Saves the board info to a file
+ */
+ void saveToFile(string sfile)throw (cv::Exception);
+ /**Reads board info from a file
+ */
+ void readFromFile(string sfile)throw (cv::Exception);
+ /**Indicates if the corners are expressed in meters
+ */
+ bool isExpressedInMeters()const {
+ return mInfoType==METERS;
+ }
+ /**Indicates if the corners are expressed in meters
+ */
+ bool isExpressedInPixels()const {
+ return mInfoType==PIX;
+ }
+ /**Returns the index of the marker with id indicated, if is in the list
+ */
+ int getIndexOfMarkerId(int id)const;
+ /**Returns the Info of the marker with id specified. If not in the set, throws exception
+ */
+ const MarkerInfo& getMarkerInfo(int id)const throw (cv::Exception);
+ /**Set in the list passed the set of the ids
+ */
+ void getIdList(vector<int> &ids,bool append=true)const;
+private:
+ /**Saves the board info to a file
+ */
+ void saveToFile(cv::FileStorage &fs)throw (cv::Exception);
+ /**Reads board info from a file
+ */
+ void readFromFile(cv::FileStorage &fs)throw (cv::Exception);
+};
+
+/**
+*/
+class ARUCO_EXPORTS Board:public vector<Marker>
+{
+
+public:
+ BoardConfiguration conf;
+ //matrices of rotation and translation respect to the camera
+ cv::Mat Rvec,Tvec;
+ /**
+ */
+ Board()
+ {
+ Rvec.create(3,1,CV_32FC1);
+ Tvec.create(3,1,CV_32FC1);
+ for (int i=0;i<3;i++)
+ Tvec.at<float>(i,0)=Rvec.at<float>(i,0)=-999999;
+ }
+
+ /**Given the extrinsic camera parameters returns the GL_MODELVIEW matrix for opengl.
+ * Setting this matrix, the reference corrdinate system will be set in this board
+ */
+ void glGetModelViewMatrix(double modelview_matrix[16])throw(cv::Exception);
+
+ /**
+ * Returns position vector and orientation quaternion for an Ogre scene node or entity.
+ * Use:
+ * ...
+ * Ogre::Vector3 ogrePos (position[0], position[1], position[2]);
+ * Ogre::Quaternion ogreOrient (orientation[0], orientation[1], orientation[2], orientation[3]);
+ * mySceneNode->setPosition( ogrePos );
+ * mySceneNode->setOrientation( ogreOrient );
+ * ...
+ */
+ void OgreGetPoseParameters( double position[3], double orientation[4] )throw(cv::Exception);
+
+
+ /**Save this from a file
+ */
+ void saveToFile(string filePath)throw(cv::Exception);
+ /**Read this from a file
+ */
+ void readFromFile(string filePath)throw(cv::Exception);
+
+};
+}
+
+#endif
diff --git a/ftnoir_tracker_aruco/include/boarddetector.h b/ftnoir_tracker_aruco/include/boarddetector.h
new file mode 100644
index 00000000..4770b5c9
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/boarddetector.h
@@ -0,0 +1,139 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _Aruco_BoardDetector_H
+#define _Aruco_BoardDetector_H
+#include <opencv2/opencv.hpp>
+#include "exports.h"
+#include "board.h"
+#include "cameraparameters.h"
+#include "markerdetector.h"
+using namespace std;
+
+namespace aruco
+{
+
+/**\brief This class detects AR boards
+ * Version 1.2
+ * There are two modes for board detection.
+ * First, the old way. (You first detect markers with MarkerDetector and then call to detect in this class.
+ *
+ * Second: New mode, marker detection is included in the class
+ * \code
+
+ CameraParameters CP;
+ CP.readFromFile(path_cp)
+ BoardConfiguration BC;
+ BC.readFromFile(path_bc);
+ BoardDetector BD;
+ BD.setParams(BC,CP); //or only BD.setParams(BC)
+ //capture image
+ cv::Mat im;
+ capture_image(im);
+
+ float prob=BD.detect(im);
+ if (prob>0.3)
+ CvDrawingUtils::draw3DAxis(im,BD.getDetectedBoard(),CP);
+
+ \endcode
+ *
+*/
+class ARUCO_EXPORTS BoardDetector
+{
+public:
+ /** See discussion in @see enableRotateXAxis.
+ * Do not change unless you know what you are doing
+ */
+ BoardDetector(bool setYPerperdicular=true);
+
+
+ /**
+ * Use if you plan to let this class to perform marker detection too
+ */
+ void setParams(const BoardConfiguration &bc,const CameraParameters &cp, float markerSizeMeters=-1);
+ void setParams(const BoardConfiguration &bc);
+ /**
+ * Detect markers, and then, look for the board indicated in setParams()
+ * @return value indicating the likelihood of having found the marker
+ */
+ float detect(const cv::Mat &im)throw (cv::Exception);
+ /**Returns a reference to the board detected
+ */
+ Board & getDetectedBoard(){return _boardDetected;}
+ /**Returns a reference to the internal marker detector
+ */
+ MarkerDetector &getMarkerDetector(){return _mdetector;}
+ /**Returns the vector of markers detected
+ */
+ vector<Marker> &getDetectedMarkers(){return _vmarkers;}
+
+
+ //ALTERNATIVE DETECTION METHOD, BASED ON MARKERS PREVIOUSLY DETECTED
+
+ /** Given the markers detected, determines if there is the board passed
+ * @param detectedMarkers result provided by aruco::ArMarkerDetector
+ * @param BConf the board you want to see if is present
+ * @param Bdetected output information of the detected board
+ * @param camMatrix camera matrix with intrinsics
+ * @param distCoeff camera distorsion coeff
+ * @param camMatrix intrinsic camera information.
+ * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion
+ * @param markerSizeMeters size of the marker sides expressed in meters
+ * @return value indicating the likelihood of having found the marker
+ */
+ float detect(const vector<Marker> &detectedMarkers,const BoardConfiguration &BConf, Board &Bdetected, cv::Mat camMatrix=cv::Mat(),cv::Mat distCoeff=cv::Mat(), float markerSizeMeters=-1 )throw (cv::Exception);
+ float detect(const vector<Marker> &detectedMarkers,const BoardConfiguration &BConf, Board &Bdetected,const CameraParameters &cp, float markerSizeMeters=-1 )throw (cv::Exception);
+
+
+ /**
+ * By default, the Y axis is set to point up. However this is not the default
+ * operation mode of opencv, which produces the Z axis pointing up instead.
+ * So, to achieve this change, we have to rotate the X axis.
+ */
+ void setYPerperdicular(bool enable){_setYPerperdicular=enable;}
+
+
+
+
+private:
+ void rotateXAxis(cv::Mat &rotation);
+ bool _setYPerperdicular;
+
+ //-- Functionality to detect markers inside
+ bool _areParamsSet;
+ BoardConfiguration _bconf;
+ Board _boardDetected;
+ float _markerSize;
+ CameraParameters _camParams;
+ MarkerDetector _mdetector;//internal markerdetector
+ vector<Marker> _vmarkers;//markers detected in the call to : float detect(const cv::Mat &im);
+
+};
+
+}
+#endif
+
diff --git a/ftnoir_tracker_aruco/include/cameraparameters.h b/ftnoir_tracker_aruco/include/cameraparameters.h
new file mode 100644
index 00000000..c3381a74
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/cameraparameters.h
@@ -0,0 +1,137 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _Aruco_CameraParameters_H
+#define _Aruco_CameraParameters_H
+#include "exports.h"
+#include <opencv2/opencv.hpp>
+#include <string>
+using namespace std;
+namespace aruco
+{
+/**\brief Parameters of the camera
+ */
+
+class ARUCO_EXPORTS CameraParameters
+{
+public:
+
+ // 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1)
+ cv::Mat CameraMatrix;
+ //4x1 matrix (k1,k2,p1,p2)
+ cv::Mat Distorsion;
+ //size of the image
+ cv::Size CamSize;
+
+ /**Empty constructor
+ */
+ CameraParameters() ;
+ /**Creates the object from the info passed
+ * @param cameraMatrix 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1)
+ * @param distorsionCoeff 4x1 matrix (k1,k2,p1,p2)
+ * @param size image size
+ */
+ CameraParameters(cv::Mat cameraMatrix,cv::Mat distorsionCoeff,cv::Size size) throw(cv::Exception);
+ /**Sets the parameters
+ * @param cameraMatrix 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1)
+ * @param distorsionCoeff 4x1 matrix (k1,k2,p1,p2)
+ * @param size image size
+ */
+ void setParams(cv::Mat cameraMatrix,cv::Mat distorsionCoeff,cv::Size size) throw(cv::Exception);
+ /**Copy constructor
+ */
+ CameraParameters(const CameraParameters &CI) ;
+
+ /**Indicates whether this object is valid
+ */
+ bool isValid()const {
+ return CameraMatrix.rows!=0 && CameraMatrix.cols!=0 && Distorsion.rows!=0 && Distorsion.cols!=0 && CamSize.width!=-1 && CamSize.height!=-1;
+ }
+ /**Assign operator
+ */
+ CameraParameters & operator=(const CameraParameters &CI);
+ /**Reads the camera parameters from a file generated using saveToFile.
+ */
+ void readFromFile(string path)throw(cv::Exception);
+ /**Saves this to a file
+ */
+ void saveToFile(string path,bool inXML=true)throw(cv::Exception);
+
+ /**Reads from a YAML file generated with the opencv2.2 calibration utility
+ */
+ void readFromXMLFile(string filePath)throw(cv::Exception);
+
+ /**Adjust the parameters to the size of the image indicated
+ */
+ void resize(cv::Size size)throw(cv::Exception);
+
+ /**Returns the location of the camera in the reference system given by the rotation and translation vectors passed
+ * NOT TESTED
+ */
+ static cv::Point3f getCameraLocation(cv::Mat Rvec,cv::Mat Tvec);
+
+ /**Given the intrinsic camera parameters returns the GL_PROJECTION matrix for opengl.
+ * PLease NOTE that when using OpenGL, it is assumed no camera distorsion! So, if it is not true, you should have
+ * undistor image
+ *
+ * @param orgImgSize size of the original image
+ * @param size of the image/window where to render (can be different from the real camera image). Please not that it must be related to CamMatrix
+ * @param proj_matrix output projection matrix to give to opengl
+ * @param gnear,gfar: visible rendering range
+ * @param invert: indicates if the output projection matrix has to yield a horizontally inverted image because image data has not been stored in the order of glDrawPixels: bottom-to-top.
+ */
+ void glGetProjectionMatrix( cv::Size orgImgSize, cv::Size size,double proj_matrix[16],double gnear,double gfar,bool invert=false )throw(cv::Exception);
+
+ /**
+ * setup camera for an Ogre project.
+ * Use:
+ * ...
+ * Ogre::Matrix4 PM(proj_matrix[0], proj_matrix[1], ... , proj_matrix[15]);
+ * yourCamera->setCustomProjectionMatrix(true, PM);
+ * yourCamera->setCustomViewMatrix(true, Ogre::Matrix4::IDENTITY);
+ * ...
+ * As in OpenGL, it assumes no camera distorsion
+ */
+ void OgreGetProjectionMatrix( cv::Size orgImgSize, cv::Size size,double proj_matrix[16],double gnear,double gfar,bool invert=false )throw(cv::Exception);
+
+
+private:
+ //GL routines
+
+ static void argConvGLcpara2( double cparam[3][4], int width, int height, double gnear, double gfar, double m[16], bool invert )throw(cv::Exception);
+ static int arParamDecompMat( double source[3][4], double cpara[3][4], double trans[3][4] )throw(cv::Exception);
+ static double norm( double a, double b, double c );
+ static double dot( double a1, double a2, double a3,
+ double b1, double b2, double b3 );
+
+
+};
+
+}
+#endif
+
+
diff --git a/ftnoir_tracker_aruco/include/cvdrawingutils.h b/ftnoir_tracker_aruco/include/cvdrawingutils.h
new file mode 100644
index 00000000..38e9986e
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/cvdrawingutils.h
@@ -0,0 +1,52 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _ArUco_DrawUtils_H_
+#define _ArUco_DrawUtils_H_
+#include "exports.h"
+#include "aruco.h"
+namespace aruco
+{
+ /**\brief A set of functions to draw in opencv images
+ */
+ class ARUCO_EXPORTS CvDrawingUtils
+ {
+ public:
+
+ static void draw3dAxis(cv::Mat &Image,Marker &m,const CameraParameters &CP);
+
+ static void draw3dCube(cv::Mat &Image,Marker &m,const CameraParameters &CP);
+
+ static void draw3dAxis(cv::Mat &Image,Board &m,const CameraParameters &CP);
+
+ static void draw3dCube(cv::Mat &Image,Board &m,const CameraParameters &CP);
+
+ };
+}
+
+#endif
+
diff --git a/ftnoir_tracker_aruco/include/exports.h b/ftnoir_tracker_aruco/include/exports.h
new file mode 100644
index 00000000..154605ec
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/exports.h
@@ -0,0 +1,46 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+
+
+
+#ifndef __OPENARUCO_CORE_TYPES_H__
+#define __OPENARUCO_CORE_TYPES_H__
+
+#if !defined _CRT_SECURE_NO_DEPRECATE && _MSC_VER > 1300
+#define _CRT_SECURE_NO_DEPRECATE /* to avoid multiple Visual Studio 2005 warnings */
+#endif
+
+
+#if (defined WIN32 || defined _WIN32 || defined WINCE) && defined DSO_EXPORTS
+ #define ARUCO_EXPORTS __declspec(dllexport)
+#else
+ #define ARUCO_EXPORTS
+#endif
+
+
+#endif
diff --git a/ftnoir_tracker_aruco/include/marker.h b/ftnoir_tracker_aruco/include/marker.h
new file mode 100644
index 00000000..dc6bb28c
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/marker.h
@@ -0,0 +1,143 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _Aruco_Marker_H
+#define _Aruco_Marker_H
+#include <vector>
+#include <iostream>
+#include <opencv2/opencv.hpp>
+#include "exports.h"
+#include "cameraparameters.h"
+using namespace std;
+namespace aruco {
+/**\brief This class represents a marker. It is a vector of the fours corners ot the marker
+ *
+ */
+
+class ARUCO_EXPORTS Marker: public std::vector<cv::Point2f>
+{
+public:
+ //id of the marker
+ int id;
+ //size of the markers sides in meters
+ float ssize;
+ //matrices of rotation and translation respect to the camera
+ cv::Mat Rvec,Tvec;
+
+ /**
+ */
+ Marker();
+ /**
+ */
+ Marker(const Marker &M);
+ /**
+ */
+ Marker(const std::vector<cv::Point2f> &corners,int _id=-1);
+ /**
+ */
+ ~Marker() {}
+ /**Indicates if this object is valid
+ */
+ bool isValid()const{return id!=-1 && size()==4;}
+
+ /**Draws this marker in the input image
+ */
+ void draw(cv::Mat &in, cv::Scalar color, int lineWidth=1,bool writeId=true)const;
+
+ /**Calculates the extrinsics (Rvec and Tvec) of the marker with respect to the camera
+ * @param markerSize size of the marker side expressed in meters
+ * @param CP parmeters of the camera
+ * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z axis
+ */
+ void calculateExtrinsics(float markerSize,const CameraParameters &CP,bool setYPerperdicular=true)throw(cv::Exception);
+ /**Calculates the extrinsics (Rvec and Tvec) of the marker with respect to the camera
+ * @param markerSize size of the marker side expressed in meters
+ * @param CameraMatrix matrix with camera parameters (fx,fy,cx,cy)
+ * @param Distorsion matrix with distorsion parameters (k1,k2,p1,p2)
+ * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z axis
+ */
+ void calculateExtrinsics(float markerSize,cv::Mat CameraMatrix,cv::Mat Distorsion=cv::Mat(),bool setYPerperdicular=true)throw(cv::Exception);
+
+ /**Given the extrinsic camera parameters returns the GL_MODELVIEW matrix for opengl.
+ * Setting this matrix, the reference coordinate system will be set in this marker
+ */
+ void glGetModelViewMatrix( double modelview_matrix[16])throw(cv::Exception);
+
+ /**
+ * Returns position vector and orientation quaternion for an Ogre scene node or entity.
+ * Use:
+ * ...
+ * Ogre::Vector3 ogrePos (position[0], position[1], position[2]);
+ * Ogre::Quaternion ogreOrient (orientation[0], orientation[1], orientation[2], orientation[3]);
+ * mySceneNode->setPosition( ogrePos );
+ * mySceneNode->setOrientation( ogreOrient );
+ * ...
+ */
+ void OgreGetPoseParameters( double position[3], double orientation[4] )throw(cv::Exception);
+
+ /**Returns the centroid of the marker
+ */
+ cv::Point2f getCenter()const;
+ /**Returns the perimeter of the marker
+ */
+ float getPerimeter()const;
+ /**Returns the area
+ */
+ float getArea()const;
+ /**
+ */
+ /**
+ */
+ friend bool operator<(const Marker &M1,const Marker&M2)
+ {
+ return M1.id<M2.id;
+ }
+ /**
+ */
+ friend ostream & operator<<(ostream &str,const Marker &M)
+ {
+ str<<M.id<<"=";
+ for (int i=0;i<4;i++)
+ str<<"("<<M[i].x<< ","<<M[i].y<<") ";
+ str<<"Txyz=";
+ for (int i=0;i<3;i++)
+ str<<M.Tvec.ptr<float>(0)[i]<<" ";
+ str<<"Rxyz=";
+ for (int i=0;i<3;i++)
+ str<<M.Rvec.ptr<float>(0)[i]<<" ";
+
+ return str;
+ }
+
+
+private:
+ void rotateXAxis(cv::Mat &rotation);
+
+};
+
+}
+#endif
diff --git a/ftnoir_tracker_aruco/include/markerdetector.h b/ftnoir_tracker_aruco/include/markerdetector.h
new file mode 100644
index 00000000..4d6e7b90
--- /dev/null
+++ b/ftnoir_tracker_aruco/include/markerdetector.h
@@ -0,0 +1,357 @@
+/*****************************
+Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Rafael Muñoz Salinas.
+********************************/
+#ifndef _ARUCO_MarkerDetector_H
+#define _ARUCO_MarkerDetector_H
+#include <opencv2/opencv.hpp>
+#include <cstdio>
+#include <iostream>
+#include "cameraparameters.h"
+#include "exports.h"
+#include "marker.h"
+using namespace std;
+
+namespace aruco
+{
+
+/**\brief Main class for marker detection
+ *
+ */
+class ARUCO_EXPORTS MarkerDetector
+{
+ //Represent a candidate to be a maker
+ class MarkerCandidate: public Marker{
+ public:
+ MarkerCandidate(){}
+ MarkerCandidate(const Marker &M): Marker(M){}
+ MarkerCandidate(const MarkerCandidate &M): Marker(M){
+ contour=M.contour;
+ idx=M.idx;
+ }
+ MarkerCandidate operator=(const MarkerCandidate &M){
+ if (this == &M)
+ return *this;
+ (*(Marker*)this)=(*(Marker*)&M);
+ contour=M.contour;
+ idx=M.idx;
+ return M;
+ }
+
+ vector<cv::Point> contour;//all the points of its contour
+ int idx;//index position in the global contour list
+ };
+public:
+
+ /**
+ * See
+ */
+ MarkerDetector();
+
+ /**
+ */
+ ~MarkerDetector();
+
+ /**Detects the markers in the image passed
+ *
+ * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of the markers are detected
+ *
+ * @param input input color image
+ * @param detectedMarkers output vector with the markers detected
+ * @param camMatrix intrinsic camera information.
+ * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion
+ * @param markerSizeMeters size of the marker sides expressed in meters
+ * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z axis
+ */
+ void detect(const cv::Mat &input,std::vector<Marker> &detectedMarkers,cv::Mat camMatrix=cv::Mat(),cv::Mat distCoeff=cv::Mat(),float markerSizeMeters=-1,bool setYPerperdicular=true) throw (cv::Exception);
+ /**Detects the markers in the image passed
+ *
+ * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of the markers are detected
+ *
+ * @param input input color image
+ * @param detectedMarkers output vector with the markers detected
+ * @param camParams Camera parameters
+ * @param markerSizeMeters size of the marker sides expressed in meters
+ * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z axis
+ */
+ void detect(const cv::Mat &input,std::vector<Marker> &detectedMarkers, CameraParameters camParams,float markerSizeMeters=-1,bool setYPerperdicular=true) throw (cv::Exception);
+
+ /**This set the type of thresholding methods available
+ */
+
+ enum ThresholdMethods {FIXED_THRES,ADPT_THRES,CANNY};
+
+
+
+ /**Sets the threshold method
+ */
+ void setThresholdMethod(ThresholdMethods m) {
+ _thresMethod=m;
+ }
+ /**Returns the current threshold method
+ */
+ ThresholdMethods getThresholdMethod()const {
+ return _thresMethod;
+ }
+ /**
+ * Set the parameters of the threshold method
+ * We are currently using the Adptive threshold ee opencv doc of adaptiveThreshold for more info
+ * @param param1: blockSize of the pixel neighborhood that is used to calculate a threshold value for the pixel
+ * @param param2: The constant subtracted from the mean or weighted mean
+ */
+ void setThresholdParams(double param1,double param2) {
+ _thresParam1=param1;
+ _thresParam2=param2;
+ }
+ /**
+ * Set the parameters of the threshold method
+ * We are currently using the Adptive threshold ee opencv doc of adaptiveThreshold for more info
+ * param1: blockSize of the pixel neighborhood that is used to calculate a threshold value for the pixel
+ * param2: The constant subtracted from the mean or weighted mean
+ */
+ void getThresholdParams(double &param1,double &param2)const {
+ param1=_thresParam1;
+ param2=_thresParam2;
+ }
+
+
+ /**Returns a reference to the internal image thresholded. It is for visualization purposes and to adjust manually
+ * the parameters
+ */
+ const cv::Mat & getThresholdedImage() {
+ return thres;
+ }
+ /**Methods for corner refinement
+ */
+ enum CornerRefinementMethod {NONE,HARRIS,SUBPIX,LINES};
+ /**
+ */
+ void setCornerRefinementMethod(CornerRefinementMethod method) {
+ _cornerMethod=method;
+ }
+ /**
+ */
+ CornerRefinementMethod getCornerRefinementMethod()const {
+ return _cornerMethod;
+ }
+ /**Specifies the min and max sizes of the markers as a fraction of the image size. By size we mean the maximum
+ * of cols and rows.
+ * @param min size of the contour to consider a possible marker as valid (0,1]
+ * @param max size of the contour to consider a possible marker as valid [0,1)
+ *
+ */
+ void setMinMaxSize(float min=0.03,float max=0.5)throw(cv::Exception);
+
+ /**reads the min and max sizes employed
+ * @param min output size of the contour to consider a possible marker as valid (0,1]
+ * @param max output size of the contour to consider a possible marker as valid [0,1)
+ *
+ */
+ void getMinMaxSize(float &min,float &max){min=_minSize;max=_maxSize;}
+
+ /**Enables/Disables erosion process that is REQUIRED for chessboard like boards.
+ * By default, this property is enabled
+ */
+ void enableErosion(bool enable){_doErosion=enable;}
+
+ /**
+ * Specifies a value to indicate the required speed for the internal processes. If you need maximum speed (at the cost of a lower detection rate),
+ * use the value 3, If you rather a more precise and slow detection, set it to 0.
+ *
+ * Actually, the main differences are that in highspeed mode, we employ setCornerRefinementMethod(NONE) and internally, we use a small canonical
+ * image to detect the marker. In low speed mode, we use setCornerRefinementMethod(HARRIS) and a bigger size for the canonical marker image
+ */
+ void setDesiredSpeed(int val);
+ /**
+ */
+ int getDesiredSpeed()const {
+ return _speed;
+ }
+
+ /**
+ * Allows to specify the function that identifies a marker. Therefore, you can create your own type of markers different from these
+ * employed by default in the library.
+ * The marker function must have the following structure:
+ *
+ * int myMarkerIdentifier(const cv::Mat &in,int &nRotations);
+ *
+ * The marker function receives the image 'in' with the region that migh contain one of your markers. These are the rectangular regions with black
+ * in the image.
+ *
+ * As output your marker function must indicate the following information. First, the output parameter nRotations must indicate how many times the marker
+ * must be rotated clockwise 90 deg to be in its ideal position. (The way you would see it when you print it). This is employed to know
+ * always which is the corner that acts as reference system. Second, the function must return -1 if the image does not contains one of your markers, and its id otherwise.
+ *
+ */
+ void setMakerDetectorFunction(int (* markerdetector_func)(const cv::Mat &in,int &nRotations) ) {
+ markerIdDetector_ptrfunc=markerdetector_func;
+ }
+
+ /** Use an smaller version of the input image for marker detection.
+ * If your marker is small enough, you can employ an smaller image to perform the detection without noticeable reduction in the precision.
+ * Internally, we are performing a pyrdown operation
+ *
+ * @param level number of times the image size is divided by 2. Internally, we are performing a pyrdown.
+ */
+ void pyrDown(unsigned int level){pyrdown_level=level;}
+
+ ///-------------------------------------------------
+ /// Methods you may not need
+ /// Thesde methods do the hard work. They have been set public in case you want to do customizations
+ ///-------------------------------------------------
+
+ /**
+ * Thesholds the passed image with the specified method.
+ */
+ void thresHold(int method,const cv::Mat &grey,cv::Mat &thresImg,double param1=-1,double param2=-1)throw(cv::Exception);
+ /**
+ * Detection of candidates to be markers, i.e., rectangles.
+ * This function returns in candidates all the rectangles found in a thresolded image
+ */
+ void detectRectangles(const cv::Mat &thresImg,vector<std::vector<cv::Point2f> > & candidates);
+
+ /**Returns a list candidates to be markers (rectangles), for which no valid id was found after calling detectRectangles
+ */
+ const vector<std::vector<cv::Point2f> > &getCandidates() {
+ return _candidates;
+ }
+
+ /**Given the iput image with markers, creates an output image with it in the canonical position
+ * @param in input image
+ * @param out image with the marker
+ * @param size of out
+ * @param points 4 corners of the marker in the image in
+ * @return true if the operation succeed
+ */
+ bool warp(cv::Mat &in,cv::Mat &out,cv::Size size, std::vector<cv::Point2f> points)throw (cv::Exception);
+
+
+
+ /** Refine MarkerCandidate Corner using LINES method
+ * @param candidate candidate to refine corners
+ */
+ void refineCandidateLines(MarkerCandidate &candidate);
+
+
+ /**DEPRECATED!!! Use the member function in CameraParameters
+ *
+ * Given the intrinsic camera parameters returns the GL_PROJECTION matrix for opengl.
+ * PLease NOTE that when using OpenGL, it is assumed no camera distorsion! So, if it is not true, you should have
+ * undistor image
+ *
+ * @param CamMatrix arameters of the camera specified.
+ * @param orgImgSize size of the original image
+ * @param size of the image/window where to render (can be different from the real camera image). Please not that it must be related to CamMatrix
+ * @param proj_matrix output projection matrix to give to opengl
+ * @param gnear,gfar: visible rendering range
+ * @param invert: indicates if the output projection matrix has to yield a horizontally inverted image because image data has not been stored in the order of glDrawPixels: bottom-to-top.
+ */
+ static void glGetProjectionMatrix( CameraParameters & CamMatrix,cv::Size orgImgSize, cv::Size size,double proj_matrix[16],double gnear,double gfar,bool invert=false )throw(cv::Exception);
+
+private:
+
+ bool _enableCylinderWarp;
+ bool warp_cylinder ( cv::Mat &in,cv::Mat &out,cv::Size size, MarkerCandidate& mc ) throw ( cv::Exception );
+ /**
+ * Detection of candidates to be markers, i.e., rectangles.
+ * This function returns in candidates all the rectangles found in a thresolded image
+ */
+ void detectRectangles(const cv::Mat &thresImg,vector<MarkerCandidate> & candidates);
+ //Current threshold method
+ ThresholdMethods _thresMethod;
+ //Threshold parameters
+ double _thresParam1,_thresParam2;
+ //Current corner method
+ CornerRefinementMethod _cornerMethod;
+ //minimum and maximum size of a contour lenght
+ float _minSize,_maxSize;
+ //Speed control
+ int _speed;
+ int _markerWarpSize;
+ bool _doErosion;
+ //vectr of candidates to be markers. This is a vector with a set of rectangles that have no valid id
+ vector<std::vector<cv::Point2f> > _candidates;
+ //level of image reduction
+ int pyrdown_level;
+ //Images
+ cv::Mat grey,thres,thres2,reduced;
+ //pointer to the function that analizes a rectangular region so as to detect its internal marker
+ int (* markerIdDetector_ptrfunc)(const cv::Mat &in,int &nRotations);
+
+ /**
+ */
+ bool isInto(cv::Mat &contour,std::vector<cv::Point2f> &b);
+ /**
+ */
+ int perimeter(std::vector<cv::Point2f> &a);
+
+
+// //GL routines
+//
+// static void argConvGLcpara2( double cparam[3][4], int width, int height, double gnear, double gfar, double m[16], bool invert )throw(cv::Exception);
+// static int arParamDecompMat( double source[3][4], double cpara[3][4], double trans[3][4] )throw(cv::Exception);
+// static double norm( double a, double b, double c );
+// static double dot( double a1, double a2, double a3,
+// double b1, double b2, double b3 );
+//
+
+ //detection of the
+ void findBestCornerInRegion_harris(const cv::Mat & grey,vector<cv::Point2f> & Corners,int blockSize);
+
+
+ // auxiliar functions to perform LINES refinement
+ void interpolate2Dline( const vector< cv::Point > &inPoints, cv::Point3f &outLine);
+ cv::Point2f getCrossPoint(const cv::Point3f& line1, const cv::Point3f& line2);
+
+
+ /**Given a vector vinout with elements and a boolean vector indicating the lements from it to remove,
+ * this function remove the elements
+ * @param vinout
+ * @param toRemove
+ */
+ template<typename T>
+ void removeElements(vector<T> & vinout,const vector<bool> &toRemove)
+ {
+ //remove the invalid ones by setting the valid in the positions left by the invalids
+ size_t indexValid=0;
+ for (size_t i=0;i<toRemove.size();i++) {
+ if (!toRemove[i]) {
+ if (indexValid!=i) vinout[indexValid]=vinout[i];
+ indexValid++;
+ }
+ }
+ vinout.resize(indexValid);
+ }
+
+ //graphical debug
+ void drawApproxCurve(cv::Mat &in,std::vector<cv::Point> &approxCurve ,cv::Scalar color);
+ void drawContour(cv::Mat &in,std::vector<cv::Point> &contour,cv::Scalar );
+ void drawAllContours(cv::Mat input, std::vector<std::vector<cv::Point> > &contours);
+ void draw(cv::Mat out,const std::vector<Marker> &markers );
+
+};
+}
+#endif
diff --git a/ftnoir_tracker_base/ftnoir_tracker_base.h b/ftnoir_tracker_base/ftnoir_tracker_base.h
new file mode 100644
index 00000000..09723d84
--- /dev/null
+++ b/ftnoir_tracker_base/ftnoir_tracker_base.h
@@ -0,0 +1,65 @@
+/********************************************************************************
+* 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/>. *
+* This class implements a tracker-base *
+*********************************************************************************/
+#ifndef FTNOIR_TRACKER_BASE_H
+#define FTNOIR_TRACKER_BASE_H
+
+#include "ftnoir_tracker_base_global.h"
+#include "ftnoir_tracker_types.h"
+#include <QWidget>
+#include <QFrame>
+#include <QWaitCondition>
+#include <QMutex>
+#include <QFrame>
+
+////////////////////////////////////////////////////////////////////////////////
+// COM-Like abstract interface.
+// This interface doesn't require __declspec(dllexport/dllimport) specifier.
+// Method calls are dispatched via virtual table.
+// Any C++ compiler can use it.
+// Instances are obtained via factory function.
+struct ITracker
+{
+ virtual ~ITracker() = 0;
+ virtual void StartTracker( QFrame* frame ) = 0;
+ virtual void GetHeadPoseData(double *data) = 0;
+ virtual int preferredHz() { return 200; }
+};
+
+inline ITracker::~ITracker() { }
+
+////////////////////////////////////////////////////////////////////////////////
+// COM-Like abstract interface.
+// This interface doesn't require __declspec(dllexport/dllimport) specifier.
+// Method calls are dispatched via virtual table.
+// Any C++ compiler can use it.
+// Instances are obtained via factory function.
+struct ITrackerDialog
+{
+ virtual ~ITrackerDialog() {}
+ virtual void registerTracker(ITracker *tracker) = 0;
+ virtual void unRegisterTracker() = 0;
+};
+
+#endif // FTNOIR_TRACKER_BASE_H
diff --git a/ftnoir_tracker_base/ftnoir_tracker_base_global.h b/ftnoir_tracker_base/ftnoir_tracker_base_global.h
new file mode 100644
index 00000000..e717d845
--- /dev/null
+++ b/ftnoir_tracker_base/ftnoir_tracker_base_global.h
@@ -0,0 +1,18 @@
+#ifndef FTNOIR_TRACKER_BASE_GLOBAL_H
+#define FTNOIR_TRACKER_BASE_GLOBAL_H
+
+#include <QtGlobal>
+
+#ifndef FTNOIR_TRACKER_BASE_EXPORT
+# ifndef OPENTRACK_MAIN
+# if !defined(_MSC_VER)
+# define FTNOIR_TRACKER_BASE_EXPORT __attribute__ ((visibility ("default")))
+# else
+# define FTNOIR_TRACKER_BASE_EXPORT Q_DECL_EXPORT
+# endif
+# else
+# define FTNOIR_TRACKER_BASE_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+#endif // FTNOIR_TRACKER_BASE_GLOBAL_H
diff --git a/ftnoir_tracker_base/ftnoir_tracker_types.h b/ftnoir_tracker_base/ftnoir_tracker_types.h
new file mode 100644
index 00000000..d38baee4
--- /dev/null
+++ b/ftnoir_tracker_base/ftnoir_tracker_types.h
@@ -0,0 +1,4 @@
+#pragma once
+enum Axis {
+ TX = 0, TY, TZ, Yaw, Pitch, Roll
+};
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp b/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp
new file mode 100644
index 00000000..76a6ba71
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht.cpp
@@ -0,0 +1,291 @@
+#include "stdafx.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "headtracker-ftnoir.h"
+#include "ftnoir_tracker_ht.h"
+#include "ftnoir_tracker_ht_dll.h"
+#include "ui_ht-trackercontrols.h"
+#include "facetracknoir/global-settings.h"
+#include <cmath>
+
+#if defined(_WIN32)
+#include <dshow.h>
+#else
+#include <unistd.h>
+#endif
+
+// delicious copypasta
+static QList<QString> get_camera_names(void) {
+ QList<QString> ret;
+#if defined(_WIN32)
+ // 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))
+ {
+ return ret;
+ }
+ // Obtain a class enumerator for the video compressor category.
+ IEnumMoniker *pEnumCat = NULL;
+ hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+ if (hr == S_OK) {
+ // Enumerate the monikers.
+ IMoniker *pMoniker = NULL;
+ ULONG cFetched;
+ while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
+ // To retrieve the filter's friendly name, do the following:
+ VARIANT varName;
+ VariantInit(&varName);
+ hr = pPropBag->Read(L"FriendlyName", &varName, 0);
+ if (SUCCEEDED(hr))
+ {
+ // Display the name in your UI somehow.
+ QString str((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
+ ret.append(str);
+ }
+ VariantClear(&varName);
+
+ ////// To create an instance of the filter, do the following:
+ ////IBaseFilter *pFilter;
+ ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
+ //// (void**)&pFilter);
+ // Now add the filter to the graph.
+ //Remember to release pFilter later.
+ pPropBag->Release();
+ }
+ pMoniker->Release();
+ }
+ pEnumCat->Release();
+ }
+ pSysDevEnum->Release();
+#elif !defined(__APPLE__)
+ for (int i = 0; i < 16; i++) {
+ char buf[128];
+ sprintf(buf, "/dev/video%d", i);
+ if (access(buf, R_OK | W_OK) == 0) {
+ ret.append(buf);
+ } else {
+ continue;
+ }
+ }
+#endif
+ return ret;
+}
+
+typedef struct {
+ int width;
+ int height;
+} resolution_tuple;
+
+static resolution_tuple resolution_choices[] = {
+ { 640, 480 },
+ { 320, 240 },
+ { 320, 200 },
+ { 0, 0 }
+};
+
+void Tracker::load_settings(ht_config_t* config)
+{
+ int nframes = 0;
+ switch (static_cast<int>(s.fps))
+ {
+ default:
+ case 0:
+ nframes = 0;
+ break;
+ case 1:
+ nframes = 30;
+ break;
+ case 2:
+ nframes = 60;
+ break;
+ case 3:
+ nframes = 120;
+ break;
+ case 4:
+ nframes = 180;
+ break;
+ }
+
+ config->classification_delay = 500;
+ config->field_of_view = s.fov;
+ config->pyrlk_pyramids = 0;
+ config->pyrlk_win_size_w = config->pyrlk_win_size_h = 21;
+ config->max_keypoints = 150;
+ config->keypoint_distance = 6;
+ config->force_fps = nframes;
+ config->camera_index = s.camera_idx - 1;
+ config->ransac_num_iters = 100;
+ config->ransac_max_reprojection_error = 10;
+ config->ransac_max_inlier_error = 10;
+ config->ransac_abs_max_mean_error = 14;
+ config->ransac_max_mean_error = 8;
+ config->debug = 0;
+ config->ransac_min_features = 0.86;
+ int res = s.resolution;
+ if (res < 0 || res >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple)))
+ res = 0;
+ resolution_tuple r = resolution_choices[res];
+ config->force_width = r.width;
+ config->force_height = r.height;
+ config->flandmark_delay = 500;
+ for (int i = 0; i < 5; i++)
+ config->dist_coeffs[i] = 0;
+}
+
+Tracker::Tracker() :
+ lck_shm(HT_SHM_NAME, HT_MUTEX_NAME, sizeof(ht_shm_t)),
+ shm(reinterpret_cast<ht_shm_t*>(lck_shm.mem)),
+ videoWidget(nullptr),
+ layout(nullptr)
+{
+ shm->terminate = 0;
+ shm->result.filled = false;
+}
+
+Tracker::~Tracker()
+{
+ if (shm) {
+ shm->terminate = true;
+ subprocess.waitForFinished(5000);
+ }
+ subprocess.kill();
+ if (shm)
+ shm->terminate = true;
+ if (layout)
+ delete layout;
+ if (videoWidget)
+ delete videoWidget;
+}
+
+void Tracker::StartTracker(QFrame* videoframe)
+{
+ videoframe->show();
+ videoWidget = new HTVideoWidget(videoframe);
+ QHBoxLayout* layout = new QHBoxLayout();
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(videoWidget);
+ if (videoframe->layout())
+ delete videoframe->layout();
+ videoframe->setLayout(layout);
+ videoWidget->show();
+ this->layout = layout;
+ load_settings(&shm->config);
+ shm->frame.channels = shm->frame.width = shm->frame.height = 0;
+ shm->pause = shm->terminate = shm->running = false;
+ shm->timer = 0;
+ subprocess.setWorkingDirectory(QCoreApplication::applicationDirPath() + "/tracker-ht");
+#if defined(_WIN32)
+ subprocess.start("\"" + QCoreApplication::applicationDirPath() + "/tracker-ht/headtracker-ftnoir" + "\"");
+#else
+ subprocess.start(QCoreApplication::applicationDirPath() + "/tracker-ht/headtracker-ftnoir");
+#endif
+}
+
+void Tracker::GetHeadPoseData(double *data)
+{
+ lck_shm.lock();
+ shm->timer = 0;
+ if (shm->frame.width > 0)
+ {
+ videoWidget->update_image(shm->frame.frame, shm->frame.width, shm->frame.height);
+ //memcpy(foo, shm->frame.frame, shm->frame.width * shm->frame.height * 3);
+ shm->frame.width = 0;
+ }
+ if (shm->result.filled) {
+ if (s.enableRX)
+ data[Yaw] = shm->result.rotx;
+ if (s.enableRY) {
+ data[Pitch] = shm->result.roty;
+ }
+ if (s.enableRZ) {
+ data[Roll] = shm->result.rotz;
+ }
+ if (s.enableTX)
+ data[TX] = shm->result.tx;
+ if (s.enableTY)
+ data[TY] = shm->result.ty;
+ if (s.enableTZ)
+ data[TZ] = shm->result.tz;
+ if (fabs(data[Yaw]) > 60 || fabs(data[Pitch]) > 50 || fabs(data[Roll]) > 40)
+ {
+ shm->pause = true;
+ }
+ } else {
+ shm->pause = false;
+ }
+ lck_shm.unlock();
+}
+
+//-----------------------------------------------------------------------------
+void TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "HT 1.0";
+}
+
+void TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "HT";
+}
+
+void TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "";
+}
+
+void TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/ht.png");
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new TrackerDll;
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new Tracker;
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
+
+TrackerControls::TrackerControls()
+{
+ ui.setupUi(this);
+ ui.cameraName->clear();
+ QList<QString> names = get_camera_names();
+ names.prepend("Any available");
+ ui.cameraName->addItems(names);
+ tie_setting(s.camera_idx, ui.cameraName);
+ tie_setting(s.fps, ui.cameraFPS);
+ tie_setting(s.fov, ui.cameraFOV);
+ tie_setting(s.enableTX, ui.tx);
+ tie_setting(s.enableTY, ui.ty);
+ tie_setting(s.enableTZ, ui.tz);
+ tie_setting(s.enableRX, ui.rx);
+ tie_setting(s.enableRY, ui.ry);
+ tie_setting(s.enableRZ, ui.rz);
+ tie_setting(s.resolution, ui.resolution);
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+}
+
+void TrackerControls::doOK()
+{
+ s.b->save();
+ this->close();
+}
+
+void TrackerControls::doCancel()
+{
+ s.b->revert();
+ this->close();
+}
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht.h b/ftnoir_tracker_ht/ftnoir_tracker_ht.h
new file mode 100644
index 00000000..583249dc
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 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.
+ */
+
+#ifndef FTNOIR_TRACKER_HT_H
+#define FTNOIR_TRACKER_HT_H
+
+#include "stdafx.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "headtracker-ftnoir.h"
+#include "ui_ht-trackercontrols.h"
+#include "ht_video_widget.h"
+#include "compat/compat.h"
+#include <QObject>
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<bool> enableTX, enableTY, enableTZ, enableRX, enableRY, enableRZ;
+ value<double> fov;
+ value<int> fps, camera_idx, resolution;
+ settings() :
+ b(bundle("HT-Tracker")),
+ enableTX(b, "enable-tx", true),
+ enableTY(b, "enable-ty", true),
+ enableTZ(b, "enable-tz", true),
+ enableRX(b, "enable-rx", true),
+ enableRY(b, "enable-ry", true),
+ enableRZ(b, "enable-rz", true),
+ fov(b, "fov", 56),
+ fps(b, "fps", 0),
+ camera_idx(b, "camera-index", 0),
+ resolution(b, "resolution", 0)
+ {}
+};
+
+class Tracker : public QObject, public ITracker
+{
+ Q_OBJECT
+public:
+ Tracker();
+ virtual ~Tracker();
+ void StartTracker(QFrame* frame);
+ void GetHeadPoseData(double *data);
+ void load_settings(ht_config_t* config);
+private:
+ settings s;
+ PortableLockedShm lck_shm;
+ ht_shm_t* shm;
+ QProcess subprocess;
+ HTVideoWidget* videoWidget;
+ QHBoxLayout* layout;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class TrackerControls : public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ explicit TrackerControls();
+ void registerTracker(ITracker *) {}
+ void unRegisterTracker() {}
+
+private:
+ Ui::Form ui;
+ settings s;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+#endif
+
diff --git a/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h b/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h
new file mode 100644
index 00000000..ffdc5262
--- /dev/null
+++ b/ftnoir_tracker_ht/ftnoir_tracker_ht_dll.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2013 Stanisław 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.
+ */
+
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "facetracknoir/global-settings.h"
+
+//-----------------------------------------------------------------------------
+class TrackerDll : public Metadata
+{
+ // ITrackerDll interface
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
diff --git a/ftnoir_tracker_ht/headtracker-ftnoir.h b/ftnoir_tracker_ht/headtracker-ftnoir.h
new file mode 100644
index 00000000..9a343bae
--- /dev/null
+++ b/ftnoir_tracker_ht/headtracker-ftnoir.h
@@ -0,0 +1,24 @@
+#pragma once
+#include <stdlib.h>
+#include <stdio.h>
+#include "ht-api.h"
+
+#define HT_SHM_NAME "ftnoir-tracker-ht-shm"
+#define HT_MUTEX_NAME "ftnoir-tracker-ht-mutex"
+
+#define HT_MAX_VIDEO_WIDTH 640
+#define HT_MAX_VIDEO_HEIGHT 480
+#define HT_MAX_VIDEO_CHANNELS 3
+
+typedef struct {
+ int width, height, channels;
+ unsigned char frame[HT_MAX_VIDEO_WIDTH * HT_MAX_VIDEO_HEIGHT * HT_MAX_VIDEO_CHANNELS];
+} ht_video_t;
+
+typedef struct {
+ ht_video_t frame;
+ ht_config_t config;
+ ht_result_t result;
+ volatile int timer;
+ volatile bool pause, terminate, running;
+} ht_shm_t;
diff --git a/ftnoir_tracker_ht/ht-api.h b/ftnoir_tracker_ht/ht-api.h
new file mode 100644
index 00000000..2ab2e840
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-api.h
@@ -0,0 +1,49 @@
+#pragma once
+#ifndef HT_API
+# if defined(_WIN32) && !defined(MINGW)
+# define HT_API(t) __declspec(dllexport) t __stdcall
+# else
+# define HT_API(t) t
+# endif
+#endif
+#if !defined(_WIN32) && !defined(_isnan)
+# define _isnan isnan
+#endif
+#include <opencv2/core/core.hpp>
+struct ht_context;
+typedef struct ht_context headtracker_t;
+
+typedef struct ht_config {
+ float field_of_view;
+ float classification_delay;
+ int pyrlk_pyramids;
+ int pyrlk_win_size_w;
+ int pyrlk_win_size_h;
+ float ransac_max_inlier_error;
+ float ransac_max_reprojection_error;
+ int max_keypoints;
+ float keypoint_distance;
+ int force_width;
+ int force_height;
+ int force_fps;
+ int camera_index;
+ bool debug;
+ int ransac_num_iters;
+ float ransac_min_features;
+ float ransac_max_mean_error;
+ float ransac_abs_max_mean_error;
+ float flandmark_delay;
+ double dist_coeffs[5];
+} ht_config_t;
+
+typedef struct {
+ double rotx, roty, rotz;
+ double tx, ty, tz;
+ bool filled;
+} ht_result_t;
+
+HT_API(headtracker_t*) ht_make_context(const ht_config_t* config, const char* filename);
+HT_API(void) ht_free_context(headtracker_t* ctx);
+HT_API(const cv::Mat) ht_get_bgr_frame(headtracker_t* ctx);
+HT_API(bool) ht_cycle(headtracker_t* ctx, ht_result_t* euler);
+HT_API(void) ht_reset(headtracker_t* ctx);
diff --git a/ftnoir_tracker_ht/ht-tracker.qrc b/ftnoir_tracker_ht/ht-tracker.qrc
new file mode 100644
index 00000000..b6af7a18
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-tracker.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/ht.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_tracker_ht/ht-trackercontrols.ui b/ftnoir_tracker_ht/ht-trackercontrols.ui
new file mode 100644
index 00000000..f57022c8
--- /dev/null
+++ b/ftnoir_tracker_ht/ht-trackercontrols.ui
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>531</width>
+ <height>166</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>750</width>
+ <height>280</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>HT tracker settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="cameraFOV">
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="minimum">
+ <double>35.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>52.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Horizontal FOV</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" rowspan="3">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Enable axes</string>
+ </property>
+ <widget class="QCheckBox" name="rx">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RX</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ry">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>40</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="rz">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>60</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RZ</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tx">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>20</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="ty">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>40</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="tz">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>60</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Frames per second</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="cameraFPS">
+ <item>
+ <property name="text">
+ <string notr="true">Default</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>60</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>120</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>180</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Camera name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="cameraName"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="resolution">
+ <item>
+ <property name="text">
+ <string>640x480</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320x240</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320x200</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Default (not recommended!)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ftnoir_tracker_ht/ht_video_widget.cpp b/ftnoir_tracker_ht/ht_video_widget.cpp
new file mode 100644
index 00000000..c6d59b34
--- /dev/null
+++ b/ftnoir_tracker_ht/ht_video_widget.cpp
@@ -0,0 +1,44 @@
+/* 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.
+ */
+
+#include "ht_video_widget.h"
+
+#include <QDebug>
+
+using namespace std;
+
+void HTVideoWidget::update_image(unsigned char *frame, int width, int height)
+{
+ QMutexLocker foo(&mtx);
+ memcpy(fb, frame, width * height * 3);
+ this->width = width;
+ this->height = height;
+}
+
+void HTVideoWidget::update_and_repaint()
+{
+ QMutexLocker foo(&mtx);
+ if (width*height <= 0)
+ return;
+ QImage qframe = QImage(width, height, QImage::Format_RGB888);
+ uchar* data = qframe.bits();
+ const int pitch = qframe.bytesPerLine();
+ for (int y = 0; y < height; y++)
+ {
+ const int part = y*width;
+ for (int x = 0; x < width; x++)
+ {
+ const int pos = 3 * (part + x);
+ data[y * pitch + x * 3 + 0] = fb[pos + 2];
+ data[y * pitch + x * 3 + 1] = fb[pos + 1];
+ data[y * pitch + x * 3 + 2] = fb[pos + 0];
+ }
+ }
+ auto qframe2 = qframe.scaled(size(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
+ texture = qframe2;
+ update();
+}
diff --git a/ftnoir_tracker_ht/ht_video_widget.h b/ftnoir_tracker_ht/ht_video_widget.h
new file mode 100644
index 00000000..cbfe6ddc
--- /dev/null
+++ b/ftnoir_tracker_ht/ht_video_widget.h
@@ -0,0 +1,47 @@
+/* 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 VIDEOWIDGET_H
+#define VIDEOWIDGET_H
+
+#include <QTime>
+#include <QWidget>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QLabel>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QTimer>
+
+// ----------------------------------------------------------------------------
+class HTVideoWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ HTVideoWidget(QWidget *parent) : QWidget(parent), fb(), width(0), height(0) {
+ connect(&timer, SIGNAL(timeout()), this, SLOT(update_and_repaint()));
+ timer.start(60);
+ }
+ void update_image(unsigned char* frame, int width, int height);
+protected slots:
+ void paintEvent( QPaintEvent* e ) {
+ QMutexLocker foo(&mtx);
+ QPainter painter(this);
+ painter.drawImage(e->rect(), texture);
+ }
+ void update_and_repaint();
+
+private:
+ QMutex mtx;
+ QImage texture;
+ QTimer timer;
+ char fb[2048*2048*3];
+ int width,height;
+};
+
+#endif // VIDEOWIDGET_H
diff --git a/ftnoir_tracker_ht/images/ht.png b/ftnoir_tracker_ht/images/ht.png
new file mode 100644
index 00000000..19c73d21
--- /dev/null
+++ b/ftnoir_tracker_ht/images/ht.png
Binary files differ
diff --git a/ftnoir_tracker_ht/stdafx.h b/ftnoir_tracker_ht/stdafx.h
new file mode 100644
index 00000000..6f1539b7
--- /dev/null
+++ b/ftnoir_tracker_ht/stdafx.h
@@ -0,0 +1,13 @@
+#include <QWidget>
+#include <QMessageBox>
+#include <QProcess>
+#include <QImage>
+#include <QLabel>
+#include <QCoreApplication>
+#include <QIcon>
+#include <QHBoxLayout>
+#include <QTimer>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QList>
+#include <QString>
diff --git a/ftnoir_tracker_hydra/ftnoir_hydra_clientcontrols.ui b/ftnoir_tracker_hydra/ftnoir_hydra_clientcontrols.ui
new file mode 100644
index 00000000..e5e41bec
--- /dev/null
+++ b/ftnoir_tracker_hydra/ftnoir_hydra_clientcontrols.ui
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UIHydraControls</class>
+ <widget class="QWidget" name="UIHydraControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>172</width>
+ <height>145</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Sixense Hydra tracker settings FaceTrackNoIR</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="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>85</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Enable Axis</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_61">
+ <property name="text">
+ <string>Pitch:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="chkEnablePitch">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>X:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QCheckBox" name="chkEnableX">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_91">
+ <property name="text">
+ <string>Yaw:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkEnableYaw">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="chkEnableY">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_111">
+ <property name="text">
+ <string>Roll:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkEnableRoll">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QCheckBox" name="chkEnableZ">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_tracker_hydra/ftnoir_tracker_hydra.cpp b/ftnoir_tracker_hydra/ftnoir_tracker_hydra.cpp
new file mode 100644
index 00000000..70af2893
--- /dev/null
+++ b/ftnoir_tracker_hydra/ftnoir_tracker_hydra.cpp
@@ -0,0 +1,73 @@
+/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
+#include "ftnoir_tracker_hydra.h"
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/rotation.h"
+#include <cstdio>
+#ifdef _WIN32
+# define SIXENSE_STATIC_LIB
+# define SIXENSE_UTILS_STATIC_LIB
+#endif
+#include <sixense.h>
+#include <sixense_math.hpp>
+
+Hydra_Tracker::Hydra_Tracker() : should_quit(false)
+{
+ for (int i = 0; i < 6; i++)
+ newHeadPose[i] = 0;
+}
+
+Hydra_Tracker::~Hydra_Tracker()
+{
+
+ sixenseExit();
+}
+
+void Hydra_Tracker::StartTracker(QFrame*)
+{
+ sixenseInit();
+}
+
+void Hydra_Tracker::GetHeadPoseData(double *data)
+{
+
+ sixenseSetActiveBase(0);
+ sixenseAllControllerData acd;
+ sixenseGetAllNewestData( &acd );
+ sixenseMath::Matrix4 mat = sixenseMath::Matrix4(acd.controllers[0].rot_mat);
+
+ float ypr[3];
+
+ mat.getEulerAngles().fill(ypr);
+ newHeadPose[Yaw] = ypr[0];
+ newHeadPose[Pitch] = ypr[1];
+ newHeadPose[Roll] = ypr[2];
+
+
+ newHeadPose[TX] = acd.controllers[0].pos[0]/50.0f;
+ newHeadPose[TY] = acd.controllers[0].pos[1]/50.0f;
+ newHeadPose[TZ] = acd.controllers[0].pos[2]/50.0f;
+
+ if (s.bEnableX) {
+ data[TX] = newHeadPose[TX];
+ }
+ if (s.bEnableY) {
+ data[TY] = newHeadPose[TY];
+ }
+ if (s.bEnableY) {
+ data[TZ] = newHeadPose[TZ];
+ }
+ if (s.bEnableYaw) {
+ data[Yaw] = newHeadPose[Yaw] * 57.295781f;
+ }
+ if (s.bEnablePitch) {
+ data[Pitch] = newHeadPose[Pitch] * 57.295781f;
+ }
+ if (s.bEnableRoll) {
+ data[Roll] = newHeadPose[Roll] * 57.295781f;
+ }
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new Hydra_Tracker;
+}
diff --git a/ftnoir_tracker_hydra/ftnoir_tracker_hydra.h b/ftnoir_tracker_hydra/ftnoir_tracker_hydra.h
new file mode 100644
index 00000000..05a8b076
--- /dev/null
+++ b/ftnoir_tracker_hydra/ftnoir_tracker_hydra.h
@@ -0,0 +1,65 @@
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_ftnoir_hydra_clientcontrols.h"
+#include <QMessageBox>
+#include <QWaitCondition>
+#include <math.h>
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<bool> bEnableRoll, bEnablePitch, bEnableYaw, bEnableX, bEnableY, bEnableZ;
+ settings() :
+ b(bundle("tracker-hydra")),
+ bEnableRoll(b, "enable-rz", true),
+ bEnablePitch(b, "enable-ry", true),
+ bEnableYaw(b, "enable-rx", true),
+ bEnableX(b, "enable-tx", true),
+ bEnableY(b, "enable-ty", true),
+ bEnableZ(b, "enable-tz", true)
+ {}
+};
+
+class Hydra_Tracker : public ITracker
+{
+public:
+ Hydra_Tracker();
+ ~Hydra_Tracker();
+ void StartTracker(QFrame *) virt_override;
+ void GetHeadPoseData(double *data) virt_override;
+ volatile bool should_quit;
+protected:
+ void run(); // qthread override run method
+private:
+ settings s;
+ bool isCalibrated;
+ double newHeadPose[6]; // Structure with new headpose
+ QMutex mutex;
+ virtual int preferredHz() virt_override { return 250; }
+};
+
+class TrackerControls: public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ explicit TrackerControls();
+ void registerTracker(ITracker *) {}
+ void unRegisterTracker() {}
+private:
+ settings s;
+ Ui::UIHydraControls ui;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_TrackerDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
+
diff --git a/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dialog.cpp b/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dialog.cpp
new file mode 100644
index 00000000..14be2c37
--- /dev/null
+++ b/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dialog.cpp
@@ -0,0 +1,34 @@
+#include "ftnoir_tracker_hydra.h"
+#include "facetracknoir/global-settings.h"
+
+TrackerControls::TrackerControls() :
+QWidget()
+{
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.bEnableYaw, ui.chkEnableYaw);
+ tie_setting(s.bEnablePitch, ui.chkEnablePitch);
+ tie_setting(s.bEnableRoll, ui.chkEnableRoll);
+ tie_setting(s.bEnableX, ui.chkEnableX);
+ tie_setting(s.bEnableY, ui.chkEnableY);
+ tie_setting(s.bEnableZ, ui.chkEnableZ);
+}
+
+void TrackerControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void TrackerControls::doCancel() {
+ s.b->revert();
+ close();
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
diff --git a/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dll.cpp b/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dll.cpp
new file mode 100644
index 00000000..a2cc7c01
--- /dev/null
+++ b/ftnoir_tracker_hydra/ftnoir_tracker_hydra_dll.cpp
@@ -0,0 +1,38 @@
+/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
+#include "ftnoir_tracker_hydra.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+void FTNoIR_TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "Hydra";
+}
+
+void FTNoIR_TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "Hydra";
+}
+
+void FTNoIR_TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "Hydra";
+}
+
+void FTNoIR_TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/facetracknoir.png");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory function that creates instances if the Tracker object.
+
+// Export both decorated and undecorated names.
+// GetTrackerDll - Undecorated name, which can be easily used with GetProcAddress
+// Win32 API function.
+// _GetTrackerDll@0 - Common name decoration for __stdcall functions in C language.
+//#pragma comment(linker, "/export:GetTrackerDll=_GetTrackerDll@0")
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_TrackerDll;
+}
diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp
new file mode 100644
index 00000000..f9789dce
--- /dev/null
+++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp
@@ -0,0 +1,230 @@
+#include "ftnoir_tracker_joystick.h"
+#include "facetracknoir/global-settings.h"
+#undef NDEBUG
+#include <QMutexLocker>
+
+FTNoIR_Tracker::FTNoIR_Tracker() :
+ g_pDI(nullptr),
+ g_pJoystick(nullptr),
+ iter(-1),
+ mtx(QMutex::Recursive)
+{
+ for (int i = 0; i < 6; i++)
+ min_[i] = max_[i] = 0;
+ GUID bar = {0};
+}
+
+void FTNoIR_Tracker::reload()
+{
+ s.b->reload();
+ QMutexLocker foo(&mtx);
+ if (g_pJoystick)
+ {
+ g_pJoystick->Unacquire();
+ g_pJoystick->Release();
+ }
+ if (g_pDI)
+ g_pDI->Release();
+
+ g_pJoystick = nullptr;
+ g_pDI = nullptr;
+
+ StartTracker(frame);
+}
+
+FTNoIR_Tracker::~FTNoIR_Tracker()
+{
+ if (g_pJoystick)
+ {
+ g_pJoystick->Unacquire();
+ g_pJoystick->Release();
+ }
+ if (g_pDI)
+ {
+ g_pDI->Release();
+ }
+}
+
+static BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
+ VOID* pContext )
+{
+ auto self = (FTNoIR_Tracker*) pContext;
+
+ // For axes that are returned, set the DIPROP_RANGE property for the
+ // enumerated axis in order to scale min/max values.
+ if( pdidoi->dwType & DIDFT_AXIS )
+ {
+ DIPROPRANGE diprg = {0};
+ diprg.diph.dwSize = sizeof( DIPROPRANGE );
+ diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER );
+ diprg.diph.dwHow = DIPH_BYID;
+ diprg.diph.dwObj = pdidoi->dwType;
+
+ // Set the range for the axis
+
+ if( FAILED( self->g_pJoystick->GetProperty( DIPROP_RANGE, &diprg.diph ) ) )
+ return DIENUM_STOP;
+
+ self->min_[self->iter] = diprg.lMin;
+ self->max_[self->iter] = diprg.lMax;
+ qDebug() << "axis" << self->iter << diprg.lMin << diprg.lMax;
+ self->iter++;
+ }
+
+ return self->iter == 8 ? DIENUM_STOP : DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
+{
+ auto self = reinterpret_cast<FTNoIR_Tracker*>(pContext);
+ bool stop = QString(pdidInstance->tszInstanceName) == self->s.joyid;
+
+ if (stop)
+ {
+ (void) self->g_pDI->CreateDevice( pdidInstance->guidInstance, &self->g_pJoystick, NULL);
+ qDebug() << "device" << static_cast<QString>(self->s.joyid);
+ }
+
+ return stop ? DIENUM_STOP : DIENUM_CONTINUE;
+}
+
+void FTNoIR_Tracker::StartTracker(QFrame* frame)
+{
+ QMutexLocker foo(&mtx);
+ this->frame = frame;
+ iter = 0;
+ auto hr = CoInitialize( nullptr );
+
+ if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION,
+ IID_IDirectInput8, ( VOID** )&g_pDI, NULL ) ) )
+ {
+ qDebug() << "create";
+ goto fail;
+ }
+
+ if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,
+ EnumJoysticksCallback,
+ this,
+ DIEDFL_ATTACHEDONLY)))
+ {
+ qDebug() << "enum2";
+ goto fail;
+ }
+
+ if (!g_pJoystick)
+ {
+ qDebug() << "ENODEV";
+ goto fail;
+ }
+
+ if (FAILED(g_pJoystick->SetDataFormat(&c_dfDIJoystick)))
+ {
+ qDebug() << "format";
+ goto fail;
+ }
+
+ if (FAILED(g_pJoystick->SetCooperativeLevel((HWND) frame->window()->winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
+ {
+ qDebug() << "coop";
+ goto fail;
+ }
+
+ iter = 0;
+
+ if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback,
+ ( VOID* )this, DIDFT_ALL )))
+ {
+ qDebug() << "enum axes";
+ goto fail;
+ }
+
+ qDebug() << "joy init success";
+
+ return;
+
+fail:
+ if (g_pJoystick)
+ g_pJoystick->Release();
+ if (g_pDI)
+ g_pDI->Release();
+ g_pJoystick = nullptr;
+ g_pDI = nullptr;
+
+ qDebug() << "joy init failure";
+}
+
+void FTNoIR_Tracker::GetHeadPoseData(double *data)
+{
+ QMutexLocker foo(&mtx);
+ DIJOYSTATE js = {0};
+
+ if( !g_pDI || !g_pJoystick)
+ return;
+
+ auto hr = g_pJoystick->Poll();
+ if( FAILED( hr ))
+ {
+ hr = g_pJoystick->Acquire();
+ for (int i = 0; hr == DIERR_INPUTLOST && i < 200; i++)
+ hr = g_pJoystick->Acquire();
+ if (hr != DI_OK)
+ {
+ qDebug() << "joy read failure" << hr;
+ return;
+ }
+ }
+
+ if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof( js ), &js ) ) )
+ return;
+
+ const LONG values[] = {
+ js.lRx,
+ js.lRy,
+ js.lRz,
+ js.lX,
+ js.lY,
+ js.lZ,
+ js.rglSlider[0],
+ js.rglSlider[1]
+ };
+
+ const double limits[] = {
+ 100,
+ 100,
+ 100,
+ 180,
+ 90,
+ 180
+ };
+
+ int axes[] = {
+ s.axis_0,
+ s.axis_1,
+ s.axis_2,
+ s.axis_3,
+ s.axis_4,
+ s.axis_5
+ };
+
+ for (int i = 0; i < 6; i++)
+ {
+ auto idx = axes[i] - 1;
+ if (idx < 0 || idx > 7)
+ {
+ data[i] = 0;
+ }
+ else {
+ auto mid = (min_[idx] + max_[idx]) / 2;
+ auto val = values[idx] - mid;
+
+ auto max = (max_[idx] - min_[idx]) / 2;
+ auto min = (min_[idx] - max_[idx]) / -2;
+ data[i] = val * limits[i] / (double) (val >= 0 ? max : min);
+ }
+ }
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Tracker;
+}
diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h
new file mode 100644
index 00000000..06d06186
--- /dev/null
+++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 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.
+ */
+#pragma once
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_ftnoir_tracker_joystick_controls.h"
+#include <QComboBox>
+#include <QCheckBox>
+#include <QSpinBox>
+#include <QMessageBox>
+#include <QSettings>
+#include <QList>
+#include <QMutex>
+#include <QFrame>
+#include <cmath>
+#include "facetracknoir/global-settings.h"
+#ifndef DIRECTINPUT_VERSION
+# define DIRECTINPUT_VERSION 0x800
+#endif
+#include <windows.h>
+#include <commctrl.h>
+#include <basetsd.h>
+#include <dinput.h>
+#include <oleauto.h>
+#include <shellapi.h>
+
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> axis_0;
+ value<int> axis_1;
+ value<int> axis_2;
+ value<int> axis_3;
+ value<int> axis_4;
+ value<int> axis_5;
+ value<QString> joyid;
+ settings() :
+ b(bundle("tracker-joystick")),
+ axis_0(b, "axis-0", 0),
+ axis_1(b, "axis-1", 0),
+ axis_2(b, "axis-2", 0),
+ axis_3(b, "axis-3", 0),
+ axis_4(b, "axis-4", 0),
+ axis_5(b, "axis-5", 0),
+ joyid(b, "joy-id", "")
+ {}
+};
+
+class FTNoIR_Tracker : public ITracker
+{
+public:
+ FTNoIR_Tracker();
+ ~FTNoIR_Tracker();
+ void StartTracker(QFrame *frame);
+ void GetHeadPoseData(double *data);
+ void reload();
+ LPDIRECTINPUT8 g_pDI;
+ LPDIRECTINPUTDEVICE8 g_pJoystick;
+ int min_[8], max_[8];
+ QMutex mtx;
+ QFrame* frame;
+ DIDEVICEINSTANCE def;
+ int iter; // XXX bad style
+ settings s;
+};
+
+class TrackerControls: public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ TrackerControls();
+ void registerTracker(ITracker *foo) {
+ tracker = dynamic_cast<FTNoIR_Tracker*>(foo);
+ }
+ void unRegisterTracker() {
+ tracker = NULL;
+ }
+ QList<GUID> guids;
+ Ui::UIJoystickControls ui;
+ FTNoIR_Tracker* tracker;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_TrackerDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+private:
+ QString trackerFullName; // Trackers' name and description
+ QString trackerShortName;
+ QString trackerDescription;
+};
+
diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui
new file mode 100644
index 00000000..5d349169
--- /dev/null
+++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui
@@ -0,0 +1,439 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UIJoystickControls</class>
+ <widget class="QWidget" name="UIJoystickControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>216</width>
+ <height>259</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Joystick protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>../facetracknoir/images/facetracknoir.png</normaloff>../facetracknoir/images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>85</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Axis enablement</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QComboBox" name="comboBox_5">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QComboBox" name="comboBox_6">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="comboBox_4">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="comboBox_2">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="comboBox">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="comboBox_3">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>#8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Joy Id</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="joylist">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>joylist</tabstop>
+ <tabstop>comboBox</tabstop>
+ <tabstop>comboBox_2</tabstop>
+ <tabstop>comboBox_3</tabstop>
+ <tabstop>comboBox_4</tabstop>
+ <tabstop>comboBox_5</tabstop>
+ <tabstop>comboBox_6</tabstop>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp
new file mode 100644
index 00000000..b0766634
--- /dev/null
+++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp
@@ -0,0 +1,66 @@
+#include "ftnoir_tracker_joystick.h"
+#include "facetracknoir/global-settings.h"
+
+static BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
+{
+ auto self = ( TrackerControls* )pContext;
+
+ self->guids.push_back(pdidInstance->guidInstance);
+ self->ui.joylist->addItem(QString(pdidInstance->tszInstanceName));
+
+ return DIENUM_CONTINUE;
+}
+
+TrackerControls::TrackerControls() : tracker(nullptr)
+{
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.axis_0, ui.comboBox);
+ tie_setting(s.axis_1, ui.comboBox_2);
+ tie_setting(s.axis_2, ui.comboBox_3);
+ tie_setting(s.axis_3, ui.comboBox_4);
+ tie_setting(s.axis_4, ui.comboBox_5);
+ tie_setting(s.axis_5, ui.comboBox_6);
+
+ {
+ auto hr = CoInitialize( nullptr );
+ LPDIRECTINPUT8 g_pDI = nullptr;
+
+ if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION,
+ IID_IDirectInput8, ( VOID** )&g_pDI, NULL ) ) )
+ goto fin;
+
+ if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,
+ EnumJoysticksCallback,
+ this,
+ DIEDFL_ATTACHEDONLY )))
+ goto fin;
+
+fin:
+ if (g_pDI)
+ g_pDI->Release();
+ }
+
+ tie_setting(s.joyid, ui.joylist);
+}
+
+void TrackerControls::doOK() {
+ s.b->save();
+ if (tracker)
+ tracker->reload();
+ this->close();
+}
+
+void TrackerControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dll.cpp b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dll.cpp
new file mode 100644
index 00000000..325d24a4
--- /dev/null
+++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dll.cpp
@@ -0,0 +1,28 @@
+#include "ftnoir_tracker_joystick.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+void FTNoIR_TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "Joystick";
+}
+
+void FTNoIR_TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "Joystick";
+}
+
+void FTNoIR_TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "Joystick";
+}
+
+void FTNoIR_TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/facetracknoir.png");
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_TrackerDll;
+}
diff --git a/ftnoir_tracker_rift/ftnoir_rift.qrc b/ftnoir_tracker_rift/ftnoir_rift.qrc
new file mode 100644
index 00000000..cd174fc4
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_rift.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/rift_medium.png</file>
+ <file>images/rift_small.png</file>
+ <file>images/rift_tiny.png</file>
+ </qresource>
+</RCC>
diff --git a/ftnoir_tracker_rift/ftnoir_rift_clientcontrols.ui b/ftnoir_tracker_rift/ftnoir_rift_clientcontrols.ui
new file mode 100644
index 00000000..a9168239
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_rift_clientcontrols.ui
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UIRiftControls</class>
+ <widget class="QWidget" name="UIRiftControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>209</width>
+ <height>288</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Oculus Rift tracker settings FaceTrackNoIR</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_2">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Enable Axis</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pitch:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="chkEnablePitch">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Yaw:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkEnableYaw">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Roll:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkEnableRoll">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Yaw spring</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="yawSpring">
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Persistence</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="persistence">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>5</number>
+ </property>
+ <property name="minimum">
+ <double>0.900000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.001000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Constant drift</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="constantDrift">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>5</number>
+ </property>
+ <property name="minimum">
+ <double>0.000100000000000</double>
+ </property>
+ <property name="maximum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.001000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDoubleSpinBox" name="deadzone">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>5</number>
+ </property>
+ <property name="maximum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.010000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_tracker_rift/ftnoir_tracker_rift.cpp b/ftnoir_tracker_rift/ftnoir_tracker_rift.cpp
new file mode 100644
index 00000000..b548db71
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_tracker_rift.cpp
@@ -0,0 +1,92 @@
+/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
+#include "ftnoir_tracker_rift.h"
+#include "facetracknoir/global-settings.h"
+#include "OVR.h"
+#include <cstdio>
+
+using namespace OVR;
+
+Rift_Tracker::Rift_Tracker()
+{
+ should_quit = false;
+ pManager = NULL;
+ pSensor = NULL;
+ pSFusion = NULL;
+ old_yaw = 0;
+}
+
+Rift_Tracker::~Rift_Tracker()
+{
+ if (pSensor)
+ pSensor->Release();
+ if (pSFusion)
+ delete pSFusion;
+ if (pManager)
+ pManager->Release();
+ System::Destroy();
+}
+
+void Rift_Tracker::StartTracker(QFrame*)
+{
+ System::Init(Log::ConfigureDefaultLog(LogMask_All));
+ pManager = DeviceManager::Create();
+ if (pManager != NULL)
+ {
+ DeviceEnumerator<OVR::SensorDevice> enumerator = pManager->EnumerateDevices<OVR::SensorDevice>();
+ if (enumerator.IsAvailable())
+ {
+ pSensor = enumerator.CreateDevice();
+
+ if (pSensor){
+ pSFusion = new OVR::SensorFusion();
+ pSFusion->Reset();
+ pSFusion->AttachToSensor(pSensor);
+ }else{
+ QMessageBox::warning(0,"FaceTrackNoIR Error", "Unable to create Rift sensor",QMessageBox::Ok,QMessageBox::NoButton);
+ }
+
+ }else{
+ QMessageBox::warning(0,"FaceTrackNoIR Error", "Unable to enumerate Rift tracker",QMessageBox::Ok,QMessageBox::NoButton);
+ }
+ }else{
+ QMessageBox::warning(0,"FaceTrackNoIR Error", "Unable to start Rift tracker",QMessageBox::Ok,QMessageBox::NoButton);
+ }
+}
+
+
+void Rift_Tracker::GetHeadPoseData(double *data)
+{
+ if (pSFusion != NULL && pSensor != NULL) {
+ Quatf hmdOrient = pSFusion->GetOrientation();
+ double newHeadPose[6];
+
+ float yaw = 0.0f;
+ float pitch = 0.0f;
+ float roll = 0.0f;
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &pitch , &roll);
+ newHeadPose[Pitch] = pitch;
+ newHeadPose[Roll] = roll;
+ newHeadPose[Yaw] = yaw;
+ if (s.useYawSpring)
+ {
+ newHeadPose[Yaw] = old_yaw*s.persistence + (yaw-old_yaw);
+ if(newHeadPose[Yaw]>s.deadzone)newHeadPose[Yaw]-= s.constant_drift;
+ if(newHeadPose[Yaw]<-s.deadzone)newHeadPose[Yaw]+= s.constant_drift;
+ old_yaw=yaw;
+ }
+ if (s.bEnableYaw) {
+ data[Yaw] = newHeadPose[Yaw] * 57.295781f;
+ }
+ if (s.bEnablePitch) {
+ data[Pitch] = newHeadPose[Pitch] * 57.295781f;
+ }
+ if (s.bEnableRoll) {
+ data[Roll] = newHeadPose[Roll] * 57.295781f;
+ }
+ }
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new Rift_Tracker;
+}
diff --git a/ftnoir_tracker_rift/ftnoir_tracker_rift.h b/ftnoir_tracker_rift/ftnoir_tracker_rift.h
new file mode 100644
index 00000000..7162b7ca
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_tracker_rift.h
@@ -0,0 +1,83 @@
+#pragma once
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_ftnoir_rift_clientcontrols.h"
+#include <QMessageBox>
+#include <QWaitCondition>
+#include <cmath>
+#include "facetracknoir/global-settings.h"
+#include "OVR.h"
+#include <memory>
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<bool> bEnableYaw, bEnablePitch, bEnableRoll, useYawSpring;
+ value<double> constant_drift, persistence, deadzone;
+ settings() :
+ b(bundle("Rift")),
+ bEnableYaw(b, "EnableYaw", true),
+ bEnablePitch(b, "EnablePitch", true),
+ bEnableRoll(b, "EnableRoll", true),
+ useYawSpring(b, "yaw-spring", false),
+ constant_drift(b, "constant-drift", 0.000005),
+ persistence(b, "persistence", 0.99999),
+ deadzone(b, "deadzone", 0.02)
+ {}
+};
+
+class Rift_Tracker : public ITracker
+{
+public:
+ Rift_Tracker();
+ virtual ~Rift_Tracker() virt_override;
+
+ void StartTracker(QFrame *) virt_override;
+ void GetHeadPoseData(double *data) virt_override;
+ virtual int preferredHz() virt_override { return 250; }
+ volatile bool should_quit;
+protected:
+ void run(); // qthread override run method
+
+private:
+ static bool isInitialised;
+ OVR::DeviceManager* pManager;
+ OVR::SensorDevice* pSensor;
+ OVR::SensorFusion* pSFusion;
+ settings s;
+ double old_yaw;
+};
+
+class TrackerControls: public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ explicit TrackerControls();
+
+ void registerTracker(ITracker *) {}
+ void unRegisterTracker() {}
+
+private:
+ Ui::UIRiftControls ui;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_TrackerDll : public Metadata
+{
+public:
+ FTNoIR_TrackerDll();
+ ~FTNoIR_TrackerDll();
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+
+private:
+ QString trackerFullName; // Trackers' name and description
+ QString trackerShortName;
+ QString trackerDescription;
+};
+
diff --git a/ftnoir_tracker_rift/ftnoir_tracker_rift_dialog.cpp b/ftnoir_tracker_rift/ftnoir_tracker_rift_dialog.cpp
new file mode 100644
index 00000000..ad532100
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_tracker_rift_dialog.cpp
@@ -0,0 +1,36 @@
+#include "ftnoir_tracker_rift.h"
+#include "facetracknoir/global-settings.h"
+
+TrackerControls::TrackerControls() :
+QWidget()
+{
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.bEnableYaw, ui.chkEnableYaw);
+ tie_setting(s.bEnablePitch, ui.chkEnablePitch);
+ tie_setting(s.bEnableRoll, ui.chkEnableRoll);
+
+ tie_setting(s.constant_drift, ui.constantDrift);
+ tie_setting(s.deadzone, ui.deadzone);
+ tie_setting(s.persistence, ui.persistence);
+ tie_setting(s.useYawSpring, ui.yawSpring);
+}
+
+void TrackerControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void TrackerControls::doCancel() {
+ s.b->revert();
+ close();
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
diff --git a/ftnoir_tracker_rift/ftnoir_tracker_rift_dll.cpp b/ftnoir_tracker_rift/ftnoir_tracker_rift_dll.cpp
new file mode 100644
index 00000000..2b24411c
--- /dev/null
+++ b/ftnoir_tracker_rift/ftnoir_tracker_rift_dll.cpp
@@ -0,0 +1,41 @@
+/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
+#include "ftnoir_tracker_rift.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+FTNoIR_TrackerDll::FTNoIR_TrackerDll() {
+ //populate the description strings
+ trackerFullName = "Rift";
+ trackerShortName = "Rift";
+ trackerDescription = "Rift";
+}
+
+FTNoIR_TrackerDll::~FTNoIR_TrackerDll()
+{
+
+}
+
+void FTNoIR_TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = trackerFullName;
+}
+
+void FTNoIR_TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = trackerShortName;
+}
+
+void FTNoIR_TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = trackerDescription;
+}
+
+void FTNoIR_TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/rift_tiny.png");
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_TrackerDll;
+}
diff --git a/ftnoir_tracker_rift/images/medium.png b/ftnoir_tracker_rift/images/medium.png
new file mode 100644
index 00000000..a5ba49e7
--- /dev/null
+++ b/ftnoir_tracker_rift/images/medium.png
Binary files differ
diff --git a/ftnoir_tracker_rift/images/rift_medium.png b/ftnoir_tracker_rift/images/rift_medium.png
new file mode 100644
index 00000000..a5ba49e7
--- /dev/null
+++ b/ftnoir_tracker_rift/images/rift_medium.png
Binary files differ
diff --git a/ftnoir_tracker_rift/images/rift_small.png b/ftnoir_tracker_rift/images/rift_small.png
new file mode 100644
index 00000000..3f18080c
--- /dev/null
+++ b/ftnoir_tracker_rift/images/rift_small.png
Binary files differ
diff --git a/ftnoir_tracker_rift/images/rift_tiny.png b/ftnoir_tracker_rift/images/rift_tiny.png
new file mode 100644
index 00000000..76fe0f58
--- /dev/null
+++ b/ftnoir_tracker_rift/images/rift_tiny.png
Binary files differ
diff --git a/ftnoir_tracker_rift/images/small.png b/ftnoir_tracker_rift/images/small.png
new file mode 100644
index 00000000..3f18080c
--- /dev/null
+++ b/ftnoir_tracker_rift/images/small.png
Binary files differ
diff --git a/ftnoir_tracker_rift/images/tiny.png b/ftnoir_tracker_rift/images/tiny.png
new file mode 100644
index 00000000..76fe0f58
--- /dev/null
+++ b/ftnoir_tracker_rift/images/tiny.png
Binary files differ
diff --git a/ftnoir_tracker_udp/ftnoir_ftnclientcontrols.ui b/ftnoir_tracker_udp/ftnoir_ftnclientcontrols.ui
new file mode 100644
index 00000000..5c602792
--- /dev/null
+++ b/ftnoir_tracker_udp/ftnoir_ftnclientcontrols.ui
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFTNClientControls</class>
+ <widget class="QWidget" name="UICFTNClientControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>411</width>
+ <height>232</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>UDP tracker settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>../facetracknoir/images/facetracknoir.png</normaloff>../facetracknoir/images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="_vertical_layout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Port-number</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinPortNumber">
+ <property name="minimum">
+ <number>5550</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>85</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Enable Axis</string>
+ </property>
+ <widget class="QWidget" name="layoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>147</width>
+ <height>68</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Roll:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Pitch:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Yaw:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="chkEnableRoll">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkEnablePitch">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkEnableYaw">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>X:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QCheckBox" name="chkEnableX">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QCheckBox" name="chkEnableY">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QCheckBox" name="chkEnableZ">
+ <property name="maximumSize">
+ <size>
+ <width>20</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Enter the port-number for the remote PC.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Remember: you may have to change firewall-settings too!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnOK">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCancel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>spinPortNumber</tabstop>
+ <tabstop>btnOK</tabstop>
+ <tabstop>btnCancel</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp b/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp
new file mode 100644
index 00000000..02ae21f0
--- /dev/null
+++ b/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp
@@ -0,0 +1,83 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* Homepage: http://facetracknoir.sourceforge.net/home/default.htm *
+* *
+* 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 "ftnoir_tracker_udp.h"
+#include "facetracknoir/global-settings.h"
+
+FTNoIR_Tracker::FTNoIR_Tracker() : should_quit(false)
+{
+ should_quit = false;
+
+ for (int i = 0; i < 6; i++)
+ newHeadPose[i] = 0;
+}
+
+FTNoIR_Tracker::~FTNoIR_Tracker()
+{
+ should_quit = true;
+ wait();
+}
+
+/** QThread run @override **/
+void FTNoIR_Tracker::run() {
+ forever {
+ if (should_quit)
+ break;
+ while (inSocket.hasPendingDatagrams()) {
+ QMutexLocker foo(&mutex);
+ QByteArray datagram;
+ datagram.resize(sizeof(newHeadPose));
+ inSocket.readDatagram((char * ) newHeadPose, sizeof(double[6]));
+ }
+ usleep(10000);
+ }
+}
+
+void FTNoIR_Tracker::StartTracker(QFrame*)
+{
+ (void) inSocket.bind(QHostAddress::Any, (int) s.port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
+ start();
+}
+
+void FTNoIR_Tracker::GetHeadPoseData(double *data)
+{
+ QMutexLocker foo(&mutex);
+ if (s.enable_x)
+ data[TX] = newHeadPose[TX];
+ if (s.enable_y)
+ data[TY] = newHeadPose[TY];
+ if (s.enable_z)
+ data[TZ] = newHeadPose[TZ];
+ if (s.enable_yaw)
+ data[Yaw] = newHeadPose[Yaw];
+ if (s.enable_pitch)
+ data[Pitch] = newHeadPose[Pitch];
+ if (s.enable_roll)
+ data[Roll] = newHeadPose[Roll];
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
+{
+ return new FTNoIR_Tracker;
+}
diff --git a/ftnoir_tracker_udp/ftnoir_tracker_udp.h b/ftnoir_tracker_udp/ftnoir_tracker_udp.h
new file mode 100644
index 00000000..62eb67df
--- /dev/null
+++ b/ftnoir_tracker_udp/ftnoir_tracker_udp.h
@@ -0,0 +1,77 @@
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "ui_ftnoir_ftnclientcontrols.h"
+#include <QThread>
+#include <QUdpSocket>
+#include <QMessageBox>
+#include <QMutex>
+#include <QWaitCondition>
+#include <math.h>
+#include "facetracknoir/global-settings.h"
+#include "facetracknoir/options.h"
+using namespace options;
+
+struct settings {
+ pbundle b;
+ value<int> port;
+ value<bool> enable_roll, enable_pitch, enable_yaw,
+ enable_x, enable_y, enable_z;
+ settings() :
+ b(bundle("udp-tracker")),
+ port(b, "port", 4242),
+ enable_roll(b, "enable-roll", true),
+ enable_pitch(b, "enable-pitch", true),
+ enable_yaw(b, "enable-yaw", true),
+ enable_x(b, "enable-x", true),
+ enable_y(b, "enable-y", true),
+ enable_z(b, "enable-y", true)
+ {}
+};
+
+class FTNoIR_Tracker : public ITracker, public QThread
+{
+public:
+ FTNoIR_Tracker();
+ ~FTNoIR_Tracker();
+ void StartTracker(QFrame *);
+ void GetHeadPoseData(double *data);
+ volatile bool should_quit;
+protected:
+ void run(); // qthread override run method
+private:
+ QUdpSocket inSocket;
+ QHostAddress destIP;
+ QHostAddress srcIP;
+ double newHeadPose[6];
+ QMutex mutex;
+ settings s;
+};
+
+// Widget that has controls for FTNoIR protocol client-settings.
+class TrackerControls: public QWidget, public ITrackerDialog
+{
+ Q_OBJECT
+public:
+
+ explicit TrackerControls();
+ void registerTracker(ITracker *) {}
+ void unRegisterTracker() {}
+private:
+ Ui::UICFTNClientControls ui;
+ settings s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+//*******************************************************************************************************
+// FaceTrackNoIR Tracker DLL. Functions used to get general info on the Tracker
+//*******************************************************************************************************
+class FTNoIR_TrackerDll : public Metadata
+{
+public:
+ void getFullName(QString *strToBeFilled);
+ void getShortName(QString *strToBeFilled);
+ void getDescription(QString *strToBeFilled);
+ void getIcon(QIcon *icon);
+};
+
diff --git a/ftnoir_tracker_udp/ftnoir_tracker_udp_dialog.cpp b/ftnoir_tracker_udp/ftnoir_tracker_udp_dialog.cpp
new file mode 100644
index 00000000..8d1b99f2
--- /dev/null
+++ b/ftnoir_tracker_udp/ftnoir_tracker_udp_dialog.cpp
@@ -0,0 +1,58 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 Wim Vriend (Developing) *
+* Ron Hendriks (Researching and Testing) *
+* *
+* Homepage: http://facetracknoir.sourceforge.net/home/default.htm *
+* *
+* 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 "ftnoir_tracker_udp.h"
+#include "facetracknoir/global-settings.h"
+
+TrackerControls::TrackerControls() :
+QWidget()
+{
+ ui.setupUi( this );
+
+ connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK()));
+ connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel()));
+
+ tie_setting(s.enable_x, ui.chkEnableX);
+ tie_setting(s.enable_y, ui.chkEnableY);
+ tie_setting(s.enable_z, ui.chkEnableZ);
+ tie_setting(s.enable_yaw, ui.chkEnableYaw);
+ tie_setting(s.enable_pitch, ui.chkEnablePitch);
+ tie_setting(s.enable_roll, ui.chkEnableRoll);
+ tie_setting(s.port, ui.spinPortNumber);
+}
+
+void TrackerControls::doOK() {
+ s.b->save();
+ this->close();
+}
+
+void TrackerControls::doCancel() {
+ s.b->revert();
+ this->close();
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT ITrackerDialog* CALLING_CONVENTION GetDialog( )
+{
+ return new TrackerControls;
+}
diff --git a/ftnoir_tracker_udp/ftnoir_tracker_udp_dll.cpp b/ftnoir_tracker_udp/ftnoir_tracker_udp_dll.cpp
new file mode 100644
index 00000000..22dc7daa
--- /dev/null
+++ b/ftnoir_tracker_udp/ftnoir_tracker_udp_dll.cpp
@@ -0,0 +1,52 @@
+/********************************************************************************
+* FaceTrackNoIR This program is a private project of some enthusiastic *
+* gamers from Holland, who don't like to pay much for *
+* head-tracking. *
+* *
+* Copyright (C) 2012 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 "ftnoir_tracker_udp.h"
+#include <QDebug>
+#include "facetracknoir/global-settings.h"
+
+void FTNoIR_TrackerDll::getFullName(QString *strToBeFilled)
+{
+ *strToBeFilled = "UDP";
+}
+
+void FTNoIR_TrackerDll::getShortName(QString *strToBeFilled)
+{
+ *strToBeFilled = "UDP";
+}
+
+void FTNoIR_TrackerDll::getDescription(QString *strToBeFilled)
+{
+ *strToBeFilled = "UDP";
+}
+
+void FTNoIR_TrackerDll::getIcon(QIcon *icon)
+{
+ *icon = QIcon(":/images/facetracknoir.png");
+}
+
+extern "C" FTNOIR_TRACKER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata()
+{
+ return new FTNoIR_TrackerDll;
+}
diff --git a/install-fail-tool b/install-fail-tool
new file mode 100755
index 00000000..99f8fbdf
--- /dev/null
+++ b/install-fail-tool
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test -n "$1" || exit 1
+
+dir="$1"
+
+for i in "$dir"/* "$dir"/*/*; do
+ { test -x "$i" && test -f "$i"; } || continue
+ echo ---- $i ----
+ install_name_tool -id "@executable_path/$(basename -- "$i")" "$i"
+
+ otool -L "$i" | awk '{ print $1 }' |
+ while read l; do
+ j="$(basename -- "$l")"
+ if test -e "$dir/$j"; then
+ install_name_tool -change "$l" "@executable_path/$j" "$i"
+ fi
+ done
+done
diff --git a/installer/opentrack-installer.iss b/installer/opentrack-installer.iss
new file mode 100644
index 00000000..cf0ebec6
--- /dev/null
+++ b/installer/opentrack-installer.iss
@@ -0,0 +1,52 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+
+#include "../build-mingw-w64/opentrack-version.h"
+#define MyAppName "opentrack"
+#define MyAppVersion OPENTRACK_VERSION
+#define MyAppPublisher "opentrack"
+#define MyAppURL "http://github.com/opentrack/opentrack"
+#define MyAppExeName "opentrack.exe"
+
+[Setup]
+; NOTE: The value of AppId uniquely identifies this application.
+; Do not use the same AppId value in installers for other applications.
+; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
+AppId={{E454805B-11A6-469F-9FA9-865BEAD787D0}
+AppName={#MyAppName}
+AppVersion={#MyAppVersion}
+;AppVerName={#MyAppName} {#MyAppVersion}
+AppPublisher={#MyAppPublisher}
+AppPublisherURL={#MyAppURL}
+AppSupportURL={#MyAppURL}
+AppUpdatesURL={#MyAppURL}
+DefaultDirName={pf}\{#MyAppName}
+DefaultGroupName={#MyAppName}
+AllowNoIcons=yes
+OutputBaseFilename={#MyAppVersion}-win32-setup
+SetupIconFile=..\facetracknoir\facetracknoir.ico
+Compression=lzma2/ultra64
+SolidCompression=yes
+DisableWelcomePage=True
+DisableReadyPage=True
+DisableReadyMemo=True
+RestartIfNeededByRun=False
+InternalCompressLevel=ultra
+CompressionThreads=2
+MinVersion=0,5.01sp2
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Tasks]
+Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+
+[Files]
+Source: "..\build-mingw-w64\install\*"; DestDir: "{app}"; Flags: ignoreversion createallsubdirs recursesubdirs
+
+[Icons]
+Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
+Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
+
+[Run]
+Filename: "{app}\{#MyAppExeName}"; Flags: nowait postinstall skipifsilent; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"
diff --git a/opentrack-api/context.cpp b/opentrack-api/context.cpp
new file mode 100644
index 00000000..04fa5ac2
--- /dev/null
+++ b/opentrack-api/context.cpp
@@ -0,0 +1,112 @@
+#include "opentrack-guts.h"
+#include "opentrack.h"
+
+#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
+
+static Metadata* get_metadata(DynamicLibrary* lib, QString& longName, QIcon& icon)
+{
+ Metadata* meta;
+ if (!lib->Metadata || ((meta = lib->Metadata()), !meta))
+ return NULL;
+ meta->getFullName(&longName);
+ meta->getIcon(&icon);
+ return meta;
+}
+
+static QList<opentrack_meta> list_files(QString filter)
+{
+ QList<opentrack_meta> ret;
+ QStringList filenames = QDir((qApp->applicationDirPath())).entryList(
+ QStringList() << (LIB_PREFIX + filter + ("*." SONAME)),
+ QDir::Files, QDir::Name );
+ for ( int i = 0; i < filenames.size(); i++) {
+ QIcon icon;
+ QString long_name;
+ QString str = filenames.at(i);
+ DynamicLibrary* lib = new DynamicLibrary(str);
+ qDebug() << "Loading" << str;
+ std::cout.flush();
+ Metadata* meta;
+ if (!(meta = get_metadata(lib, long_name, icon)))
+ {
+ delete lib;
+ continue;
+ }
+ /* TODO perhaps return full name and somesuch */
+ delete meta;
+ QString prefix(LIB_PREFIX + filter);
+ QString suffix("." SONAME);
+ if (str.size() > prefix.size() + suffix.size() && str.startsWith(prefix) && str.endsWith(suffix))
+ {
+ auto str2 = str.mid(prefix.size(), str.size() - prefix.size() - suffix.size());
+ opentrack_meta item(str2, lib);
+ ret.push_back(item);
+ }
+ }
+
+ return ret;
+}
+
+opentrack_ctx::opentrack_ctx(int argc, char** argv, void* window_parent) :
+ app(argc, argv),
+ meta_list(list_files("opentrack-tracker-")),
+ fake_frame(window_parent)
+{
+ const int count = meta_list.size();
+ list = new char*[count + 1];
+ for (int i = 0; i < count; i++)
+ {
+ QByteArray tmp = meta_list.at(i).path.toUtf8();
+ int len = tmp.size();
+ auto foo = new char[len+1];
+ for (int j = 0; j < len; j++)
+ foo[j] = tmp.at(j);
+ foo[len] = '\0';
+ list[i] = foo;
+ }
+ list[count] = NULL;
+}
+
+opentrack_ctx::~opentrack_ctx()
+{
+ for (int i = 0; list[i]; i++)
+ {
+ delete[] list[i];
+ }
+ delete[] list;
+}
+
+extern "C"
+{
+
+OPENTRACK_EXPORT const char** opentrack_enum_trackers(opentrack ctx)
+{
+ return const_cast<const char**>(ctx->list);
+}
+
+OPENTRACK_EXPORT opentrack opentrack_make_ctx(int argc, char** argv, void* window_parent)
+{
+ return new opentrack_ctx(argc, argv, window_parent);
+}
+
+OPENTRACK_EXPORT void opentrack_finalize_ctx(opentrack foo)
+{
+ delete foo;
+}
+
+}
+
diff --git a/opentrack-api/gnuc-version-script.txt b/opentrack-api/gnuc-version-script.txt
new file mode 100644
index 00000000..cd3a568d
--- /dev/null
+++ b/opentrack-api/gnuc-version-script.txt
@@ -0,0 +1,12 @@
+{
+ global:
+ opentrack_make_ctx;
+ opentrack_finalize_ctx;
+ opentrack_enum_trackers;
+ opentrack_make_tracker;
+ opentrack_tracker_start;
+ opentrack_tracker_tick;
+ opentrack_finalize_tracker;
+ local:
+ *;
+};
diff --git a/opentrack-api/opentrack-guts.h b/opentrack-api/opentrack-guts.h
new file mode 100644
index 00000000..c8e3309a
--- /dev/null
+++ b/opentrack-api/opentrack-guts.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <QFrame>
+#include <QDir>
+#include <QList>
+#include <QStringList>
+#include <QDebug>
+#include <QIcon>
+#include <QShowEvent>
+#include <iostream>
+#include <cstring>
+#include <QString>
+#include <QApplication>
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+#include "facetracknoir/global-settings.h"
+#include <memory>
+
+typedef ITracker* opentrack_tracker;
+
+class opentrack_meta {
+public:
+ QString path;
+ std::shared_ptr<DynamicLibrary> lib;
+
+ opentrack_meta(QString& path, DynamicLibrary* lib) :
+ path(path), lib(lib)
+ {}
+};
+
+class MyFrame : public QFrame {
+ Q_OBJECT
+public:
+ MyFrame(void* parent)
+ {
+ if (parent == (void*) -1)
+ {
+ show();
+ setVisible(false);
+ hide();
+ }
+ else
+ {
+ create((WId) parent);
+ }
+ }
+ explicit MyFrame() {}
+};
+
+typedef class opentrack_ctx {
+public:
+ QApplication app;
+ char** list;
+ QList<opentrack_meta> meta_list;
+ MyFrame fake_frame;
+ opentrack_ctx(int argc, char** argv, void* window_parent);
+ ~opentrack_ctx();
+} *opentrack;
diff --git a/opentrack-api/opentrack.h b/opentrack-api/opentrack.h
new file mode 100644
index 00000000..88ba6cf0
--- /dev/null
+++ b/opentrack-api/opentrack.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef IN_OPENTRACK
+/* opaque pointers, forward definitions */
+struct opentrack_opaque_ctx;
+typedef struct opentrack_opaque_ctx* opentrack;
+struct opentrack_opaque_tracker;
+typedef struct opentrack_opaque_tracker* opentrack_tracker;
+#endif
+
+#ifdef IN_OPENTRACK
+# ifdef _WIN32
+# define OPENTRACK_EXPORT __declspec(dllexport)
+# else
+# define OPENTRACK_EXPORT
+# endif
+#else
+# ifdef _WIN32
+# define OPENTRACK_EXPORT __declspec(dllimport)
+# else
+# define OPENTRACK_EXPORT
+# endif
+#endif
+
+/* for `opentrack_tracker_tick', individual headpose elts */
+#ifndef IN_OPENTRACK
+enum opentrack_dof {
+ TX = 0,
+ TY,
+ TZ,
+ Yaw,
+ Pitch,
+ Roll,
+ DOF_count
+};
+#endif
+
+OPENTRACK_EXPORT opentrack opentrack_make_ctx(int argc, char** argv, void* window_parent);
+OPENTRACK_EXPORT void opentrack_finalize_ctx(opentrack self);
+
+/* no need to free the return value; invalid to modify it */
+OPENTRACK_EXPORT const char** opentrack_enum_trackers(opentrack self);
+
+/*
+ * don't `opentrack_tracker_tick an unstarted tracker, it's invalid to do so
+ * it's also invalid to start a finalized tracker
+ */
+OPENTRACK_EXPORT opentrack_tracker opentrack_make_tracker(opentrack ctx, const char* name);
+OPENTRACK_EXPORT void opentrack_tracker_start(opentrack self, opentrack_tracker tracker);
+OPENTRACK_EXPORT void opentrack_tracker_tick(opentrack_tracker tracker, double* headpose);
+OPENTRACK_EXPORT void opentrack_finalize_tracker(opentrack_tracker tracker);
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/opentrack-api/trackers.cpp b/opentrack-api/trackers.cpp
new file mode 100644
index 00000000..5027ec1d
--- /dev/null
+++ b/opentrack-api/trackers.cpp
@@ -0,0 +1,38 @@
+#include "opentrack-guts.h"
+#include "opentrack.h"
+
+extern "C" {
+
+opentrack_tracker OPENTRACK_EXPORT opentrack_make_tracker(opentrack ctx, const char* name)
+{
+ for (int i = 0; i < ctx->meta_list.size(); i++)
+ {
+ auto meta = ctx->meta_list.at(i);
+ if (ctx->meta_list.at(i).path == name)
+ {
+ ITracker* foo = static_cast<ITracker*>(meta.lib->Constructor());
+ return foo;
+ }
+ }
+ return NULL;
+}
+
+void OPENTRACK_EXPORT opentrack_finalize_tracker(opentrack_tracker tracker)
+{
+ delete tracker;
+}
+
+void OPENTRACK_EXPORT opentrack_tracker_start(opentrack self, opentrack_tracker tracker)
+{
+ // hot damn, this is problematic!
+ // need to pass QFrame from somewhere
+ return tracker->StartTracker(&self->fake_frame);
+}
+
+void OPENTRACK_EXPORT opentrack_tracker_tick(opentrack_tracker tracker, double* headpose)
+{
+ tracker->GetHeadPoseData(headpose);
+ QApplication::processEvents(0, 5);
+}
+
+}
diff --git a/opentrack-version.h b/opentrack-version.h
new file mode 100644
index 00000000..f31d5edf
--- /dev/null
+++ b/opentrack-version.h
@@ -0,0 +1,7 @@
+#ifndef OPENTRACK_VERSION
+# define OPENTRACK_VERSION @OPENTRACK_COMMIT_VERSION@
+#else
+# ifndef OPENTRACK_VERSION
+# define OPENTRACK_VERSION "Mourns-For-Trees"
+# endif
+#endif
diff --git a/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp
new file mode 100644
index 00000000..1a9da10a
--- /dev/null
+++ b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp
@@ -0,0 +1,111 @@
+/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl>
+ * Adapted to FaceTrackNoIR by Wim Vriend.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+#include "qfunctionconfigurator.h"
+
+#include <QtCore/QtPlugin>
+#include "qfunctionconfiguratorplugin.h"
+
+
+QFunctionConfiguratorPlugin::QFunctionConfiguratorPlugin(QObject *parent)
+ : QObject(parent)
+{
+ initialized = false;
+}
+
+void QFunctionConfiguratorPlugin::initialize(QDesignerFormEditorInterface *)
+{
+ if (initialized)
+ return;
+
+ initialized = true;
+}
+
+bool QFunctionConfiguratorPlugin::isInitialized() const
+{
+ return initialized;
+}
+
+QWidget *QFunctionConfiguratorPlugin::createWidget(QWidget *parent)
+{
+ return new QFunctionConfigurator(parent);
+}
+
+QString QFunctionConfiguratorPlugin::name() const
+{
+ return "QFunctionConfigurator";
+}
+
+QString QFunctionConfiguratorPlugin::group() const
+{
+ return "My Plugins";
+}
+
+QIcon QFunctionConfiguratorPlugin::icon() const
+{
+ return QIcon();
+}
+
+QString QFunctionConfiguratorPlugin::toolTip() const
+{
+ return QString();
+}
+
+QString QFunctionConfiguratorPlugin::whatsThis() const
+{
+ return QString();
+}
+
+bool QFunctionConfiguratorPlugin::isContainer() const
+{
+ return false;
+}
+
+QString QFunctionConfiguratorPlugin::domXml() const
+{
+ return "<widget class=\"QFunctionConfigurator\" name=\"qFunctionA\">\n"
+ " <property name=\"geometry\">\n"
+ " <rect>\n"
+ " <x>0</x>\n"
+ " <y>0</y>\n"
+ " <width>161</width>\n"
+ " <height>220</height>\n"
+ " </rect>\n"
+ " </property>\n"
+ " <property name=\"colorBezier\">\n"
+ " <color>\n"
+ " <red>255</red>\n"
+ " <green>170</green>\n"
+ " <blue>0</blue>\n"
+ " </color>\n"
+ " </property>\n"
+ " <property name=\"stringInputEGU\" stdset=\"0\">\n"
+ " <string>Input Yaw (degr.)</string>\n"
+ " </property>\n"
+ " <property name=\"stringOutputEGU\" stdset=\"0\">\n"
+ " <string>Output Yaw (degr.)</string>\n"
+ " </property>\n"
+ " <property name=\"maxInputEGU\" stdset=\"0\">\n"
+ " <number>50</number>\n"
+ " </property>\n"
+ " <property name=\"maxOutputEGU\" stdset=\"0\">\n"
+ " <number>180</number>\n"
+ " </property>\n"
+ " <property name=\"pixPerEGU_Input\" stdset=\"0\">\n"
+ " <number>2</number>\n"
+ " </property>\n"
+ " <property name=\"pixPerEGU_Output\" stdset=\"0\">\n"
+ " <number>1</number>\n"
+ " </property>\n"
+ "</widget>\n";
+}
+
+QString QFunctionConfiguratorPlugin::includeFile() const
+{
+ return "qfunctionconfigurator.h";
+}
+
+Q_EXPORT_PLUGIN2(qfunctionconfigurator, QFunctionConfiguratorPlugin)
diff --git a/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h
new file mode 100644
index 00000000..bc637338
--- /dev/null
+++ b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl>
+ * Adapted to FaceTrackNoIR by Wim Vriend.
+ * 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 QFUNCTIONCONFIGURATORPLUGIN_H
+#define QFUNCTIONCONFIGURATORPLUGIN_H
+
+#include <QDesignerCustomWidgetInterface>
+
+class QFunctionConfiguratorPlugin : public QObject, public QDesignerCustomWidgetInterface
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface" FILE "analogclock.json")
+ Q_INTERFACES(QDesignerCustomWidgetInterface)
+
+public:
+ QFunctionConfiguratorPlugin(QObject *parent = 0);
+
+ bool isContainer() const;
+ bool isInitialized() const;
+ QIcon icon() const;
+ QString domXml() const;
+ QString group() const;
+ QString includeFile() const;
+ QString name() const;
+ QString toolTip() const;
+ QString whatsThis() const;
+ QWidget *createWidget(QWidget *parent);
+ void initialize(QDesignerFormEditorInterface *core);
+
+private:
+ bool initialized;
+};
+
+#endif // QFUNCTIONCONFIGURATORPLUGIN_H
diff --git a/qfunctionconfigurator/functionconfig.cpp b/qfunctionconfigurator/functionconfig.cpp
new file mode 100644
index 00000000..97a6db24
--- /dev/null
+++ b/qfunctionconfigurator/functionconfig.cpp
@@ -0,0 +1,281 @@
+/* Copyright (c) 2012, 2013 Stanisław 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.
+ */
+
+#include <QMutexLocker>
+#include <QCoreApplication>
+#include <QPointF>
+#include <QList>
+#include "functionconfig.h"
+#include <QtAlgorithms>
+#include <QtAlgorithms>
+#include <QSettings>
+#include <math.h>
+#include <QPixmap>
+
+//
+// Constructor with List of Points in argument.
+//
+FunctionConfig::FunctionConfig(QString title, int intMaxInput, int intMaxOutput) :
+ _mutex(QMutex::Recursive)
+{
+ _title = title;
+ _points = QList<QPointF>();
+ _data = 0;
+ _size = 0;
+ lastValueTracked = QPointF(0,0);
+ _tracking_active = false;
+ _max_Input = intMaxInput; // Added WVR 20120805
+ _max_Output = intMaxOutput;
+ QSettings settings("opentrack"); // Registry settings (in HK_USER)
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile( currentFile, QSettings::IniFormat );
+ loadSettings(iniFile);
+ reload();
+}
+
+void FunctionConfig::setTrackingActive(bool blnActive)
+{
+ _tracking_active = blnActive;
+}
+
+FunctionConfig::FunctionConfig() :
+ _mutex(QMutex::Recursive),
+ _data(0),
+ _size(0),
+ _tracking_active(false),
+ _max_Input(0),
+ _max_Output(0)
+{
+}
+
+//
+// Calculate the value of the function, given the input 'x'.
+// Used to draw the curve and, most importantly, to translate input to output.
+// The return-value is also stored internally, so the Widget can show the current value, when the Tracker is running.
+//
+float FunctionConfig::getValue(float x) {
+ QMutexLocker foo(&_mutex);
+ int x2 = (int) (std::min<float>(std::max<float>(x, -360), 360) * MEMOIZE_PRECISION);
+ float ret = getValueInternal(x2);
+ lastValueTracked.setX(x);
+ lastValueTracked.setY(ret);
+ return ret;
+}
+
+//
+// The return-value is also stored internally, so the Widget can show the current value, when the Tracker is running.
+//
+bool FunctionConfig::getLastPoint(QPointF& point ) {
+ QMutexLocker foo(&_mutex);
+ point = lastValueTracked;
+ return _tracking_active;
+}
+
+float FunctionConfig::getValueInternal(int x) {
+ float sign = x < 0 ? -1 : 1;
+ x = x < 0 ? -x : x;
+ float ret;
+ if (!_data)
+ ret = 0;
+ else if (_size == 0)
+ ret = 0;
+ else if (x < 0)
+ ret = 0;
+ else if (x < _size && x >= 0)
+ ret = _data[x];
+ else
+ ret = _data[_size - 1];
+ return ret * sign;
+}
+
+static __inline QPointF ensureInBounds(QList<QPointF> points, int i) {
+ int siz = points.size();
+ if (siz == 0 || i < 0)
+ return QPointF(0, 0);
+ if (siz > i)
+ return points[i];
+ return points[siz - 1];
+}
+
+static bool sortFn(const QPointF& one, const QPointF& two) {
+ return one.x() < two.x();
+}
+
+void FunctionConfig::reload() {
+ _size = 0;
+
+ if (_points.size())
+ qStableSort(_points.begin(), _points.end(), sortFn);
+
+ if (_data)
+ delete[] _data;
+ _data = NULL;
+ if (_points.size()) {
+ _data = new float[_size = MEMOIZE_PRECISION * _points[_points.size() - 1].x()];
+
+ for (int i = 0; i < _size; i++)
+ _data[i] = -1e6;
+
+ for (int k = 0; k < _points[0].x() * MEMOIZE_PRECISION; k++) {
+ if (k < _size)
+ _data[k] = _points[0].y() * k / (_points[0].x() * MEMOIZE_PRECISION);
+ }
+
+ for (int i = 0; i < _points.size(); i++) {
+ QPointF p0 = ensureInBounds(_points, i - 1);
+ QPointF p1 = ensureInBounds(_points, i);
+ QPointF p2 = ensureInBounds(_points, i + 1);
+ QPointF p3 = ensureInBounds(_points, i + 2);
+
+ int end = p2.x() * MEMOIZE_PRECISION;
+ int start = p1.x() * MEMOIZE_PRECISION;
+
+ for (int j = start; j < end && j < _size; j++) {
+ double t = (j - start) / (double) (end - start);
+ double t2 = t*t;
+ double t3 = t*t*t;
+
+ int x = .5 * ((2. * p1.x()) +
+ (-p0.x() + p2.x()) * t +
+ (2. * p0.x() - 5. * p1.x() + 4. * p2.x() - p3.x()) * t2 +
+ (-p0.x() + 3. * p1.x() - 3. * p2.x() + p3.x()) * t3)
+ * MEMOIZE_PRECISION;
+
+ float y = .5 * ((2. * p1.y()) +
+ (-p0.y() + p2.y()) * t +
+ (2. * p0.y() - 5. * p1.y() + 4. * p2.y() - p3.y()) * t2 +
+ (-p0.y() + 3. * p1.y() - 3. * p2.y() + p3.y()) * t3);
+
+ if (x >= 0 && x < _size)
+ _data[x] = y;
+ }
+ }
+
+ float last = 0;
+
+ for (int i = 0; i < _size; i++)
+ {
+ if (_data[i] == -1e6)
+ _data[i] = last;
+ last = _data[i];
+ }
+ }
+}
+
+FunctionConfig::~FunctionConfig() {
+ if (_data)
+ delete[] _data;
+}
+
+//
+// Remove a Point from the Function.
+// Used by the Widget.
+//
+void FunctionConfig::removePoint(int i) {
+ QMutexLocker foo(&_mutex);
+ if (i >= 0 && i < _points.size())
+ {
+ _points.removeAt(i);
+ reload();
+ }
+}
+
+//
+// Add a Point to the Function.
+// Used by the Widget and by loadSettings.
+//
+void FunctionConfig::addPoint(QPointF pt) {
+ QMutexLocker foo(&_mutex);
+ _points.append(pt);
+ reload();
+}
+
+//
+// Move a Function Point.
+// Used by the Widget.
+//
+void FunctionConfig::movePoint(int idx, QPointF pt) {
+ QMutexLocker foo(&_mutex);
+ if (idx >= 0 && idx < _points.size())
+ {
+ _points[idx] = pt;
+ reload();
+ }
+}
+
+//
+// Return the List of Points.
+// Used by the Widget.
+//
+QList<QPointF> FunctionConfig::getPoints() {
+ QList<QPointF> ret;
+ QMutexLocker foo(&_mutex);
+ for (int i = 0; i < _points.size(); i++) {
+ ret.append(_points[i]);
+ }
+ return ret;
+}
+
+//
+// Load the Points for the Function from the INI-file designated by settings.
+// Settings for a specific Curve are loaded from their own Group in the INI-file.
+//
+void FunctionConfig::loadSettings(QSettings& settings) {
+ QMutexLocker foo(&_mutex);
+ QPointF newPoint;
+
+ QList<QPointF> points;
+ settings.beginGroup(QString("Curves-%1").arg(_title));
+
+ int max = settings.value("point-count", 0).toInt();
+
+ for (int i = 0; i < max; i++) {
+ newPoint = QPointF(settings.value(QString("point-%1-x").arg(i), 0).toFloat(),
+ settings.value(QString("point-%1-y").arg(i), 0).toFloat());
+ //
+ // Make sure the new Point fits in the Function Range.
+ // Maybe this can be improved?
+ //
+ if (newPoint.x() > _max_Input) {
+ newPoint.setX(_max_Input);
+ }
+ if (newPoint.y() > _max_Output) {
+ newPoint.setY(_max_Output);
+ }
+ points.append(newPoint);
+ }
+ settings.endGroup();
+ _points = points;
+ reload();
+}
+
+//
+// Save the Points for the Function to the INI-file designated by settings.
+// Settings for a specific Curve are saved in their own Group in the INI-file.
+// The number of Points is also saved, to make loading more convenient.
+//
+void FunctionConfig::saveSettings(QSettings& settings) {
+ QMutexLocker foo(&_mutex);
+ settings.beginGroup(QString("Curves-%1").arg(_title));
+ int max = _points.size();
+ settings.setValue("point-count", max);
+ for (int i = 0; i < max; i++) {
+ settings.setValue(QString("point-%1-x").arg(i), _points[i].x());
+ settings.setValue(QString("point-%1-y").arg(i), _points[i].y());
+ }
+
+ for (int i = max; true; i++)
+ {
+ QString x = QString("point-%1-x").arg(i);
+ if (!settings.contains(x))
+ break;
+ settings.remove(x);
+ settings.remove(QString("point-%1-y").arg(i));
+ }
+ settings.endGroup();
+}
diff --git a/qfunctionconfigurator/functionconfig.h b/qfunctionconfigurator/functionconfig.h
new file mode 100644
index 00000000..4d771dfd
--- /dev/null
+++ b/qfunctionconfigurator/functionconfig.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2011-2012, 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.
+ */
+
+#pragma once
+
+#include <QList>
+#include <QPointF>
+#include <QString>
+#include <QSettings>
+#include <QMutex>
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+
+#define MEMOIZE_PRECISION 100
+
+class FTNOIR_TRACKER_BASE_EXPORT FunctionConfig {
+private:
+ QMutex _mutex;
+ QList<QPointF> _points;
+ void reload();
+ float* _data;
+ int _size;
+ QString _title;
+ float getValueInternal(int x);
+ QPointF lastValueTracked; // The last input value requested by the Tracker, with it's output-value.
+ volatile bool _tracking_active;
+ int _max_Input;
+ int _max_Output;
+ FunctionConfig(const FunctionConfig&) = delete;
+public:
+ int maxInput() const { return _max_Input; }
+ int maxOutput() const { return _max_Output; }
+ //
+ // Contructor(s) and destructor
+ //
+ FunctionConfig();
+ FunctionConfig(QString title, int intMaxInput, int intMaxOutput);
+ ~FunctionConfig();
+
+ float getValue(float x);
+ bool getLastPoint(QPointF& point); // Get the last Point that was requested.
+
+ //
+ // Functions to manipulate the Function
+ //
+ void removePoint(int i);
+ void removeAllPoints() {
+ QMutexLocker foo(&_mutex);
+ _points.clear();
+ reload();
+ }
+
+ void addPoint(QPointF pt);
+ void movePoint(int idx, QPointF pt);
+ QList<QPointF> getPoints();
+ void setMaxInput(int MaxInput) {
+ _max_Input = MaxInput;
+ }
+ void setMaxOutput(int MaxOutput) {
+ _max_Output = MaxOutput;
+ }
+
+ //
+ // Functions to load/save the Function-Points to an INI-file
+ //
+ void saveSettings(QSettings& settings);
+ void loadSettings(QSettings& settings);
+
+ void setTrackingActive(bool blnActive);
+ QString getTitle() { return _title; }
+};
diff --git a/qfunctionconfigurator/qfunctionconfigurator.cpp b/qfunctionconfigurator/qfunctionconfigurator.cpp
new file mode 100644
index 00000000..55d1e1bc
--- /dev/null
+++ b/qfunctionconfigurator/qfunctionconfigurator.cpp
@@ -0,0 +1,413 @@
+/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl>
+ * Adapted to FaceTrackNoIR by Wim Vriend.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include "qfunctionconfigurator/qfunctionconfigurator.h"
+#include <QPainter>
+#include <QPaintEvent>
+#include <QPainterPathStroker>
+#include <QPainterPath>
+#include <QBrush>
+#include <QFileDialog>
+#include <QPen>
+#include <QMessageBox>
+#include <QImage>
+#include <QPixmap>
+#include <QTimer>
+#include <QtDebug>
+#include <cmath>
+#include <QTabWidget>
+#include <QTabBar>
+#include <QFontMetrics>
+
+static const int pointSize = 5;
+
+QFunctionConfigurator::QFunctionConfigurator(QWidget *parent)
+ : QWidget(parent)
+{
+ movingPoint = -1; // Index of that same point
+ _config = 0;
+ _draw_background = true;
+ _draw_function = true;
+ update_range();
+ setMouseTracking(true);
+}
+
+void QFunctionConfigurator::setConfig(FunctionConfig* config) {
+ QSettings settings("opentrack"); // Registry settings (in HK_USER)
+ QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
+ QSettings iniFile( currentFile, QSettings::IniFormat ); // Application settings (in INI-file)
+
+ config->loadSettings(iniFile);
+ _config = config;
+ _draw_function = _draw_background = true;
+ update_range();
+ update();
+}
+
+void QFunctionConfigurator::saveSettings(QString settingsFile) {
+ QSettings iniFile( settingsFile, QSettings::IniFormat ); // Application settings (in INI-file)
+
+ if (_config) {
+ _config->saveSettings(iniFile);
+ }
+}
+
+void QFunctionConfigurator::drawBackground()
+{
+ if (!_config)
+ return;
+ _background = QPixmap(width(), height());
+ QPainter painter(&_background);
+ painter.fillRect(rect(), QColor::fromRgb(204, 204, 204));
+ painter.setRenderHint(QPainter::Antialiasing);
+ QColor bg_color(112, 154, 209);
+ painter.fillRect(range, bg_color);
+
+ QFont font;
+ font.setPointSize(8);
+ painter.setFont(font);
+ QFontMetrics metrics(font);
+
+ QPen pen(QColor(55, 104, 170, 127), 1, Qt::SolidLine);
+
+ const int xstep = 10, ystep = 10;
+ const int maxx = _config->maxInput();
+ const int maxy = _config->maxOutput();
+
+ // horizontal grid
+
+ for (int i = 0; i < maxy; i += xstep)
+ {
+ double y = range.height() - i * c.y() + range.y();
+ drawLine(&painter,
+ QPointF(range.x(), y),
+ QPointF(range.x() + range.width(), y),
+ pen);
+ painter.drawText(QRectF(10,
+ y - metrics.height()/2,
+ range.left(),
+ metrics.height()),
+ QString::number(i));
+ }
+
+ {
+ const int i = maxy;
+ double y = range.height() - i * c.y() + range.y();
+ drawLine(&painter,
+ QPointF(range.x(), y),
+ QPointF(range.x() + range.width(), y),
+ pen);
+ painter.drawText(QRectF(10,
+ y - metrics.height()/2,
+ range.x() - 10,
+ metrics.height()),
+ QString::number(i));
+ }
+
+ // vertical grid
+
+ for (int i = 0; i < maxx; i += ystep)
+ {
+ double x = range.x() + i * c.x();
+ drawLine(&painter,
+ QPointF(x, range.y()),
+ QPointF(x, range.y() + range.height()),
+ pen);
+ const QString text = QString::number(i);
+ painter.drawText(QRectF(x - metrics.width(text)/2,
+ range.height() + 10 + metrics.height(),
+ metrics.width(text),
+ metrics.height()),
+ text);
+ }
+ {
+ const int i = maxx;
+ double x = range.x() + i * c.x();
+ drawLine(&painter,
+ QPointF(x, range.y()),
+ QPointF(x, range.y() + range.height()),
+ pen);
+ const QString text = QString::number(i);
+ painter.drawText(QRectF(x - metrics.width(text)/2,
+ range.height() + 10 + metrics.height(),
+ metrics.width(text),
+ metrics.height()),
+ text);
+ }
+}
+
+void QFunctionConfigurator::drawFunction()
+{
+ if (!_config)
+ return;
+ int i;
+ QPointF prevPoint;
+ QPointF currentPoint;
+
+ _function = QPixmap(_background);
+ QPainter painter(&_function);
+
+ painter.save();
+ painter.setRenderHint(QPainter::Antialiasing, true);
+
+ QList<QPointF> points = _config->getPoints();
+
+ for (i = 0; i < points.size(); i++) {
+ currentPoint = point_to_pixel( points[i] ); // Get the next point and convert it to Widget measures
+ drawPoint(&painter, currentPoint, QColor(200, 200, 210, 120));
+ lastPoint = currentPoint; // Remember which point is the rightmost in the graph
+ }
+
+
+ QPen pen(colBezier, 1.2, Qt::SolidLine);
+
+ prevPoint = point_to_pixel( QPointF(0,0) ); // Start at the Axis
+ double max = _config->maxInput();
+ QPointF prev = point_to_pixel(QPointF(0, 0));
+ const double step = 1.01;
+ for (double i = 0; i < max; i += step) {
+ double val = _config->getValue(i);
+ QPointF cur = point_to_pixel(QPointF(i, val));
+ drawLine(&painter, prev, cur, pen);
+ prev = cur;
+ }
+ painter.restore();
+}
+
+void QFunctionConfigurator::paintEvent(QPaintEvent *e)
+{
+ QPointF prevPoint;
+ QPointF currentPoint;
+ QPointF actualPos;
+ int i;
+
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ if (_draw_background) {
+ drawBackground();
+ _draw_background = false;
+ }
+ p.drawPixmap(e->rect(), _background);
+
+ if (_draw_function) {
+ drawFunction(); // Draw the Function on a Pixmap
+ _draw_function = false;
+ }
+ p.drawPixmap(e->rect(), _function); // Always draw the background and the function
+
+ if (_config) {
+ QPen pen(Qt::white, 1, Qt::SolidLine);
+ QList<QPointF> points = _config->getPoints();
+ if (movingPoint >= 0 && movingPoint < points.size()) {
+ prevPoint = point_to_pixel( QPointF(0,0) ); // Start at the Axis
+ for (i = 0; i < points.size(); i++) {
+ currentPoint = point_to_pixel( points[i] ); // Get the next point and convert it to Widget measures
+ drawLine(&p, prevPoint, currentPoint, pen);
+ prevPoint = currentPoint;
+ }
+ pen.setWidth(1);
+ pen.setColor( Qt::white );
+ pen.setStyle( Qt::DashLine );
+ actualPos = point_to_pixel(points[movingPoint]);
+ drawLine(&p, QPoint(range.left(), actualPos.y()), QPoint(actualPos.x(), actualPos.y()), pen);
+ drawLine(&p, QPoint(actualPos.x(), actualPos.y()), QPoint(actualPos.x(), range.height() + range.top()), pen);
+ }
+
+ //
+ // If the Tracker is active, the 'Last Point' it requested is recorded.
+ // Show that point on the graph, with some lines to assist.
+ // This new feature is very handy for tweaking the curves!
+ //
+ if (_config->getLastPoint( currentPoint )) {
+ actualPos = point_to_pixel( QPointF(fabs(currentPoint.x()), fabs(currentPoint.y())) );
+ drawPoint(&p, actualPos, QColor(255, 0, 0, 120));
+
+ pen.setWidth(1);
+ pen.setColor( Qt::black );
+ pen.setStyle( Qt::SolidLine );
+ drawLine(&p, QPoint(range.left(), actualPos.y()), QPoint(actualPos.x(), actualPos.y()), pen);
+ drawLine(&p, QPoint(actualPos.x(), actualPos.y()), QPoint(actualPos.x(), range.width()), pen);
+ }
+
+ }
+}
+
+//
+// Draw the handle, to move the Bezier-curve.
+//
+void QFunctionConfigurator::drawPoint(QPainter *painter, const QPointF &pos, QColor colBG )
+{
+ painter->save();
+ painter->setPen(QColor(50, 100, 120, 200));
+ painter->setBrush( colBG );
+ painter->drawEllipse(QRectF(pos.x() - pointSize,
+ pos.y() - pointSize,
+ pointSize*2, pointSize*2));
+ painter->restore();
+}
+
+void QFunctionConfigurator::drawLine(QPainter *painter, const QPointF &start, const QPointF &end, QPen pen)
+{
+ painter->save();
+ painter->setPen(pen);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawLine(start, end);
+ painter->restore();
+}
+
+void QFunctionConfigurator::mousePressEvent(QMouseEvent *e)
+{
+ if (!_config)
+ return;
+ QList<QPointF> points = _config->getPoints();
+ if (e->button() == Qt::LeftButton) {
+ bool bTouchingPoint = false;
+ movingPoint = -1;
+ if (_config) {
+ for (int i = 0; i < points.size(); i++) {
+ if ( point_within_pixel(points[i], e->pos() ) ) {
+ bTouchingPoint = true;
+ movingPoint = i;
+ timer.restart();
+ break;
+ }
+ }
+ if (!bTouchingPoint) {
+ _config->addPoint(pixel_coord_to_point(e->pos()));
+ emit CurveChanged( true );
+ }
+ }
+ }
+
+ if (e->button() == Qt::RightButton) {
+ if (_config) {
+ int found_pt = -1;
+ for (int i = 0; i < points.size(); i++) {
+ if ( point_within_pixel(points[i], e->pos() ) ) {
+ found_pt = i;
+ break;
+ }
+ }
+
+ if (found_pt != -1) {
+ _config->removePoint(found_pt);
+ emit CurveChanged( true );
+ }
+ movingPoint = -1;
+ }
+ }
+ _draw_function = true;
+ update();
+}
+
+void QFunctionConfigurator::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!_config)
+ return;
+ QList<QPointF> points = _config->getPoints();
+ const int refresh_delay = 50;
+
+ if (movingPoint >= 0 && movingPoint < points.size()) {
+ setCursor(Qt::ClosedHandCursor);
+
+ if (timer.isValid() && timer.elapsed() > refresh_delay)
+ {
+ timer.restart();
+ QPointF new_pt = pixel_coord_to_point(e->pos());
+ points[movingPoint] = new_pt;
+ _config->movePoint(movingPoint, new_pt);
+ _draw_function = true;
+ update();
+ }
+ }
+ else {
+ bool bTouchingPoint = false;
+ if (_config) {
+ for (int i = 0; i < points.size(); i++) {
+ if ( point_within_pixel(points[i], e->pos() ) ) {
+ bTouchingPoint = true;
+ }
+ }
+ }
+
+ if ( bTouchingPoint ) {
+ setCursor(Qt::OpenHandCursor);
+ }
+ else {
+ setCursor(Qt::ArrowCursor);
+ }
+ }
+}
+
+void QFunctionConfigurator::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (!_config)
+ return;
+ QList<QPointF> points = _config->getPoints();
+
+ if (e->button() == Qt::LeftButton) {
+ timer.invalidate();
+ if (movingPoint >= 0 && movingPoint < points.size()) {
+ emit CurveChanged( true );
+ if (_config) {
+ _config->movePoint(movingPoint, pixel_coord_to_point(e->pos()));
+ }
+ }
+ setCursor(Qt::ArrowCursor);
+ movingPoint = -1;
+ }
+
+ _draw_function = true;
+ update();
+}
+
+bool QFunctionConfigurator::point_within_pixel(QPointF pt, QPointF pixel) const
+{
+ QPointF pixel2(range.x() + pt.x() * c.x(), (range.y() + range.height() - pt.y() * c.y()));
+ return pixel2.x() >= pixel.x() - pointSize && pixel2.x() < pixel.x() + pointSize &&
+ pixel2.y() >= pixel.y() - pointSize && pixel2.y() < pixel.y() + pointSize;
+}
+
+QPointF QFunctionConfigurator::pixel_coord_to_point(QPointF point) const
+{
+ if (!_config)
+ return QPointF(-1, -1);
+
+ double x = (point.x() - range.x()) / c.x();
+ double y = (range.height() - point.y() + range.y()) / c.y();
+
+ if (x < 0)
+ x = 0;
+ if (x > _config->maxInput())
+ x = _config->maxInput();
+
+ if (y < 0)
+ y = 0;
+ if (y > _config->maxOutput())
+ y = _config->maxOutput();
+
+ return QPointF(x, y);
+}
+
+QPointF QFunctionConfigurator::point_to_pixel(QPointF point) const
+{
+ return QPointF(range.x() + point.x() * c.x(),
+ range.y() + range.height() - point.y() * c.y());
+}
+
+void QFunctionConfigurator::setColorBezier(QColor color)
+{
+ colBezier = color;
+ update();
+}
+
+void QFunctionConfigurator::resizeEvent(QResizeEvent *)
+{
+ update_range();
+ repaint();
+}
diff --git a/qfunctionconfigurator/qfunctionconfigurator.h b/qfunctionconfigurator/qfunctionconfigurator.h
new file mode 100644
index 00000000..1f6b4f78
--- /dev/null
+++ b/qfunctionconfigurator/qfunctionconfigurator.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl>
+ * Adapted to FaceTrackNoIR by Wim Vriend.
+ * 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 QFUNCTIONCONFIGURATOR_H
+#define QFUNCTIONCONFIGURATOR_H
+
+#include <QWidget>
+#include <QtGui>
+#include <QPointF>
+#include <QElapsedTimer>
+#include "qfunctionconfigurator/functionconfig.h"
+#include "ftnoir_tracker_base/ftnoir_tracker_base.h"
+
+//
+// The FunctionConfigurator Widget is used to display and configure a function (curve).
+// The Function is used by FaceTrackNoIR to 'translate' the actual head-pose to the virtual headpose. Every axis is configured by a separate Function.
+//
+// The Function is coded in a separate Class and can exists, without the Widget. When the widget is displayed (therefore 'created'), the Function can be attached to the
+// Widget and the Widget used to change the Function.
+//
+
+class FTNOIR_TRACKER_BASE_EXPORT QFunctionConfigurator : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor colorBezier READ colorBezier WRITE setColorBezier)
+ QColor colorBezier() const
+ {
+ return colBezier;
+ }
+public:
+ QFunctionConfigurator(QWidget *parent = 0);
+ FunctionConfig* config();
+
+ void setConfig(FunctionConfig* config);
+ void saveSettings(QString settingsFile);
+
+signals:
+ void CurveChanged(bool);
+
+public slots:
+ void setColorBezier(QColor);
+protected slots:
+ void paintEvent(QPaintEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+
+protected:
+ void drawBackground();
+ void drawFunction();
+ void drawPoint(QPainter *painter, const QPointF &pt, QColor colBG );
+ void drawLine(QPainter *painter, const QPointF &start, const QPointF &end, QPen pen);
+ bool point_within_pixel(QPointF pt, QPointF pixel) const;
+
+protected:
+ virtual void resizeEvent(QResizeEvent *);
+
+private:
+ void update_range() {
+ if (!_config)
+ return;
+ double w = width(), h = height();
+ const double mwl = 40, mhl = 20;
+ const double mwr = 15, mhr = 35;
+ range = QRectF(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr));
+ c = QPointF(range.width() / _config->maxInput(), range.height() / _config->maxOutput());
+ _draw_function = _draw_background = true;
+ }
+
+ QRectF range; // The actual rectangle for the Bezier-curve
+ QPointF lastPoint; // The right-most point of the Function
+ QPointF pixel_coord_to_point (QPointF point) const; // Convert the graphical Point to a real-life Point
+ QPointF point_to_pixel (QPointF point) const; // Convert the Point to a graphical Point
+
+ int movingPoint;
+ QElapsedTimer timer;
+ QPointF c;
+
+ QColor colBezier; // Color of Bezier curve
+
+ bool _draw_background; // Flag to determine if the background should be (re-)drawn on the QPixmap
+ QPixmap _background; // Image of the static parts (axis, lines, etc.)
+ bool _draw_function; // Flag to determine if the function should be (re-)drawn on the QPixmap
+ QPixmap _function; // Image of the function (static unless edited by the user)
+
+ FunctionConfig* _config;
+};
+
+#endif // QFUNCTIONCONFIGURATOR_H
diff --git a/qxt-mini/QxtGlobalShortcut b/qxt-mini/QxtGlobalShortcut
new file mode 100644
index 00000000..d673035b
--- /dev/null
+++ b/qxt-mini/QxtGlobalShortcut
@@ -0,0 +1,2 @@
+#include "qxtglobalshortcut.h"
+
diff --git a/qxt-mini/plat/qxtglobalshortcut_mac.cpp b/qxt-mini/plat/qxtglobalshortcut_mac.cpp
new file mode 100644
index 00000000..f43c773a
--- /dev/null
+++ b/qxt-mini/plat/qxtglobalshortcut_mac.cpp
@@ -0,0 +1,265 @@
+#include <Carbon/Carbon.h>
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#include "qxtglobalshortcut_p.h"
+#include <QMap>
+#include <QHash>
+#include <QtDebug>
+#include <QApplication>
+
+typedef QPair<uint, uint> Identifier;
+static QMap<quint32, EventHotKeyRef> keyRefs;
+static QHash<Identifier, quint32> keyIDs;
+static quint32 hotKeySerial = 0;
+static bool qxt_mac_handler_installed = false;
+
+OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data)
+{
+ Q_UNUSED(nextHandler);
+ Q_UNUSED(data);
+ if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
+ {
+ EventHotKeyID keyID;
+ GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
+ Identifier id = keyIDs.key(keyID.id);
+ if(id != Identifier())
+ QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first);
+ }
+ return noErr;
+}
+
+quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
+{
+ quint32 native = 0;
+ if (modifiers & Qt::ShiftModifier)
+ native |= shiftKey;
+ if (modifiers & Qt::ControlModifier)
+ native |= cmdKey;
+ if (modifiers & Qt::AltModifier)
+ native |= optionKey;
+ if (modifiers & Qt::MetaModifier)
+ native |= controlKey;
+ if (modifiers & Qt::KeypadModifier)
+ native |= kEventKeyModifierNumLockMask;
+ return native;
+}
+
+quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
+{
+ UTF16Char ch;
+ // Constants found in NSEvent.h from AppKit.framework
+ switch (key)
+ {
+ case Qt::Key_Return:
+ return kVK_Return;
+ case Qt::Key_Enter:
+ return kVK_ANSI_KeypadEnter;
+ case Qt::Key_Tab:
+ return kVK_Tab;
+ case Qt::Key_Space:
+ return kVK_Space;
+ case Qt::Key_Backspace:
+ return kVK_Delete;
+ case Qt::Key_Control:
+ return kVK_Command;
+ case Qt::Key_Shift:
+ return kVK_Shift;
+ case Qt::Key_CapsLock:
+ return kVK_CapsLock;
+ case Qt::Key_Option:
+ return kVK_Option;
+ case Qt::Key_Meta:
+ return kVK_Control;
+ case Qt::Key_F17:
+ return kVK_F17;
+ case Qt::Key_VolumeUp:
+ return kVK_VolumeUp;
+ case Qt::Key_VolumeDown:
+ return kVK_VolumeDown;
+ case Qt::Key_F18:
+ return kVK_F18;
+ case Qt::Key_F19:
+ return kVK_F19;
+ case Qt::Key_F20:
+ return kVK_F20;
+ case Qt::Key_F5:
+ return kVK_F5;
+ case Qt::Key_F6:
+ return kVK_F6;
+ case Qt::Key_F7:
+ return kVK_F7;
+ case Qt::Key_F3:
+ return kVK_F3;
+ case Qt::Key_F8:
+ return kVK_F8;
+ case Qt::Key_F9:
+ return kVK_F9;
+ case Qt::Key_F11:
+ return kVK_F11;
+ case Qt::Key_F13:
+ return kVK_F13;
+ case Qt::Key_F16:
+ return kVK_F16;
+ case Qt::Key_F14:
+ return kVK_F14;
+ case Qt::Key_F10:
+ return kVK_F10;
+ case Qt::Key_F12:
+ return kVK_F12;
+ case Qt::Key_F15:
+ return kVK_F15;
+ case Qt::Key_Help:
+ return kVK_Help;
+ case Qt::Key_Home:
+ return kVK_Home;
+ case Qt::Key_PageUp:
+ return kVK_PageUp;
+ case Qt::Key_Delete:
+ return kVK_ForwardDelete;
+ case Qt::Key_F4:
+ return kVK_F4;
+ case Qt::Key_End:
+ return kVK_End;
+ case Qt::Key_F2:
+ return kVK_F2;
+ case Qt::Key_PageDown:
+ return kVK_PageDown;
+ case Qt::Key_F1:
+ return kVK_F1;
+ case Qt::Key_Left:
+ return kVK_LeftArrow;
+ case Qt::Key_Right:
+ return kVK_RightArrow;
+ case Qt::Key_Down:
+ return kVK_DownArrow;
+ case Qt::Key_Up:
+ return kVK_UpArrow;
+ default:
+ ;
+ }
+
+ if (key == Qt::Key_Escape) ch = 27;
+ else if (key == Qt::Key_Return) ch = 13;
+ else if (key == Qt::Key_Enter) ch = 3;
+ else if (key == Qt::Key_Tab) ch = 9;
+ else ch = key;
+
+ CFDataRef currentLayoutData;
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+
+ if (currentKeyboard == NULL)
+ return 0;
+
+ currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ CFRelease(currentKeyboard);
+ if (currentLayoutData == NULL)
+ return 0;
+
+ UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
+ UCKeyboardTypeHeader* table = header->keyboardTypeList;
+
+ uint8_t *data = (uint8_t*)header;
+ // God, would a little documentation for this shit kill you...
+ for (quint32 i=0; i < header->keyboardTypeCount; i++)
+ {
+ UCKeyStateRecordsIndex* stateRec = 0;
+ if (table[i].keyStateRecordsIndexOffset != 0)
+ {
+ stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
+ if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
+ }
+
+ UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
+ if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
+
+ for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
+ {
+ UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
+ for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
+ {
+ if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
+ {
+ long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
+ if (stateRec && idx < stateRec->keyStateRecordCount)
+ {
+ UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
+ if (rec->stateZeroCharData == ch) return k;
+ }
+ }
+ else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
+ {
+ if (keyToChar[k] == ch) return k;
+ }
+ } // for k
+ } // for j
+ } // for i
+ return 0;
+}
+
+bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
+{
+ if (!qxt_mac_handler_installed)
+ {
+ qxt_mac_handler_installed = true;
+ EventTypeSpec t;
+ t.eventClass = kEventClassKeyboard;
+ t.eventKind = kEventHotKeyPressed;
+ InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
+ }
+
+ EventHotKeyID keyID;
+ keyID.signature = 'cute';
+ keyID.id = ++hotKeySerial;
+
+ EventHotKeyRef ref = 0;
+ bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
+ if (rv)
+ {
+ keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
+ keyRefs.insert(keyID.id, ref);
+ }
+ return rv;
+}
+
+bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
+{
+ Identifier id(nativeMods, nativeKey);
+ if (!keyIDs.contains(id)) return false;
+
+ EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
+ keyIDs.remove(id);
+ return !UnregisterEventHotKey(ref);
+}
+bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
+ void *message, long *result)
+{
+ return false;
+}
diff --git a/qxt-mini/plat/qxtglobalshortcut_x11.cpp b/qxt-mini/plat/qxtglobalshortcut_x11.cpp
new file mode 100644
index 00000000..0c203dd8
--- /dev/null
+++ b/qxt-mini/plat/qxtglobalshortcut_x11.cpp
@@ -0,0 +1,235 @@
+#include "../qxtglobalshortcut_p.h"
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#include <QVector>
+#include <QApplication>
+// include private header for great justice -sh 20131015
+#include <X11/Xlib.h>
+#include <xcb/xcb.h>
+#include "qplatformnativeinterface.h"
+
+namespace {
+
+const QVector<quint32> maskModifiers = QVector<quint32>()
+ << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask);
+
+typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event);
+
+class QxtX11ErrorHandler {
+public:
+ static bool error;
+
+ static int qxtX11ErrorHandler(Display *display, XErrorEvent *event)
+ {
+ Q_UNUSED(display);
+ switch (event->error_code)
+ {
+ case BadAccess:
+ case BadValue:
+ case BadWindow:
+ if (event->request_code == 33 /* X_GrabKey */ ||
+ event->request_code == 34 /* X_UngrabKey */)
+ {
+ error = true;
+ //TODO:
+ //char errstr[256];
+ //XGetErrorText(dpy, err->error_code, errstr, 256);
+ }
+ }
+ return 0;
+ }
+
+ QxtX11ErrorHandler()
+ {
+ error = false;
+ m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler);
+ }
+
+ ~QxtX11ErrorHandler()
+ {
+ XSetErrorHandler(m_previousErrorHandler);
+ }
+
+private:
+ X11ErrorHandler m_previousErrorHandler;
+};
+
+bool QxtX11ErrorHandler::error = false;
+
+class QxtX11Data {
+public:
+ QxtX11Data()
+ {
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ m_display = QX11Info::display();
+#else
+ QPlatformNativeInterface *native = qApp->platformNativeInterface();
+ void *display = native->nativeResourceForScreen(QByteArray("display"),
+ QGuiApplication::primaryScreen());
+ m_display = reinterpret_cast<Display *>(display);
+#endif
+ }
+
+ bool isValid()
+ {
+ return m_display != 0;
+ }
+
+ Display *display()
+ {
+ Q_ASSERT(isValid());
+ return m_display;
+ }
+
+ Window rootWindow()
+ {
+ return DefaultRootWindow(display());
+ }
+
+ bool grabKey(quint32 keycode, quint32 modifiers, Window window)
+ {
+ QxtX11ErrorHandler errorHandler;
+
+ for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) {
+ XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True,
+ GrabModeAsync, GrabModeAsync);
+ }
+
+ if (errorHandler.error) {
+ ungrabKey(keycode, modifiers, window);
+ return false;
+ }
+
+ return true;
+ }
+
+ bool ungrabKey(quint32 keycode, quint32 modifiers, Window window)
+ {
+ QxtX11ErrorHandler errorHandler;
+
+ foreach (quint32 maskMods, maskModifiers) {
+ XUngrabKey(display(), keycode, modifiers | maskMods, window);
+ }
+
+ return !errorHandler.error;
+ }
+
+private:
+ Display *m_display;
+};
+
+} // namespace
+
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+bool QxtGlobalShortcutPrivate::eventFilter(void *message)
+{
+ XEvent *event = static_cast<XEvent *>(message);
+ if (event->type == KeyPress)
+ {
+ XKeyEvent *key = reinterpret_cast<XKeyEvent *>(event);
+ unsigned int keycode = key->keycode;
+ unsigned int keystate = key->state;
+#else
+bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
+ void *message, long *result)
+{
+ Q_UNUSED(result);
+
+ xcb_key_press_event_t *kev = 0;
+ if (eventType == "xcb_generic_event_t") {
+ xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
+ if ((ev->response_type & 127) == XCB_KEY_PRESS)
+ kev = static_cast<xcb_key_press_event_t *>(message);
+ }
+
+ if (kev != 0) {
+ unsigned int keycode = kev->detail;
+ unsigned int keystate = 0;
+ if(kev->state & XCB_MOD_MASK_1)
+ keystate |= Mod1Mask;
+ if(kev->state & XCB_MOD_MASK_CONTROL)
+ keystate |= ControlMask;
+ if(kev->state & XCB_MOD_MASK_4)
+ keystate |= Mod4Mask;
+ if(kev->state & XCB_MOD_MASK_SHIFT)
+ keystate |= ShiftMask;
+#endif
+ activateShortcut(keycode,
+ // Mod1Mask == Alt, Mod4Mask == Meta
+ keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask));
+ }
+ return false;
+}
+
+quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
+{
+ // ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask
+ quint32 native = 0;
+ if (modifiers & Qt::ShiftModifier)
+ native |= ShiftMask;
+ if (modifiers & Qt::ControlModifier)
+ native |= ControlMask;
+ if (modifiers & Qt::AltModifier)
+ native |= Mod1Mask;
+ if (modifiers & Qt::MetaModifier)
+ native |= Mod4Mask;
+
+ // TODO: resolve these?
+ //if (modifiers & Qt::MetaModifier)
+ //if (modifiers & Qt::KeypadModifier)
+ //if (modifiers & Qt::GroupSwitchModifier)
+ return native;
+}
+
+quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
+{
+ QxtX11Data x11;
+ if (!x11.isValid())
+ return 0;
+
+ KeySym keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data());
+ if (keysym == NoSymbol)
+ keysym = static_cast<ushort>(key);
+
+ return XKeysymToKeycode(x11.display(), keysym);
+}
+
+bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
+{
+ QxtX11Data x11;
+ return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow());
+}
+
+bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
+{
+ QxtX11Data x11;
+ return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow());
+}
diff --git a/qxt-mini/qplatformnativeinterface.h b/qxt-mini/qplatformnativeinterface.h
new file mode 100644
index 00000000..eaa24a9e
--- /dev/null
+++ b/qxt-mini/qplatformnativeinterface.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLATFORMNATIVEINTERFACE_H
+#define QPLATFORMNATIVEINTERFACE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the QPA API and is not meant to be used
+// in applications. Usage of this API may make your code
+// source and binary incompatible with future versions of Qt.
+//
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+
+
+class QOpenGLContext;
+class QScreen;
+class QWindow;
+class QPlatformWindow;
+class QBackingStore;
+
+class Q_GUI_EXPORT QPlatformNativeInterface : public QObject
+{
+ Q_OBJECT
+public:
+ virtual void *nativeResourceForIntegration(const QByteArray &resource);
+ virtual void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context);
+ virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen);
+ virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window);
+ virtual void *nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *backingStore);
+
+ typedef void * (*NativeResourceForIntegrationFunction)();
+ typedef void * (*NativeResourceForContextFunction)(QOpenGLContext *context);
+ typedef void * (*NativeResourceForScreenFunction)(QScreen *screen);
+ typedef void * (*NativeResourceForWindowFunction)(QWindow *window);
+ typedef void * (*NativeResourceForBackingStoreFunction)(QBackingStore *backingStore);
+ virtual NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource);
+ virtual NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource);
+ virtual NativeResourceForScreenFunction nativeResourceFunctionForScreen(const QByteArray &resource);
+ virtual NativeResourceForWindowFunction nativeResourceFunctionForWindow(const QByteArray &resource);
+ virtual NativeResourceForBackingStoreFunction nativeResourceFunctionForBackingStore(const QByteArray &resource);
+
+ virtual QVariantMap windowProperties(QPlatformWindow *window) const;
+ virtual QVariant windowProperty(QPlatformWindow *window, const QString &name) const;
+ virtual QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const;
+ virtual void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value);
+
+Q_SIGNALS:
+ void windowPropertyChanged(QPlatformWindow *window, const QString &propertyName);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMNATIVEINTERFACE_H
diff --git a/qxt-mini/qxtglobal.h b/qxt-mini/qxtglobal.h
new file mode 100644
index 00000000..7d5abfbe
--- /dev/null
+++ b/qxt-mini/qxtglobal.h
@@ -0,0 +1,233 @@
+
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#ifndef QXTGLOBAL_H
+#define QXTGLOBAL_H
+
+#include <QtGlobal>
+
+#define QXT_VERSION 0x000700
+#define QXT_VERSION_STR "0.7.0"
+
+//--------------------------global macros------------------------------
+
+#ifndef QXT_NO_MACROS
+
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof(*x))
+#endif
+
+#endif // QXT_NO_MACROS
+
+//--------------------------export macros------------------------------
+
+#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_CORE)
+# define QXT_CORE_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_CORE_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_CORE_EXPORT
+#endif // BUILD_QXT_CORE
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_GUI)
+# define QXT_GUI_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_GUI_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_GUI_EXPORT
+#endif // BUILD_QXT_GUI
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_NETWORK)
+# define QXT_NETWORK_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_NETWORK_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_NETWORK_EXPORT
+#endif // BUILD_QXT_NETWORK
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_SQL)
+# define QXT_SQL_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_SQL_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_SQL_EXPORT
+#endif // BUILD_QXT_SQL
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_WEB)
+# define QXT_WEB_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_WEB_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_WEB_EXPORT
+#endif // BUILD_QXT_WEB
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_BERKELEY)
+# define QXT_BERKELEY_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_BERKELEY_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_BERKELEY_EXPORT
+#endif // BUILD_QXT_BERKELEY
+
+#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
+# if defined(BUILD_QXT_ZEROCONF)
+# define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT
+# else
+# define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QXT_ZEROCONF_EXPORT
+#endif // QXT_ZEROCONF_EXPORT
+
+#if defined(BUILD_QXT_CORE) || defined(BUILD_QXT_GUI) || defined(BUILD_QXT_SQL) || defined(BUILD_QXT_NETWORK) || defined(BUILD_QXT_WEB) || defined(BUILD_QXT_BERKELEY) || defined(BUILD_QXT_ZEROCONF)
+# define BUILD_QXT
+#endif
+
+QXT_CORE_EXPORT const char* qxtVersion();
+
+#ifndef QT_BEGIN_NAMESPACE
+#define QT_BEGIN_NAMESPACE
+#endif
+
+#ifndef QT_END_NAMESPACE
+#define QT_END_NAMESPACE
+#endif
+
+#ifndef QT_FORWARD_DECLARE_CLASS
+#define QT_FORWARD_DECLARE_CLASS(Class) class Class;
+#endif
+
+/****************************************************************************
+** This file is derived from code bearing the following notice:
+** The sole author of this file, Adam Higerd, has explicitly disclaimed all
+** copyright interest and protection for the content within. This file has
+** been placed in the public domain according to United States copyright
+** statute and case law. In jurisdictions where this public domain dedication
+** is not legally recognized, anyone who receives a copy of this file is
+** permitted to use, modify, duplicate, and redistribute this file, in whole
+** or in part, with no restrictions or conditions. In these jurisdictions,
+** this file shall be copyright (C) 2006-2008 by Adam Higerd.
+****************************************************************************/
+
+#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface<PUB, PUB##Private> qxt_d;
+#define QXT_DECLARE_PUBLIC(PUB) friend class PUB;
+#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this);
+#define QXT_D(PUB) PUB##Private& d = qxt_d()
+#define QXT_P(PUB) PUB& p = qxt_p()
+
+template <typename PUB>
+class QxtPrivate
+{
+public:
+ virtual ~QxtPrivate()
+ {}
+ inline void QXT_setPublic(PUB* pub)
+ {
+ qxt_p_ptr = pub;
+ }
+
+protected:
+ inline PUB& qxt_p()
+ {
+ return *qxt_p_ptr;
+ }
+ inline const PUB& qxt_p() const
+ {
+ return *qxt_p_ptr;
+ }
+ inline PUB* qxt_ptr()
+ {
+ return qxt_p_ptr;
+ }
+ inline const PUB* qxt_ptr() const
+ {
+ return qxt_p_ptr;
+ }
+
+private:
+ PUB* qxt_p_ptr;
+};
+
+template <typename PUB, typename PVT>
+class QxtPrivateInterface
+{
+ friend class QxtPrivate<PUB>;
+public:
+ QxtPrivateInterface()
+ {
+ pvt = new PVT;
+ }
+ ~QxtPrivateInterface()
+ {
+ delete pvt;
+ }
+
+ inline void setPublic(PUB* pub)
+ {
+ pvt->QXT_setPublic(pub);
+ }
+ inline PVT& operator()()
+ {
+ return *static_cast<PVT*>(pvt);
+ }
+ inline const PVT& operator()() const
+ {
+ return *static_cast<PVT*>(pvt);
+ }
+ inline PVT * operator->()
+ {
+ return static_cast<PVT*>(pvt);
+ }
+ inline const PVT * operator->() const
+ {
+ return static_cast<PVT*>(pvt);
+ }
+private:
+ QxtPrivateInterface(const QxtPrivateInterface&) { }
+ QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
+ QxtPrivate<PUB>* pvt;
+};
+
+#endif // QXT_GLOBAL
diff --git a/qxt-mini/qxtglobalshortcut.cpp b/qxt-mini/qxtglobalshortcut.cpp
new file mode 100644
index 00000000..8515a6b2
--- /dev/null
+++ b/qxt-mini/qxtglobalshortcut.cpp
@@ -0,0 +1,224 @@
+#include "qxtglobalshortcut.h"
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#include "qxtglobalshortcut_p.h"
+#include <QAbstractEventDispatcher>
+#include <QtDebug>
+
+#ifndef Q_OS_MAC
+int QxtGlobalShortcutPrivate::ref = 0;
+# if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+QAbstractEventDispatcher::EventFilter QxtGlobalShortcutPrivate::prevEventFilter = 0;
+# endif
+#endif // Q_OS_MAC
+QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts;
+
+QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier)
+{
+#ifndef Q_OS_MAC
+ if (ref == 0) {
+# if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter);
+# else
+ QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
+#endif
+ }
+ ++ref;
+#endif // Q_OS_MAC
+}
+
+QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate()
+{
+#ifndef Q_OS_MAC
+ --ref;
+ if (ref == 0) {
+ QAbstractEventDispatcher *ed = QAbstractEventDispatcher::instance();
+ if (ed != 0) {
+# if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ ed->setEventFilter(prevEventFilter);
+# else
+ ed->removeNativeEventFilter(this);
+# endif
+ }
+ }
+#endif // Q_OS_MAC
+}
+
+bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut)
+{
+ if (shortcut.toString() == "") return false;
+ Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;
+ key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]);
+ mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods);
+ const quint32 nativeKey = nativeKeycode(key);
+ const quint32 nativeMods = nativeModifiers(mods);
+ const bool res = registerShortcut(nativeKey, nativeMods);
+ if (res)
+ shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p());
+ else
+ qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString();
+ return res;
+}
+
+bool QxtGlobalShortcutPrivate::unsetShortcut()
+{
+ bool res = false;
+ const quint32 nativeKey = nativeKeycode(key);
+ const quint32 nativeMods = nativeModifiers(mods);
+ if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p())
+ res = unregisterShortcut(nativeKey, nativeMods);
+ if (res)
+ shortcuts.remove(qMakePair(nativeKey, nativeMods));
+ else
+ qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString();
+ key = Qt::Key(0);
+ mods = Qt::KeyboardModifiers(0);
+ return res;
+}
+
+void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
+{
+ QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
+ if (shortcut && shortcut->isEnabled())
+ emit shortcut->activated();
+}
+
+/*!
+ \class QxtGlobalShortcut
+ \inmodule QxtWidgets
+ \brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey".
+
+ A global shortcut triggers even if the application is not active. This
+ makes it easy to implement applications that react to certain shortcuts
+ still if some other application is active or if the application is for
+ example minimized to the system tray.
+
+ Example usage:
+ \code
+ QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
+ connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
+ shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));
+ \endcode
+
+ \bold {Note:} Since Qxt 0.6 QxtGlobalShortcut no more requires QxtApplication.
+ */
+
+/*!
+ \fn QxtGlobalShortcut::activated()
+
+ This signal is emitted when the user types the shortcut's key sequence.
+
+ \sa shortcut
+ */
+
+/*!
+ Constructs a new QxtGlobalShortcut with \a parent.
+ */
+QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent)
+ : QObject(parent)
+{
+ QXT_INIT_PRIVATE(QxtGlobalShortcut);
+}
+
+/*!
+ Constructs a new QxtGlobalShortcut with \a shortcut and \a parent.
+ */
+QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent)
+ : QObject(parent)
+{
+ QXT_INIT_PRIVATE(QxtGlobalShortcut);
+ setShortcut(shortcut);
+}
+
+/*!
+ Destructs the QxtGlobalShortcut.
+ */
+QxtGlobalShortcut::~QxtGlobalShortcut()
+{
+ if (qxt_d().key != 0)
+ qxt_d().unsetShortcut();
+}
+
+/*!
+ \property QxtGlobalShortcut::shortcut
+ \brief the shortcut key sequence
+
+ \bold {Note:} Notice that corresponding key press and release events are not
+ delivered for registered global shortcuts even if they are disabled.
+ Also, comma separated key sequences are not supported.
+ Only the first part is used:
+
+ \code
+ qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B"));
+ Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A"));
+ \endcode
+ */
+QKeySequence QxtGlobalShortcut::shortcut() const
+{
+ return QKeySequence(qxt_d().key | qxt_d().mods);
+}
+
+bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut)
+{
+ if (qxt_d().key != 0)
+ qxt_d().unsetShortcut();
+ return qxt_d().setShortcut(shortcut);
+}
+
+/*!
+ \property QxtGlobalShortcut::enabled
+ \brief whether the shortcut is enabled
+
+ A disabled shortcut does not get activated.
+
+ The default value is \c true.
+
+ \sa setDisabled()
+ */
+bool QxtGlobalShortcut::isEnabled() const
+{
+ return qxt_d().enabled;
+}
+
+void QxtGlobalShortcut::setEnabled(bool enabled)
+{
+ qxt_d().enabled = enabled;
+}
+
+/*!
+ Sets the shortcut \a disabled.
+
+ \sa enabled
+ */
+void QxtGlobalShortcut::setDisabled(bool disabled)
+{
+ qxt_d().enabled = !disabled;
+}
diff --git a/qxt-mini/qxtglobalshortcut.h b/qxt-mini/qxtglobalshortcut.h
new file mode 100644
index 00000000..a81942d2
--- /dev/null
+++ b/qxt-mini/qxtglobalshortcut.h
@@ -0,0 +1,64 @@
+#ifndef QXTGLOBALSHORTCUT_H
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#define QXTGLOBALSHORTCUT_H
+
+#include "qxtglobal.h"
+#include <QObject>
+#include <QKeySequence>
+class QxtGlobalShortcutPrivate;
+
+class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject
+{
+ Q_OBJECT
+ QXT_DECLARE_PRIVATE(QxtGlobalShortcut)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
+
+public:
+ explicit QxtGlobalShortcut(QObject* parent = 0);
+ explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);
+ virtual ~QxtGlobalShortcut();
+
+ QKeySequence shortcut() const;
+ bool setShortcut(const QKeySequence& shortcut);
+
+ bool isEnabled() const;
+
+public Q_SLOTS:
+ void setEnabled(bool enabled = true);
+ void setDisabled(bool disabled = true);
+
+Q_SIGNALS:
+ void activated();
+};
+
+#endif // QXTGLOBALSHORTCUT_H
diff --git a/qxt-mini/qxtglobalshortcut_p.h b/qxt-mini/qxtglobalshortcut_p.h
new file mode 100644
index 00000000..407f968e
--- /dev/null
+++ b/qxt-mini/qxtglobalshortcut_p.h
@@ -0,0 +1,81 @@
+#ifndef QXTGLOBALSHORTCUT_P_H
+/****************************************************************************
+** Copyright (c) 2006 - 2011, the LibQxt project.
+** See the Qxt AUTHORS file for a list of authors and copyright holders.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the LibQxt project nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+** <http://libqxt.org> <foundation@libqxt.org>
+*****************************************************************************/
+
+#define QXTGLOBALSHORTCUT_P_H
+
+#include "qxtglobalshortcut.h"
+#include <QAbstractEventDispatcher>
+#include <QKeySequence>
+#include <QHash>
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+#include <QAbstractNativeEventFilter>
+#endif
+
+class QxtGlobalShortcutPrivate : public QxtPrivate<QxtGlobalShortcut>
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+ ,public QAbstractNativeEventFilter
+#endif
+{
+public:
+ QXT_DECLARE_PUBLIC(QxtGlobalShortcut)
+ QxtGlobalShortcutPrivate();
+ ~QxtGlobalShortcutPrivate();
+
+ bool enabled;
+ Qt::Key key;
+ Qt::KeyboardModifiers mods;
+
+ bool setShortcut(const QKeySequence& shortcut);
+ bool unsetShortcut();
+
+ static bool error;
+ static int ref;
+# if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ static QAbstractEventDispatcher::EventFilter prevEventFilter;
+# else
+ static bool eventFilter(void* message);
+ virtual bool nativeEventFilter(const QByteArray & eventType, void * message, long * result);
+# endif // QT_VERSION < QT_VERSION_CHECK(5,0,0)
+
+ static void activateShortcut(quint32 nativeKey, quint32 nativeMods);
+
+private:
+ static quint32 nativeKeycode(Qt::Key keycode);
+ static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers);
+
+ static bool registerShortcut(quint32 nativeKey, quint32 nativeMods);
+ static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods);
+
+ static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
+};
+
+#endif // QXTGLOBALSHORTCUT_P_H
diff --git a/x-plane-plugin/plugin.c b/x-plane-plugin/plugin.c
new file mode 100644
index 00000000..62c9d6f0
--- /dev/null
+++ b/x-plane-plugin/plugin.c
@@ -0,0 +1,152 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <XPLMPlugin.h>
+#include <XPLMDisplay.h>
+#include <XPLMDataAccess.h>
+#include <XPLMCamera.h>
+#include <XPLMProcessing.h>
+
+#include "ftnoir_tracker_base/ftnoir_tracker_types.h"
+
+#ifndef PLUGIN_API
+#define PLUGIN_API
+#endif
+
+/* using Wine name to ease things */
+#define WINE_SHM_NAME "facetracknoir-wine-shm"
+#define WINE_MTX_NAME "facetracknoir-wine-mtx"
+
+typedef struct PortableLockedShm {
+ void* mem;
+ int fd, size;
+} PortableLockedShm;
+
+typedef struct WineSHM {
+ double data[6];
+ int gameid, gameid2;
+ unsigned char table[8];
+ bool stop;
+} WineSHM;
+
+static PortableLockedShm* lck_posix = NULL;
+static WineSHM* shm_posix = NULL;
+static void *view_x, *view_y, *view_z, *view_heading, *view_pitch;
+static float offset_x, offset_y, offset_z;
+
+static void reinit_offset() {
+ offset_x = XPLMGetDataf(view_x);
+ offset_y = XPLMGetDataf(view_y);
+ offset_z = XPLMGetDataf(view_z);
+}
+
+#ifdef __GNUC__
+# define OT_UNUSED(varname) varname __attribute__((__unused__))
+#else
+# define OT_UNUSED(varname) varname
+#endif
+
+PortableLockedShm* PortableLockedShm_init(const char *shmName, const char *OT_UNUSED(mutexName), int mapSize)
+{
+ PortableLockedShm* self = malloc(sizeof(PortableLockedShm));
+ char shm_filename[NAME_MAX];
+ shm_filename[0] = '/';
+ strncpy(shm_filename+1, shmName, NAME_MAX-2);
+ shm_filename[NAME_MAX-1] = '\0';
+ /* (void) shm_unlink(shm_filename); */
+
+ self->fd = shm_open(shm_filename, O_RDWR | O_CREAT, 0600);
+ (void) ftruncate(self->fd, mapSize);
+ self->mem = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, self->fd, (off_t)0);
+ return self;
+}
+
+void PortableLockedShm_free(PortableLockedShm* self)
+{
+ /*(void) shm_unlink(shm_filename);*/
+ (void) munmap(self->mem, self->size);
+ (void) close(self->fd);
+ free(self);
+}
+
+void PortableLockedShm_lock(PortableLockedShm* self)
+{
+ flock(self->fd, LOCK_SH);
+}
+
+void PortableLockedShm_unlock(PortableLockedShm* self)
+{
+ flock(self->fd, LOCK_UN);
+}
+
+int write_head_position(
+ XPLMDrawingPhase OT_UNUSED(inPhase),
+ int OT_UNUSED(inIsBefore),
+ void * OT_UNUSED(inRefcon))
+{
+ if (lck_posix != NULL && shm_posix != NULL) {
+ PortableLockedShm_lock(lck_posix);
+ XPLMSetDataf(view_x, shm_posix->data[TX] * 1e-3 + offset_x);
+ XPLMSetDataf(view_y, shm_posix->data[TY] * 1e-3 + offset_y);
+ XPLMSetDataf(view_z, shm_posix->data[TZ] * 1e-3 + offset_z);
+ XPLMSetDataf(view_heading, shm_posix->data[Yaw] * 180 / 3.141592654);
+ XPLMSetDataf(view_pitch, shm_posix->data[Pitch] * 180 / 3.141592654);
+ PortableLockedShm_unlock(lck_posix);
+ }
+ return 1;
+}
+
+PLUGIN_API int XPluginStart ( char * outName, char * outSignature, char * outDescription ) {
+ view_x = XPLMFindDataRef("sim/aircraft/view/acf_peX");
+ view_y = XPLMFindDataRef("sim/aircraft/view/acf_peY");
+ view_z = XPLMFindDataRef("sim/aircraft/view/acf_peZ");
+ view_heading = XPLMFindDataRef("sim/graphics/view/pilots_head_psi");
+ view_pitch = XPLMFindDataRef("sim/graphics/view/pilots_head_the");
+ if (view_x && view_y && view_z && view_heading && view_pitch) {
+ lck_posix = PortableLockedShm_init(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM));
+ if (lck_posix->mem == (void*)-1) {
+ fprintf(stderr, "opentrack failed to init SHM!\n");
+ return 0;
+ }
+ shm_posix = (WineSHM*) lck_posix->mem;
+ memset(shm_posix, 0, sizeof(WineSHM));
+ strcpy(outName, "opentrack");
+ strcpy(outSignature, "opentrack - freetrack lives!");
+ strcpy(outDescription, "head tracking view control");
+ fprintf(stderr, "opentrack init complete\n");
+ return 1;
+ }
+ return 0;
+}
+
+PLUGIN_API void XPluginStop ( void ) {
+ if (lck_posix)
+ {
+ PortableLockedShm_free(lck_posix);
+ lck_posix = NULL;
+ shm_posix = NULL;
+ }
+}
+
+PLUGIN_API void XPluginEnable ( void ) {
+ XPLMRegisterDrawCallback(write_head_position, xplm_Phase_LastScene, 1, NULL);
+}
+
+PLUGIN_API void XPluginDisable ( void ) {
+ XPLMUnregisterDrawCallback(write_head_position, xplm_Phase_LastScene, 1, NULL);
+}
+
+PLUGIN_API void XPluginReceiveMessage(
+ XPLMPluginID OT_UNUSED(inFromWho),
+ int OT_UNUSED(inMessage),
+ void * OT_UNUSED(inParam))
+{
+ if (inMessage == XPLM_MSG_AIRPORT_LOADED)
+ reinit_offset();
+}
diff --git a/x-plane-plugin/version-script.txt b/x-plane-plugin/version-script.txt
new file mode 100644
index 00000000..cfb84ec8
--- /dev/null
+++ b/x-plane-plugin/version-script.txt
@@ -0,0 +1,10 @@
+{
+ global:
+ XPluginStart;
+ XPluginStop;
+ XPluginEnable;
+ XPluginDisable;
+ XPluginReceiveMessage;
+ local:
+ *;
+};