/******************************************************************************** * FaceTrackNoIR This program is a private project of the some enthusiastic * * gamers from Holland, who don't like to pay 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 . * *********************************************************************************/ #ifndef __TRACKER_H__ #define __TRACKER_H__ #include #include #include #include #include #include #include #include #define DIRECTINPUT_VERSION 0x0800 #include #include "FTServer.h" // Freetrack-server #include "FGServer.h" // FlightGear-server #include "PPJoyServer.h" // Virtual Joystick #include "FTIRServer.h" // FakeTIR-server // include the DirectX Library files #pragma comment (lib, "dinput8.lib") #pragma comment (lib, "dxguid.lib") using namespace sm::faceapi; using namespace sm::faceapi::qt; enum AngleName { PITCH = 0, YAW = 1, ROLL = 2, X = 3, Y = 4, Z = 5 }; enum FTNoIR_Client { FREE_TRACK = 0, FLIGHTGEAR = 1, FTNOIR = 2, PPJOY = 3, TRACKIR = 4 }; class FaceTrackNoIR; // pre-define parent-class to avoid circular includes // // Structure to hold all variables concerning one of 6 DOF's // struct THeadPoseDOF { float headPos; // Current position (from faceTracker, radials or meters) float initial_headPos; // Position on startup (first valid value) float offset_headPos; // Offset for centering float sens; // Sensitivity (multiplication factor) float invert; // Invert measured value (= 1.0f or -1.0f) float red; // Reduction factor (used for EWMA-filtering, between 0.0f and 1.0f) QList rawList; // List of 'n' headPos values (used for moving average) int maxItems; // Maximum number of elements is rawList float newPos; // New Position (used locally) float prevPos; // Previous Position float prevRawPos; // Previous Raw Position }; // // Structure to hold keycode and CTRL, SHIFT, ALT for shortkeys // struct TShortKey { BYTE keycode; // Required Key bool shift; // Modifiers to examine bool ctrl; bool alt; }; class Tracker : public QThread { Q_OBJECT private: // Handles to neatly terminate thread... HANDLE m_StopThread; HANDLE m_WaitThread; FTNoIR_Client selectedClient; /** face api variables **/ APIScope *faceapi_scope; QSharedPointer _engine; QSharedPointer _headtracker; smEngineHandle _engine_handle; /** static callback method for the head pose tracking **/ static void STDCALL receiveHeadPose(void *,smEngineHeadPoseData head_pose, smCameraVideoFrame video_frame); static void addRaw2List ( QList *rawList, float maxIndex, float raw ); static float lowPassFilter ( float newvalue, float *oldvalue, float dt, float coeff); static float rateLimiter ( float newvalue, float *oldvalue, float dt, float max_rate); static float getCorrectedNewRaw ( float NewRaw, float rotNeutral ); /** static member variables for saving the head pose **/ static THeadPoseDOF Pitch; // Head-rotation X-direction (Up/Down) static THeadPoseDOF Yaw; // Head-rotation Y-direction () static THeadPoseDOF Roll; // Head-rotation Z-direction () static THeadPoseDOF X; // Head-movement X-direction (Left/Right) static THeadPoseDOF Y; // Head-movement Y-direction (Up/Down) static THeadPoseDOF Z; // Head-movement Z-direction (To/From camera) static TShortKey CenterKey; // ShortKey to Center headposition static TShortKey StartStopKey; // ShortKey to Start/stop tracking // Flags to start/stop/reset tracking static bool confid; // Tracker data is OK static bool set_initial; // initial headpose is set static bool do_tracking; // Start/stop tracking, using MINUS key on keyboard static bool do_center; // Center head-position, using EQUALS key on keyboard static bool useFilter; static float rotNeutralZone; // Neutral Zone for rotations (rad). static long prevHeadPoseTime; // Time from previous sample /** QT objects **/ QLineEdit *headXLine; QLineEdit *headYLine; QLineEdit *headZLine; QLineEdit *headRotXLine; QLineEdit *headRotYLine; QLineEdit *headRotZLine; QWidget *headPoseWidget; FaceTrackNoIR *mainApp; FTServer *server_FT; // Freetrack Server FGServer *server_FG; // FlightGear Server PPJoyServer *server_PPJoy; // PPJoy Server FTIRServer *server_FTIR; // Fake TIR Server protected: // qthread override run method void run(); public: Tracker( int clientID ); ~Tracker(); void setup(QWidget *head, FaceTrackNoIR *parent); void registerHeadPoseCallback(); bool handleGameCommand ( int command ); QString getGameProgramName(); // Get the ProgramName from the game and display it. void loadSettings(); // Load settings from the INI-file bool isShortKeyPressed( TShortKey *key, BYTE *keystate ); QSharedPointer getEngine() { return _engine; }; // smEngineHandle getEngineHandle() { return _engine->handle(); }; static float getHeadPosX() {return X.headPos;} static float getHeadPosY() {return Y.headPos;} static float getHeadPosZ() {return Z.headPos;} static void setHeadPosX(float x) { X.headPos = x; } static void setHeadPosY(float y) { Y.headPos = y; } static void setHeadPosZ(float z) { Z.headPos = z; } static float getHeadRotX() {return Pitch.headPos;} static float getHeadRotY() {return Yaw.headPos;} static float getHeadRotZ() {return Roll.headPos;} static void setHeadRotX(float x) { Pitch.headPos = x; } static void setHeadRotY(float y) { Yaw.headPos = y; } static void setHeadRotZ(float z) { Roll.headPos = z; } static bool getConfid() { return confid; } static void setSensPitch(int x) { Pitch.sens = x/100.0f; } static void setSensYaw(int x) { Yaw.sens = x/100.0f; } static void setSensRoll(int x) { Roll.sens = x/100.0f; } static void setSensX(int x) { X.sens = x/100.0f; } static void setSensY(int x) { Y.sens = x/100.0f; } static void setSensZ(int x) { Z.sens = x/100.0f; } static void setInvertPitch(bool invert) { Pitch.invert = invert?-1.0f:+1.0f; } static void setInvertYaw(bool invert) { Yaw.invert = invert?-1.0f:+1.0f; } static void setInvertRoll(bool invert) { Roll.invert = invert?-1.0f:+1.0f; } static void setInvertX(bool invert) { X.invert = invert?-1.0f:+1.0f; } static void setInvertY(bool invert) { Y.invert = invert?-1.0f:+1.0f; } static void setInvertZ(bool invert) { Z.invert = invert?-1.0f:+1.0f; } static void setUseFilter(bool set) { useFilter = set; } static void setRedYaw(int x) { Yaw.red = x/100.0f; } static void setRedPitch(int x) { Pitch.red = x/100.0f; } static void setRedRoll(int x) { Roll.red = x/100.0f; } static void setRedX(int x) { X.red = x/100.0f; } static void setRedY(int x) { Y.red = x/100.0f; } static void setRedZ(int x) { Z.red = x/100.0f; } static void setNeutralZone(int x) { rotNeutralZone = (x * 2.0f * 3.14159)/360.0f; } float getSmoothFromList ( QList *rawList ); float getDegreesFromRads ( float rads ) { return (rads * 57.295781f); } // For now, use one slider for all void setSmoothing(int x) { Pitch.maxItems = x; Yaw.maxItems = x; Roll.maxItems = x; X.maxItems = x; Y.maxItems = x; Z.maxItems = x; } }; #endif