/******************************************************************************** * FreeTrackClientDll Implements the FreeTrack 2.0 interface for FT-enabled * * games. * * It uses the FreeTrack protocol (memory mapping) to * * receive data from FaceTrackNoIR (or FreeTrack, or ...). * * * * Copyright (C) 2013 Wim Vriend (Developing) * * Ron Hendriks (Testing and Research) * * * * 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 . * * * ********************************************************************************/ /* Modifications (last one on top): 20130208 - WVR: The old DLL from FreeTrack seems to crash ArmA2. And we need 64-bit support. */ /* * COMPILE WITH: * % i686-w64-mingw32-gcc -shared -o FreeTrackClient.dll -O2 FreeTrackClient.c * DO NOT USE MSVC! IT INTRODUCES FAULTY MANIFEST!!! * -- sthalik */ #ifdef WIN64 #pragma comment(linker, "/export:FTGetData") #pragma comment(linker, "/export:FTReportName") #pragma comment(linker, "/export:FTGetDllVersion") #pragma comment(linker, "/export:FTProvider") #endif #define FT_DECLSPEC __declspec(dllexport) #define FT_EXPORT(t) FT_DECLSPEC t __stdcall #define NP_AXIS_MAX 16383 #include #include #include #include #include #include typedef 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; } TFreeTrackData; typedef TFreeTrackData * PFreetrackData; // // Functions to create/open the file-mapping // and to destroy it again. // float scale2AnalogLimits( float x, float min_x, float max_x ); float getDegreesFromRads ( float rads ); FT_EXPORT(BOOL) FTCreateMapping(void); #if 0 static FILE *debug_stream; #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 // #define FT_MM_DATA "FT_SharedMem" #define FREETRACK_MUTEX "FT_Mutext" static HANDLE hFTMemMap = 0; static TFreeTrackData *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. // FT_EXPORT(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"); // debug_stream = fopen("c:\\FreeTrackClient.log", "a"); 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(TFreeTrackData* data) { static int frame = 0; static int prevDataID = 0; static int dlyTrackingOff = 0; static int tracking = 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->DataID) { *data = *pMemData; dlyTrackingOff = 0; } else { dlyTrackingOff++; if (dlyTrackingOff > 20) { dlyTrackingOff = 100; tracking = FALSE; } } prevDataID = pMemData->DataID; // // Limit the range of DataID // if (pMemData->DataID > 1000) { pMemData->DataID = 0; } data->DataID = pMemData->DataID; // // Send the ID to FaceTrackNoIR, so it can display the game-name. // This could be a FreeTrack-specific ID // //sprintf(pMemData->GameID, "%d", 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! // // #pragma comment(linker, "/export:FTCreateMapping@0=FTCreateMapping") FT_EXPORT(BOOL) FTCreateMapping(void) { BOOL bMappingExists = FALSE; PDWORD_PTR MsgResult = 0; // // 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( TFreeTrackData ), (LPCSTR) FT_MM_DATA ); if ( ( hFTMemMap != 0 ) && ( GetLastError() == ERROR_ALREADY_EXISTS ) ) { dbg_report("FTCreateMapping: Mapping already exists.\n"); bMappingExists = TRUE; // So the server was (probably) already started! CloseHandle( hFTMemMap ); hFTMemMap = 0; } // // Create a new FileMapping, Read/Write access // hFTMemMap = OpenFileMappingA( FILE_MAP_ALL_ACCESS , FALSE , (LPCSTR) FT_MM_DATA ); if ( ( hFTMemMap != 0 ) ) { dbg_report("FTCreateMapping: Mapping opened.\n"); pMemData = (TFreeTrackData *) MapViewOfFile(hFTMemMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof( TFreeTrackData ) ); hFTMutex = CreateMutexA(NULL, FALSE, FREETRACK_MUTEX); } else { return FALSE; } return TRUE; } // // Destory the FileMapping to the shared memory // #pragma comment(linker, "/export:FTDestroyMapping@0=FTDestroyMapping") FT_EXPORT(void) FTDestroyMapping(void) { if ( pMemData != NULL ) { UnmapViewOfFile ( pMemData ); } CloseHandle( hFTMutex ); CloseHandle( hFTMemMap ); pMemData = 0; hFTMemMap = 0; } // // 4 convenience // float getDegreesFromRads ( float rads ) { return (rads * 57.295781f); } // // Scale the measured value to the TIR values // 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; }