File SDKClient.cpp
File List > api > cppSDK > SDKClient > SDKClient.cpp
Go to the documentation of this file
#include "SDKClient.hpp"
#include "ManusSDKTypes.h"
#include <fstream>
#include <iostream>
#include "ClientPlatformSpecific.hpp"
#define GO_TO_DISPLAY(p_Key,p_Function) if (GetKeyDown(p_Key)) { ClearConsole();\
m_CurrentInteraction = std::bind(&SDKClient::p_Function, this); return ClientReturnCode::ClientReturnCode_Success;}
#define GO_TO_MENU_IF_REQUESTED() if (GetKeyDown('Q')) { ClearConsole();\
m_CurrentInteraction = nullptr; return ClientReturnCode::ClientReturnCode_Success;}
SDKClient* SDKClient::s_Instance = nullptr;
SDKClient::SDKClient()
{
s_Instance = this;
// using initializers like these ensure that the data is set to its default values.
ErgonomicsData_Init(&m_LeftGloveErgoData);
ErgonomicsData_Init(&m_RightGloveErgoData);
TestTimestamp();
}
SDKClient::~SDKClient()
{
s_Instance = nullptr;
}
ClientReturnCode SDKClient::Initialize()
{
if (!PlatformSpecificInitialization())
{
return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificInitialization;
}
// although resizewindow is not technically needed to setup the SDK , it is nice to see what we are doing in this example client.
// thus we make sure we have a nice console window to use.
// if this fails, then the window is being resized during startup (due to windows management tools) or by giving it invalid values.
if (!ResizeWindow(m_ConsoleWidth, m_ConsoleHeight, m_ConsoleScrollback))
{
// An error message will be logged in the function, so don't print anything here.
return ClientReturnCode::ClientReturnCode_FailedToResizeWindow;
}
const ClientReturnCode t_IntializeResult = InitializeSDK();
if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success)
{
spdlog::error("Failed to initialize the Core functionality. The value returned was {}.", t_IntializeResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::Run()
{
ClearConsole();
ClientReturnCode t_Result{};
while (!m_RequestedExit)
{
if (m_ConsoleClearTickCount >= 100 || m_State != m_PreviousState)
{
ClearConsole();
m_ConsoleClearTickCount = 0;
}
UpdateInput();
// in this example SDK Client we have several phases during our main loop to make sure the SDK is in the right state to work.
m_PreviousState = m_State;
switch (m_State)
{
case ClientState::ClientState_PickingConnectionType:
{
t_Result = PickingConnectionType();
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
} break;
case ClientState::ClientState_LookingForHosts:
{
t_Result = LookingForHosts();
if (t_Result != ClientReturnCode::ClientReturnCode_Success &&
t_Result != ClientReturnCode::ClientReturnCode_FailedToFindHosts) {
return t_Result;
}
} break;
case ClientState::ClientState_NoHostsFound:
{
t_Result = NoHostsFound();
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
} break;
case ClientState::ClientState_PickingHost:
{
t_Result = PickingHost();
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
} break;
case ClientState::ClientState_ConnectingToCore:
{
t_Result = ConnectingToCore();
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
} break;
case ClientState::ClientState_DisplayingData:
{
UpdateBeforeDisplayingData();
if (m_CurrentInteraction == nullptr)
{
t_Result = DisplayingData();
}
else
{
t_Result = m_CurrentInteraction();
}
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
} break;
case ClientState::ClientState_Disconnected:
{
t_Result = DisconnectedFromCore();
if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
}break;
default:
{
spdlog::error("Encountered the unrecognized state {}.", static_cast<int>(m_State));
return ClientReturnCode::ClientReturnCode_UnrecognizedStateEncountered;
}
} // switch(m_State)
if (GetKeyDown(VK_ESCAPE))
{
spdlog::info("Pressed escape, so the client will now close.");
m_RequestedExit = true;
}
m_ConsoleClearTickCount++;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::ShutDown()
{
const SDKReturnCode t_Result = CoreSdk_ShutDown();
if (t_Result != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to shut down the SDK wrapper. The value returned was {}.", t_Result);
return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK;
}
if (!PlatformSpecificShutdown())
{
return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificShutdown;
}
return ClientReturnCode::ClientReturnCode_Success;
}
void SDKClient::OnConnectedCallback(const ManusHost* const p_Host)
{
spdlog::info("Connected to manus core.");
//No need to initialize these as they get filled in the CoreSdk_GetVersionsAndCheckCompatibility
ManusVersion t_SdkVersion;
ManusVersion t_CoreVersion;
bool t_IsCompatible;
const SDKReturnCode t_Result = CoreSdk_GetVersionsAndCheckCompatibility(&t_SdkVersion, &t_CoreVersion, &t_IsCompatible);
if (t_Result == SDKReturnCode::SDKReturnCode_Success)
{
const std::string t_Versions = "Sdk version : " + std::string(t_SdkVersion.versionInfo) + ", Core version : " + std::string(t_CoreVersion.versionInfo) + ".";
if (t_IsCompatible)
{
spdlog::info("Versions are compatible.{}", t_Versions);
}
else
{
spdlog::warn("Versions are not compatible with each other.{}", t_Versions);
}
}
else
{
spdlog::error("Failed to get the versions from the SDK. The value returned was {}.", t_Result);
}
uint32_t t_SessionId;
const SDKReturnCode t_SessionIdResult = CoreSdk_GetSessionId(&t_SessionId);
if (t_SessionIdResult == SDKReturnCode::SDKReturnCode_Success && t_SessionId != 0)
{
spdlog::info("Session Id: {}", t_SessionId);
s_Instance->m_SessionId = t_SessionId;
}
else
{
spdlog::info("Failed to get the Session ID from Core. The value returned was{}.", t_SessionIdResult);
}
ManusHost t_Host(*p_Host);
s_Instance->m_Host = std::make_unique<ManusHost>(t_Host);
// Only setting state to displaying data on automatic reconnect
if (s_Instance->m_State == ClientState::ClientState_Disconnected)
{
s_Instance->m_State = ClientState::ClientState_DisplayingData;
}
}
void SDKClient::OnDisconnectedCallback(const ManusHost* const p_Host)
{
spdlog::info("Disconnected from manus core.");
s_Instance->m_TimeSinceLastDisconnect = std::chrono::high_resolution_clock::now();
ManusHost t_Host(*p_Host);
s_Instance->m_Host = std::make_unique<ManusHost>(t_Host);
s_Instance->m_State = ClientState::ClientState_Disconnected;
}
void SDKClient::OnSkeletonStreamCallback(const SkeletonStreamInfo* const p_SkeletonStreamInfo)
{
if (s_Instance)
{
ClientSkeletonCollection* t_NxtClientSkeleton = new ClientSkeletonCollection();
t_NxtClientSkeleton->skeletons.resize(p_SkeletonStreamInfo->skeletonsCount);
for (uint32_t i = 0; i < p_SkeletonStreamInfo->skeletonsCount; i++)
{
CoreSdk_GetSkeletonInfo(i, &t_NxtClientSkeleton->skeletons[i].info);
t_NxtClientSkeleton->skeletons[i].nodes.resize(t_NxtClientSkeleton->skeletons[i].info.nodesCount);
t_NxtClientSkeleton->skeletons[i].info.publishTime = p_SkeletonStreamInfo->publishTime;
CoreSdk_GetSkeletonData(i, t_NxtClientSkeleton->skeletons[i].nodes.data(), t_NxtClientSkeleton->skeletons[i].info.nodesCount);
}
s_Instance->m_SkeletonMutex.lock();
if (s_Instance->m_NextSkeleton != nullptr) delete s_Instance->m_NextSkeleton;
s_Instance->m_NextSkeleton = t_NxtClientSkeleton;
s_Instance->m_SkeletonMutex.unlock();
}
}
void SDKClient::OnRawSkeletonStreamCallback(const SkeletonStreamInfo* const p_RawSkeletonStreamInfo)
{
if (s_Instance)
{
ClientRawSkeletonCollection* t_NxtClientRawSkeleton = new ClientRawSkeletonCollection();
t_NxtClientRawSkeleton->skeletons.resize(p_RawSkeletonStreamInfo->skeletonsCount);
for (uint32_t i = 0; i < p_RawSkeletonStreamInfo->skeletonsCount; i++)
{
CoreSdk_GetRawSkeletonInfo(i, &t_NxtClientRawSkeleton->skeletons[i].info);
t_NxtClientRawSkeleton->skeletons[i].nodes.resize(t_NxtClientRawSkeleton->skeletons[i].info.nodesCount);
t_NxtClientRawSkeleton->skeletons[i].info.publishTime = p_RawSkeletonStreamInfo->publishTime;
CoreSdk_GetRawSkeletonData(i, t_NxtClientRawSkeleton->skeletons[i].nodes.data(), t_NxtClientRawSkeleton->skeletons[i].info.nodesCount);
}
s_Instance->m_RawSkeletonMutex.lock();
if (s_Instance->m_NextRawSkeleton != nullptr) delete s_Instance->m_NextRawSkeleton;
s_Instance->m_NextRawSkeleton = t_NxtClientRawSkeleton;
s_Instance->m_RawSkeletonMutex.unlock();
}
}
void SDKClient::OnTrackerStreamCallback(const TrackerStreamInfo* const p_TrackerStreamInfo)
{
if (s_Instance)
{
TrackerDataCollection* t_TrackerData = new TrackerDataCollection();
t_TrackerData->trackerData.resize(p_TrackerStreamInfo->trackerCount);
for (uint32_t i = 0; i < p_TrackerStreamInfo->trackerCount; i++)
{
CoreSdk_GetTrackerData(i, &t_TrackerData->trackerData[i]);
}
s_Instance->m_TrackerMutex.lock();
if (s_Instance->m_NextTrackerData != nullptr) delete s_Instance->m_NextTrackerData;
s_Instance->m_NextTrackerData = t_TrackerData;
s_Instance->m_TrackerMutex.unlock();
}
}
void SDKClient::OnGestureStreamCallback(const GestureStreamInfo* const p_GestureStream)
{
if (s_Instance)
{
for (uint32_t i = 0; i < p_GestureStream->gestureProbabilitiesCount; i++)
{
GestureProbabilities t_Probs;
CoreSdk_GetGestureStreamData(i, 0, &t_Probs);
if (t_Probs.isUserID)continue;
if (t_Probs.id != s_Instance->m_FirstLeftGloveID && t_Probs.id != s_Instance->m_FirstRightGloveID)continue;
ClientGestures* t_Gest = new ClientGestures();
t_Gest->info = t_Probs;
t_Gest->probabilities.reserve(t_Gest->info.totalGestureCount);
uint32_t t_BatchCount = (t_Gest->info.totalGestureCount / MAX_GESTURE_DATA_CHUNK_SIZE) + 1;
uint32_t t_ProbabilityIdx = 0;
for (uint32_t b = 0; b < t_BatchCount; b++)
{
for (uint32_t j = 0; j < t_Probs.gestureCount; j++)
{
t_Gest->probabilities.push_back(t_Probs.gestureData[j]);
}
t_ProbabilityIdx += t_Probs.gestureCount;
CoreSdk_GetGestureStreamData(i, t_ProbabilityIdx, &t_Probs); //this will get more data, if needed for the next iteration.
}
s_Instance->m_GestureMutex.lock();
if (t_Probs.id == s_Instance->m_FirstLeftGloveID)
{
if (s_Instance->m_NewFirstLeftGloveGestures != nullptr) delete s_Instance->m_NewFirstLeftGloveGestures;
s_Instance->m_NewFirstLeftGloveGestures = t_Gest;
}
else
{
if (s_Instance->m_NewFirstRightGloveGestures != nullptr) delete s_Instance->m_NewFirstRightGloveGestures;
s_Instance->m_NewFirstRightGloveGestures = t_Gest;
}
s_Instance->m_GestureMutex.unlock();
}
}
}
void SDKClient::OnLandscapeCallback(const Landscape* const p_Landscape)
{
if (s_Instance == nullptr)return;
Landscape* t_Landscape = new Landscape(*p_Landscape);
s_Instance->m_LandscapeMutex.lock();
if (s_Instance->m_NewLandscape != nullptr) delete s_Instance->m_NewLandscape;
s_Instance->m_NewLandscape = t_Landscape;
s_Instance->m_NewGestureLandscapeData.resize(t_Landscape->gestureCount);
CoreSdk_GetGestureLandscapeData(s_Instance->m_NewGestureLandscapeData.data(), (uint32_t)s_Instance->m_NewGestureLandscapeData.size());
s_Instance->m_LandscapeMutex.unlock();
}
void SDKClient::OnSystemCallback(const SystemMessage* const p_SystemMessage)
{
if (s_Instance)
{
s_Instance->m_SystemMessageMutex.lock();
switch (p_SystemMessage->type)
{
case SystemMessageType::SystemMessageType_TemporarySkeletonModified:
// if the message was triggered by a temporary skeleton being modified then save the skeleton index,
// this information will be used to get and load the skeleton into core
s_Instance->m_ModifiedSkeletonIndex = p_SystemMessage->infoUInt;
break;
default:
s_Instance->m_SystemMessageCode = p_SystemMessage->type;
s_Instance->m_SystemMessage = p_SystemMessage->infoString;
break;
}
s_Instance->m_SystemMessageMutex.unlock();
}
}
void SDKClient::OnErgonomicsCallback(const ErgonomicsStream* const p_Ergo)
{
if (s_Instance)
{
for (uint32_t i = 0; i < p_Ergo->dataCount; i++)
{
if (p_Ergo->data[i].isUserID)continue;
ErgonomicsData* t_Ergo = nullptr;
if (p_Ergo->data[i].id == s_Instance->m_FirstLeftGloveID)
{
t_Ergo = &s_Instance->m_LeftGloveErgoData;
}
if (p_Ergo->data[i].id == s_Instance->m_FirstRightGloveID)
{
t_Ergo = &s_Instance->m_RightGloveErgoData;
}
if (t_Ergo == nullptr)continue;
CoreSdk_GetTimestampInfo(p_Ergo->publishTime, &s_Instance->m_ErgoTimestampInfo);
t_Ergo->id = p_Ergo->data[i].id;
t_Ergo->isUserID = p_Ergo->data[i].isUserID;
for (int j = 0; j < ErgonomicsDataType::ErgonomicsDataType_MAX_SIZE; j++)
{
t_Ergo->data[j] = p_Ergo->data[i].data[j];
}
}
}
}
float SDKClient::RoundFloatValue(float p_Value, int p_NumDecimalsToKeep)
{
// Since C++11, powf is supposed to be declared in <cmath>.
// Unfortunately, gcc decided to be non-compliant on this for no apparent
// reason, so now we have to do this.
// https://stackoverflow.com/questions/5483930/powf-is-not-a-member-of-std
float t_Power = static_cast<float>(std::pow(
10.0,
static_cast<double>(p_NumDecimalsToKeep)));
return std::round(p_Value * t_Power) / t_Power;
}
void SDKClient::AdvanceConsolePosition(short int p_Y)
{
if (p_Y < 0)
{
m_ConsoleCurrentOffset = 0;
}
else
{
m_ConsoleCurrentOffset += p_Y;
}
ApplyConsolePosition(m_ConsoleCurrentOffset);
}
ClientReturnCode SDKClient::InitializeSDK()
{
// before we can use the SDK, some internal SDK bits need to be initialized.
// however after initializing, the SDK is not yet connected to a host or doing anything network related just yet.
const SDKReturnCode t_InitializeResult = CoreSdk_Initialize(m_ClientType);
if (t_InitializeResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to initialize the Manus Core SDK. The value returned was {}.", t_InitializeResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
const ClientReturnCode t_CallBackResults = RegisterAllCallbacks();
if (t_CallBackResults != ::ClientReturnCode::ClientReturnCode_Success)
{
spdlog::error("Failed to initialize callbacks.");
return t_CallBackResults;
}
// after everything is registered and initialized as seen above
// we must also set the coordinate system being used for the data in this client.
// (each client can have their own settings. unreal and unity for instance use different coordinate systems)
// if this is not set, the SDK will not connect to any Manus core host.
CoordinateSystemVUH t_VUH;
CoordinateSystemVUH_Init(&t_VUH);
t_VUH.handedness = Side::Side_Left; // this is currently set to unreal mode.
t_VUH.up = AxisPolarity::AxisPolarity_PositiveY;
t_VUH.view = AxisView::AxisView_ZFromViewer;
t_VUH.unitScale = 1.0f; //1.0 is meters, 0.01 is cm, 0.001 is mm.
const SDKReturnCode t_CoordinateResult = CoreSdk_InitializeCoordinateSystemWithVUH(t_VUH, false);
/* this is an example if you want to use the other coordinate system instead of VUH
CoordinateSystemDirection t_Direction;
t_Direction.x = AxisDirection::AD_Right;
t_Direction.y = AxisDirection::AD_Up;
t_Direction.z = AxisDirection::AD_Forward;
const SDKReturnCode t_InitializeResult = CoreSdk_InitializeCoordinateSystemWithDirection(t_Direction);
*/
if (t_CoordinateResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to initialize the Manus Core SDK coordinate system. The value returned was {}.", t_InitializeResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::RestartSDK()
{
const SDKReturnCode t_ShutDownResult = CoreSdk_ShutDown();
if (t_ShutDownResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to shutdown the SDK. The value returned was {}.", t_ShutDownResult);
return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK;
}
const ClientReturnCode t_IntializeResult = InitializeSDK();
if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success)
{
spdlog::error("Failed to initialize the SDK functionality. The value returned was {}.", t_IntializeResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::RegisterAllCallbacks()
{
// Register the callback for when manus core is connected to the SDK
// it is optional, but helps trigger your client nicely if needed.
// see the function OnConnectedCallback for more details
const SDKReturnCode t_RegisterConnectCallbackResult = CoreSdk_RegisterCallbackForOnConnect(*OnConnectedCallback);
if (t_RegisterConnectCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for after connecting to Manus Core. The value returned was {}.", t_RegisterConnectCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is disconnected to the SDK
// it is optional, but helps trigger your client nicely if needed.
// see OnDisconnectedCallback for more details.
const SDKReturnCode t_RegisterDisconnectCallbackResult = CoreSdk_RegisterCallbackForOnDisconnect(*OnDisconnectedCallback);
if (t_RegisterDisconnectCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for after disconnecting from Manus Core. The value returned was {}.", t_RegisterDisconnectCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending Skeleton data
// it is optional, but without it you can not see any resulting skeleton data.
// see OnSkeletonStreamCallback for more details.
const SDKReturnCode t_RegisterSkeletonCallbackResult = CoreSdk_RegisterCallbackForSkeletonStream(*OnSkeletonStreamCallback);
if (t_RegisterSkeletonCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for processing skeletal data from Manus Core. The value returned was {}.", t_RegisterSkeletonCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending landscape data
// it is optional, but this allows for a reactive adjustment of device information.
const SDKReturnCode t_RegisterLandscapeCallbackResult = CoreSdk_RegisterCallbackForLandscapeStream(*OnLandscapeCallback);
if (t_RegisterLandscapeCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback for landscape from Manus Core. The value returned was {}.", t_RegisterLandscapeCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending System messages
// This is usually not used by client applications unless they want to show errors/events from core.
// see OnSystemCallback for more details.
const SDKReturnCode t_RegisterSystemCallbackResult = CoreSdk_RegisterCallbackForSystemStream(*OnSystemCallback);
if (t_RegisterSystemCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for system feedback from Manus Core. The value returned was {}.", t_RegisterSystemCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending Ergonomics data
// it is optional, but helps trigger your client nicely if needed.
// see OnErgonomicsCallback for more details.
const SDKReturnCode t_RegisterErgonomicsCallbackResult = CoreSdk_RegisterCallbackForErgonomicsStream(*OnErgonomicsCallback);
if (t_RegisterErgonomicsCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for ergonomics data from Manus Core. The value returned was {}.", t_RegisterErgonomicsCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending Raw Skeleton data
// it is optional, but without it you can not see any resulting skeleton data.
// see OnSkeletonStreamCallback for more details.
const SDKReturnCode t_RegisterRawSkeletonCallbackResult = CoreSdk_RegisterCallbackForRawSkeletonStream(*OnRawSkeletonStreamCallback);
if (t_RegisterRawSkeletonCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for processing raw skeletal data from Manus Core. The value returned was {}.", t_RegisterRawSkeletonCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
// Register the callback for when manus core is sending Raw Skeleton data
// it is optional, but without it you can not see any resulting skeleton data.
// see OnSkeletonStreamCallback for more details.
const SDKReturnCode t_RegisterTrackerCallbackResult = CoreSdk_RegisterCallbackForTrackerStream(*OnTrackerStreamCallback);
if (t_RegisterTrackerCallbackResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to register callback function for processing tracker data from Manus Core. The value returned was {}.", t_RegisterTrackerCallbackResult);
return ClientReturnCode::ClientReturnCode_FailedToInitialize;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::PickingConnectionType()
{
if (m_ConsoleClearTickCount == 0)
{
ClearConsole();
AdvanceConsolePosition(-1);
bool t_BuiltInDebug = false;
SDKReturnCode t_Result = CoreSdk_WasDllBuiltInDebugConfiguration(&t_BuiltInDebug);
if (t_Result == SDKReturnCode::SDKReturnCode_Success)
{
if (t_BuiltInDebug)
{
spdlog::warn("The DLL was built in debug configuration, please rebuild in release before releasing.");
}
}
else
{
spdlog::error("Failed to check if the DLL was built in Debug Configuration. The value returned was {}.", t_Result);
}
spdlog::info("Press a key to choose a connection type, or [ESC] to exit.");
spdlog::info("[L] Local -> Automatically connect to Core running on this computer.");
spdlog::info("[H] Host -> Find a host running Core anywhere on the network.");
spdlog::info("[G] GRPC -> Try to connect to the preset GRPC address (See settings folder).");
}
if (GetKeyDown('L'))
{
spdlog::info("Picked local.");
m_ShouldConnectLocally = true;
m_ShouldConnectGRPC = false;
m_State = ClientState::ClientState_LookingForHosts;
}
else if (GetKeyDown('H'))
{
spdlog::info("Picked host.");
m_ShouldConnectLocally = false;
m_ShouldConnectGRPC = false;
m_State = ClientState::ClientState_LookingForHosts;
}
if (GetKeyDown('G'))
{
spdlog::info("Picked GRPC.");
m_ShouldConnectGRPC = true;
m_State = ClientState::ClientState_ConnectingToCore;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::LookingForHosts()
{
spdlog::info("Looking for hosts...");
// Underlying function will sleep for m_SecondsToFindHosts to allow servers to reply.
const SDKReturnCode t_StartResult = CoreSdk_LookForHosts(m_SecondsToFindHosts, m_ShouldConnectLocally);
if (t_StartResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to look for hosts. The error given was {}.", t_StartResult);
return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
}
m_NumberOfHostsFound = 0;
const SDKReturnCode t_NumberResult = CoreSdk_GetNumberOfAvailableHostsFound(&m_NumberOfHostsFound);
if (t_NumberResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get the number of available hosts. The error given was {}.", t_NumberResult);
return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
}
if (m_NumberOfHostsFound == 0)
{
spdlog::warn("No hosts found.");
m_State = ClientState::ClientState_NoHostsFound;
return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
}
m_AvailableHosts.reset(new ManusHost[m_NumberOfHostsFound]);
const SDKReturnCode t_HostsResult = CoreSdk_GetAvailableHostsFound(m_AvailableHosts.get(), m_NumberOfHostsFound);
if (t_HostsResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get the available hosts. The error given was {}.", t_HostsResult);
return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
}
if (m_ShouldConnectLocally)
{
m_State = ClientState::ClientState_ConnectingToCore;
return ClientReturnCode::ClientReturnCode_Success;
}
m_State = ClientState::ClientState_PickingHost;
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::NoHostsFound()
{
if (m_ConsoleClearTickCount == 0)
{
AdvanceConsolePosition(-1);
spdlog::info("No hosts were found. Retry?");
spdlog::info("[R] retry");
spdlog::info("[ESC] exit");
}
if (GetKeyDown('R'))
{
spdlog::info("Retrying.");
m_State = ClientState::ClientState_PickingConnectionType;
}
// Note: escape is handled by default below.
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::PickingHost()
{
if (m_ConsoleClearTickCount == 0)
{
AdvanceConsolePosition(-1);
spdlog::info("[R] retry [ESC] exit");
spdlog::info("Pick a host to connect to.");
spdlog::info("Found the following hosts:");
// Note: only 10 hosts are shown, to match the number of number keys, for easy selection.
for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++)
{
spdlog::info(
"[{}] hostname \"{}\", IP address \"{}\" Version {}.{}.{}",
t_HostNumber,
m_AvailableHosts[t_HostNumber].hostName,
m_AvailableHosts[t_HostNumber].ipAddress,
m_AvailableHosts[t_HostNumber].manusCoreVersion.major,
m_AvailableHosts[t_HostNumber].manusCoreVersion.minor,
m_AvailableHosts[t_HostNumber].manusCoreVersion.patch);
}
}
for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++)
{
if (GetKeyDown('0' + t_HostNumber))
{
spdlog::info("Selected host {}.", t_HostNumber);
m_HostToConnectTo = t_HostNumber;
m_State = ClientState::ClientState_ConnectingToCore;
break;
}
}
if (GetKeyDown('R'))
{
spdlog::info("Retrying.");
m_State = ClientState::ClientState_PickingConnectionType;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::ConnectingToCore()
{
SDKReturnCode t_ConnectResult = SDKReturnCode::SDKReturnCode_Error;
if (m_ShouldConnectGRPC)
{
t_ConnectResult = CoreSdk_ConnectGRPC();
}
else
{
if (m_ShouldConnectLocally) { m_HostToConnectTo = 0; }
t_ConnectResult = CoreSdk_ConnectToHost(m_AvailableHosts[m_HostToConnectTo]);
}
if (t_ConnectResult == SDKReturnCode::SDKReturnCode_NotConnected)
{
m_State = ClientState::ClientState_NoHostsFound;
return ClientReturnCode::ClientReturnCode_Success; // Differentiating between error and no connect
}
if (t_ConnectResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to connect to Core. The error given was {}.", t_ConnectResult);
return ClientReturnCode::ClientReturnCode_FailedToConnect;
}
m_State = ClientState::ClientState_DisplayingData;
// Note: a log message from somewhere in the SDK during the connection process can cause text
// to permanently turn green after this step. Adding a sleep here of 2+ seconds "fixes" the
// issue. It seems to be caused by a threading issue somewhere, resulting in a log call being
// interrupted while it is printing the green [info] text. The log output then gets stuck in
// green mode.
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::UpdateBeforeDisplayingData()
{
AdvanceConsolePosition(-1);
m_SkeletonMutex.lock();
if (m_NextSkeleton != nullptr)
{
if (m_Skeleton != nullptr)delete m_Skeleton;
m_Skeleton = m_NextSkeleton;
m_NextSkeleton = nullptr;
}
m_SkeletonMutex.unlock();
m_RawSkeletonMutex.lock();
if (m_NextRawSkeleton != nullptr)
{
if (m_RawSkeleton != nullptr)delete m_RawSkeleton;
m_RawSkeleton = m_NextRawSkeleton;
m_NextRawSkeleton = nullptr;
}
m_RawSkeletonMutex.unlock();
m_TrackerMutex.lock();
if (m_NextTrackerData != nullptr)
{
if (m_TrackerData != nullptr)delete m_TrackerData;
m_TrackerData = m_NextTrackerData;
m_NextTrackerData = nullptr;
}
m_TrackerMutex.unlock();
m_LandscapeMutex.lock();
if (m_NewLandscape != nullptr)
{
if (m_Landscape != nullptr)
{
delete m_Landscape;
}
m_Landscape = m_NewLandscape;
m_NewLandscape = nullptr;
m_GestureLandscapeData.swap(m_NewGestureLandscapeData);
}
m_LandscapeMutex.unlock();
m_FirstLeftGloveID = 0;
m_FirstRightGloveID = 0;
if (m_Landscape == nullptr)return ClientReturnCode::ClientReturnCode_Success;
for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++)
{
if (m_FirstLeftGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Left)
{
m_FirstLeftGloveID = m_Landscape->gloveDevices.gloves[i].id;
continue;
}
if (m_FirstRightGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Right)
{
m_FirstRightGloveID = m_Landscape->gloveDevices.gloves[i].id;
continue;
}
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingData()
{
SPDLOG_INFO("<<Main Menu>> [ESC] quit");
SPDLOG_INFO("[G] Go To Gloves & Dongle Menu");
SPDLOG_INFO("[S] Go To Skeleton Menu");
SPDLOG_INFO("[X] Go To Temporary Skeleton Menu");
SPDLOG_INFO("[T] Go To Tracker Menu");
SPDLOG_INFO("[D] Go To Landscape Time Info");
SPDLOG_INFO("[J] Go To Gestures Menu");
AdvanceConsolePosition(8);
GO_TO_DISPLAY('G', DisplayingDataGlove);
GO_TO_DISPLAY('S', DisplayingDataSkeleton);
GO_TO_DISPLAY('X', DisplayingDataTemporarySkeleton);
GO_TO_DISPLAY('T', DisplayingDataTracker);
GO_TO_DISPLAY('D', DisplayingLandscapeTimeData);
GO_TO_DISPLAY('J', DisplayingDataGestures);
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingDataGlove()
{
SPDLOG_INFO("[Q] Back <<Gloves & Dongles>> [ESC] quit");
SPDLOG_INFO("Haptic keys: left:([1]-[5] = pinky-thumb.) right:([6]-[0] = thumb-pinky.)");
AdvanceConsolePosition(3);
GO_TO_MENU_IF_REQUESTED();
HandleHapticCommands();
PrintErgonomicsData();
PrintDongleData();
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingDataSkeleton()
{
SPDLOG_INFO("[Q] Back <<Skeleton>> [ESC] quit");
SPDLOG_INFO("<Skeleton>[N] Load Skeleton [M] Unload Skeleton");
SPDLOG_INFO("<Skeleton Haptics> left:([1]-[5] = pinky-thumb) right:([6]-[0] = thumb-pinky)");
AdvanceConsolePosition(4);
GO_TO_MENU_IF_REQUESTED();
HandleSkeletonCommands();
HandleSkeletonHapticCommands();
PrintSkeletonData();
PrintSkeletonInfo();
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingDataTracker()
{
SPDLOG_INFO("[Q] Back <<Gloves & Dongles>> [ESC] quit");
SPDLOG_INFO("[O] Toggle Test Tracker [G] Toggle per user tracker display");
AdvanceConsolePosition(3);
GO_TO_MENU_IF_REQUESTED();
HandleTrackerCommands();
PrintRawSkeletonData();
PrintTrackerData();
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingDataTemporarySkeleton()
{
SPDLOG_INFO("[Q] Back <<Temporary Skeleton>> [ESC] quit");
SPDLOG_INFO("<Skeleton>[A] Auto allocate chains and load skeleton");
SPDLOG_INFO("<Skeleton>[B] Build Temporary Skeleton [C] Clear Temporary Skeleton [D] Clear All Temporary Skeletons For The Current Session");
SPDLOG_INFO("<Skeleton>[E] Save Temporary Skeleton To File, [F] Get Temporary Skeleton From File");
AdvanceConsolePosition(4);
GO_TO_MENU_IF_REQUESTED();
HandleTemporarySkeletonCommands();
PrintTemporarySkeletonInfo();
GetTemporarySkeletonIfModified();
AdvanceConsolePosition(4);
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingLandscapeTimeData()
{
SPDLOG_INFO("[Q] Back <<Landscape Time Data>> [ESC] quit");
AdvanceConsolePosition(2);
GO_TO_MENU_IF_REQUESTED();
PrintLandscapeTimeData();
AdvanceConsolePosition(3);
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisplayingDataGestures()
{
SPDLOG_INFO("[Q] Back <<Gesture Data>> [ESC] quit");
SPDLOG_INFO("<Gestures>[H] Show other Hand");
AdvanceConsolePosition(2);
GO_TO_MENU_IF_REQUESTED();
HandleGesturesCommands();
PrintGestureData();
AdvanceConsolePosition(3);
PrintSystemMessage();
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::DisconnectedFromCore()
{
if (m_Host == nullptr) { return ClientReturnCode::ClientReturnCode_FailedToConnect; }
AdvanceConsolePosition(-1);
auto t_Duration = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - m_TimeSinceLastDisconnect).count();
spdlog::info("The SDK lost connection with Manus Core {} seconds ago.", t_Duration);
spdlog::info("[P] Pick a new host. [ESC] exit");
AdvanceConsolePosition(3);
if (m_ShouldConnectGRPC)
{
spdlog::info("Automatically trying to reconnect to GRPC address.");
ClientReturnCode t_ReconnectResult = ReconnectingToCore();
if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
{
return t_ReconnectResult;
}
}
else if (m_ShouldConnectLocally)
{
spdlog::info("Automatically trying to reconnect to local host.");
ClientReturnCode t_ReconnectResult = ReconnectingToCore();
if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
{
return t_ReconnectResult;
}
}
else
{
spdlog::info("[R] Try to reconnect to the last host {} at {}.", m_Host->hostName, m_Host->ipAddress);
if (GetKeyDown('R'))
{
spdlog::info("Reconnecting");
ClientReturnCode t_ReconnectResult = ReconnectingToCore(m_SecondsToAttemptReconnecting, m_MaxReconnectionAttempts);
if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
{
return t_ReconnectResult;
}
}
}
AdvanceConsolePosition(10);
if (GetKeyDown('P'))
{
spdlog::info("Picking new host.");
// Restarting and initializing CoreConnection to make sure a new connection can be set up
const ClientReturnCode t_RestartResult = RestartSDK();
if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success)
{
spdlog::error("Failed to Restart CoreConnection.");
return ClientReturnCode::ClientReturnCode_FailedToRestart;
}
m_State = ClientState::ClientState_PickingConnectionType;
}
return ClientReturnCode::ClientReturnCode_Success;
}
ClientReturnCode SDKClient::ReconnectingToCore(int32_t p_ReconnectionTime, int32_t p_ReconnectionAttempts)
{
if (p_ReconnectionTime <= 0) { p_ReconnectionTime = std::numeric_limits<int32_t>::max(); }
if (p_ReconnectionAttempts <= 0) { p_ReconnectionAttempts = std::numeric_limits<int32_t>::max(); }
// Restarting and initializing CoreConnection to make sure a new connection can be set up
const ClientReturnCode t_RestartResult = RestartSDK();
if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success)
{
spdlog::error("Failed to Restart CoreConnection.");
return ClientReturnCode::ClientReturnCode_FailedToRestart;
}
std::chrono::high_resolution_clock::time_point t_Start = std::chrono::high_resolution_clock::now();
int t_Attempt = 0;
while ((p_ReconnectionAttempts > 0) && (p_ReconnectionTime > 0))
{
spdlog::info("Trying to reconnect to {} at {}. Attempt {}.", m_Host->hostName, m_Host->ipAddress, t_Attempt);
spdlog::info("Attempts remaining: {}. Seconds before time out: {}.", p_ReconnectionAttempts, p_ReconnectionTime);
if (m_ShouldConnectGRPC)
{
SDKReturnCode t_ConnectionResult = CoreSdk_ConnectGRPC();
if (t_ConnectionResult == SDKReturnCode::SDKReturnCode_Success)
{
spdlog::info("Reconnected to ManusCore.");
return ClientReturnCode::ClientReturnCode_Success;
}
}
else if (m_ShouldConnectLocally)
{
ClientReturnCode t_ConnectionResult = LookingForHosts();
if (t_ConnectionResult == ClientReturnCode::ClientReturnCode_Success)
{
spdlog::info("Reconnected to ManusCore.");
return ClientReturnCode::ClientReturnCode_Success;
}
}
else
{
SDKReturnCode t_ConnectionResult = CoreSdk_ConnectToHost(*m_Host.get());
if (t_ConnectionResult == SDKReturnCode::SDKReturnCode_Success)
{
spdlog::info("Reconnected to ManusCore.");
return ClientReturnCode::ClientReturnCode_Success;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_SleepBetweenReconnectingAttemptsInMs));
p_ReconnectionTime -= static_cast<int32_t>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - t_Start).count());
--p_ReconnectionAttempts;
++t_Attempt;
}
spdlog::info("Failed to reconnect to ManusCore.");
m_State = ClientState::ClientState_Disconnected;
return ClientReturnCode::ClientReturnCode_FailedToConnect;
}
void SDKClient::PrintHandErgoData(ErgonomicsData& p_ErgoData, bool p_Left)
{
const std::string t_FingerNames[NUM_FINGERS_ON_HAND] = { "[thumb] ", "[index] ", "[middle]", "[ring] ", "[pinky] " };
const std::string t_FingerJointNames[NUM_FINGERS_ON_HAND] = { "mcp", "pip", "dip" };
const std::string t_ThumbJointNames[NUM_FINGERS_ON_HAND] = { "cmc", "mcp", "ip " };
int t_DataOffset = 0;
if (!p_Left)t_DataOffset = 20;
const std::string* t_JointNames = t_ThumbJointNames;
for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
{
spdlog::info("{} {} spread: {:>6}, {} stretch: {:>6}, {} stretch: {:>6}, {} stretch: {:>6} ",
t_FingerNames[t_FingerNumber], // Name of the finger.
t_JointNames[0],
RoundFloatValue(p_ErgoData.data[t_DataOffset], 2),
t_JointNames[0],
RoundFloatValue(p_ErgoData.data[t_DataOffset + 1], 2),
t_JointNames[1],
RoundFloatValue(p_ErgoData.data[t_DataOffset + 2], 2),
t_JointNames[2],
RoundFloatValue(p_ErgoData.data[t_DataOffset + 3], 2));
t_JointNames = t_FingerJointNames;
t_DataOffset += 4;
}
}
void SDKClient::PrintErgonomicsData()
{
// for testing purposes we only look at the first 2 gloves available
spdlog::info(" -- Ergo Timestamp {:02d}:{:02d}:{:02d}.{:03d} ~ {:02d}/{:02d}/{:d}(D/M/Y)",
m_ErgoTimestampInfo.hour, m_ErgoTimestampInfo.minute, m_ErgoTimestampInfo.second, m_ErgoTimestampInfo.fraction,
m_ErgoTimestampInfo.day, m_ErgoTimestampInfo.month, m_ErgoTimestampInfo.year);
spdlog::info(" -- Left Glove -- 0x{:X} - Angles in degrees", m_FirstLeftGloveID);
if (m_LeftGloveErgoData.id == m_FirstLeftGloveID)
{
PrintHandErgoData(m_LeftGloveErgoData, true);
}
else
{
spdlog::info(" ...No Data...");
}
spdlog::info(" -- Right Glove -- 0x{:X} - Angles in degrees", m_FirstRightGloveID);
if (m_RightGloveErgoData.id == m_FirstRightGloveID)
{
PrintHandErgoData(m_RightGloveErgoData, false);
}
else
{
spdlog::info(" ...No Data...");
}
AdvanceConsolePosition(14);
}
std::string ConvertDeviceClassTypeToString(DeviceClassType p_Type)
{
switch (p_Type)
{
case DeviceClassType_Dongle:
return "Dongle";
case DeviceClassType_Glove:
return "Glove";
case DeviceClassType_Glongle:
return "Glongle (Glove Dongle)";
default:
return "Unknown";
}
}
std::string ConvertDeviceFamilyTypeToString(DeviceFamilyType p_Type)
{
switch (p_Type)
{
case DeviceFamilyType_Prime1:
return "Prime 1";
case DeviceFamilyType_Prime2:
return "Prime 2";
case DeviceFamilyType_PrimeX:
return "Prime X";
case DeviceFamilyType_Quantum:
return "Quantum";
case DeviceFamilyType_Prime3:
return "Prime 3";
case DeviceFamilyType_Virtual:
return "Virtual";
default:
return "Unknown";
}
}
void SDKClient::PrintDongleData()
{
// get a dongle id
uint32_t t_DongleCount = 0;
if (CoreSdk_GetNumberOfDongles(&t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
if (t_DongleCount == 0) return; // we got no gloves to work on anyway!
uint32_t* t_DongleIds = new uint32_t[t_DongleCount]();
if (CoreSdk_GetDongleIds(t_DongleIds, t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
DongleLandscapeData t_DongleData;
for (uint32_t i = 0; i < t_DongleCount; i++)
{
SDKReturnCode t_Result = CoreSdk_GetDataForDongle(t_DongleIds[i], &t_DongleData);
spdlog::info(" -- Dongle -- 0x{:X}", t_DongleData.id);
if (t_Result == SDKReturnCode::SDKReturnCode_Success)
{
spdlog::info(" Type: {} - {}",
ConvertDeviceClassTypeToString(t_DongleData.classType),
ConvertDeviceFamilyTypeToString(t_DongleData.familyType));
spdlog::info(" License: {}", t_DongleData.licenseType);
}
else
{
spdlog::info(" ...No Data...");
}
AdvanceConsolePosition(4);
}
}
void SDKClient::PrintSystemMessage()
{
m_SystemMessageMutex.lock();
spdlog::info("Received System data:{} / code:{}", m_SystemMessage, m_SystemMessageCode);
m_SystemMessageMutex.unlock();
AdvanceConsolePosition(2);
}
void SDKClient::PrintSkeletonData()
{
if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0)
{
return;
}
spdlog::info("Received Skeleton data. skeletons:{} first skeleton id:{}", m_Skeleton->skeletons.size(), m_Skeleton->skeletons[0].info.id);
AdvanceConsolePosition(2);
}
void SDKClient::PrintRawSkeletonData()
{
if (m_RawSkeleton == nullptr || m_RawSkeleton->skeletons.size() == 0)
{
return;
}
if (m_FirstLeftGloveID == 0 && m_FirstRightGloveID == 0) return; // no gloves connected to core
uint32_t t_NodeCount = 0;
uint32_t t_GloveId = 0;
if (m_FirstLeftGloveID != 0)
{
t_GloveId = m_FirstLeftGloveID;
}
else
{
t_GloveId = m_FirstRightGloveID;
}
SDKReturnCode t_Result = CoreSdk_GetRawSkeletonNodeCount(t_GloveId, t_NodeCount);
if (t_Result != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get Estimation Node Count. The error given was {}.", t_Result);
return;
}
// now get the hierarchy data, this can be used to reconstruct the positions of each node in case the user set up the system with a local coordinate system
// having a node position defined as local means that this will be related to its parent
NodeInfo* t_NodeInfo = new NodeInfo[t_NodeCount];
t_Result = CoreSdk_GetRawSkeletonNodeInfo(t_GloveId, t_NodeInfo);
if (t_Result != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get Estimation Hierarchy. The error given was {}.", t_Result);
return;
}
spdlog::info("Received Skeleton glove data from the estimation system. skeletons:{} first skeleton glove id:{}", m_RawSkeleton->skeletons.size(), m_RawSkeleton->skeletons[0].info.gloveId);
AdvanceConsolePosition(2);
}
void SDKClient::PrintTrackerData()
{
spdlog::info("Tracker test active: {}.", m_TrackerTest); //To show that test tracker is being sent to core
spdlog::info("Per user tracker display: {}.", m_TrackerDataDisplayPerUser);
AdvanceConsolePosition(2);
if (m_TrackerDataDisplayPerUser)
{
PrintTrackerDataPerUser();
AdvanceConsolePosition(10);
}
else
{
PrintTrackerDataGlobal();
AdvanceConsolePosition(3);
}
// now, as a test, print the tracker data received from the stream
if (m_TrackerData == nullptr || m_TrackerData->trackerData.size() == 0)
{
return;
}
spdlog::info("Received Tracker data. number of received trackers:{} first tracker type:{}", m_TrackerData->trackerData.size(), m_TrackerData->trackerData[0].trackerType);
AdvanceConsolePosition(1);
}
void SDKClient::PrintTrackerDataGlobal()
{
uint32_t t_NumberOfAvailabletrackers = 0;
SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackers(&t_NumberOfAvailabletrackers);
if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
return;
}
spdlog::info("received available trackers :{} ", t_NumberOfAvailabletrackers);
if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do.
TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers];
t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackers(t_TrackerId, t_NumberOfAvailabletrackers);
if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
return;
}
}
void SDKClient::PrintTrackerDataPerUser()
{
uint32_t t_NumberOfAvailableUsers = 0;
SDKReturnCode t_UserResult = CoreSdk_GetNumberOfAvailableUsers(&t_NumberOfAvailableUsers);
if (t_UserResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get user count. The error given was {}.", t_UserResult);
return;
}
if (t_NumberOfAvailableUsers == 0) return; // nothing to get yet
for (uint32_t i = 0; i < t_NumberOfAvailableUsers; i++)
{
uint32_t t_NumberOfAvailabletrackers = 0;
SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackersForUserIndex(&t_NumberOfAvailabletrackers, i);
if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
return;
}
if (t_NumberOfAvailabletrackers == 0) continue;
spdlog::info("received available trackers for user index[{}] :{} ", i, t_NumberOfAvailabletrackers);
if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do.
TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers];
t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackersForUserIndex(t_TrackerId, i, t_NumberOfAvailabletrackers);
if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
return;
}
}
}
std::string GetFPSEnumName(TimecodeFPS p_FPS)
{
switch (p_FPS)
{
case TimecodeFPS::TimecodeFPS_23_976:
return "23.976 FPS (24 dropframe)";
case TimecodeFPS::TimecodeFPS_24:
return "24 FPS";
case TimecodeFPS::TimecodeFPS_25:
return "25 FPS";
case TimecodeFPS::TimecodeFPS_29_97:
return "29.97 FPS (30 dropframe)";
case TimecodeFPS::TimecodeFPS_30:
return "30 FPS";
case TimecodeFPS::TimecodeFPS_50:
return "50 FPS";
case TimecodeFPS::TimecodeFPS_59_94:
return "59.94 FPS (60 dropframe)";
case TimecodeFPS::TimecodeFPS_60:
return "60 FPS";
default:
return "Undefined FPS";
}
}
void SDKClient::PrintLandscapeTimeData()
{
spdlog::info("Total count of Interfaces: {}", m_Landscape->time.interfaceCount);
spdlog::info("Current Interface: {} {} at index {}", m_Landscape->time.currentInterface.name, m_Landscape->time.currentInterface.api, m_Landscape->time.currentInterface.index);
spdlog::info("FPS: {}", GetFPSEnumName(m_Landscape->time.fps));
spdlog::info("Fake signal: {} | Sync Pulse: {} | Sync Status: {}", m_Landscape->time.fakeTimecode, m_Landscape->time.useSyncPulse, m_Landscape->time.syncStatus);
spdlog::info("Device keep alive: {} | Timecode Status: {}", m_Landscape->time.deviceKeepAlive, m_Landscape->time.timecodeStatus);
AdvanceConsolePosition(6);
}
void SDKClient::PrintGestureData()
{
ClientGestures* t_Gest = m_FirstLeftGloveGestures;
std::string t_Side = "Left";
if (!m_ShowLeftGestures)
{
t_Side = "Right";
t_Gest = m_FirstRightGloveGestures;
}
if (t_Gest == nullptr)
{
spdlog::info("No Gesture information for first {} glove.", t_Side);
AdvanceConsolePosition(3);
return;
}
spdlog::info("Total count of gestures for the {} glove: {}", t_Side, t_Gest->info.totalGestureCount);
uint32_t t_Max = t_Gest->info.totalGestureCount;
if (t_Max > 20) t_Max = 20;
spdlog::info("Showing result of first {} gestures.", t_Max);
for (uint32_t i = 0; i < t_Max; i++)
{
char* t_Name = "";
for (uint32_t g = 0; g < m_GestureLandscapeData.size(); g++)
{
if (m_GestureLandscapeData[g].id == t_Gest->probabilities[i].id)
{
t_Name = m_GestureLandscapeData[g].name;
}
}
spdlog::info("Gesture {} ({}) has a probability of {}%.", t_Name,t_Gest->probabilities[i].id, t_Gest->probabilities[i].percent * 100.0f);
}
AdvanceConsolePosition(6);
}
void SDKClient::PrintSkeletonInfo()
{
switch (m_ChainType)
{
case ChainType::ChainType_FingerIndex:
{
spdlog::info("received Skeleton chain type: ChainType_FingerIndex");
break;
}
case ChainType::ChainType_FingerMiddle:
{
spdlog::info("received Skeleton chain type: ChainType_FingerMiddle");
break;
}
case ChainType::ChainType_FingerPinky:
{
spdlog::info("received Skeleton chain type: ChainType_FingerPinky");
break;
}
case ChainType::ChainType_FingerRing:
{
spdlog::info("received Skeleton chain type: ChainType_FingerRing");
break;
}
case ChainType::ChainType_FingerThumb:
{
spdlog::info("received Skeleton chain type: ChainType_FingerThumb");
break;
}
case ChainType::ChainType_Hand:
{
spdlog::info("received Skeleton chain type: ChainType_Hand");
break;
}
case ChainType::ChainType_Head:
{
spdlog::info("received Skeleton chain type: ChainType_Head");
break;
}
case ChainType::ChainType_Leg:
{
spdlog::info("received Skeleton chain type: ChainType_Leg");
break;
}
case ChainType::ChainType_Neck:
{
spdlog::info("received Skeleton chain type: ChainType_Neck");
break;
}
case ChainType::ChainType_Pelvis:
{
spdlog::info("received Skeleton chain type: ChainType_Pelvis");
break;
}
case ChainType::ChainType_Shoulder:
{
spdlog::info("received Skeleton chain type: ChainType_Shoulder");
break;
}
case ChainType::ChainType_Spine:
{
spdlog::info("received Skeleton chain type: ChainType_Spine");
break;
}
case ChainType::ChainType_Arm:
{
spdlog::info("received Skeleton chain type: ChainType_Arm");
break;
}
case ChainType::ChainType_Invalid:
default:
{
spdlog::info("received Skeleton chain type: ChainType_Invalid");
break;
}
}
AdvanceConsolePosition(2);
}
void SDKClient::GetTemporarySkeletonIfModified()
{
// if a temporary skeleton associated to the current session has been modified we can get it and, potentially, load it
if (m_ModifiedSkeletonIndex != UINT_MAX)
{
// get the temporary skeleton
uint32_t t_SessionId = m_SessionId;
SDKReturnCode t_Res = CoreSdk_GetTemporarySkeleton(m_ModifiedSkeletonIndex, t_SessionId);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get temporary skeleton. The error given was {}.", t_Res);
return;
}
// At this point if we are satisfied with the modifications to the skeleton we can load it into Core.
// Remember to always call function CoreSdk_ClearTemporarySkeleton after loading a temporary skeleton,
// this will keep the temporary skeleton list in sync between Core and the SDK.
//uint32_t t_ID = 0;
//SDKReturnCode t_Res = CoreSdk_LoadSkeleton(m_ModifiedSkeletonIndex, &t_ID);
//if (t_Res != SDKReturnCode::SDKReturnCode_Success)
//{
// spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
// return;
//}
//if (t_ID == 0)
//{
// spdlog::error("Failed to give skeleton an ID.");
//}
//m_LoadedSkeletons.push_back(t_ID);
//t_Res = CoreSdk_ClearTemporarySkeleton(m_ModifiedSkeletonIndex, m_SessionId);
//if (t_Res != SDKReturnCode::SDKReturnCode_Success)
//{
// spdlog::error("Failed to clear temporary skeleton. The error given was {}.", t_Res);
// return;
//}
m_ModifiedSkeletonIndex = UINT_MAX;
}
}
void SDKClient::PrintTemporarySkeletonInfo()
{
spdlog::info("Number of temporary skeletons in the SDK: {} ", m_TemporarySkeletons.size());
static uint32_t t_TotalNumberOfTemporarySkeletonsInCore = 0;
auto t_TimeSinceLastTemporarySkeletonUpdate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_LastTemporarySkeletonUpdate).count();
if (t_TimeSinceLastTemporarySkeletonUpdate < static_cast<unsigned int>(MILLISECONDS_BETWEEN_TEMPORARY_SKELETONS_UPDATE))
{
spdlog::info("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore);
return;
}
TemporarySkeletonCountForAllSessions t_TemporarySkeletonCountForAllSessions;
SDKReturnCode t_Res = CoreSdk_GetTemporarySkeletonCountForAllSessions(&t_TemporarySkeletonCountForAllSessions);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get all temporary skeletons. The error given was {}.", t_Res);
return;
}
t_TotalNumberOfTemporarySkeletonsInCore = 0;
for (uint32_t i = 0; i < t_TemporarySkeletonCountForAllSessions.sessionsCount; i++)
{
t_TotalNumberOfTemporarySkeletonsInCore += t_TemporarySkeletonCountForAllSessions.temporarySkeletonCountForSessions[i].skeletonCount;
}
// print total number of temporary skeletons:
spdlog::info("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore);
m_LastTemporarySkeletonUpdate = std::chrono::high_resolution_clock::now();
}
void SDKClient::HandleHapticCommands()
{
if (m_FirstLeftGloveID == 0 && m_FirstRightGloveID == 0) return; // we got no gloves to work on anyway!
// get a dongle id
uint32_t t_DongleId = 0;
uint32_t t_GloveIds[2] = { 0,0 };
uint32_t t_DongleCount = 0;
if (CoreSdk_GetNumberOfDongles(&t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
if (t_DongleCount == 0) return; // we got no gloves to work on anyway!
uint32_t* t_DongleIds = new uint32_t[t_DongleCount]();
if (CoreSdk_GetDongleIds(t_DongleIds, t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
for (uint32_t i = 0; i < t_DongleCount; i++)
{
// now lets see if it has gloves. otherwise its still not useful
CoreSdk_GetGlovesForDongle(t_DongleIds[i], &t_GloveIds[0], &t_GloveIds[1]);
if (!t_GloveIds[0] && !t_GloveIds[1]) continue;
t_DongleId = t_DongleIds[i]; // during tests we only expect 1 or 2 gloves, so this code is ok, in more real situation we want to make sure we got the right dongle instead of the first available.
break;
}
if (t_DongleId == 0) return; // still no valid data. (though this is very unlikely)
ClientHapticSettings t_HapticState[NUMBER_OF_HANDS_SUPPORTED]{};
const int t_LeftHand = 0;
const int t_RightHand = 1;
// The strange key number sequence here results from having gloves lie in front of you, and have the keys and haptics in the same order.
t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb
t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index
t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle
t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring
t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky
t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb
t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index
t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle
t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring
t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky
// Note: this timer is apparently not very accurate.
// It is good enough for this test client, but should probably be replaced for other uses.
static std::chrono::high_resolution_clock::time_point s_TimeOfLastHapticsCommandSent;
const std::chrono::high_resolution_clock::time_point s_Now = std::chrono::high_resolution_clock::now();
const long long s_MillisecondsSinceLastHapticCommand = std::chrono::duration_cast<std::chrono::milliseconds>(s_Now - s_TimeOfLastHapticsCommandSent).count();
if (s_MillisecondsSinceLastHapticCommand < static_cast<unsigned int>(MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS))
{
return;
}
const Side s_Hands[NUMBER_OF_HANDS_SUPPORTED] = { Side::Side_Left, Side::Side_Right };
const float s_FullPower = 1.0f;
for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
{
if (t_GloveIds[t_HandNumber] == 0) continue; // no glove available. skip.
GloveLandscapeData t_Glove;
if (CoreSdk_GetDataForGlove_UsingGloveId(t_GloveIds[t_HandNumber], &t_Glove) != SDKReturnCode::SDKReturnCode_Success)
{
continue;
}
if (t_Glove.familyType != DeviceFamilyType::DeviceFamilyType_Prime1)
{
continue;
}
}
// This is an example that shows how to send the haptics commands based on dongle id:
uint32_t* t_HapticsDongles = new uint32_t[MAX_NUMBER_OF_DONGLES];
for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
{
uint32_t t_NumberOfHapticsDongles = 0;
if ((CoreSdk_GetNumberOfHapticsDongles(&t_NumberOfHapticsDongles) != SDKReturnCode::SDKReturnCode_Success) ||
(t_NumberOfHapticsDongles == 0))
{
continue;
}
if (CoreSdk_GetHapticsDongleIds(t_HapticsDongles, t_NumberOfHapticsDongles) != SDKReturnCode::SDKReturnCode_Success)
{
continue;
}
float t_HapticsPowers[NUM_FINGERS_ON_HAND]{};
for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
{
t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f;
}
GloveLandscapeData t_GloveLandscapeData;
if (CoreSdk_GetDataForGlove_UsingGloveId(t_GloveIds[t_HandNumber], &t_GloveLandscapeData) != SDKReturnCode::SDKReturnCode_Success)
{
continue;
}
if (!t_GloveLandscapeData.isHaptics) // if the glove is not Haptics do not vibrate.
{
continue;
}
CoreSdk_VibrateFingers(t_HapticsDongles[0], s_Hands[t_HandNumber], t_HapticsPowers);
}
delete[] t_HapticsDongles;
}
void SDKClient::HandleSkeletonCommands()
{
if (GetKeyDown('N'))
{
LoadTestSkeleton();
}
if (GetKeyDown('M'))
{
UnloadTestSkeleton();
}
}
void SDKClient::HandleSkeletonHapticCommands()
{
if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0)
{
return;
}
ClientHapticSettings t_HapticState[NUMBER_OF_HANDS_SUPPORTED]{};
const int t_LeftHand = 0;
const int t_RightHand = 1;
// The strange key number sequence here results from having gloves lie in front of you, and have the keys and haptics in the same order.
t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb
t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index
t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle
t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring
t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky
t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb
t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index
t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle
t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring
t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky
// Note: this timer is apparently not very accurate.
// It is good enough for this test client, but should probably be replaced for other uses.
static std::chrono::high_resolution_clock::time_point s_TimeOfLastHapticsCommandSent;
const std::chrono::high_resolution_clock::time_point s_Now = std::chrono::high_resolution_clock::now();
const long long s_MillisecondsSinceLastHapticCommand = std::chrono::duration_cast<std::chrono::milliseconds>(s_Now - s_TimeOfLastHapticsCommandSent).count();
if (s_MillisecondsSinceLastHapticCommand < static_cast<unsigned int>(MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS))
{
return;
}
const Side s_Hands[NUMBER_OF_HANDS_SUPPORTED] = { Side::Side_Left, Side::Side_Right };
const float s_FullPower = 1.0f;
// The preferred way of sending the haptics commands is based on skeleton id
for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
{
float t_HapticsPowers[NUM_FINGERS_ON_HAND]{};
for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
{
t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f;
}
bool t_IsHaptics = false;
if (CoreSdk_DoesSkeletonGloveSupportHaptics(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], &t_IsHaptics) != SDKReturnCode::SDKReturnCode_Success)
{
continue;
}
if (!t_IsHaptics)
{
continue;
}
CoreSdk_VibrateFingersForSkeleton(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], t_HapticsPowers);
}
}
void SDKClient::HandleTemporarySkeletonCommands()
{
if (GetKeyDown('A'))
{
AllocateChains();
}
if (GetKeyDown('B'))
{
BuildTemporarySkeleton();
}
if (GetKeyDown('C'))
{
ClearTemporarySkeleton();
}
if (GetKeyDown('D'))
{
ClearAllTemporarySkeletons();
}
if (GetKeyDown('E'))
{
SaveTemporarySkeletonToFile();
}
if (GetKeyDown('F'))
{
GetTemporarySkeletonFromFile();
}
}
void SDKClient::HandleTrackerCommands()
{
if (GetKeyDown('O'))
{
m_TrackerTest = !m_TrackerTest;
}
if (GetKeyDown('G'))
{
m_TrackerDataDisplayPerUser = !m_TrackerDataDisplayPerUser;
}
if (m_TrackerTest)
{
m_TrackerOffset += 0.0005f;
if (m_TrackerOffset >= 10.0f)
{
m_TrackerOffset = 0.0f;
}
TrackerId t_TrackerId;
CopyString(t_TrackerId.id, sizeof(t_TrackerId.id), std::string("Test Tracker"));
TrackerData t_TrackerData = {};
t_TrackerData.isHmd = false;
t_TrackerData.trackerId = t_TrackerId;
t_TrackerData.trackerType = TrackerType::TrackerType_Unknown;
t_TrackerData.position = { 0.0f, m_TrackerOffset, 0.0f };
t_TrackerData.rotation = { 1.0f, 0.0f, 0.0f, 0.0f };
t_TrackerData.quality = TrackerQuality::TrackingQuality_Trackable;
TrackerData t_TrackerDatas[MAX_NUMBER_OF_TRACKERS];
t_TrackerDatas[0] = t_TrackerData;
const SDKReturnCode t_TrackerSend = CoreSdk_SendDataForTrackers(t_TrackerDatas, 1);
if (t_TrackerSend != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to send tracker data. The error given was {}.", t_TrackerSend);
return;
}
}
}
void SDKClient::HandleGesturesCommands()
{
if (GetKeyDown('H'))
{
m_ShowLeftGestures = !m_ShowLeftGestures;
}
}
NodeSetup SDKClient::CreateNodeSetup(uint32_t p_Id, uint32_t p_ParentId, float p_PosX, float p_PosY, float p_PosZ, std::string p_Name)
{
NodeSetup t_Node;
NodeSetup_Init(&t_Node);
t_Node.id = p_Id; //Every ID needs to be unique per node in a skeleton.
CopyString(t_Node.name, sizeof(t_Node.name), p_Name);
t_Node.type = NodeType::NodeType_Joint;
//Every node should have a parent unless it is the Root node.
t_Node.parentID = p_ParentId; //Setting the node ID to its own ID ensures it has no parent.
t_Node.settings.usedSettings = NodeSettingsFlag::NodeSettingsFlag_None;
t_Node.transform.position.x = p_PosX;
t_Node.transform.position.y = p_PosY;
t_Node.transform.position.z = p_PosZ;
return t_Node;
}
ManusVec3 SDKClient::CreateManusVec3(float p_X, float p_Y, float p_Z)
{
ManusVec3 t_Vec;
t_Vec.x = p_X;
t_Vec.y = p_Y;
t_Vec.z = p_Z;
return t_Vec;
}
bool SDKClient::SetupHandNodes(uint32_t p_SklIndex)
{
// Define number of fingers per hand and number of joints per finger
const uint32_t t_NumFingers = 5;
const uint32_t t_NumJoints = 4;
// Create an array with the initial position of each hand node.
// Note, these values are just an example of node positions and refer to the hand laying on a flat surface.
ManusVec3 t_Fingers[t_NumFingers * t_NumJoints] = {
CreateManusVec3(0.024950f, 0.000000f, 0.025320f), //Thumb CMC joint
CreateManusVec3(0.000000f, 0.000000f, 0.032742f), //Thumb MCP joint
CreateManusVec3(0.000000f, 0.000000f, 0.028739f), //Thumb IP joint
CreateManusVec3(0.000000f, 0.000000f, 0.028739f), //Thumb Tip joint
//CreateManusVec3(0.011181f, 0.031696f, 0.000000f), //Index CMC joint // Note: we are not adding the matacarpal bones in this example, if you want to animate the metacarpals add each of them to the corresponding finger chain.
CreateManusVec3(0.011181f, 0.000000f, 0.052904f), //Index MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.052904f)
CreateManusVec3(0.000000f, 0.000000f, 0.038257f), //Index PIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.020884f), //Index DIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.018759f), //Index Tip joint
//CreateManusVec3(0.000000f, 0.033452f, 0.000000f), //Middle CMC joint
CreateManusVec3(0.000000f, 0.000000f, 0.051287f), //Middle MCP joint
CreateManusVec3(0.000000f, 0.000000f, 0.041861f), //Middle PIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.024766f), //Middle DIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.019683f), //Middle Tip joint
//CreateManusVec3(-0.011274f, 0.031696f, 0.000000f), //Ring CMC joint
CreateManusVec3(-0.011274f, 0.000000f, 0.049802f), //Ring MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.049802f),
CreateManusVec3(0.000000f, 0.000000f, 0.039736f), //Ring PIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.023564f), //Ring DIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.019868f), //Ring Tip joint
//CreateManusVec3(-0.020145f, 0.027538f, 0.000000f), //Pinky CMC joint
CreateManusVec3(-0.020145f, 0.000000f, 0.047309f), //Pinky MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.047309f),
CreateManusVec3(0.000000f, 0.000000f, 0.033175f), //Pinky PIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.018020f), //Pinky DIP joint
CreateManusVec3(0.000000f, 0.000000f, 0.019129f), //Pinky Tip joint
};
// skeleton entry is already done. just the nodes now.
// setup a very simple node hierarchy for fingers
// first setup the root node
//
// root, This node has ID 0 and parent ID 0, to indicate it has no parent.
SDKReturnCode t_Res = CoreSdk_AddNodeToSkeletonSetup(p_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "Hand"));
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
return false;
}
// then loop for 5 fingers
int t_FingerId = 0;
for (uint32_t i = 0; i < t_NumFingers; i++)
{
uint32_t t_ParentID = 0;
// then the digits of the finger that are linked to the root of the finger.
for (uint32_t j = 0; j < t_NumJoints; j++)
{
t_Res = CoreSdk_AddNodeToSkeletonSetup(p_SklIndex, CreateNodeSetup(1 + t_FingerId + j, t_ParentID, t_Fingers[i * 4 + j].x, t_Fingers[i * 4 + j].y, t_Fingers[i * 4 + j].z, "fingerdigit"));
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
printf("Failed to Add Node To Skeleton Setup. The error given %d.", t_Res);
return false;
}
t_ParentID = 1 + t_FingerId + j;
}
t_FingerId += t_NumJoints;
}
return true;
}
bool SDKClient::SetupHandChains(uint32_t p_SklIndex)
{
// Add the Hand chain, this identifies the wrist of the hand
{
ChainSettings t_ChainSettings;
ChainSettings_Init(&t_ChainSettings);
t_ChainSettings.usedSettings = ChainType::ChainType_Hand;
t_ChainSettings.hand.handMotion = HandMotion::HandMotion_IMU;
t_ChainSettings.hand.fingerChainIdsUsed = 5; //we will have 5 fingers
t_ChainSettings.hand.fingerChainIds[0] = 1; //links to the other chains we will define further down
t_ChainSettings.hand.fingerChainIds[1] = 2;
t_ChainSettings.hand.fingerChainIds[2] = 3;
t_ChainSettings.hand.fingerChainIds[3] = 4;
t_ChainSettings.hand.fingerChainIds[4] = 5;
ChainSetup t_Chain;
ChainSetup_Init(&t_Chain);
t_Chain.id = 0; //Every ID needs to be unique per chain in a skeleton.
t_Chain.type = ChainType::ChainType_Hand;
t_Chain.dataType = ChainType::ChainType_Hand;
t_Chain.side = Side::Side_Left;
t_Chain.dataIndex = 0;
t_Chain.nodeIdCount = 1;
t_Chain.nodeIds[0] = 0; //this links to the hand node created in the SetupHandNodes
t_Chain.settings = t_ChainSettings;
SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
return false;
}
}
// Add the 5 finger chains
const ChainType t_FingerTypes[5] = { ChainType::ChainType_FingerThumb,
ChainType::ChainType_FingerIndex,
ChainType::ChainType_FingerMiddle,
ChainType::ChainType_FingerRing,
ChainType::ChainType_FingerPinky };
for (int i = 0; i < 5; i++)
{
ChainSettings t_ChainSettings;
ChainSettings_Init(&t_ChainSettings);
t_ChainSettings.usedSettings = t_FingerTypes[i];
t_ChainSettings.finger.handChainId = 0; //This links to the wrist chain above.
//This identifies the metacarpal bone, if none exists, or the chain is a thumb it should be set to -1.
//The metacarpal bone should not be part of the finger chain, unless you are defining a thumb which does need it.
t_ChainSettings.finger.metacarpalBoneId = -1;
t_ChainSettings.finger.useLeafAtEnd = false; //this is set to true if there is a leaf bone to the tip of the finger.
ChainSetup t_Chain;
ChainSetup_Init(&t_Chain);
t_Chain.id = i + 1; //Every ID needs to be unique per chain in a skeleton.
t_Chain.type = t_FingerTypes[i];
t_Chain.dataType = t_FingerTypes[i];
t_Chain.side = Side::Side_Left;
t_Chain.dataIndex = 0;
if (i == 0) // Thumb
{
t_Chain.nodeIdCount = 4; //The amount of node id's used in the array
t_Chain.nodeIds[0] = 1; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[1] = 2; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[2] = 3; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[3] = 4; //this links to the hand node created in the SetupHandNodes
}
else // All other fingers
{
t_Chain.nodeIdCount = 4; //The amount of node id's used in the array
t_Chain.nodeIds[0] = (i * 4) + 1; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[1] = (i * 4) + 2; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[2] = (i * 4) + 3; //this links to the hand node created in the SetupHandNodes
t_Chain.nodeIds[3] = (i * 4) + 4; //this links to the hand node created in the SetupHandNodes
}
t_Chain.settings = t_ChainSettings;
SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
return false;
}
}
return true;
}
void SDKClient::LoadTestSkeleton()
{
uint32_t t_SklIndex = 0;
SkeletonSetupInfo t_SKL;
SkeletonSetupInfo_Init(&t_SKL);
t_SKL.type = SkeletonType::SkeletonType_Hand;
t_SKL.settings.scaleToTarget = true;
t_SKL.settings.useEndPointApproximations = true;
t_SKL.settings.targetType = SkeletonTargetType::SkeletonTargetType_UserIndexData;
//If the user does not exist then the added skeleton will not be animated.
//Same goes for any other skeleton made for invalid users/gloves.
t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0;
CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("LeftHand"));
SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.push_back(t_SklIndex);
// setup nodes and chains for the skeleton hand
if (!SetupHandNodes(t_SklIndex)) return;
if (!SetupHandChains(t_SklIndex)) return;
// load skeleton
uint32_t t_ID = 0;
t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
return;
}
RemoveIndexFromTemporarySkeletonList(t_SklIndex);
if (t_ID == 0)
{
spdlog::error("Failed to give skeleton an ID.");
}
m_LoadedSkeletons.push_back(t_ID);
}
void SDKClient::UnloadTestSkeleton()
{
if (m_LoadedSkeletons.size() == 0)
{
spdlog::error("There was no skeleton for us to unload.");
return;
}
SDKReturnCode t_Res = CoreSdk_UnloadSkeleton(m_LoadedSkeletons[0]);
m_LoadedSkeletons.erase(m_LoadedSkeletons.begin());
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to unload skeleton. The error given was {}.", t_Res);
return;
}
}
void SDKClient::AllocateChains()
{
m_ChainType = ChainType::ChainType_Invalid;
uint32_t t_SklIndex = 0;
SkeletonSettings t_Settings;
SkeletonSettings_Init(&t_Settings);
t_Settings.scaleToTarget = true;
t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_UserData;
t_Settings.skeletonTargetUserData.userID = 0;
SkeletonSetupInfo t_SKL;
SkeletonSetupInfo_Init(&t_SKL);
t_SKL.id = 0;
t_SKL.type = SkeletonType::SkeletonType_Hand;
t_SKL.settings = t_Settings;
CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("hand"));
SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.push_back(t_SklIndex);
// setup nodes for the skeleton hand
SetupHandNodes(t_SklIndex);
// allocate chains for skeleton
t_Res = CoreSdk_AllocateChainsForSkeletonSetup(t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to allocate chains for skeleton. The error given was {}.", t_Res);
return;
}
// get the skeleton info
SkeletonSetupArraySizes t_SkeletonInfo;
t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get info about skeleton. The error given was {}.", t_Res);
return;
}
ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount];
// now get the chain data
t_Res = CoreSdk_GetSkeletonSetupChains(t_SklIndex, t_Chains);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get skeleton setup chains. The error given was {}.", t_Res);
delete[] t_Chains;
return;
}
// as proof store the first chain type
m_ChainType = t_Chains[0].dataType;
// but since we want to cleanly load the skeleton without holding everything up
// we need to set its side first
for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++)
{
if (t_Chains[i].dataType == ChainType::ChainType_Hand)
{
t_Chains[i].side = Side::Side_Left; // we're just picking a side here.
t_Res = CoreSdk_OverwriteChainToSkeletonSetup(t_SklIndex, t_Chains[i]);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to overwrite Chain To Skeleton Setup. The error given was {}.", t_Res);
delete[] t_Chains;
return;
}
break; // no need to continue checking the others.
}
}
// cleanup
delete[] t_Chains;
// load skeleton so it is done.
uint32_t t_ID = 0;
t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
return;
}
RemoveIndexFromTemporarySkeletonList(t_SklIndex);
if (t_ID == 0)
{
spdlog::error("Failed to give skeleton an ID.");
}
m_LoadedSkeletons.push_back(t_ID);
}
void SDKClient::BuildTemporarySkeleton()
{
// define the session Id for which we want to save
// in this example we want to save a skeleton for the current session so we use our own Session Id
uint32_t t_SessionId = m_SessionId;
bool t_IsSkeletonModified = false; // this bool is set to true by the Dev Tools after saving any modification to the skeleton,
// this triggers the OnSyStemCallback which is used in the SDK to be notified about a change to its temporary skeletons.
// for the purpose of this example setting this bool to true is not really necessary.
// first create a skeleton setup of type Body
uint32_t t_SklIndex = 0;
SkeletonSettings t_Settings;
SkeletonSettings_Init(&t_Settings);
t_Settings.scaleToTarget = true;
t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_UserData;
t_Settings.skeletonTargetUserData.userID = 0; // this needs to be a real user Id when retargeting, when editing the temporary skeleton this may (hopefully) not cause issues
SkeletonSetupInfo t_SKL;
SkeletonSetupInfo_Init(&t_SKL);
t_SKL.id = 0;
t_SKL.type = SkeletonType::SkeletonType_Body;
t_SKL.settings = t_Settings;
CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body"));
SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.push_back(t_SklIndex);
//Add 3 nodes to the skeleton setup
t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "root"));
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
return;
}
t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(1, 0, 0, 1, 0, "branch"));
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
return;
}
t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(2, 1, 0, 2, 0, "leaf"));
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
return;
}
//Add one chain of type Leg to the skeleton setup
ChainSettings t_ChainSettings;
ChainSettings_Init(&t_ChainSettings);
t_ChainSettings.usedSettings = ChainType::ChainType_Leg;
t_ChainSettings.leg.footForwardOffset = 0;
t_ChainSettings.leg.footSideOffset = 0;
t_ChainSettings.leg.reverseKneeDirection = false;
t_ChainSettings.leg.kneeRotationOffset = 0;
ChainSetup t_Chain;
ChainSetup_Init(&t_Chain);
t_Chain.id = 0;
t_Chain.type = ChainType::ChainType_Leg;
t_Chain.dataType = ChainType::ChainType_Leg;
t_Chain.dataIndex = 0;
t_Chain.nodeIdCount = 3;
t_Chain.nodeIds[0] = 0;
t_Chain.nodeIds[1] = 1;
t_Chain.nodeIds[2] = 2;
t_Chain.settings = t_ChainSettings;
t_Chain.side = Side::Side_Left;
t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
return;
}
// save the temporary skeleton
t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
return;
}
// if we want to go on with the modifications to the same temporary skeleton
// get the skeleton
t_Res = CoreSdk_GetTemporarySkeleton(t_SklIndex, t_SessionId);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get temporary skeleton. The error given was {}.", t_Res);
return;
}
// now add second chain to the same temporary skeleton
t_ChainSettings.usedSettings = ChainType::ChainType_Head;
t_Chain.id = 1;
t_Chain.type = ChainType::ChainType_Head;
t_Chain.dataType = ChainType::ChainType_Head;
t_Chain.dataIndex = 0;
t_Chain.nodeIdCount = 1;
t_Chain.nodeIds[0] = 0;
t_Chain.settings = t_ChainSettings;
t_Chain.side = Side::Side_Center;
t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
return;
}
// save the temporary skeleton
t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
return;
}
// get the skeleton info (number of nodes and chains for that skeleton)
SkeletonSetupArraySizes t_SkeletonInfo;
t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get info about skeleton. The error given was {}.", t_Res);
return;
}
// now get the chain data
ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount];
t_Res = CoreSdk_GetSkeletonSetupChains(t_SklIndex, t_Chains);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get skeleton setup chains. The error given was {}.", t_Res);
return;
}
// get the node data
NodeSetup* t_Nodes = new NodeSetup[t_SkeletonInfo.nodesCount];
t_Res = CoreSdk_GetSkeletonSetupNodes(t_SklIndex, t_Nodes);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get skeleton setup nodes. The error given was {}.", t_Res);
return;
}
// just as an example try to get the skeleton setup info
SkeletonSetupInfo t_SKeletonSetupInfo;
SkeletonSetupInfo_Init(&t_SKeletonSetupInfo);
t_Res = CoreSdk_GetSkeletonSetupInfo(t_SklIndex, &t_SKeletonSetupInfo);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to overwrite Skeleton Setup. The error given was {}.", t_Res);
return;
}
// if we want to modify the skeleton setup or if we want to apply some changes to the chains or nodes:
// first overwrite the existing skeleton setup and then re-add all the chains and nodes to it
SkeletonSettings_Init(&t_Settings);
t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_GloveData;
t_SKL.settings = t_Settings;
CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body2"));
// this way we overwrite the temporary skeleton with index t_SklIndex with the modified skeleton setup
t_Res = CoreSdk_OverwriteSkeletonSetup(t_SklIndex, t_SKL);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to overwrite Skeleton Setup. The error given was {}.", t_Res);
return;
}
// modify chains and nodes
t_Chains[0].side = Side::Side_Right;
t_Nodes[0].type = NodeType::NodeType_Mesh;
// add all the existing nodes to the new skeleton setup
for (size_t i = 0; i < t_SkeletonInfo.nodesCount; i++)
{
t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, t_Nodes[i]);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
return;
}
}
// then add all the existing chains to the new skeleton setup
for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++)
{
t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chains[i]);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Add Chains To Skeleton Setup. The error given was {}.", t_Res);
return;
}
}
// cleanup
delete[] t_Chains;
delete[] t_Nodes;
// save temporary skeleton
// in the Dev Tools this bool is set to true when saving the temporary skeleton, this triggers OnSystemCallback which
// notifies the SDK sessions about a modifications to one of their temporary skeletons.
// setting the bool to true in this example is not really necessary, it's just for testing purposes.
t_IsSkeletonModified = true;
t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
return;
}
}
void SDKClient::ClearTemporarySkeleton()
{
// clear the first element of the temporary skeleton list
if (m_TemporarySkeletons.size() == 0)
{
spdlog::error("There are no Temporary Skeletons to clear!");
return;
}
uint32_t t_SklIndex = m_TemporarySkeletons[0];
SDKReturnCode t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, m_SessionId);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Clear Temporary Skeleton. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.erase(m_TemporarySkeletons.begin());
}
void SDKClient::ClearAllTemporarySkeletons()
{
if (m_TemporarySkeletons.size() == 0)
{
spdlog::error("There are no Temporary Skeletons to clear!");
return;
}
SDKReturnCode t_Res = CoreSdk_ClearAllTemporarySkeletons();
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Clear All Temporary Skeletons. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.clear();
}
void SDKClient::SaveTemporarySkeletonToFile()
{
// this example shows how to save a temporary skeleton to a file
// first create a temporary skeleton:
// define the session Id for which we want to save
uint32_t t_SessionId = m_SessionId;
bool t_IsSkeletonModified = false; // setting this bool to true is not necessary here, it is mostly used by the Dev Tools
// to notify the SDK sessions about their skeleton being modified.
// first create a skeleton setup
uint32_t t_SklIndex = 0;
SkeletonSetupInfo t_SKL;
SkeletonSetupInfo_Init(&t_SKL);
t_SKL.type = SkeletonType::SkeletonType_Hand;
t_SKL.settings.scaleToTarget = true;
t_SKL.settings.targetType = SkeletonTargetType::SkeletonTargetType_GloveData;
t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0;
CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("LeftHand"));
SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.push_back(t_SklIndex);
// setup nodes and chains for the skeleton hand
if (!SetupHandNodes(t_SklIndex)) return;
if (!SetupHandChains(t_SklIndex)) return;
// save the temporary skeleton
t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
return;
}
// now compress the temporary skeleton data and get the size of the compressed data:
uint32_t t_TemporarySkeletonLengthInBytes;
t_Res = CoreSdk_CompressTemporarySkeletonAndGetSize(t_SklIndex, t_SessionId, &t_TemporarySkeletonLengthInBytes);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to compress temporary skeleton and get size. The error given was {}.", t_Res);
return;
}
unsigned char* t_TemporarySkeletonData = new unsigned char[t_TemporarySkeletonLengthInBytes];
// get the array of bytes with the compressed temporary skeleton data, remember to always call function CoreSdk_CompressTemporarySkeletonAndGetSize
// before trying to get the compressed temporary skeleton data
t_Res = CoreSdk_GetCompressedTemporarySkeletonData(t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to get compressed temporary skeleton data. The error given was {}.", t_Res);
return;
}
// now save the data into a .mskl file
// as an example we save the temporary skeleton in a folder called ManusTemporarySkeleton inside the documents directory
// get the path for the documents directory
std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8();
// create directory name and file name for storing the temporary skeleton
std::string t_DirectoryPath =
t_DirectoryPathString
+ s_SlashForFilesystemPath
+ "ManusTemporarySkeleton";
CreateFolderIfItDoesNotExist(t_DirectoryPath);
std::string t_DirectoryPathAndFileName =
t_DirectoryPath
+ s_SlashForFilesystemPath
+ "TemporarySkeleton.mskl";
// write the temporary skeleton data to .mskl file
std::ofstream t_File = GetOutputFileStream(t_DirectoryPathAndFileName);
t_File.write((char*)t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
t_File.close();
t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, t_SessionId);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Clear Temporary Skeleton after saving. The error given was {}.", t_Res);
return;
}
RemoveIndexFromTemporarySkeletonList(t_SklIndex);
}
void SDKClient::GetTemporarySkeletonFromFile()
{
// this example shows how to load a temporary skeleton data from a file
// as an example we try to get the temporary skeleton data previously saved as .mskl file in directory Documents/ManusTemporarySkeleton
// get the path for the documents directory
std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8();
// check if directory exists
std::string t_DirectoryPath =
t_DirectoryPathString
+ s_SlashForFilesystemPath
+ "ManusTemporarySkeleton";
if (!DoesFolderOrFileExist(t_DirectoryPath))
{
SPDLOG_WARN("Failed to read from client file, the mentioned directory does not exist");
return;
}
// create string with file name
std::string t_DirectoryPathAndFileName =
t_DirectoryPath
+ s_SlashForFilesystemPath
+ "TemporarySkeleton.mskl";
// read from file
std::ifstream t_File = GetInputFileStream(t_DirectoryPathAndFileName);
if (!t_File)
{
SPDLOG_WARN("Failed to read from client file, the file does not exist in the mentioned directory");
return;
}
// get file dimension
t_File.seekg(0, t_File.end);
int t_FileLength = (int)t_File.tellg();
t_File.seekg(0, t_File.beg);
// get temporary skeleton data from file
unsigned char* t_TemporarySkeletonData = new unsigned char[t_FileLength];
t_File.read((char*)t_TemporarySkeletonData, t_FileLength);
t_File.close();
// save the zipped temporary skeleton information, they will be used internally for sending the data to Core
uint32_t t_TemporarySkeletonLengthInBytes = t_FileLength;
if (t_TemporarySkeletonData == nullptr)
{
SPDLOG_WARN("Failed to read the compressed temporary skeleton data from file");
delete[] t_TemporarySkeletonData;
return;
}
// create a skeleton setup where we will store the temporary skeleton retrieved from file
SkeletonSetupInfo t_SKL;
SkeletonSetupInfo_Init(&t_SKL);
uint32_t t_SklIndex = 0;
SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
if (t_Res != SDKReturnCode::SDKReturnCode_Success)
{
spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
return;
}
m_TemporarySkeletons.push_back(t_SklIndex);
// associate the retrieved temporary skeleton to the current session id
uint32_t t_SessionId = m_SessionId;
// load the temporary skeleton data retrieved from the zipped file and save it with index t_SklIndex and session id of the current session
SDKReturnCode t_Result = CoreSdk_GetTemporarySkeletonFromCompressedData(t_SklIndex, t_SessionId, t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
if (t_Result != SDKReturnCode::SDKReturnCode_Success)
{
SPDLOG_WARN("Failed to load temporary skeleton data from client file in Core, the error code was: {}.", t_Result);
return;
}
delete[] t_TemporarySkeletonData;
}
void SDKClient::TestTimestamp()
{
ManusTimestamp t_TS;
ManusTimestamp_Init(&t_TS);
ManusTimestampInfo t_TSInfo;
ManusTimestampInfo_Init(&t_TSInfo);
t_TSInfo.fraction = 69;
t_TSInfo.second = 6;
t_TSInfo.minute = 9;
t_TSInfo.hour = 6;
t_TSInfo.day = 9;
t_TSInfo.month = 6;
t_TSInfo.year = 6969;
t_TSInfo.timecode = true;
CoreSdk_SetTimestampInfo(&t_TS, t_TSInfo);
ManusTimestampInfo t_TSInfo2;
CoreSdk_GetTimestampInfo(t_TS, &t_TSInfo2);
}
void SDKClient::RemoveIndexFromTemporarySkeletonList(uint32_t p_Idx)
{
for (int i = 0; i < m_TemporarySkeletons.size(); i++)
{
if (m_TemporarySkeletons[i] == p_Idx)
{
m_TemporarySkeletons.erase(m_TemporarySkeletons.begin() + i);
}
}
}