/******************************************************************************** * 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) 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 . * *********************************************************************************/ /* Modifications (last one on top): 20130201 - WVR: Remove the Protocol, when stopping the Thread. 20121215 - WVR: Fixed crash after message: protocol not installed correctly... by terminating the thread. 20120921 - WVR: Fixed centering when no filter is selected. 20120917 - WVR: Added Mouse-buttons to ShortKeys. 20120827 - WVR: Signal tracking = false to Curve-widget(s) when quitting run(). Also when Alternative Pitch curve is used. 20120805 - WVR: The FunctionConfig-widget is used to configure the Curves. It was tweaked some more, because the Accela filter now also uses the Curve(s). ToDo: make the ranges configurable by the user. Development on the Toradex IMU makes us realize, that a fixed input-range may not be so handy after all.. 20120427 - WVR: The Protocol-code was already in separate DLLs, but the ListBox was still filled ´statically´. Now, a Dir() of the EXE-folder is done, to locate Protocol-DLLs. The Icons were also moved to the DLLs 20120317 - WVR: The Filter and Tracker-code was moved to separate DLLs. The calling-method was changed accordingly. The face-tracker member-functions NotifyZeroed and refreshVideo were added, as requested by Stanislaw. 20110411 - WVR: Finished moving all Protocols to separate C++ projects. Every protocol now has it's own Class, that's inside it's own DLL. This reduces the size of the program, makes it more structured and enables a more sophisticated installer. 20110328 - WVR: Changed the camera-structs into class-instances. This makes initialisation easier and hopefully solves the remaining 'start-up problem'. 20110313 - WVR: Removed 'set_initial'. Less is more. 20110109 - WVR: Added setZero option to define behaviour after STOP tracking via shortkey. 20110104 - WVR: Removed a few nasty bugs (it was impossible to stop tracker without crash). 20101224 - WVR: Removed the QThread inheritance of the Base Class for the protocol-servers. Again, this drastically simplifies the code in the protocols. 20101217 - WVR: Created Base Class for the protocol-servers. This drastically simplifies the code needed here. 20101024 - WVR: Added shortkey to disable/enable one or more axis during tracking. 20101021 - WVR: Added FSUIPC server for FS2004. 20101011 - WVR: Added SimConnect server. 20101007 - WVR: Created 6DOF-curves and drastically changed the tracker for that. Also eliminated a 'glitch' in the process. 20100607 - WVR: Re-installed Rotation Neutral Zone and improved reaction after 'start/stop'. MessageBeep when confidence is back... 20100604 - WVR: Created structure for DOF-data and changed timing of ReceiveHeadPose end run(). 20100602 - WVR: Implemented EWMA-filtering, according to the example of Melchior Franz. Works like a charm... 20100601 - WVR: Added DirectInput keyboard-handling. '=' used for center, 'BACK' for start (+center)/stop. 20100517 - WVR: Added upstream command(s) from FlightGear 20100523 - WVR: Checkboxes to invert 6DOF's was implemented. Multiply by 1 or (-1). */ #include "tracker.h" #include "FaceTrackNoIR.h" // Flags bool Tracker::confid = false; bool Tracker::do_tracking = true; bool Tracker::do_center = false; bool Tracker::do_inhibit = false; bool Tracker::do_game_zero = false; bool Tracker::do_axis_reverse = false; bool Tracker::setZero = true; bool Tracker::setEngineStop = true; HANDLE Tracker::hTrackMutex = 0; bool Tracker::useAxisReverse = false; // Use Axis Reverse float Tracker::YawAngle4ReverseAxis = 40.0f; // Axis Reverse settings float Tracker::Z_Pos4ReverseAxis = -20.0f; float Tracker::Z_PosWhenReverseAxis = 50.0f; T6DOF Tracker::current_camera(0,0,0,0,0,0); // Used for filtering T6DOF Tracker::target_camera(0,0,0,0,0,0); T6DOF Tracker::new_camera(0,0,0,0,0,0); T6DOF Tracker::output_camera(0,0,0,0,0,0); // Position sent to game protocol THeadPoseDOF Tracker::Pitch("PitchUp", "PitchDown", 50, 180, 50, 90); // One structure for each of 6DOF's THeadPoseDOF Tracker::Yaw("Yaw", "", 50, 180); THeadPoseDOF Tracker::Roll("Roll", "", 50, 180); THeadPoseDOF Tracker::X("X","", 50, 180); THeadPoseDOF Tracker::Y("Y","", 50, 180); THeadPoseDOF Tracker::Z("Z","", 50, 180); TShortKey Tracker::CenterKey; // ShortKey to Center headposition TShortKey Tracker::StartStopKey; // ShortKey to Start/stop tracking TShortKey Tracker::InhibitKey; // ShortKey to inhibit axis while tracking TShortKey Tracker::GameZeroKey; // ShortKey to Set Game Zero bool Tracker::DisableBeep = false; // Disable beep when center //TShortKey Tracker::AxisReverseKey; // ShortKey to start/stop axis reverse while tracking int Tracker::CenterMouseKey; // ShortKey to Center headposition int Tracker::StartStopMouseKey; // ShortKey to Start/stop tracking int Tracker::InhibitMouseKey; // ShortKey to inhibit axis while tracking int Tracker::GameZeroMouseKey; // ShortKey to Set Game Zero //ITrackerPtr Tracker::pTracker; // Pointer to Tracker instance (in DLL) IProtocolPtr Tracker::pProtocol; // Pointer to Protocol instance (in DLL) IFilterPtr Tracker::pFilter; // Pointer to Filter instance (in DLL) /** constructor **/ Tracker::Tracker( FaceTrackNoIR *parent ) { QString libName; importGetTracker getIT; QLibrary *trackerLib; importGetFilter getFilter; QLibrary *filterLib; importGetProtocol getProtocol; QLibrary *protocolLib; QFrame *video_frame; // Retieve the pointer to the parent mainApp = parent; // Create events m_StopThread = CreateEvent(0, TRUE, FALSE, 0); m_WaitThread = CreateEvent(0, TRUE, FALSE, 0); Tracker::hTrackMutex = CreateMutexA(NULL, false, "HeadPose_mutex"); // // Initialize the headpose-data // Tracker::Yaw.initHeadPoseData(); Tracker::Pitch.initHeadPoseData(); Tracker::Roll.initHeadPoseData(); Tracker::X.initHeadPoseData(); Tracker::Y.initHeadPoseData(); Tracker::Z.initHeadPoseData(); // // Locate the video-frame, for the DLL // video_frame = 0; video_frame = mainApp->getVideoWidget(); qDebug() << "Tracker::Tracker VideoFrame = " << video_frame; // // Load the Tracker-engine DLL, get the tracker-class from it and do stuff... // pTracker = NULL; libName = mainApp->getCurrentTrackerName(); if (!libName.isEmpty()) { trackerLib = new QLibrary(libName); getIT = (importGetTracker) trackerLib->resolve("GetTracker"); qDebug() << "Tracker::Tracker libName = " << libName; if (getIT) { ITracker *ptrXyz(getIT()); // Get the Class if (ptrXyz) { pTracker = ptrXyz; pTracker->Initialize( video_frame ); qDebug() << "Tracker::setup Function Resolved!"; } } else { QMessageBox::warning(0,"FaceTrackNoIR Error", libName + " DLL not loaded",QMessageBox::Ok,QMessageBox::NoButton); } } // // Load the Tracker-engine DLL, get the tracker-class from it and do stuff... // pSecondTracker = NULL; libName = mainApp->getSecondTrackerName(); if ((!libName.isEmpty()) && (libName != "None")) { trackerLib = new QLibrary(libName); getIT = (importGetTracker) trackerLib->resolve("GetTracker"); if (getIT) { ITracker *ptrXyz(getIT()); // Get the Class if (ptrXyz) { pSecondTracker = ptrXyz; pSecondTracker->Initialize( NULL ); qDebug() << "Tracker::setup Function Resolved!"; } } else { QMessageBox::warning(0,"FaceTrackNoIR Error", libName + " DLL not loaded",QMessageBox::Ok,QMessageBox::NoButton); } } // // Load the DLL with the protocol-logic and retrieve a pointer to the Protocol-class. // libName = mainApp->getCurrentProtocolName(); if (!libName.isEmpty()) { protocolLib = new QLibrary(libName); getProtocol = (importGetProtocol) protocolLib->resolve("GetProtocol"); if (getProtocol) { IProtocolPtr ptrXyz(getProtocol()); if (ptrXyz) { pProtocol = ptrXyz; pProtocol->Initialize(); qDebug() << "Protocol::setup Function Resolved!"; } } else { QMessageBox::warning(0,"FaceTrackNoIR Error", "Protocol-DLL not loaded",QMessageBox::Ok,QMessageBox::NoButton); return; } } // // Load the DLL with the filter-logic and retrieve a pointer to the Filter-class. // pFilter = NULL; libName = mainApp->getCurrentFilterName(); if ((!libName.isEmpty()) && (libName != "None")) { filterLib = new QLibrary(libName); getFilter = (importGetFilter) filterLib->resolve("GetFilter"); if (getFilter) { IFilterPtr ptrXyz(getFilter()); if (ptrXyz) { pFilter = ptrXyz; qDebug() << "Filter::setup Function Resolved!"; } } else { QMessageBox::warning(0,"FaceTrackNoIR Error", "Filter-DLL not loaded",QMessageBox::Ok,QMessageBox::NoButton); return; } } // Load the settings from the INI-file loadSettings(); } /** destructor empty **/ Tracker::~Tracker() { // Stop the Tracker(s) if (pTracker) { pTracker->StopTracker( true ); } if (pSecondTracker) { pSecondTracker->StopTracker( true ); } // Trigger thread to stop ::SetEvent(m_StopThread); // Wait until thread finished if (isRunning()) { ::WaitForSingleObject(m_WaitThread, INFINITE); } // // Remove the Tracker // 20120615, WVR: As suggested by Stanislaw if (pTracker) { delete pTracker; pTracker = NULL; } if (pSecondTracker) { delete pSecondTracker; pSecondTracker = NULL; } // // Remove the Protocol // if (pProtocol) { delete pProtocol; pProtocol = NULL; } // Close handles ::CloseHandle(m_StopThread); ::CloseHandle(m_WaitThread); if (Tracker::hTrackMutex != 0) { ::CloseHandle( Tracker::hTrackMutex ); } # ifdef USE_DEBUG_CLIENT debug_Client->deleteLater(); // Delete Excel protocol-server # endif qDebug() << "Tracker::~Tracker Finished..."; } /** setting up the tracker engine **/ void Tracker::setup() { bool DLL_Ok; // retrieve pointers to the User Interface and the main Application if (pTracker) { pTracker->StartTracker( mainApp->winId() ); } if (pSecondTracker) { pSecondTracker->StartTracker( mainApp->winId() ); } // // 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) { DLL_Ok = pProtocol->checkServerInstallationOK( mainApp->winId() ); if (!DLL_Ok) { // Trigger thread to stop ::SetEvent(m_StopThread); QMessageBox::information(mainApp, "FaceTrackNoIR error", "Protocol is not (correctly) installed!"); } } # ifdef USE_DEBUG_CLIENT DLL_Ok = debug_Client->checkServerInstallationOK( mainApp->winId() ); // Check installation if (!DLL_Ok) { QMessageBox::information(mainApp, "FaceTrackNoIR error", "Excel Protocol is not (correctly) installed!"); } # endif } /** QThread run method @override **/ void Tracker::run() { /** Direct Input variables **/ // // The DirectX stuff was found here: http://www.directxtutorial.com/tutorial9/e-directinput/dx9e2.aspx // LPDIRECTINPUT8 din; // the pointer to our DirectInput interface LPDIRECTINPUTDEVICE8 dinkeyboard; // the pointer to the keyboard device LPDIRECTINPUTDEVICE8 dinmouse; // the pointer to the mouse device BYTE keystate[256]; // the storage for the key-information DIMOUSESTATE mousestate; // the storage for the mouse-information HRESULT retAcquire; bool lastCenterKey = false; // Remember state, to detect rising edge bool lastStartStopKey = false; bool lastInhibitKey = false; bool lastGameZeroKey = false; bool lastCenterMouseKey = false; // Remember state, to detect rising edge bool lastStartStopMouseKey = false; bool lastInhibitMouseKey = false; bool lastGameZeroMouseKey = false; bool waitAxisReverse = false; bool waitThroughZero = false; double actualYaw = 0.0f; double actualZ = 0.0f; T6DOF offset_camera(0,0,0,0,0,0); T6DOF gamezero_camera(0,0,0,0,0,0); T6DOF gameoutput_camera(0,0,0,0,0,0); bool bInitialCenter1 = true; bool bInitialCenter2 = true; bool bTracker1Confid = false; bool bTracker2Confid = false; Tracker::do_tracking = true; // Start initially Tracker::do_center = false; // Center initially // // Test some Filter-stuff // if (pFilter) { QString filterName; //pFilter->getFullName(&filterName); //qDebug() << "Tracker::run() FilterName = " << filterName; } // // Setup the DirectInput for keyboard strokes // // create the DirectInput interface if (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din, NULL) != DI_OK) { // COM stuff, so we'll set it to NULL qDebug() << "Tracker::setup DirectInput8 Creation failed!" << GetLastError(); } // create the keyboard device if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) { qDebug() << "Tracker::setup CreateDevice function failed!" << GetLastError(); } // create the mouse device din->CreateDevice(GUID_SysMouse, &dinmouse, NULL); // set the data format to keyboard format if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) { qDebug() << "Tracker::setup SetDataFormat function failed!" << GetLastError(); } // set the data format to mouse format dinmouse->SetDataFormat(&c_dfDIMouse); // set the control you will have over the keyboard if (dinkeyboard->SetCooperativeLevel(mainApp->winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) { qDebug() << "Tracker::setup SetCooperativeLevel function failed!" << GetLastError(); } // set the control you will have over the mouse dinmouse->SetCooperativeLevel(mainApp->winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); forever { // Check event for stop thread if(::WaitForSingleObject(m_StopThread, 0) == WAIT_OBJECT_0) { dinkeyboard->Unacquire(); // Unacquire keyboard dinkeyboard->Release(); dinmouse->Unacquire(); // Unacquire mouse dinmouse->Release(); din->Release(); // Release DirectInput // Set event ::SetEvent(m_WaitThread); qDebug() << "Tracker::run terminated run()"; X.curvePtr->setTrackingActive( false ); Y.curvePtr->setTrackingActive( false ); Z.curvePtr->setTrackingActive( false ); Yaw.curvePtr->setTrackingActive( false ); Pitch.curvePtr->setTrackingActive( false ); Pitch.curvePtrAlt->setTrackingActive( false ); Roll.curvePtr->setTrackingActive( false ); return; } // // Check the mouse // // get access if we don't have it already retAcquire = dinmouse->Acquire(); if ( (retAcquire != DI_OK) && (retAcquire != S_FALSE) ) { qDebug() << "Tracker::run Acquire function failed!" << GetLastError(); } else { if (dinmouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&mousestate) != DI_OK) { qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError(); } else { // // Check the state of the StartStop MouseKey // if ( isMouseKeyPressed( &StartStopMouseKey, &mousestate ) && (!lastStartStopMouseKey) ) { Tracker::do_tracking = !Tracker::do_tracking; // // To start tracking again and to be at '0', execute Center command too // if (Tracker::do_tracking) { Tracker::confid = false; if (pTracker) { pTracker->StartTracker( mainApp->winId() ); } if (pSecondTracker) { pSecondTracker->StartTracker( mainApp->winId() ); } } else { if (setEngineStop) { // Only stop engine when option is checked if (pTracker) { pTracker->StopTracker( false ); } if (pSecondTracker) { pSecondTracker->StopTracker( false ); } } } qDebug() << "Tracker::run() says StartStop pressed, do_tracking =" << Tracker::do_tracking; } lastStartStopMouseKey = isMouseKeyPressed( &StartStopMouseKey, &mousestate ); // Remember // // Check the state of the Center MouseKey // if ( isMouseKeyPressed( &CenterMouseKey, &mousestate ) && (!lastCenterMouseKey) ) { Tracker::do_center = true; qDebug() << "Tracker::run() says Center MouseKey pressed"; } lastCenterMouseKey = isMouseKeyPressed( &CenterMouseKey, &mousestate ); // Remember // // Check the state of the GameZero MouseKey // if ( isMouseKeyPressed( &GameZeroMouseKey, &mousestate ) && (!lastGameZeroMouseKey) ) { Tracker::do_game_zero = true; qDebug() << "Tracker::run() says GameZero MouseKey pressed"; } lastGameZeroMouseKey = isMouseKeyPressed( &GameZeroMouseKey, &mousestate ); // Remember // // Check the state of the Inhibit MouseKey // if ( isMouseKeyPressed( &InhibitMouseKey, &mousestate ) && (!lastInhibitMouseKey) ) { Tracker::do_inhibit = !Tracker::do_inhibit; qDebug() << "Tracker::run() says Inhibit MouseKey pressed"; // // Execute Center command too, when inhibition ends. // if (!Tracker::do_inhibit) { Tracker::do_center = true; } } lastInhibitMouseKey = isMouseKeyPressed( &InhibitMouseKey, &mousestate ); // Remember } } // // Check the keyboard // // get access if we don't have it already retAcquire = dinkeyboard->Acquire(); if ( (retAcquire != DI_OK) && (retAcquire != S_FALSE) ) { qDebug() << "Tracker::run Acquire function failed!" << GetLastError(); } else { // get the input data if (dinkeyboard->GetDeviceState(256, (LPVOID)keystate) != DI_OK) { qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError(); } else { // // Check the state of the Start/Stop key // if ( isShortKeyPressed( &StartStopKey, &keystate[0] ) && (!lastStartStopKey) ) { Tracker::do_tracking = !Tracker::do_tracking; // // To start tracking again and to be at '0', execute Center command too // if (Tracker::do_tracking) { Tracker::confid = false; if (pTracker) { pTracker->StartTracker( mainApp->winId() ); } if (pSecondTracker) { pSecondTracker->StartTracker( mainApp->winId() ); } } else { if (setEngineStop) { // Only stop engine when option is checked if (pTracker) { pTracker->StopTracker( false ); } if (pSecondTracker) { pSecondTracker->StopTracker( false ); } } } qDebug() << "Tracker::run() says StartStop pressed, do_tracking =" << Tracker::do_tracking; } lastStartStopKey = isShortKeyPressed( &StartStopKey, &keystate[0] ); // Remember // // Check the state of the Center key // if ( isShortKeyPressed( &CenterKey, &keystate[0] ) && (!lastCenterKey) ) { Tracker::do_center = true; qDebug() << "Tracker::run() says Center pressed"; } lastCenterKey = isShortKeyPressed( &CenterKey, &keystate[0] ); // Remember // // Check the state of the GameZero key // if ( isShortKeyPressed( &GameZeroKey, &keystate[0] ) && (!lastGameZeroKey) ) { Tracker::do_game_zero = true; qDebug() << "Tracker::run() says GameZero pressed"; } lastGameZeroKey = isShortKeyPressed( &GameZeroKey, &keystate[0] ); // Remember // // Check the state of the Inhibit key // if ( isShortKeyPressed( &InhibitKey, &keystate[0] ) && (!lastInhibitKey) ) { Tracker::do_inhibit = !Tracker::do_inhibit; qDebug() << "Tracker::run() says Inhibit pressed"; // // Execute Center command too, when inhibition ends. // if (!Tracker::do_inhibit) { Tracker::do_center = true; } } lastInhibitKey = isShortKeyPressed( &InhibitKey, &keystate[0] ); // Remember } } // // Reset the 'wait' flag. Moving above 90 with the key pressed, will (de-)activate Axis Reverse. // // qDebug() << "Tracker::run() says actualZ = " << actualZ << ", terwijl Z_Pos4 = " << Z_Pos4ReverseAxis; if (useAxisReverse) { Tracker::do_axis_reverse = ((fabs(actualYaw) > YawAngle4ReverseAxis) && (actualZ < Z_Pos4ReverseAxis)); } else { Tracker::do_axis_reverse = false; } if (WaitForSingleObject(Tracker::hTrackMutex, 100) == WAIT_OBJECT_0) { THeadPoseData newpose; newpose.pitch = 0.0f; newpose.roll = 0.0f; newpose.yaw = 0.0f; newpose.x = 0.0f; newpose.y = 0.0f; newpose.z = 0.0f; // // The second tracker serves as 'secondary'. So if an axis is written by the second tracker it CAN be overwritten by the Primary tracker. // This is enforced by the sequence below. // if (pSecondTracker) { bTracker2Confid = pSecondTracker->GiveHeadPoseData(&newpose); } else { bTracker2Confid = false; bInitialCenter2 = false; } if (pTracker) { bTracker1Confid = pTracker->GiveHeadPoseData(&newpose); // qDebug() << "Tracker::run() says Roll = " << newpose.roll; } else { bTracker1Confid = false; bInitialCenter1 = false; } Tracker::confid = (bTracker1Confid || bTracker2Confid); if ( Tracker::confid ) { addHeadPose(newpose); } // // If Center is pressed, copy the current values to the offsets. // if ((Tracker::do_center) || ((bInitialCenter1 && bTracker1Confid ) || (bInitialCenter2 && bTracker2Confid))) { if (!DisableBeep) { MessageBeep (MB_ICONASTERISK); // Acknowledge the key-press with a beep. } if (pTracker && bTracker1Confid) { pTracker->notifyCenter(); // Send 'center' to the tracker bInitialCenter1 = false; } if (pSecondTracker && bTracker2Confid) { pSecondTracker->notifyCenter(); // Send 'center' to the second tracker bInitialCenter2 = false; } // // Only copy valid values // if (Tracker::confid) { offset_camera.x = getSmoothFromList( &X.rawList ); offset_camera.y = getSmoothFromList( &Y.rawList ); offset_camera.z = getSmoothFromList( &Z.rawList ); offset_camera.pitch = getSmoothFromList( &Pitch.rawList ); offset_camera.yaw = getSmoothFromList( &Yaw.rawList ); offset_camera.roll = getSmoothFromList( &Roll.rawList ); } Tracker::do_center = false; } // // If Set Game Zero is pressed, copy the current values to the offsets. // Change requested by Stanislaw // if (Tracker::confid && Tracker::do_game_zero) { if (pTracker) { if (!pTracker->notifyZeroed()) gamezero_camera = gameoutput_camera; } // gamezero_camera = gameoutput_camera; Tracker::do_game_zero = false; } if (Tracker::do_tracking && Tracker::confid) { // get values target_camera.x = getSmoothFromList( &X.rawList ); target_camera.y = getSmoothFromList( &Y.rawList ); target_camera.z = getSmoothFromList( &Z.rawList ); target_camera.pitch = getSmoothFromList( &Pitch.rawList ); target_camera.yaw = getSmoothFromList( &Yaw.rawList ); target_camera.roll = getSmoothFromList( &Roll.rawList ); // qDebug() << "Tracker::run() says Roll from Smoothing = " << target_camera.roll; // do the centering target_camera = target_camera - offset_camera; // // Use advanced filtering, when a filter was selected. // if (pFilter) { pFilter->FilterHeadPoseData(¤t_camera, &target_camera, &new_camera, Tracker::Pitch.newSample); // qDebug() << "Tracker::run() says Roll in Filter = " << current_camera.roll << ", Roll to output = " << new_camera.roll; } else { new_camera = target_camera; // qDebug() << "Tracker::run() says Roll to output = " << new_camera.roll; } output_camera.x = X.invert * X.curvePtr->getValue(new_camera.x); output_camera.y = Y.invert * Y.curvePtr->getValue(new_camera.y); output_camera.z = Z.invert * Z.curvePtr->getValue(new_camera.z); // // Determine, which curve (Up or Down) must be used for Pitch // bool altp = (new_camera.pitch < 0); if (altp) { output_camera.pitch = Pitch.invert * Pitch.curvePtrAlt->getValue(new_camera.pitch); Pitch.curvePtr->setTrackingActive( false ); Pitch.curvePtrAlt->setTrackingActive( true ); } else { output_camera.pitch = Pitch.invert * Pitch.curvePtr->getValue(new_camera.pitch); Pitch.curvePtr->setTrackingActive( true ); Pitch.curvePtrAlt->setTrackingActive( false ); } output_camera.yaw = Yaw.invert * Yaw.curvePtr->getValue(new_camera.yaw); output_camera.roll = Roll.invert * Roll.curvePtr->getValue(new_camera.roll); X.curvePtr->setTrackingActive( true ); Y.curvePtr->setTrackingActive( true ); Z.curvePtr->setTrackingActive( true ); Yaw.curvePtr->setTrackingActive( true ); Roll.curvePtr->setTrackingActive( true ); // // Reverse Axis. // actualYaw = output_camera.yaw; // Save the actual Yaw, otherwise we can't check for +90 actualZ = output_camera.z; // Also the Z if (Tracker::do_axis_reverse) { output_camera.z = Z_PosWhenReverseAxis; // Set the desired Z-position } // // Reset value for the selected axis, if inhibition is active // if (Tracker::do_inhibit) { if (InhibitKey.doPitch) output_camera.pitch = 0.0f; if (InhibitKey.doYaw) output_camera.yaw = 0.0f; if (InhibitKey.doRoll) output_camera.roll = 0.0f; if (InhibitKey.doX) output_camera.x = 0.0f; if (InhibitKey.doY) output_camera.y = 0.0f; if (InhibitKey.doZ) output_camera.z = 0.0f; } // // Send the headpose to the game // if (pProtocol) { gameoutput_camera = output_camera + gamezero_camera; pProtocol->sendHeadposeToGame( &gameoutput_camera, &newpose ); // degrees & centimeters } } else { // // Go to initial position // if (pProtocol && setZero) { output_camera.pitch = 0.0f; output_camera.yaw = 0.0f; output_camera.roll = 0.0f; output_camera.x = 0.0f; output_camera.y = 0.0f; output_camera.z = 0.0f; gameoutput_camera = output_camera + gamezero_camera; pProtocol->sendHeadposeToGame( &gameoutput_camera, &newpose ); // degrees & centimeters } X.curvePtr->setTrackingActive( false ); Y.curvePtr->setTrackingActive( false ); Z.curvePtr->setTrackingActive( false ); Yaw.curvePtr->setTrackingActive( false ); Pitch.curvePtr->setTrackingActive( false ); Pitch.curvePtrAlt->setTrackingActive( false ); Roll.curvePtr->setTrackingActive( false ); } } Tracker::Pitch.newSample = false; ReleaseMutex(Tracker::hTrackMutex); //for lower cpu load usleep(10000); yieldCurrentThread(); } } /** Add the headpose-data to the Lists **/ void Tracker::addHeadPose( THeadPoseData head_pose ) { // Pitch Tracker::Pitch.headPos = head_pose.pitch; // degrees addRaw2List ( &Pitch.rawList, Pitch.maxItems, Tracker::Pitch.headPos ); // Tracker::Pitch.confidence = head_pose.confidence; // Just this one ... Tracker::Pitch.newSample = true; // Yaw Tracker::Yaw.headPos = head_pose.yaw; // degrees addRaw2List ( &Yaw.rawList, Yaw.maxItems, Tracker::Yaw.headPos ); // Roll Tracker::Roll.headPos = head_pose.roll; // degrees addRaw2List ( &Roll.rawList, Roll.maxItems, Tracker::Roll.headPos ); // X-position Tracker::X.headPos = head_pose.x; // centimeters addRaw2List ( &X.rawList, X.maxItems, Tracker::X.headPos ); // Y-position Tracker::Y.headPos = head_pose.y; // centimeters addRaw2List ( &Y.rawList, Y.maxItems, Tracker::Y.headPos ); // Z-position (distance to camera, absolute!) Tracker::Z.headPos = head_pose.z; // centimeters addRaw2List ( &Z.rawList, Z.maxItems, Tracker::Z.headPos ); } // // Get the ProgramName from the Game and return it. // QString Tracker::getGameProgramName() { QString str; char dest[100]; str = QString("No protocol active?"); if (pProtocol) { pProtocol->getNameFromGame( dest ); str = QString( dest ); } return str; } // // Handle the command, send upstream by the game. // Valid values are: // 1 = reset Headpose // bool Tracker::handleGameCommand ( int command ) { qDebug() << "handleGameCommand says: Command =" << command; switch ( command ) { case 1: // reset headtracker Tracker::do_center = true; break; default: break; } return false; } // // Add the new Raw value to the QList. // Remove the last item(s), depending on the set maximum list-items. // void Tracker::addRaw2List ( QList *rawList, float maxIndex, float raw ) { // // Remove old values from the end of the QList. // If the setting for MaxItems was lowered, the QList is shortened here... // while (rawList->size() >= maxIndex) { rawList->removeLast(); } // // Insert the newest at the beginning. // rawList->prepend ( raw ); } // // Get the raw headpose, so it can be displayed. // void Tracker::getHeadPose( THeadPoseData *data ) { data->x = Tracker::X.headPos; // centimeters data->y = Tracker::Y.headPos; data->z = Tracker::Z.headPos; data->pitch = Tracker::Pitch.headPos; // degrees data->yaw = Tracker::Yaw.headPos; data->roll = Tracker::Roll.headPos; } // // Get the output-headpose, so it can be displayed. // void Tracker::getOutputHeadPose( THeadPoseData *data ) { data->x = output_camera.x; // centimeters data->y = output_camera.y; data->z = output_camera.z; data->pitch = output_camera.pitch; // degrees data->yaw = output_camera.yaw; data->roll = output_camera.roll; } // // Get the Smoothed value from the QList. // float Tracker::getSmoothFromList ( QList *rawList ) { float sum = 0; if (rawList->isEmpty()) return 0.0f; // // Add the Raw values and divide. // for ( int i = 0; i < rawList->size(); i++) { sum += rawList->at(i); } return sum / rawList->size(); } // // Load the current Settings from the currently 'active' INI-file. // void Tracker::loadSettings() { //int NeutralZone; //int sensYaw, sensPitch, sensRoll; //int sensX, sensY, sensZ; qDebug() << "Tracker::loadSettings says: Starting "; QSettings settings("Abbequerque Inc.", "FaceTrackNoIR"); // 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) qDebug() << "loadSettings says: iniFile = " << currentFile; // // Read the Tracking settings, to fill the curves. // //iniFile.beginGroup ( "Tracking" ); //NeutralZone = iniFile.value ( "NeutralZone", 5 ).toInt(); //sensYaw = iniFile.value ( "sensYaw", 100 ).toInt(); //sensPitch = iniFile.value ( "sensPitch", 100 ).toInt(); //sensRoll = iniFile.value ( "sensRoll", 100 ).toInt(); //sensX = iniFile.value ( "sensX", 100 ).toInt(); //sensY = iniFile.value ( "sensY", 100 ).toInt(); //sensZ = iniFile.value ( "sensZ", 100 ).toInt(); //iniFile.endGroup (); // // Read the keyboard shortcuts. // iniFile.beginGroup ( "KB_Shortcuts" ); // Center key CenterMouseKey = iniFile.value ( "MouseKey_Center", 0 ).toInt(); CenterKey.keycode = iniFile.value ( "Keycode_Center", DIK_HOME ).toInt(); CenterKey.shift = iniFile.value ( "Shift_Center", 0 ).toBool(); CenterKey.ctrl = iniFile.value ( "Ctrl_Center", 0 ).toBool(); CenterKey.alt = iniFile.value ( "Alt_Center", 0 ).toBool(); DisableBeep = iniFile.value ( "Disable_Beep", 0 ).toBool(); // StartStop key StartStopMouseKey = iniFile.value ( "MouseKey_StartStop", 0 ).toInt(); StartStopKey.keycode = iniFile.value ( "Keycode_StartStop", DIK_END ).toInt(); StartStopKey.shift = iniFile.value ( "Shift_StartStop", 0 ).toBool(); StartStopKey.ctrl = iniFile.value ( "Ctrl_StartStop", 0 ).toBool(); StartStopKey.alt = iniFile.value ( "Alt_StartStop", 0 ).toBool(); setZero = iniFile.value ( "SetZero", 1 ).toBool(); setEngineStop = iniFile.value ( "SetEngineStop", 1 ).toBool(); // Inhibit key InhibitMouseKey = iniFile.value ( "MouseKey_Inhibit", 0 ).toInt(); InhibitKey.keycode = iniFile.value ( "Keycode_Inhibit", 0 ).toInt(); InhibitKey.shift = iniFile.value ( "Shift_Inhibit", 0 ).toBool(); InhibitKey.ctrl = iniFile.value ( "Ctrl_Inhibit", 0 ).toBool(); InhibitKey.alt = iniFile.value ( "Alt_Inhibit", 0 ).toBool(); InhibitKey.doPitch = iniFile.value ( "Inhibit_Pitch", 0 ).toBool(); InhibitKey.doYaw = iniFile.value ( "Inhibit_Yaw", 0 ).toBool(); InhibitKey.doRoll = iniFile.value ( "Inhibit_Roll", 0 ).toBool(); InhibitKey.doX = iniFile.value ( "Inhibit_X", 0 ).toBool(); InhibitKey.doY = iniFile.value ( "Inhibit_Y", 0 ).toBool(); InhibitKey.doZ = iniFile.value ( "Inhibit_Z", 0 ).toBool(); // Game Zero key GameZeroMouseKey = iniFile.value ( "MouseKey_GameZero", 0 ).toInt(); GameZeroKey.keycode = iniFile.value ( "Keycode_GameZero", 0 ).toInt(); GameZeroKey.shift = iniFile.value ( "Shift_GameZero", 0 ).toBool(); GameZeroKey.ctrl = iniFile.value ( "Ctrl_GameZero", 0 ).toBool(); GameZeroKey.alt = iniFile.value ( "Alt_GameZero", 0 ).toBool(); // Axis Reverse key //AxisReverseKey.keycode = DIK_R; //AxisReverseKey.shift = false; //AxisReverseKey.ctrl = false; //AxisReverseKey.alt = false; // Reverse Axis useAxisReverse = iniFile.value ( "Enable_ReverseAxis", 0 ).toBool(); YawAngle4ReverseAxis = iniFile.value ( "RA_Yaw", 40 ).toInt(); Z_Pos4ReverseAxis = iniFile.value ( "RA_ZPos", 50 ).toInt(); Z_PosWhenReverseAxis = iniFile.value ( "RA_ToZPos", 80 ).toInt(); iniFile.endGroup (); } // // Determine if the ShortKey (incl. CTRL, SHIFT and/or ALT) is pressed. // bool Tracker::isShortKeyPressed( TShortKey *key, BYTE *keystate ){ bool shift; bool ctrl; bool alt; // // First, check if the right key is pressed. If so, check the modifiers // 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; } else { return false; } } // // Determine if the MouseKey is pressed. // bool Tracker::isMouseKeyPressed( int *key, DIMOUSESTATE *mousestate ){ // // If key == NONE, or invalid: ready! // if ((*key <= 0) || (*key > 5)) { return false; } // // Now, check if the right key is pressed. // if (mousestate->rgbButtons[*key-1] & 0x80) { return true; } else { return false; } }