////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // FaceAPI2FSX program implementation // Merges the old CockpitCamera.cpp and TestAppConsole.cpp into a single file // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Precompiled header #include "stdafx.h" //FaceAPI headers #include "sm_api.h" #include "ftnoir_tracker_sm_types.h" #include "utils.h" //local headers #include "build_options.h" //namespaces using namespace std; using namespace sm::faceapi::samplecode; // // global variables // HANDLE hSMMemMap = NULL; SMMemMap *pMemData; HANDLE hSMMutex; smEngineHeadPoseData new_head_pose; bool stopCommand = false; //enums enum GROUP_ID { GROUP0=0, }; enum EVENT_ID { EVENT_PING=0, EVENT_INIT, }; enum INPUT_ID { INPUT0=0, }; //function definitions void updateHeadPose(smEngineHeadPoseData* temp_head_pose); bool SMCreateMapping(); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // //FaceAPI function implementations // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void STDCALL receiveLogMessage(void *, const char *buf, int /*buf_len*/) { Lock lock(g_mutex); // serialize logging calls from different threads to avoid garbled output. //cout << string(buf); } // Callback function for face-data void STDCALL receiveFaceData(void *, smEngineFaceData face_data, smCameraVideoFrame video_frame) { Lock lock(g_mutex); // Get info including data pointer to original image from camera smImageInfo video_frame_image_info; THROW_ON_ERROR(smImageGetInfo(video_frame.image_handle, &video_frame_image_info)); // reentrant, so ok // video_frame_image_info.plane_addr[*] now point to the image memory planes. // The memory is only valid until the end of this routine unless you call smImageAddRef(video_frame.image_handle). // So you can deep copy the image data here, or use smImageAddRef() and just copy the pointer. // If you use smImageAddRef() you are responsible for calling smImageDestroy() to avoid a memory leak later. // In this callback you will typically want to copy the smEngineFaceData data into your own data-structure. // Since the smEngineFaceData contains multiple pod types copying it is not atomic and // a mutex is required to avoid the race-condition with any thread simultaneously // reading from your data-structure. // Such a race condition will not crash your code but will create weird noise in the tracking data. if (g_do_face_data_printing) { //cout << video_frame << " " << face_data; // Save any face texture to a PNG file if (face_data.texture) { // Create a unique filename std::stringstream filename; filename << "face_" << video_frame.frame_num << ".png"; // Try saving to a file if (saveToPNGFile(filename.str(), face_data.texture->image_info) == SM_API_OK) { cout << "Saved face-texture to " << filename.str() << std::endl; } else { cout << "Error saving face-texture to " << filename.str() << std::endl; } } } } // Callback function for head-pose void STDCALL receiveHeadPose(void *,smEngineHeadPoseData head_pose, smCameraVideoFrame video_frame) { Lock lock(g_mutex); // Get info including data pointer to original image from camera smImageInfo video_frame_image_info; THROW_ON_ERROR(smImageGetInfo(video_frame.image_handle, &video_frame_image_info)); // reentrant, so ok // video_frame_image_info.plane_addr[*] now point to the image memory planes. // The memory is only valid until the end of this routine unless you call smImageAddRef(video_frame.image_handle). // So you can deep copy the image data here, or use smImageAddRef() and just copy the pointer. // If you use smImageAddRef() you are responsible for calling smImageDestroy() to avoid a memory leak later. // In this callback you will typically want to copy the smEngineFaceData data into your own data-structure. // Since the smEngineFaceData contains multiple pod types copying it is not atomic and // a mutex is required to avoid the race-condition with any thread simultaneously // reading from your data-structure. // Such a race condition will not crash your code but will create weird noise in the tracking data. if (g_do_head_pose_printing) { //cout << video_frame << " " << head_pose << std::endl; } //make a copy of the new head pose data and send it to simconnect //when we get a simmconnect frame event the new offset will be applied to the camera updateHeadPose(&head_pose); } // Create the first available camera detected on the system, and return its handle smCameraHandle createFirstCamera() { // Detect cameras smCameraInfoList info_list; THROW_ON_ERROR(smCameraCreateInfoList(&info_list)); if (info_list.num_cameras == 0) { throw runtime_error("No cameras were detected"); } else { cout << "The followings cameras were detected: " << endl; for (int i=0; iconfidence > 0.0f) { memcpy(&pMemData->data.new_pose,temp_head_pose,sizeof(smEngineHeadPoseData)); sprintf(msg, "HeadPose Yaw: %.2f\n", pMemData->data.new_pose.head_rot.x_rads); OutputDebugStringA(msg); } if (pMemData->command == 100) { stopCommand = TRUE; } ReleaseMutex(hSMMutex); } }; // // Create a memory-mapping to the faceAPI data. // It contains the tracking data, a command-code from FaceTrackNoIR // // bool SMCreateMapping() { OutputDebugString(_T("FTCreateMapping says: Starting Function\n")); // // A FileMapping is used to create 'shared memory' between the faceAPI and FaceTrackNoIR. // // Try to create a FileMapping to the Shared Memory. // If one already exists: close it. // hSMMemMap = CreateFileMappingA( INVALID_HANDLE_VALUE , 00 , PAGE_READWRITE , 0 , sizeof( TFaceData ) + sizeof( HANDLE ) + 100, (LPCSTR) SM_MM_DATA ); if ( hSMMemMap != 0 ) { OutputDebugString(_T("FTCreateMapping says: FileMapping Created!\n")); } if ( ( hSMMemMap != 0 ) && ( (long) GetLastError == ERROR_ALREADY_EXISTS ) ) { CloseHandle( hSMMemMap ); hSMMemMap = 0; } // // Create a new FileMapping, Read/Write access // hSMMemMap = OpenFileMappingA( FILE_MAP_ALL_ACCESS , false , (LPCSTR) SM_MM_DATA ); if ( ( hSMMemMap != 0 ) ) { OutputDebugString(_T("FTCreateMapping says: FileMapping Created again...\n")); pMemData = (SMMemMap *) MapViewOfFile(hSMMemMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TFaceData)); if (pMemData != NULL) { OutputDebugString(_T("FTCreateMapping says: MapViewOfFile OK.\n")); // pMemData->handle = handle; // The game uses the handle, to send a message that the Program-Name was set! } hSMMutex = CreateMutexA(NULL, false, SM_MUTEX); } else { OutputDebugString(_T("Error creating Shared Memory for faceAPI\n")); cerr << "Error creating Shared Memory for faceAPI" << endl; return false; } //if (pMemData != NULL) { // pMemData->data.DataID = 1; // pMemData->data.CamWidth = 100; // pMemData->data.CamHeight = 250; //} return true; }