C++ SDK for MANUS Core 2.1
Loading...
Searching...
No Matches
SDKClient.cpp
Go to the documentation of this file.
1#include "SDKClient.hpp"
2#include "ManusSDKTypes.h"
3#include <fstream>
4#include <iostream>
5
7
8#define GO_TO_DISPLAY(p_Key,p_Function) if (GetKeyDown(p_Key)) { ClearConsole();\
9 m_CurrentInteraction = std::bind(&SDKClient::p_Function, this); return ClientReturnCode::ClientReturnCode_Success;}
10
11#define GO_TO_MENU_IF_REQUESTED() if (GetKeyDown('Q')) { ClearConsole();\
12 m_CurrentInteraction = nullptr; return ClientReturnCode::ClientReturnCode_Success;}
13
15
17{
18 s_Instance = this;
19
20 // using initializers like these ensure that the data is set to its default values.
21 ErgonomicsData_Init(&m_LeftGloveErgoData);
22 ErgonomicsData_Init(&m_RightGloveErgoData);
23
25}
26
28{
29 s_Instance = nullptr;
30}
31
35{
37 {
38 return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificInitialization;
39 }
40
41 // although resizewindow is not technically needed to setup the SDK , it is nice to see what we are doing in this example client.
42 // thus we make sure we have a nice console window to use.
43 // if this fails, then the window is being resized during startup (due to windows management tools) or by giving it invalid values.
45 {
46 // An error message will be logged in the function, so don't print anything here.
47 return ClientReturnCode::ClientReturnCode_FailedToResizeWindow;
48 }
49
50 const ClientReturnCode t_IntializeResult = InitializeSDK();
51 if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success)
52 {
53 spdlog::error("Failed to initialize the Core functionality. The value returned was {}.", t_IntializeResult);
54 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
55 }
56
57 return ClientReturnCode::ClientReturnCode_Success;
58}
59
63{
65
66 ClientReturnCode t_Result{};
67 while (!m_RequestedExit)
68 {
70 {
72
74 }
75
77
78 // 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.
80 switch (m_State)
81 {
82 case ClientState::ClientState_PickingConnectionType:
83 {
84 t_Result = PickingConnectionType();
85 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
86 } break;
87 case ClientState::ClientState_LookingForHosts:
88 {
89 t_Result = LookingForHosts();
90 if (t_Result != ClientReturnCode::ClientReturnCode_Success &&
91 t_Result != ClientReturnCode::ClientReturnCode_FailedToFindHosts) {
92 return t_Result;
93 }
94 } break;
95 case ClientState::ClientState_NoHostsFound:
96 {
97 t_Result = NoHostsFound();
98 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
99 } break;
100 case ClientState::ClientState_PickingHost:
101 {
102 t_Result = PickingHost();
103 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
104 } break;
105 case ClientState::ClientState_ConnectingToCore:
106 {
107 t_Result = ConnectingToCore();
108 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
109 } break;
110 case ClientState::ClientState_DisplayingData:
111 {
113 if (m_CurrentInteraction == nullptr)
114 {
115 t_Result = DisplayingData();
116 }
117 else
118 {
119 t_Result = m_CurrentInteraction();
120 }
121 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
122 } break;
123 case ClientState::ClientState_Disconnected:
124 {
125 t_Result = DisconnectedFromCore();
126 if (t_Result != ClientReturnCode::ClientReturnCode_Success) { return t_Result; }
127 }break;
128 default:
129 {
130 spdlog::error("Encountered the unrecognized state {}.", static_cast<int>(m_State));
131 return ClientReturnCode::ClientReturnCode_UnrecognizedStateEncountered;
132 }
133 } // switch(m_State)
134
136 {
137 spdlog::info("Pressed escape, so the client will now close.");
138
139 m_RequestedExit = true;
140 }
141
143 std::this_thread::sleep_for(std::chrono::milliseconds(10));
144 }
145
146 return ClientReturnCode::ClientReturnCode_Success;
147}
148
153{
154 const SDKReturnCode t_Result = CoreSdk_ShutDown();
155 if (t_Result != SDKReturnCode::SDKReturnCode_Success)
156 {
157 spdlog::error("Failed to shut down the SDK wrapper. The value returned was {}.", t_Result);
158 return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK;
159 }
160
162 {
163 return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificShutdown;
164 }
165
166 return ClientReturnCode::ClientReturnCode_Success;
167}
168
172void SDKClient::OnConnectedCallback(const ManusHost* const p_Host)
173{
174 spdlog::info("Connected to manus core.");
175
176 //No need to initialize these as they get filled in the CoreSdk_GetVersionsAndCheckCompatibility
177 ManusVersion t_SdkVersion;
178 ManusVersion t_CoreVersion;
179 bool t_IsCompatible;
180
181 const SDKReturnCode t_Result = CoreSdk_GetVersionsAndCheckCompatibility(&t_SdkVersion, &t_CoreVersion, &t_IsCompatible);
182
183 if (t_Result == SDKReturnCode::SDKReturnCode_Success)
184 {
185 const std::string t_Versions = "Sdk version : " + std::string(t_SdkVersion.versionInfo) + ", Core version : " + std::string(t_CoreVersion.versionInfo) + ".";
186
187 if (t_IsCompatible)
188 {
189 spdlog::info("Versions are compatible.{}", t_Versions);
190 }
191 else
192 {
193 spdlog::warn("Versions are not compatible with each other.{}", t_Versions);
194 }
195 }
196 else
197 {
198 spdlog::error("Failed to get the versions from the SDK. The value returned was {}.", t_Result);
199 }
200
201 uint32_t t_SessionId;
202 const SDKReturnCode t_SessionIdResult = CoreSdk_GetSessionId(&t_SessionId);
203 if (t_SessionIdResult == SDKReturnCode::SDKReturnCode_Success && t_SessionId != 0)
204 {
205 spdlog::info("Session Id: {}", t_SessionId);
206 s_Instance->m_SessionId = t_SessionId;
207 }
208 else
209 {
210 spdlog::info("Failed to get the Session ID from Core. The value returned was{}.", t_SessionIdResult);
211 }
212
213 ManusHost t_Host(*p_Host);
214 s_Instance->m_Host = std::make_unique<ManusHost>(t_Host);
215
216 // Only setting state to displaying data on automatic reconnect
217 if (s_Instance->m_State == ClientState::ClientState_Disconnected)
218 {
219 s_Instance->m_State = ClientState::ClientState_DisplayingData;
220 }
221}
222
225void SDKClient::OnDisconnectedCallback(const ManusHost* const p_Host)
226{
227 spdlog::info("Disconnected from manus core.");
228 s_Instance->m_TimeSinceLastDisconnect = std::chrono::high_resolution_clock::now();
229 ManusHost t_Host(*p_Host);
230 s_Instance->m_Host = std::make_unique<ManusHost>(t_Host);
231 s_Instance->m_State = ClientState::ClientState_Disconnected;
232}
233
236void SDKClient::OnSkeletonStreamCallback(const SkeletonStreamInfo* const p_SkeletonStreamInfo)
237{
238 if (s_Instance)
239 {
240 ClientSkeletonCollection* t_NxtClientSkeleton = new ClientSkeletonCollection();
241 t_NxtClientSkeleton->skeletons.resize(p_SkeletonStreamInfo->skeletonsCount);
242
243 for (uint32_t i = 0; i < p_SkeletonStreamInfo->skeletonsCount; i++)
244 {
245 CoreSdk_GetSkeletonInfo(i, &t_NxtClientSkeleton->skeletons[i].info);
246 t_NxtClientSkeleton->skeletons[i].nodes.resize(t_NxtClientSkeleton->skeletons[i].info.nodesCount);
247 t_NxtClientSkeleton->skeletons[i].info.publishTime = p_SkeletonStreamInfo->publishTime;
248 CoreSdk_GetSkeletonData(i, t_NxtClientSkeleton->skeletons[i].nodes.data(), t_NxtClientSkeleton->skeletons[i].info.nodesCount);
249 }
251 if (s_Instance->m_NextSkeleton != nullptr) delete s_Instance->m_NextSkeleton;
252 s_Instance->m_NextSkeleton = t_NxtClientSkeleton;
253 s_Instance->m_SkeletonMutex.unlock();
254 }
255}
256
259void SDKClient::OnLandscapeCallback(const Landscape* const p_Landscape)
260{
261 if (s_Instance == nullptr)return;
262
263 Landscape* t_Landscape = new Landscape(*p_Landscape);
265 if (s_Instance->m_NewLandscape != nullptr) delete s_Instance->m_NewLandscape;
266 s_Instance->m_NewLandscape = t_Landscape;
268}
269
270
273void SDKClient::OnSystemCallback(const SystemMessage* const p_SystemMessage)
274{
275 if (s_Instance)
276 {
278
279 switch (p_SystemMessage->type)
280 {
281 case SystemMessageType::SystemMessageType_TemporarySkeletonModified:
282 // if the message was triggered by a temporary skeleton being modified then save the skeleton index,
283 // this information will be used to get and load the skeleton into core
284 s_Instance->m_ModifiedSkeletonIndex = p_SystemMessage->infoUInt;
285 break;
286 default:
287 s_Instance->m_SystemMessageCode = p_SystemMessage->type;
288 s_Instance->m_SystemMessage = p_SystemMessage->infoString;
289 break;
290 }
292 }
293}
294
301void SDKClient::OnErgonomicsCallback(const ErgonomicsStream* const p_Ergo)
302{
303 if (s_Instance)
304 {
305 for (uint32_t i = 0; i < p_Ergo->dataCount; i++)
306 {
307 if (p_Ergo->data[i].isUserID)continue;
308
309 ErgonomicsData* t_Ergo = nullptr;
310 if (p_Ergo->data[i].id == s_Instance->m_FirstLeftGloveID)
311 {
313 }
314 if (p_Ergo->data[i].id == s_Instance->m_FirstRightGloveID)
315 {
317 }
318 if (t_Ergo == nullptr)continue;
319 CoreSdk_GetTimestampInfo(p_Ergo->publishTime, &s_Instance->m_ErgoTimestampInfo);
320 t_Ergo->id = p_Ergo->data[i].id;
321 t_Ergo->isUserID = p_Ergo->data[i].isUserID;
322 for (int j = 0; j < ErgonomicsDataType::ErgonomicsDataType_MAX_SIZE; j++)
323 {
324 t_Ergo->data[j] = p_Ergo->data[i].data[j];
325 }
326 }
327 }
328}
329
331float SDKClient::RoundFloatValue(float p_Value, int p_NumDecimalsToKeep)
332{
333 // Since C++11, powf is supposed to be declared in <cmath>.
334 // Unfortunately, gcc decided to be non-compliant on this for no apparent
335 // reason, so now we have to do this.
336 // https://stackoverflow.com/questions/5483930/powf-is-not-a-member-of-std
337 float t_Power = static_cast<float>(std::pow(
338 10.0,
339 static_cast<double>(p_NumDecimalsToKeep)));
340 return std::round(p_Value * t_Power) / t_Power;
341}
342
346{
347 if (p_Y < 0)
348 {
350 }
351 else
352 {
354 }
355
357}
358
362{
363 // before we can use the SDK, some internal SDK bits need to be initialized.
364 // however after initializing, the SDK is not yet connected to a host or doing anything network related just yet.
365 const SDKReturnCode t_InitializeResult = CoreSdk_Initialize(m_ClientType);
366 if (t_InitializeResult != SDKReturnCode::SDKReturnCode_Success)
367 {
368 spdlog::error("Failed to initialize the Manus Core SDK. The value returned was {}.", t_InitializeResult);
369 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
370 }
371
372 const ClientReturnCode t_CallBackResults = RegisterAllCallbacks();
373 if (t_CallBackResults != ::ClientReturnCode::ClientReturnCode_Success)
374 {
375 spdlog::error("Failed to initialize callbacks.");
376 return t_CallBackResults;
377 }
378
379 // after everything is registered and initialized as seen above
380 // we must also set the coordinate system being used for the data in this client.
381 // (each client can have their own settings. unreal and unity for instance use different coordinate systems)
382 // if this is not set, the SDK will not connect to any Manus core host.
383 CoordinateSystemVUH t_VUH;
384 CoordinateSystemVUH_Init(&t_VUH);
385 t_VUH.handedness = Side::Side_Left; // this is currently set to unreal mode.
386 t_VUH.up = AxisPolarity::AxisPolarity_PositiveY;
387 t_VUH.view = AxisView::AxisView_ZFromViewer;
388 t_VUH.unitScale = 1.0f; //1.0 is meters, 0.01 is cm, 0.001 is mm.
389
390 const SDKReturnCode t_CoordinateResult = CoreSdk_InitializeCoordinateSystemWithVUH(t_VUH, false);
391 /* this is an example if you want to use the other coordinate system instead of VUH
392 CoordinateSystemDirection t_Direction;
393 t_Direction.x = AxisDirection::AD_Right;
394 t_Direction.y = AxisDirection::AD_Up;
395 t_Direction.z = AxisDirection::AD_Forward;
396 const SDKReturnCode t_InitializeResult = CoreSdk_InitializeCoordinateSystemWithDirection(t_Direction);
397 */
398
399 if (t_CoordinateResult != SDKReturnCode::SDKReturnCode_Success)
400 {
401 spdlog::error("Failed to initialize the Manus Core SDK coordinate system. The value returned was {}.", t_InitializeResult);
402 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
403 }
404
405 return ClientReturnCode::ClientReturnCode_Success;
406}
407
412{
413 const SDKReturnCode t_ShutDownResult = CoreSdk_ShutDown();
414 if (t_ShutDownResult != SDKReturnCode::SDKReturnCode_Success)
415 {
416 spdlog::error("Failed to shutdown the SDK. The value returned was {}.", t_ShutDownResult);
417 return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK;
418 }
419
420 const ClientReturnCode t_IntializeResult = InitializeSDK();
421 if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success)
422 {
423 spdlog::error("Failed to initialize the SDK functionality. The value returned was {}.", t_IntializeResult);
424 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
425 }
426
427 return ClientReturnCode::ClientReturnCode_Success;
428}
429
434{
435 // Register the callback for when manus core is connected to the SDK
436 // it is optional, but helps trigger your client nicely if needed.
437 // see the function OnConnectedCallback for more details
438 const SDKReturnCode t_RegisterConnectCallbackResult = CoreSdk_RegisterCallbackForOnConnect(*OnConnectedCallback);
439 if (t_RegisterConnectCallbackResult != SDKReturnCode::SDKReturnCode_Success)
440 {
441 spdlog::error("Failed to register callback function for after connecting to Manus Core. The value returned was {}.", t_RegisterConnectCallbackResult);
442 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
443 }
444
445 // Register the callback for when manus core is disconnected to the SDK
446 // it is optional, but helps trigger your client nicely if needed.
447 // see OnDisconnectedCallback for more details.
448 const SDKReturnCode t_RegisterDisconnectCallbackResult = CoreSdk_RegisterCallbackForOnDisconnect(*OnDisconnectedCallback);
449 if (t_RegisterDisconnectCallbackResult != SDKReturnCode::SDKReturnCode_Success)
450 {
451 spdlog::error("Failed to register callback function for after disconnecting from Manus Core. The value returned was {}.", t_RegisterDisconnectCallbackResult);
452 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
453 }
454
455 // Register the callback for when manus core is sending Skeleton data
456 // it is optional, but without it you can not see any resulting skeleton data.
457 // see OnSkeletonStreamCallback for more details.
458 const SDKReturnCode t_RegisterSkeletonCallbackResult = CoreSdk_RegisterCallbackForSkeletonStream(*OnSkeletonStreamCallback);
459 if (t_RegisterSkeletonCallbackResult != SDKReturnCode::SDKReturnCode_Success)
460 {
461 spdlog::error("Failed to register callback function for processing skeletal data from Manus Core. The value returned was {}.", t_RegisterSkeletonCallbackResult);
462 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
463 }
464
465 // Register the callback for when manus core is sending landscape data
466 // it is optional, but this allows for a reactive adjustment of device information.
467 const SDKReturnCode t_RegisterLandscapeCallbackResult = CoreSdk_RegisterCallbackForLandscapeStream(*OnLandscapeCallback);
468 if (t_RegisterLandscapeCallbackResult != SDKReturnCode::SDKReturnCode_Success)
469 {
470 spdlog::error("Failed to register callback for landscape from Manus Core. The value returned was {}.", t_RegisterLandscapeCallbackResult);
471 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
472 }
473
474 // Register the callback for when manus core is sending System messages
475 // This is usually not used by client applications unless they want to show errors/events from core.
476 // see OnSystemCallback for more details.
477 const SDKReturnCode t_RegisterSystemCallbackResult = CoreSdk_RegisterCallbackForSystemStream(*OnSystemCallback);
478 if (t_RegisterSystemCallbackResult != SDKReturnCode::SDKReturnCode_Success)
479 {
480 spdlog::error("Failed to register callback function for system feedback from Manus Core. The value returned was {}.", t_RegisterSystemCallbackResult);
481 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
482 }
483
484 // Register the callback for when manus core is sending Ergonomics data
485 // it is optional, but helps trigger your client nicely if needed.
486 // see OnErgonomicsCallback for more details.
487 const SDKReturnCode t_RegisterErgonomicsCallbackResult = CoreSdk_RegisterCallbackForErgonomicsStream(*OnErgonomicsCallback);
488 if (t_RegisterErgonomicsCallbackResult != SDKReturnCode::SDKReturnCode_Success)
489 {
490 spdlog::error("Failed to register callback function for ergonomics data from Manus Core. The value returned was {}.", t_RegisterErgonomicsCallbackResult);
491 return ClientReturnCode::ClientReturnCode_FailedToInitialize;
492 }
493
494 return ClientReturnCode::ClientReturnCode_Success;
495}
496
501{
503 {
504 ClearConsole();
506
507 bool t_BuiltInDebug = false;
508 SDKReturnCode t_Result = CoreSdk_WasDllBuiltInDebugConfiguration(&t_BuiltInDebug);
509 if (t_Result == SDKReturnCode::SDKReturnCode_Success)
510 {
511 if (t_BuiltInDebug)
512 {
513 spdlog::warn("The DLL was built in debug configuration, please rebuild in release before releasing.");
514 }
515 }
516 else
517 {
518 spdlog::error("Failed to check if the DLL was built in Debug Configuration. The value returned was {}.", t_Result);
519 }
520
521 spdlog::info("Press a key to choose a connection type, or [ESC] to exit.");
522 spdlog::info("[L] Local -> Automatically connect to Core running on this computer.");
523 spdlog::info("[H] Host -> Find a host running Core anywhere on the network.");
524 spdlog::info("[G] GRPC -> Try to connect to the preset GRPC address (See settings folder).");
525 }
526
527 if (GetKeyDown('L'))
528 {
529 spdlog::info("Picked local.");
530
532 m_ShouldConnectGRPC = false;
533 m_State = ClientState::ClientState_LookingForHosts;
534 }
535 else if (GetKeyDown('H'))
536 {
537 spdlog::info("Picked host.");
538
540 m_ShouldConnectGRPC = false;
541 m_State = ClientState::ClientState_LookingForHosts;
542 }
543 if (GetKeyDown('G'))
544 {
545 spdlog::info("Picked GRPC.");
546
547 m_ShouldConnectGRPC = true;
548 m_State = ClientState::ClientState_ConnectingToCore;
549 }
550
551 return ClientReturnCode::ClientReturnCode_Success;
552}
553
557{
558 spdlog::info("Looking for hosts...");
559
560 // Underlying function will sleep for m_SecondsToFindHosts to allow servers to reply.
561 const SDKReturnCode t_StartResult = CoreSdk_LookForHosts(m_SecondsToFindHosts, m_ShouldConnectLocally);
562 if (t_StartResult != SDKReturnCode::SDKReturnCode_Success)
563 {
564 spdlog::error("Failed to look for hosts. The error given was {}.", t_StartResult);
565
566 return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
567 }
568
570 const SDKReturnCode t_NumberResult = CoreSdk_GetNumberOfAvailableHostsFound(&m_NumberOfHostsFound);
571 if (t_NumberResult != SDKReturnCode::SDKReturnCode_Success)
572 {
573 spdlog::error("Failed to get the number of available hosts. The error given was {}.", t_NumberResult);
574
575 return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
576 }
577
578 if (m_NumberOfHostsFound == 0)
579 {
580 spdlog::warn("No hosts found.");
581 m_State = ClientState::ClientState_NoHostsFound;
582
583 return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
584 }
585
586 m_AvailableHosts.reset(new ManusHost[m_NumberOfHostsFound]);
587 const SDKReturnCode t_HostsResult = CoreSdk_GetAvailableHostsFound(m_AvailableHosts.get(), m_NumberOfHostsFound);
588 if (t_HostsResult != SDKReturnCode::SDKReturnCode_Success)
589 {
590 spdlog::error("Failed to get the available hosts. The error given was {}.", t_HostsResult);
591
592 return ClientReturnCode::ClientReturnCode_FailedToFindHosts;
593 }
595 {
596 m_State = ClientState::ClientState_ConnectingToCore;
597 return ClientReturnCode::ClientReturnCode_Success;
598 }
599
600 m_State = ClientState::ClientState_PickingHost;
601 return ClientReturnCode::ClientReturnCode_Success;
602}
603
606{
608 {
610 spdlog::info("No hosts were found. Retry?");
611 spdlog::info("[R] retry");
612 spdlog::info("[ESC] exit");
613 }
614
615 if (GetKeyDown('R'))
616 {
617 spdlog::info("Retrying.");
618
619 m_State = ClientState::ClientState_PickingConnectionType;
620 }
621
622 // Note: escape is handled by default below.
623 return ClientReturnCode::ClientReturnCode_Success;
624}
625
628{
630 {
632
633 spdlog::info("[R] retry [ESC] exit");
634 spdlog::info("Pick a host to connect to.");
635 spdlog::info("Found the following hosts:");
636
637 // Note: only 10 hosts are shown, to match the number of number keys, for easy selection.
638 for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++)
639 {
640 spdlog::info(
641 "[{}] hostname \"{}\", IP address \"{}\" Version {}.{}.{}",
642 t_HostNumber,
643 m_AvailableHosts[t_HostNumber].hostName,
644 m_AvailableHosts[t_HostNumber].ipAddress,
645 m_AvailableHosts[t_HostNumber].manusCoreVersion.major,
646 m_AvailableHosts[t_HostNumber].manusCoreVersion.minor,
647 m_AvailableHosts[t_HostNumber].manusCoreVersion.patch);
648 }
649 }
650
651 for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++)
652 {
653 if (GetKeyDown('0' + t_HostNumber))
654 {
655 spdlog::info("Selected host {}.", t_HostNumber);
656
657 m_HostToConnectTo = t_HostNumber;
658 m_State = ClientState::ClientState_ConnectingToCore;
659
660 break;
661 }
662 }
663
664 if (GetKeyDown('R'))
665 {
666 spdlog::info("Retrying.");
667
668 m_State = ClientState::ClientState_PickingConnectionType;
669 }
670
671 return ClientReturnCode::ClientReturnCode_Success;
672}
673
676{
677 SDKReturnCode t_ConnectResult = SDKReturnCode::SDKReturnCode_Error;
678
680 {
681 t_ConnectResult = CoreSdk_ConnectGRPC();
682 }
683 else
684 {
686 t_ConnectResult = CoreSdk_ConnectToHost(m_AvailableHosts[m_HostToConnectTo]);
687 }
688
689 if (t_ConnectResult == SDKReturnCode::SDKReturnCode_NotConnected)
690 {
691 m_State = ClientState::ClientState_NoHostsFound;
692
693 return ClientReturnCode::ClientReturnCode_Success; // Differentating between error and no connect
694 }
695 if (t_ConnectResult != SDKReturnCode::SDKReturnCode_Success)
696 {
697 spdlog::error("Failed to connect to Core. The error given was {}.", t_ConnectResult);
698
699 return ClientReturnCode::ClientReturnCode_FailedToConnect;
700 }
701
702 m_State = ClientState::ClientState_DisplayingData;
703
704 // Note: a log message from somewhere in the SDK during the connection process can cause text
705 // to permanently turn green after this step. Adding a sleep here of 2+ seconds "fixes" the
706 // issue. It seems to be caused by a threading issue somewhere, resulting in a log call being
707 // interrupted while it is printing the green [info] text. The log output then gets stuck in
708 // green mode.
709
710 return ClientReturnCode::ClientReturnCode_Success;
711}
712
713
718{
720
721 m_SkeletonMutex.lock();
722 if (m_NextSkeleton != nullptr)
723 {
724 if (m_Skeleton != nullptr)delete m_Skeleton;
726 m_NextSkeleton = nullptr;
727 }
728 m_SkeletonMutex.unlock();
729
730 m_LandscapeMutex.lock();
731 if (m_NewLandscape != nullptr)
732 {
733 if (m_Landscape != nullptr)
734 {
735 delete m_Landscape;
736 }
738 m_NewLandscape = nullptr;
739 }
740 m_LandscapeMutex.unlock();
741
744 if (m_Landscape == nullptr)return ClientReturnCode::ClientReturnCode_Success;
745 for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++)
746 {
747 if (m_FirstLeftGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Left)
748 {
749 m_FirstLeftGloveID = m_Landscape->gloveDevices.gloves[i].id;
750 continue;
751 }
752 if (m_FirstRightGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Right)
753 {
754 m_FirstRightGloveID = m_Landscape->gloveDevices.gloves[i].id;
755 continue;
756 }
757 }
758
759 return ClientReturnCode::ClientReturnCode_Success;
760}
761
762
767{
768 SPDLOG_INFO("<<Main Menu>> [ESC] quit");
769 SPDLOG_INFO("[G] Go To Gloves & Dongle Menu");
770 SPDLOG_INFO("[S] Go To Skeleton Menu");
771 SPDLOG_INFO("[X] Go To Temporary Skeleton Menu");
772 SPDLOG_INFO("[T] Go To Tracker Menu");
773 SPDLOG_INFO("[D] Go To Landscape Time Info");
774
776
782
784
785 return ClientReturnCode::ClientReturnCode_Success;
786}
787
791{
792 SPDLOG_INFO("[Q] Back <<Gloves & Dongles>> [ESC] quit");
793 SPDLOG_INFO("Haptic keys: left:([1]-[5] = pinky-thumb.) right:([6]-[0] = thumb-pinky.)");
794
796
798
800
804
805 return ClientReturnCode::ClientReturnCode_Success;
806}
807
809{
810 SPDLOG_INFO("[Q] Back <<Skeleton>> [ESC] quit");
811 SPDLOG_INFO("<Skeleton>[N] Load Skeleton [M] Unload Skeleton");
812 SPDLOG_INFO("<Skeleton Haptics> left:([1]-[5] = pinky-thumb) right:([6]-[0] = thumb-pinky)");
813
815
817
820
824
825 return ClientReturnCode::ClientReturnCode_Success;
826}
827
829{
830 SPDLOG_INFO("[Q] Back <<Gloves & Dongles>> [ESC] quit");
831 SPDLOG_INFO("[O] Toggle Test Tracker [G] Toggle per user tracker display");
832
834
836
838
841
842 return ClientReturnCode::ClientReturnCode_Success;
843}
844
846{
847 SPDLOG_INFO("[Q] Back <<Temporary Skeleton>> [ESC] quit");
848 SPDLOG_INFO("<Skeleton>[A] Auto allocate chains and load skeleton");
849 SPDLOG_INFO("<Skeleton>[B] Build Temporary Skeleton [C] Clear Temporary Skeleton [D] Clear All Temporary Skeletons For The Current Session");
850 SPDLOG_INFO("<Skeleton>[E] Save Temporary Skeleton To File, [F] Get Temporary Skeleton From File");
851
853
855
857
862
863 return ClientReturnCode::ClientReturnCode_Success;
864}
865
867{
868 SPDLOG_INFO("[Q] Back <<Landscape Time Data>> [ESC] quit");
869
871
873
875
877
879
880 return ClientReturnCode::ClientReturnCode_Success;
881}
882
886{
887 if (m_Host == nullptr) { return ClientReturnCode::ClientReturnCode_FailedToConnect; }
888
890
891 auto t_Duration = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - m_TimeSinceLastDisconnect).count();
892 spdlog::info("The SDK lost connection with Manus Core {} seconds ago.", t_Duration);
893 spdlog::info("[P] Pick a new host. [ESC] exit");
894
896
898 {
899 spdlog::info("Automatically trying to reconnect to GRPC address.");
900
901 ClientReturnCode t_ReconnectResult = ReconnectingToCore();
902 if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
903 {
904 return t_ReconnectResult;
905 }
906 }
907 else if (m_ShouldConnectLocally)
908 {
909 spdlog::info("Automatically trying to reconnect to local host.");
910
911 ClientReturnCode t_ReconnectResult = ReconnectingToCore();
912 if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
913 {
914 return t_ReconnectResult;
915 }
916 }
917 else
918 {
919 spdlog::info("[R] Try to reconnect to the last host {} at {}.", m_Host->hostName, m_Host->ipAddress);
920 if (GetKeyDown('R'))
921 {
922 spdlog::info("Reconnecting");
923
925 if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect)
926 {
927 return t_ReconnectResult;
928 }
929 }
930 }
931
933
934
935 if (GetKeyDown('P'))
936 {
937 spdlog::info("Picking new host.");
938
939 // Restarting and initializing CoreConnection to make sure a new connection can be set up
940 const ClientReturnCode t_RestartResult = RestartSDK();
941 if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success)
942 {
943 spdlog::error("Failed to Restart CoreConnection.");
944 return ClientReturnCode::ClientReturnCode_FailedToRestart;
945 }
946
947 m_State = ClientState::ClientState_PickingConnectionType;
948 }
949
950 return ClientReturnCode::ClientReturnCode_Success;
951}
952
954ClientReturnCode SDKClient::ReconnectingToCore(int32_t p_ReconnectionTime, int32_t p_ReconnectionAttempts)
955{
956 if (p_ReconnectionTime <= 0) { p_ReconnectionTime = std::numeric_limits<int32_t>::max(); }
957 if (p_ReconnectionAttempts <= 0) { p_ReconnectionAttempts = std::numeric_limits<int32_t>::max(); }
958
959 // Restarting and initializing CoreConnection to make sure a new connection can be set up
960 const ClientReturnCode t_RestartResult = RestartSDK();
961 if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success)
962 {
963 spdlog::error("Failed to Restart CoreConnection.");
964 return ClientReturnCode::ClientReturnCode_FailedToRestart;
965 }
966
967 std::chrono::high_resolution_clock::time_point t_Start = std::chrono::high_resolution_clock::now();
968 int t_Attempt = 0;
969 while ((p_ReconnectionAttempts > 0) && (p_ReconnectionTime > 0))
970 {
971 spdlog::info("Trying to reconnect to {} at {}. Attempt {}.", m_Host->hostName, m_Host->ipAddress, t_Attempt);
972 spdlog::info("Attempts remaining: {}. Seconds before time out: {}.", p_ReconnectionAttempts, p_ReconnectionTime);
974 {
975 SDKReturnCode t_ConnectionResult = CoreSdk_ConnectGRPC();
976 if (t_ConnectionResult == SDKReturnCode::SDKReturnCode_Success)
977 {
978 spdlog::info("Reconnected to ManusCore.");
979 return ClientReturnCode::ClientReturnCode_Success;
980 }
981 }
982 else if (m_ShouldConnectLocally)
983 {
984 ClientReturnCode t_ConnectionResult = LookingForHosts();
985 if (t_ConnectionResult == ClientReturnCode::ClientReturnCode_Success)
986 {
987 spdlog::info("Reconnected to ManusCore.");
988 return ClientReturnCode::ClientReturnCode_Success;
989 }
990 }
991 else
992 {
993 SDKReturnCode t_ConnectionResult = CoreSdk_ConnectToHost(*m_Host.get());
994 if (t_ConnectionResult == SDKReturnCode::SDKReturnCode_Success)
995 {
996 spdlog::info("Reconnected to ManusCore.");
997 return ClientReturnCode::ClientReturnCode_Success;
998 }
999 }
1000 std::this_thread::sleep_for(std::chrono::milliseconds(m_SleepBetweenReconnectingAttemptsInMs));
1001 p_ReconnectionTime -= static_cast<int32_t>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - t_Start).count());
1002 --p_ReconnectionAttempts;
1003 ++t_Attempt;
1004 }
1005
1006 spdlog::info("Failed to reconnect to ManusCore.");
1007 m_State = ClientState::ClientState_Disconnected;
1008 return ClientReturnCode::ClientReturnCode_FailedToConnect;
1009}
1010
1011
1014void SDKClient::PrintHandErgoData(ErgonomicsData& p_ErgoData, bool p_Left)
1015{
1016 const std::string t_FingerNames[NUM_FINGERS_ON_HAND] = { "[thumb] ", "[index] ", "[middle]", "[ring] ", "[pinky] " };
1017 const std::string t_FingerJointNames[NUM_FINGERS_ON_HAND] = { "mcp", "pip", "dip" };
1018 const std::string t_ThumbJointNames[NUM_FINGERS_ON_HAND] = { "cmc", "mcp", "ip " };
1019
1020 int t_DataOffset = 0;
1021 if (!p_Left)t_DataOffset = 20;
1022
1023 const std::string* t_JointNames = t_ThumbJointNames;
1024 for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
1025 {
1026 spdlog::info("{} {} spread: {:>6}, {} stretch: {:>6}, {} stretch: {:>6}, {} stretch: {:>6} ",
1027 t_FingerNames[t_FingerNumber], // Name of the finger.
1028 t_JointNames[0],
1029 RoundFloatValue(p_ErgoData.data[t_DataOffset], 2),
1030 t_JointNames[0],
1031 RoundFloatValue(p_ErgoData.data[t_DataOffset + 1], 2),
1032 t_JointNames[1],
1033 RoundFloatValue(p_ErgoData.data[t_DataOffset + 2], 2),
1034 t_JointNames[2],
1035 RoundFloatValue(p_ErgoData.data[t_DataOffset + 3], 2));
1036 t_JointNames = t_FingerJointNames;
1037 t_DataOffset += 4;
1038 }
1039}
1040
1043{
1044 // for testing purposes we only look at the first 2 gloves available
1045 spdlog::info(" -- Ergo Timestamp {:02d}:{:02d}:{:02d}.{:03d} ~ {:02d}/{:02d}/{:d}(D/M/Y)",
1048 spdlog::info(" -- Left Glove -- 0x{:X} - Angles in degrees", m_FirstLeftGloveID);
1050 {
1052 }
1053 else
1054 {
1055 spdlog::info(" ...No Data...");
1056 }
1057 spdlog::info(" -- Right Glove -- 0x{:X} - Angles in degrees", m_FirstRightGloveID);
1059 {
1061 }
1062 else
1063 {
1064 spdlog::info(" ...No Data...");
1065 }
1066
1068}
1069
1070std::string ConvertDeviceClassTypeToString(DeviceClassType p_Type)
1071{
1072 switch (p_Type)
1073 {
1074 case DeviceClassType_Dongle:
1075 return "Dongle";
1076 case DeviceClassType_Glove:
1077 return "Glove";
1078 case DeviceClassType_Glongle:
1079 return "Glongle (Glove Dongle)";
1080 default:
1081 return "Unknown";
1082 }
1083}
1084
1085std::string ConvertDeviceFamilyTypeToString(DeviceFamilyType p_Type)
1086{
1087 switch (p_Type)
1088 {
1089 case DeviceFamilyType_Prime1:
1090 return "Prime 1";
1091 case DeviceFamilyType_Prime2:
1092 return "Prime 2";
1093 case DeviceFamilyType_PrimeX:
1094 return "Prime X";
1095 case DeviceFamilyType_Quantum:
1096 return "Quantum";
1097 default:
1098 return "Unknown";
1099 }
1100}
1101
1104{
1105 // get a dongle id
1106 uint32_t t_DongleCount = 0;
1107 if (CoreSdk_GetNumberOfDongles(&t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
1108 if (t_DongleCount == 0) return; // we got no gloves to work on anyway!
1109
1110 uint32_t* t_DongleIds = new uint32_t[t_DongleCount]();
1111 if (CoreSdk_GetDongleIds(t_DongleIds, t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
1112
1113 DongleLandscapeData t_DongleData;
1114
1115 for (uint32_t i = 0; i < t_DongleCount; i++)
1116 {
1117 SDKReturnCode t_Result = CoreSdk_GetDataForDongle(t_DongleIds[i], &t_DongleData);
1118 spdlog::info(" -- Dongle -- 0x{:X}", t_DongleData.id);
1119 if (t_Result == SDKReturnCode::SDKReturnCode_Success)
1120 {
1121 spdlog::info(" Type: {} - {}",
1122 ConvertDeviceClassTypeToString(t_DongleData.classType),
1123 ConvertDeviceFamilyTypeToString(t_DongleData.familyType));
1124 spdlog::info(" License: {}", t_DongleData.licenseType);
1125 }
1126 else
1127 {
1128 spdlog::info(" ...No Data...");
1129 }
1131 }
1132}
1133
1136{
1137 m_SystemMessageMutex.lock();
1138 spdlog::info("Received System data:{} / code:{}", m_SystemMessage, m_SystemMessageCode);
1139 m_SystemMessageMutex.unlock();
1141}
1142
1147{
1148 if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0)
1149 {
1150 return;
1151 }
1152
1153 spdlog::info("Received Skeleton data. skeletons:{} first skeleton id:{}", m_Skeleton->skeletons.size(), m_Skeleton->skeletons[0].info.id);
1154
1156}
1157
1162{
1163 spdlog::info("Tracker test active: {}.", m_TrackerTest); //To show that test tracker is being sent to core
1164 spdlog::info("Per user tracker display: {}.", m_TrackerDataDisplayPerUser);
1165
1167
1169 {
1172 }
1173 else
1174 {
1177 }
1178}
1179
1183{
1184 uint32_t t_NumberOfAvailabletrackers = 0;
1185 SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackers(&t_NumberOfAvailabletrackers);
1186 if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
1187 {
1188 spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
1189 return;
1190 }
1191
1192 spdlog::info("received available trackers :{} ", t_NumberOfAvailabletrackers);
1193
1194 if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do.
1195 TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers];
1196 t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackers(t_TrackerId, t_NumberOfAvailabletrackers);
1197 if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
1198 {
1199 spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
1200 return;
1201 }
1202}
1203
1206{
1207 uint32_t t_NumberOfAvailableUsers = 0;
1208 SDKReturnCode t_UserResult = CoreSdk_GetNumberOfAvailableUsers(&t_NumberOfAvailableUsers);
1209 if (t_UserResult != SDKReturnCode::SDKReturnCode_Success)
1210 {
1211 spdlog::error("Failed to get user count. The error given was {}.", t_UserResult);
1212 return;
1213 }
1214 if (t_NumberOfAvailableUsers == 0) return; // nothing to get yet
1215
1216
1217 for (uint32_t i = 0; i < t_NumberOfAvailableUsers; i++)
1218 {
1219 uint32_t t_NumberOfAvailabletrackers = 0;
1220 SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackersForUserIndex(&t_NumberOfAvailabletrackers, i);
1221 if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
1222 {
1223 spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
1224 return;
1225 }
1226
1227 if (t_NumberOfAvailabletrackers == 0) continue;
1228
1229 spdlog::info("received available trackers for user index[{}] :{} ", i, t_NumberOfAvailabletrackers);
1230
1231 if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do.
1232 TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers];
1233 t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackersForUserIndex(t_TrackerId, i, t_NumberOfAvailabletrackers);
1234 if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success)
1235 {
1236 spdlog::error("Failed to get tracker data. The error given was {}.", t_TrackerResult);
1237 return;
1238 }
1239 }
1240}
1241
1242std::string GetFPSEnumName(TimecodeFPS p_FPS)
1243{
1244 switch (p_FPS)
1245 {
1246 case TimecodeFPS::TimecodeFPS_23_976:
1247 return "23.976 FPS (24 dropframe)";
1248 case TimecodeFPS::TimecodeFPS_24:
1249 return "24 FPS";
1250 case TimecodeFPS::TimecodeFPS_25:
1251 return "25 FPS";
1252 case TimecodeFPS::TimecodeFPS_29_97:
1253 return "29.97 FPS (30 dropframe)";
1254 case TimecodeFPS::TimecodeFPS_30:
1255 return "30 FPS";
1256 case TimecodeFPS::TimecodeFPS_50:
1257 return "50 FPS";
1258 case TimecodeFPS::TimecodeFPS_59_94:
1259 return "59.94 FPS (60 dropframe)";
1260 case TimecodeFPS::TimecodeFPS_60:
1261 return "60 FPS";
1262 default:
1263 return "Undefined FPS";
1264 }
1265}
1266
1268{
1269 spdlog::info("Total count of Interfaces: {}", m_Landscape->time.interfaceCount);
1270 spdlog::info("Current Interface: {} {} at index {}", m_Landscape->time.currentInterface.name, m_Landscape->time.currentInterface.api, m_Landscape->time.currentInterface.index);
1271
1272 spdlog::info("FPS: {}", GetFPSEnumName(m_Landscape->time.fps));
1273 spdlog::info("Fake signal: {} | Sync Pulse: {} | Sync Status: {}", m_Landscape->time.fakeTimecode, m_Landscape->time.useSyncPulse, m_Landscape->time.syncStatus);
1274 spdlog::info("Device keep alive: {} | Timecode Status: {}", m_Landscape->time.deviceKeepAlive, m_Landscape->time.timecodeStatus);
1275
1277}
1278
1281{
1282 switch (m_ChainType)
1283 {
1284 case ChainType::ChainType_FingerIndex:
1285 {
1286 spdlog::info("received Skeleton chain type: ChainType_FingerIndex");
1287 break;
1288 }
1289 case ChainType::ChainType_FingerMiddle:
1290 {
1291 spdlog::info("received Skeleton chain type: ChainType_FingerMiddle");
1292 break;
1293 }
1294 case ChainType::ChainType_FingerPinky:
1295 {
1296 spdlog::info("received Skeleton chain type: ChainType_FingerPinky");
1297 break;
1298 }
1299 case ChainType::ChainType_FingerRing:
1300 {
1301 spdlog::info("received Skeleton chain type: ChainType_FingerRing");
1302 break;
1303 }
1304 case ChainType::ChainType_FingerThumb:
1305 {
1306 spdlog::info("received Skeleton chain type: ChainType_FingerThumb");
1307 break;
1308 }
1309 case ChainType::ChainType_Hand:
1310 {
1311 spdlog::info("received Skeleton chain type: ChainType_Hand");
1312 break;
1313 }
1314 case ChainType::ChainType_Head:
1315 {
1316 spdlog::info("received Skeleton chain type: ChainType_Head");
1317 break;
1318 }
1319 case ChainType::ChainType_Leg:
1320 {
1321 spdlog::info("received Skeleton chain type: ChainType_Leg");
1322 break;
1323 }
1324 case ChainType::ChainType_Neck:
1325 {
1326 spdlog::info("received Skeleton chain type: ChainType_Neck");
1327 break;
1328 }
1329 case ChainType::ChainType_Pelvis:
1330 {
1331 spdlog::info("received Skeleton chain type: ChainType_Pelvis");
1332 break;
1333 }
1334 case ChainType::ChainType_Shoulder:
1335 {
1336 spdlog::info("received Skeleton chain type: ChainType_Shoulder");
1337 break;
1338 }
1339 case ChainType::ChainType_Spine:
1340 {
1341 spdlog::info("received Skeleton chain type: ChainType_Spine");
1342 break;
1343 }
1344 case ChainType::ChainType_Arm:
1345 {
1346 spdlog::info("received Skeleton chain type: ChainType_Arm");
1347 break;
1348 }
1349 case ChainType::ChainType_Invalid:
1350 default:
1351 {
1352 spdlog::info("received Skeleton chain type: ChainType_Invalid");
1353 break;
1354 }
1355 }
1357}
1358
1363{
1364 // if a temporary skeleton associated to the current session has been modified we can get it and, potentially, load it
1365 if (m_ModifiedSkeletonIndex != UINT_MAX)
1366 {
1367 // get the temporary skeleton
1368 uint32_t t_SessionId = m_SessionId;
1369 SDKReturnCode t_Res = CoreSdk_GetTemporarySkeleton(m_ModifiedSkeletonIndex, t_SessionId);
1370 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1371 {
1372 spdlog::error("Failed to get temporary skeleton. The error given was {}.", t_Res);
1373 return;
1374 }
1375
1376 // At this point if we are satisfied with the modifications to the skeleton we can load it into Core.
1377 // Remember to always call function CoreSdk_ClearTemporarySkeleton after loading a temporary skeleton,
1378 // this will keep the temporary skeleton list in sync between Core and the SDK.
1379
1380 //uint32_t t_ID = 0;
1381 //SDKReturnCode t_Res = CoreSdk_LoadSkeleton(m_ModifiedSkeletonIndex, &t_ID);
1382 //if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1383 //{
1384 // spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
1385 // return;
1386 //}
1387 //if (t_ID == 0)
1388 //{
1389 // spdlog::error("Failed to give skeleton an ID.");
1390 //}
1391 //m_LoadedSkeletons.push_back(t_ID);
1392 //t_Res = CoreSdk_ClearTemporarySkeleton(m_ModifiedSkeletonIndex, m_SessionId);
1393 //if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1394 //{
1395 // spdlog::error("Failed to clear temporary skeleton. The error given was {}.", t_Res);
1396 // return;
1397 //}
1398 m_ModifiedSkeletonIndex = UINT_MAX;
1399 }
1400}
1404{
1405 spdlog::info("Number of temporary skeletons in the SDK: {} ", m_TemporarySkeletons.size());
1406
1407 static uint32_t t_TotalNumberOfTemporarySkeletonsInCore = 0;
1408 auto t_TimeSinceLastTemporarySkeletonUpdate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_LastTemporarySkeletonUpdate).count();
1409 if (t_TimeSinceLastTemporarySkeletonUpdate < static_cast<unsigned int>(MILLISECONDS_BETWEEN_TEMPORARY_SKELETONS_UPDATE))
1410 {
1411 spdlog::info("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore);
1412 return;
1413 }
1414 TemporarySkeletonCountForAllSessions t_TemporarySkeletonCountForAllSessions;
1415 SDKReturnCode t_Res = CoreSdk_GetTemporarySkeletonCountForAllSessions(&t_TemporarySkeletonCountForAllSessions);
1416 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1417 {
1418 spdlog::error("Failed to get all temporary skeletons. The error given was {}.", t_Res);
1419 return;
1420 }
1421
1422 t_TotalNumberOfTemporarySkeletonsInCore = 0;
1423 for (uint32_t i = 0; i < t_TemporarySkeletonCountForAllSessions.sessionsCount; i++)
1424 {
1425 t_TotalNumberOfTemporarySkeletonsInCore += t_TemporarySkeletonCountForAllSessions.temporarySkeletonCountForSessions[i].skeletonCount;
1426 }
1427
1428 // print total number of temporary skeletons:
1429 spdlog::info("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore);
1430 m_LastTemporarySkeletonUpdate = std::chrono::high_resolution_clock::now();
1431}
1432
1437{
1438 if (m_FirstLeftGloveID == 0 && m_FirstRightGloveID == 0) return; // we got no gloves to work on anyway!
1439
1440 // get a dongle id
1441 uint32_t t_DongleId = 0;
1442 uint32_t t_GloveIds[2] = { 0,0 };
1443 uint32_t t_DongleCount = 0;
1444 if (CoreSdk_GetNumberOfDongles(&t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
1445 if (t_DongleCount == 0) return; // we got no gloves to work on anyway!
1446
1447 uint32_t* t_DongleIds = new uint32_t[t_DongleCount]();
1448 if (CoreSdk_GetDongleIds(t_DongleIds, t_DongleCount) != SDKReturnCode::SDKReturnCode_Success) return;
1449
1450 for (uint32_t i = 0; i < t_DongleCount; i++)
1451 {
1452 // now lets see if it has gloves. otherwise its still not useful
1453 CoreSdk_GetGlovesForDongle(t_DongleIds[i], &t_GloveIds[0], &t_GloveIds[1]);
1454 if (!t_GloveIds[0] && !t_GloveIds[1]) continue;
1455 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.
1456 break;
1457 }
1458 if (t_DongleId == 0) return; // still no valid data. (though this is very unlikely)
1459
1461 const int t_LeftHand = 0;
1462 const int t_RightHand = 1;
1463
1464 // 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.
1465 t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb
1466 t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index
1467 t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle
1468 t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring
1469 t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky
1470 t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb
1471 t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index
1472 t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle
1473 t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring
1474 t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky
1475
1476 // Note: this timer is apparently not very accurate.
1477 // It is good enough for this test client, but should probably be replaced for other uses.
1478 static std::chrono::high_resolution_clock::time_point s_TimeOfLastHapticsCommandSent;
1479 const std::chrono::high_resolution_clock::time_point s_Now = std::chrono::high_resolution_clock::now();
1480 const long long s_MillisecondsSinceLastHapticCommand = std::chrono::duration_cast<std::chrono::milliseconds>(s_Now - s_TimeOfLastHapticsCommandSent).count();
1481
1482 if (s_MillisecondsSinceLastHapticCommand < static_cast<unsigned int>(MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS))
1483 {
1484 return;
1485 }
1486
1487 const Side s_Hands[NUMBER_OF_HANDS_SUPPORTED] = { Side::Side_Left, Side::Side_Right };
1488 const float s_FullPower = 1.0f;
1489
1490 for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
1491 {
1492 if (t_GloveIds[t_HandNumber] == 0) continue; // no glove available. skip.
1493
1494 GloveLandscapeData t_Glove;
1495 if (CoreSdk_GetDataForGlove_UsingGloveId(t_GloveIds[t_HandNumber], &t_Glove) != SDKReturnCode::SDKReturnCode_Success)
1496 {
1497 continue;
1498 }
1499
1500 if (t_Glove.familyType != DeviceFamilyType::DeviceFamilyType_Prime1)
1501 {
1502 continue;
1503 }
1504 }
1505
1506 // This is an example that shows how to send the haptics commands based on dongle id:
1507 uint32_t* t_HapticsDongles = new uint32_t[MAX_NUMBER_OF_DONGLES];
1508 for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
1509 {
1510 uint32_t t_NumberOfHapticsDongles = 0;
1511 if ((CoreSdk_GetNumberOfHapticsDongles(&t_NumberOfHapticsDongles) != SDKReturnCode::SDKReturnCode_Success) ||
1512 (t_NumberOfHapticsDongles == 0))
1513 {
1514 continue;
1515 }
1516
1517 if (CoreSdk_GetHapticsDongleIds(t_HapticsDongles, t_NumberOfHapticsDongles) != SDKReturnCode::SDKReturnCode_Success)
1518 {
1519 continue;
1520 }
1521
1522 float t_HapticsPowers[NUM_FINGERS_ON_HAND]{};
1523 for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
1524 {
1525 t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f;
1526 }
1527
1528 GloveLandscapeData t_GloveLandscapeData;
1529 if (CoreSdk_GetDataForGlove_UsingGloveId(t_GloveIds[t_HandNumber], &t_GloveLandscapeData) != SDKReturnCode::SDKReturnCode_Success)
1530 {
1531 continue;
1532 }
1533 if (!t_GloveLandscapeData.isHaptics) // if the glove is not Haptics do not vibrate.
1534 {
1535 continue;
1536 }
1537
1538 CoreSdk_VibrateFingers(t_HapticsDongles[0], s_Hands[t_HandNumber], t_HapticsPowers);
1539 }
1540 delete[] t_HapticsDongles;
1541}
1542
1545{
1546 if (GetKeyDown('N'))
1547 {
1549 }
1550 if (GetKeyDown('M'))
1551 {
1553 }
1554}
1555
1560{
1561 if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0)
1562 {
1563 return;
1564 }
1565
1567 const int t_LeftHand = 0;
1568 const int t_RightHand = 1;
1569
1570 // 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.
1571 t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb
1572 t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index
1573 t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle
1574 t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring
1575 t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky
1576 t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb
1577 t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index
1578 t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle
1579 t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring
1580 t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky
1581
1582 // Note: this timer is apparently not very accurate.
1583 // It is good enough for this test client, but should probably be replaced for other uses.
1584 static std::chrono::high_resolution_clock::time_point s_TimeOfLastHapticsCommandSent;
1585 const std::chrono::high_resolution_clock::time_point s_Now = std::chrono::high_resolution_clock::now();
1586 const long long s_MillisecondsSinceLastHapticCommand = std::chrono::duration_cast<std::chrono::milliseconds>(s_Now - s_TimeOfLastHapticsCommandSent).count();
1587
1588 if (s_MillisecondsSinceLastHapticCommand < static_cast<unsigned int>(MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS))
1589 {
1590 return;
1591 }
1592
1593 const Side s_Hands[NUMBER_OF_HANDS_SUPPORTED] = { Side::Side_Left, Side::Side_Right };
1594 const float s_FullPower = 1.0f;
1595
1596 // The preferred way of sending the haptics commands is based on skeleton id
1597
1598 for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++)
1599 {
1600 float t_HapticsPowers[NUM_FINGERS_ON_HAND]{};
1601 for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++)
1602 {
1603 t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f;
1604 }
1605 bool t_IsHaptics = false;
1606 if (CoreSdk_DoesSkeletonGloveSupportHaptics(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], &t_IsHaptics) != SDKReturnCode::SDKReturnCode_Success)
1607 {
1608 continue;
1609 }
1610 if (!t_IsHaptics)
1611 {
1612 continue;
1613 }
1614 CoreSdk_VibrateFingersForSkeleton(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], t_HapticsPowers);
1615 }
1616}
1617
1620{
1621 if (GetKeyDown('A'))
1622 {
1624 }
1625 if (GetKeyDown('B'))
1626 {
1628 }
1629 if (GetKeyDown('C'))
1630 {
1632 }
1633 if (GetKeyDown('D'))
1634 {
1636 }
1637 if (GetKeyDown('E'))
1638 {
1640 }
1641 if (GetKeyDown('F'))
1642 {
1644 }
1645}
1646
1649{
1650 if (GetKeyDown('O'))
1651 {
1653 }
1654
1655 if (GetKeyDown('G'))
1656 {
1658 }
1659
1660 if (m_TrackerTest)
1661 {
1662 m_TrackerOffset += 0.0005f;
1663 if (m_TrackerOffset >= 10.0f)
1664 {
1665 m_TrackerOffset = 0.0f;
1666 }
1667
1668 TrackerId t_TrackerId;
1669 CopyString(t_TrackerId.id, sizeof(t_TrackerId.id), std::string("Test Tracker"));
1670 TrackerData t_TrackerData = {};
1671 t_TrackerData.isHmd = false;
1672 t_TrackerData.trackerId = t_TrackerId;
1673 t_TrackerData.trackerType = TrackerType::TrackerType_Unknown;
1674 t_TrackerData.position = { 0.0f, m_TrackerOffset, 0.0f };
1675 t_TrackerData.rotation = { 1.0f, 0.0f, 0.0f, 0.0f };
1676 t_TrackerData.quality = TrackerQuality::TrackingQuality_Trackable;
1677 TrackerData t_TrackerDatas[MAX_NUMBER_OF_TRACKERS];
1678 t_TrackerDatas[0] = t_TrackerData;
1679
1680 const SDKReturnCode t_TrackerSend = CoreSdk_SendDataForTrackers(t_TrackerDatas, 1);
1681 if (t_TrackerSend != SDKReturnCode::SDKReturnCode_Success)
1682 {
1683 spdlog::error("Failed to send tracker data. The error given was {}.", t_TrackerSend);
1684 return;
1685 }
1686 }
1687}
1688
1702NodeSetup SDKClient::CreateNodeSetup(uint32_t p_Id, uint32_t p_ParentId, float p_PosX, float p_PosY, float p_PosZ, std::string p_Name)
1703{
1704 NodeSetup t_Node;
1705 NodeSetup_Init(&t_Node);
1706 t_Node.id = p_Id; //Every ID needs to be unique per node in a skeleton.
1707 CopyString(t_Node.name, sizeof(t_Node.name), p_Name);
1708 t_Node.type = NodeType::NodeType_Joint;
1709 //Every node should have a parent unless it is the Root node.
1710 t_Node.parentID = p_ParentId; //Setting the node ID to its own ID ensures it has no parent.
1711 t_Node.settings.usedSettings = NodeSettingsFlag::NodeSettingsFlag_None;
1712
1713 t_Node.transform.position.x = p_PosX;
1714 t_Node.transform.position.y = p_PosY;
1715 t_Node.transform.position.z = p_PosZ;
1716 return t_Node;
1717}
1718
1719ManusVec3 SDKClient::CreateManusVec3(float p_X, float p_Y, float p_Z)
1720{
1721 ManusVec3 t_Vec;
1722 t_Vec.x = p_X;
1723 t_Vec.y = p_Y;
1724 t_Vec.z = p_Z;
1725 return t_Vec;
1726}
1727
1734bool SDKClient::SetupHandNodes(uint32_t p_SklIndex)
1735{
1736 // Define number of fingers per hand and number of joints per finger
1737 const uint32_t t_NumFingers = 5;
1738 const uint32_t t_NumJoints = 4;
1739
1740 // Create an array with the initial position of each hand node.
1741 // Note, these values are just an example of node positions and refer to the hand laying on a flat surface.
1742 ManusVec3 t_Fingers[t_NumFingers * t_NumJoints] = {
1743 CreateManusVec3(0.024950f, 0.000000f, 0.025320f), //Thumb CMC joint
1744 CreateManusVec3(0.000000f, 0.000000f, 0.032742f), //Thumb MCP joint
1745 CreateManusVec3(0.000000f, 0.000000f, 0.028739f), //Thumb IP joint
1746 CreateManusVec3(0.000000f, 0.000000f, 0.028739f), //Thumb Tip joint
1747
1748 //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.
1749 CreateManusVec3(0.011181f, 0.000000f, 0.052904f), //Index MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.052904f)
1750 CreateManusVec3(0.000000f, 0.000000f, 0.038257f), //Index PIP joint
1751 CreateManusVec3(0.000000f, 0.000000f, 0.020884f), //Index DIP joint
1752 CreateManusVec3(0.000000f, 0.000000f, 0.018759f), //Index Tip joint
1753
1754 //CreateManusVec3(0.000000f, 0.033452f, 0.000000f), //Middle CMC joint
1755 CreateManusVec3(0.000000f, 0.000000f, 0.051287f), //Middle MCP joint
1756 CreateManusVec3(0.000000f, 0.000000f, 0.041861f), //Middle PIP joint
1757 CreateManusVec3(0.000000f, 0.000000f, 0.024766f), //Middle DIP joint
1758 CreateManusVec3(0.000000f, 0.000000f, 0.019683f), //Middle Tip joint
1759
1760 //CreateManusVec3(-0.011274f, 0.031696f, 0.000000f), //Ring CMC joint
1761 CreateManusVec3(-0.011274f, 0.000000f, 0.049802f), //Ring MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.049802f),
1762 CreateManusVec3(0.000000f, 0.000000f, 0.039736f), //Ring PIP joint
1763 CreateManusVec3(0.000000f, 0.000000f, 0.023564f), //Ring DIP joint
1764 CreateManusVec3(0.000000f, 0.000000f, 0.019868f), //Ring Tip joint
1765
1766 //CreateManusVec3(-0.020145f, 0.027538f, 0.000000f), //Pinky CMC joint
1767 CreateManusVec3(-0.020145f, 0.000000f, 0.047309f), //Pinky MCP joint, if metacarpal is present: CreateManusVec3(0.000000f, 0.000000f, 0.047309f),
1768 CreateManusVec3(0.000000f, 0.000000f, 0.033175f), //Pinky PIP joint
1769 CreateManusVec3(0.000000f, 0.000000f, 0.018020f), //Pinky DIP joint
1770 CreateManusVec3(0.000000f, 0.000000f, 0.019129f), //Pinky Tip joint
1771 };
1772
1773 // skeleton entry is already done. just the nodes now.
1774 // setup a very simple node hierarchy for fingers
1775 // first setup the root node
1776 //
1777 // root, This node has ID 0 and parent ID 0, to indicate it has no parent.
1778 SDKReturnCode t_Res = CoreSdk_AddNodeToSkeletonSetup(p_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "Hand"));
1779 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1780 {
1781 spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
1782 return false;
1783 }
1784
1785 // then loop for 5 fingers
1786 int t_FingerId = 0;
1787 for (uint32_t i = 0; i < t_NumFingers; i++)
1788 {
1789 uint32_t t_ParentID = 0;
1790 // then the digits of the finger that are linked to the root of the finger.
1791 for (uint32_t j = 0; j < t_NumJoints; j++)
1792 {
1793 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"));
1794 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1795 {
1796 printf("Failed to Add Node To Skeleton Setup. The error given %d.", t_Res);
1797 return false;
1798 }
1799 t_ParentID = 1 + t_FingerId + j;
1800 }
1801 t_FingerId += t_NumJoints;
1802 }
1803 return true;
1804}
1805
1811bool SDKClient::SetupHandChains(uint32_t p_SklIndex)
1812{
1813 // Add the Hand chain, this identifies the wrist of the hand
1814 {
1815 ChainSettings t_ChainSettings;
1816 ChainSettings_Init(&t_ChainSettings);
1817 t_ChainSettings.usedSettings = ChainType::ChainType_Hand;
1818 t_ChainSettings.hand.handMotion = HandMotion::HandMotion_IMU;
1819 t_ChainSettings.hand.fingerChainIdsUsed = 5; //we will have 5 fingers
1820 t_ChainSettings.hand.fingerChainIds[0] = 1; //links to the other chains we will define further down
1821 t_ChainSettings.hand.fingerChainIds[1] = 2;
1822 t_ChainSettings.hand.fingerChainIds[2] = 3;
1823 t_ChainSettings.hand.fingerChainIds[3] = 4;
1824 t_ChainSettings.hand.fingerChainIds[4] = 5;
1825
1826 ChainSetup t_Chain;
1827 ChainSetup_Init(&t_Chain);
1828 t_Chain.id = 0; //Every ID needs to be unique per chain in a skeleton.
1829 t_Chain.type = ChainType::ChainType_Hand;
1830 t_Chain.dataType = ChainType::ChainType_Hand;
1831 t_Chain.side = Side::Side_Left;
1832 t_Chain.dataIndex = 0;
1833 t_Chain.nodeIdCount = 1;
1834 t_Chain.nodeIds[0] = 0; //this links to the hand node created in the SetupHandNodes
1835 t_Chain.settings = t_ChainSettings;
1836
1837 SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain);
1838 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1839 {
1840 spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
1841 return false;
1842 }
1843 }
1844
1845 // Add the 5 finger chains
1846 const ChainType t_FingerTypes[5] = { ChainType::ChainType_FingerThumb,
1847 ChainType::ChainType_FingerIndex,
1848 ChainType::ChainType_FingerMiddle,
1849 ChainType::ChainType_FingerRing,
1850 ChainType::ChainType_FingerPinky };
1851 for (int i = 0; i < 5; i++)
1852 {
1853 ChainSettings t_ChainSettings;
1854 ChainSettings_Init(&t_ChainSettings);
1855 t_ChainSettings.usedSettings = t_FingerTypes[i];
1856 t_ChainSettings.finger.handChainId = 0; //This links to the wrist chain above.
1857 //This identifies the metacarpal bone, if none exists, or the chain is a thumb it should be set to -1.
1858 //The metacarpal bone should not be part of the finger chain, unless you are defining a thumb which does need it.
1859 t_ChainSettings.finger.metacarpalBoneId = -1;
1860 t_ChainSettings.finger.useLeafAtEnd = false; //this is set to true if there is a leaf bone to the tip of the finger.
1861 ChainSetup t_Chain;
1862 ChainSetup_Init(&t_Chain);
1863 t_Chain.id = i + 1; //Every ID needs to be unique per chain in a skeleton.
1864 t_Chain.type = t_FingerTypes[i];
1865 t_Chain.dataType = t_FingerTypes[i];
1866 t_Chain.side = Side::Side_Left;
1867 t_Chain.dataIndex = 0;
1868 if (i == 0) // Thumb
1869 {
1870 t_Chain.nodeIdCount = 4; //The amount of node id's used in the array
1871 t_Chain.nodeIds[0] = 1; //this links to the hand node created in the SetupHandNodes
1872 t_Chain.nodeIds[1] = 2; //this links to the hand node created in the SetupHandNodes
1873 t_Chain.nodeIds[2] = 3; //this links to the hand node created in the SetupHandNodes
1874 t_Chain.nodeIds[3] = 4; //this links to the hand node created in the SetupHandNodes
1875 }
1876 else // All other fingers
1877 {
1878 t_Chain.nodeIdCount = 4; //The amount of node id's used in the array
1879 t_Chain.nodeIds[0] = (i * 4) + 1; //this links to the hand node created in the SetupHandNodes
1880 t_Chain.nodeIds[1] = (i * 4) + 2; //this links to the hand node created in the SetupHandNodes
1881 t_Chain.nodeIds[2] = (i * 4) + 3; //this links to the hand node created in the SetupHandNodes
1882 t_Chain.nodeIds[3] = (i * 4) + 4; //this links to the hand node created in the SetupHandNodes
1883 }
1884 t_Chain.settings = t_ChainSettings;
1885
1886 SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain);
1887 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1888 {
1889 return false;
1890 }
1891 }
1892 return true;
1893}
1894
1902{
1903 uint32_t t_SklIndex = 0;
1904
1905 SkeletonSetupInfo t_SKL;
1906 SkeletonSetupInfo_Init(&t_SKL);
1907 t_SKL.type = SkeletonType::SkeletonType_Hand;
1908 t_SKL.settings.scaleToTarget = true;
1909 t_SKL.settings.useEndPointApproximations = true;
1910 t_SKL.settings.targetType = SkeletonTargetType::SkeletonTarget_UserIndexData;
1911 //If the user does not exist then the added skeleton will not be animated.
1912 //Same goes for any other skeleton made for invalid users/gloves.
1913 t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0;
1914
1915 CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("LeftHand"));
1916
1917 SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
1918 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1919 {
1920 spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
1921 return;
1922 }
1923 m_TemporarySkeletons.push_back(t_SklIndex);
1924
1925 // setup nodes and chains for the skeleton hand
1926 if (!SetupHandNodes(t_SklIndex)) return;
1927 if (!SetupHandChains(t_SklIndex)) return;
1928
1929 // load skeleton
1930 uint32_t t_ID = 0;
1931 t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID);
1932 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1933 {
1934 spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
1935 return;
1936 }
1938
1939 if (t_ID == 0)
1940 {
1941 spdlog::error("Failed to give skeleton an ID.");
1942 }
1943 m_LoadedSkeletons.push_back(t_ID);
1944}
1945
1948{
1949 if (m_LoadedSkeletons.size() == 0)
1950 {
1951 spdlog::error("There was no skeleton for us to unload.");
1952 return;
1953 }
1954 SDKReturnCode t_Res = CoreSdk_UnloadSkeleton(m_LoadedSkeletons[0]);
1955 m_LoadedSkeletons.erase(m_LoadedSkeletons.begin());
1956 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1957 {
1958 spdlog::error("Failed to unload skeleton. The error given was {}.", t_Res);
1959
1960 return;
1961 }
1962}
1963
1964
1965
1968{
1969 m_ChainType = ChainType::ChainType_Invalid;
1970
1971 uint32_t t_SklIndex = 0;
1972
1973 SkeletonSettings t_Settings;
1974 SkeletonSettings_Init(&t_Settings);
1975 t_Settings.scaleToTarget = true;
1976 t_Settings.targetType = SkeletonTargetType::SkeletonTarget_UserData;
1977 t_Settings.skeletonTargetUserData.userID = 0;
1978
1979 SkeletonSetupInfo t_SKL;
1980 SkeletonSetupInfo_Init(&t_SKL);
1981 t_SKL.id = 0;
1982 t_SKL.type = SkeletonType::SkeletonType_Hand;
1983 t_SKL.settings = t_Settings;
1984 CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("hand"));
1985
1986 SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
1987 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
1988 {
1989 spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
1990 return;
1991 }
1992 m_TemporarySkeletons.push_back(t_SklIndex);
1993
1994 // setup nodes for the skeleton hand
1995 SetupHandNodes(t_SklIndex);
1996
1997 // allocate chains for skeleton
1998 t_Res = CoreSdk_AllocateChainsForSkeletonSetup(t_SklIndex);
1999 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2000 {
2001 spdlog::error("Failed to allocate chains for skeleton. The error given was {}.", t_Res);
2002 return;
2003 }
2004
2005 // get the skeleton info
2006 SkeletonSetupArraySizes t_SkeletonInfo;
2007 t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo);
2008 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2009 {
2010 spdlog::error("Failed to get info about skeleton. The error given was {}.", t_Res);
2011 return;
2012 }
2013
2014 ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount];
2015 // now get the chain data
2016 t_Res = CoreSdk_GetSkeletonSetupChains(t_SklIndex, t_Chains);
2017 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2018 {
2019 spdlog::error("Failed to get skeleton setup chains. The error given was {}.", t_Res);
2020 delete[] t_Chains;
2021 return;
2022 }
2023 // as proof store the first chain type
2024 m_ChainType = t_Chains[0].dataType;
2025
2026 // but since we want to cleanly load the skeleton without holding everything up
2027 // we need to set its side first
2028 for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++)
2029 {
2030 if (t_Chains[i].dataType == ChainType::ChainType_Hand)
2031 {
2032 t_Chains[i].side = Side::Side_Left; // we're just picking a side here.
2033
2034 t_Res = CoreSdk_OverwriteChainToSkeletonSetup(t_SklIndex, t_Chains[i]);
2035 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2036 {
2037 spdlog::error("Failed to overwrite Chain To Skeleton Setup. The error given was {}.", t_Res);
2038 delete[] t_Chains;
2039 return;
2040 }
2041 break; // no need to continue checking the others.
2042 }
2043 }
2044 // cleanup
2045 delete[] t_Chains;
2046
2047 // load skeleton so it is done.
2048 uint32_t t_ID = 0;
2049 t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID);
2050 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2051 {
2052 spdlog::error("Failed to load skeleton. The error given was {}.", t_Res);
2053 return;
2054 }
2056
2057 if (t_ID == 0)
2058 {
2059 spdlog::error("Failed to give skeleton an ID.");
2060 }
2061 m_LoadedSkeletons.push_back(t_ID);
2062}
2063
2072{
2073 // define the session Id for which we want to save
2074 // in this example we want to save a skeleton for the current session so we use our own Session Id
2075 uint32_t t_SessionId = m_SessionId;
2076
2077 bool t_IsSkeletonModified = false; // this bool is set to true by the Dev Tools after saving any modification to the skeleton,
2078 // this triggers the OnSyStemCallback which is used in the SDK to be notified about a change to its temporary skeletons.
2079 // for the purpose of this example setting this bool to true is not really necessary.
2080
2081 // first create a skeleton setup of type Body
2082 uint32_t t_SklIndex = 0;
2083
2084 SkeletonSettings t_Settings;
2085 SkeletonSettings_Init(&t_Settings);
2086 t_Settings.scaleToTarget = true;
2087 t_Settings.targetType = SkeletonTargetType::SkeletonTarget_UserData;
2088 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
2089
2090 SkeletonSetupInfo t_SKL;
2091 SkeletonSetupInfo_Init(&t_SKL);
2092 t_SKL.id = 0;
2093 t_SKL.type = SkeletonType::SkeletonType_Body;
2094 t_SKL.settings = t_Settings;
2095 CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body"));
2096
2097 SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
2098 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2099 {
2100 spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
2101 return;
2102 }
2103 m_TemporarySkeletons.push_back(t_SklIndex);
2104 //Add 3 nodes to the skeleton setup
2105 t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "root"));
2106 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2107 {
2108 spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
2109 return;
2110 }
2111
2112 t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(1, 0, 0, 1, 0, "branch"));
2113 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2114 {
2115 spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
2116 return;
2117 }
2118
2119 t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(2, 1, 0, 2, 0, "leaf"));
2120 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2121 {
2122 spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
2123 return;
2124 }
2125
2126 //Add one chain of type Leg to the skeleton setup
2127 ChainSettings t_ChainSettings;
2128 ChainSettings_Init(&t_ChainSettings);
2129 t_ChainSettings.usedSettings = ChainType::ChainType_Leg;
2130 t_ChainSettings.leg.footForwardOffset = 0;
2131 t_ChainSettings.leg.footSideOffset = 0;
2132 t_ChainSettings.leg.reverseKneeDirection = false;
2133 t_ChainSettings.leg.kneeRotationOffset = 0;
2134
2135 ChainSetup t_Chain;
2136 ChainSetup_Init(&t_Chain);
2137 t_Chain.id = 0;
2138 t_Chain.type = ChainType::ChainType_Leg;
2139 t_Chain.dataType = ChainType::ChainType_Leg;
2140 t_Chain.dataIndex = 0;
2141 t_Chain.nodeIdCount = 3;
2142 t_Chain.nodeIds[0] = 0;
2143 t_Chain.nodeIds[1] = 1;
2144 t_Chain.nodeIds[2] = 2;
2145 t_Chain.settings = t_ChainSettings;
2146 t_Chain.side = Side::Side_Left;
2147
2148 t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain);
2149 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2150 {
2151 spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
2152 return;
2153 }
2154
2155 // save the temporary skeleton
2156 t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
2157 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2158 {
2159 spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
2160 return;
2161 }
2162
2163 // if we want to go on with the modifications to the same temporary skeleton
2164 // get the skeleton
2165 t_Res = CoreSdk_GetTemporarySkeleton(t_SklIndex, t_SessionId);
2166 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2167 {
2168 spdlog::error("Failed to get temporary skeleton. The error given was {}.", t_Res);
2169 return;
2170 }
2171
2172 // now add second chain to the same temporary skeleton
2173 t_ChainSettings.usedSettings = ChainType::ChainType_Head;
2174
2175 t_Chain.id = 1;
2176 t_Chain.type = ChainType::ChainType_Head;
2177 t_Chain.dataType = ChainType::ChainType_Head;
2178 t_Chain.dataIndex = 0;
2179 t_Chain.nodeIdCount = 1;
2180 t_Chain.nodeIds[0] = 0;
2181 t_Chain.settings = t_ChainSettings;
2182 t_Chain.side = Side::Side_Center;
2183
2184 t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain);
2185 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2186 {
2187 spdlog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", t_Res);
2188 return;
2189 }
2190
2191 // save the temporary skeleton
2192 t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
2193 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2194 {
2195 spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
2196 return;
2197 }
2198
2199 // get the skeleton info (number of nodes and chains for that skeleton)
2200 SkeletonSetupArraySizes t_SkeletonInfo;
2201 t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo);
2202 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2203 {
2204 spdlog::error("Failed to get info about skeleton. The error given was {}.", t_Res);
2205 return;
2206 }
2207
2208 // now get the chain data
2209 ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount];
2210 t_Res = CoreSdk_GetSkeletonSetupChains(t_SklIndex, t_Chains);
2211 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2212 {
2213 spdlog::error("Failed to get skeleton setup chains. The error given was {}.", t_Res);
2214 return;
2215 }
2216
2217 // get the node data
2218 NodeSetup* t_Nodes = new NodeSetup[t_SkeletonInfo.nodesCount];
2219 t_Res = CoreSdk_GetSkeletonSetupNodes(t_SklIndex, t_Nodes);
2220 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2221 {
2222 spdlog::error("Failed to get skeleton setup nodes. The error given was {}.", t_Res);
2223 return;
2224 }
2225
2226 // just as an example try to get the skeleton setup info
2227 SkeletonSetupInfo t_SKeletonSetupInfo;
2228 SkeletonSetupInfo_Init(&t_SKeletonSetupInfo);
2229 t_Res = CoreSdk_GetSkeletonSetupInfo(t_SklIndex, &t_SKeletonSetupInfo);
2230 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2231 {
2232 spdlog::error("Failed to overwrite Skeleton Setup. The error given was {}.", t_Res);
2233 return;
2234 }
2235
2236 // if we want to modify the skeleton setup or if we want to apply some changes to the chains or nodes:
2237 // first overwrite the existing skeleton setup and then re-add all the chains and nodes to it
2238 SkeletonSettings_Init(&t_Settings);
2239 t_Settings.targetType = SkeletonTargetType::SkeletonTarget_GloveData;
2240
2241 t_SKL.settings = t_Settings;
2242 CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body2"));
2243
2244 // this way we overwrite the temporary skeleton with index t_SklIndex with the modified skeleton setup
2245 t_Res = CoreSdk_OverwriteSkeletonSetup(t_SklIndex, t_SKL);
2246 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2247 {
2248 spdlog::error("Failed to overwrite Skeleton Setup. The error given was {}.", t_Res);
2249 return;
2250 }
2251
2252 // modify chains and nodes
2253 t_Chains[0].side = Side::Side_Right;
2254 t_Nodes[0].type = NodeType::NodeType_Mesh;
2255
2256 // add all the existing nodes to the new skeleton setup
2257 for (size_t i = 0; i < t_SkeletonInfo.nodesCount; i++)
2258 {
2259 t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, t_Nodes[i]);
2260 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2261 {
2262 spdlog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", t_Res);
2263 return;
2264 }
2265 }
2266
2267 // then add all the existing chains to the new skeleton setup
2268 for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++)
2269 {
2270 t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chains[i]);
2271 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2272 {
2273 spdlog::error("Failed to Add Chains To Skeleton Setup. The error given was {}.", t_Res);
2274 return;
2275 }
2276 }
2277
2278 // cleanup
2279 delete[] t_Chains;
2280 delete[] t_Nodes;
2281
2282 // save temporary skeleton
2283 // in the Dev Tools this bool is set to true when saving the temporary skeleton, this triggers OnSystemCallback which
2284 // notifies the SDK sessions about a modifications to one of their temporary skeletons.
2285 // setting the bool to true in this example is not really necessary, it's just for testing purposes.
2286 t_IsSkeletonModified = true;
2287 t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
2288 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2289 {
2290 spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
2291 return;
2292 }
2293}
2294
2298{
2299 // clear the first element of the temporary skeleton list
2300 if (m_TemporarySkeletons.size() == 0)
2301 {
2302 spdlog::error("There are no Temporary Skeletons to clear!");
2303 return;
2304 }
2305 uint32_t t_SklIndex = m_TemporarySkeletons[0];
2306 SDKReturnCode t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, m_SessionId);
2307 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2308 {
2309 spdlog::error("Failed to Clear Temporary Skeleton. The error given was {}.", t_Res);
2310 return;
2311 }
2313}
2314
2318{
2319 if (m_TemporarySkeletons.size() == 0)
2320 {
2321 spdlog::error("There are no Temporary Skeletons to clear!");
2322 return;
2323 }
2324 SDKReturnCode t_Res = CoreSdk_ClearAllTemporarySkeletons();
2325 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2326 {
2327 spdlog::error("Failed to Clear All Temporary Skeletons. The error given was {}.", t_Res);
2328 return;
2329 }
2330 m_TemporarySkeletons.clear();
2331}
2332
2334{
2335 // this example shows how to save a temporary skeleton to a file
2336 // first create a temporary skeleton:
2337
2338 // define the session Id for which we want to save
2339 uint32_t t_SessionId = m_SessionId;
2340
2341 bool t_IsSkeletonModified = false; // setting this bool to true is not necessary here, it is mostly used by the Dev Tools
2342 // to notify the SDK sessions about their skeleton being modified.
2343
2344 // first create a skeleton setup
2345 uint32_t t_SklIndex = 0;
2346
2347 SkeletonSetupInfo t_SKL;
2348 SkeletonSetupInfo_Init(&t_SKL);
2349 t_SKL.type = SkeletonType::SkeletonType_Hand;
2350 t_SKL.settings.scaleToTarget = true;
2351 t_SKL.settings.targetType = SkeletonTargetType::SkeletonTarget_GloveData;
2352 t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0;
2353
2354 CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("LeftHand"));
2355
2356 SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
2357 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2358 {
2359 spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
2360 return;
2361 }
2362 m_TemporarySkeletons.push_back(t_SklIndex);
2363
2364 // setup nodes and chains for the skeleton hand
2365 if (!SetupHandNodes(t_SklIndex)) return;
2366 if (!SetupHandChains(t_SklIndex)) return;
2367
2368 // save the temporary skeleton
2369 t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified);
2370 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2371 {
2372 spdlog::error("Failed to save temporary skeleton. The error given was {}.", t_Res);
2373 return;
2374 }
2375
2376 // now compress the temporary skeleton data and get the size of the compressed data:
2377 uint32_t t_TemporarySkeletonLengthInBytes;
2378
2379 t_Res = CoreSdk_CompressTemporarySkeletonAndGetSize(t_SklIndex, t_SessionId, &t_TemporarySkeletonLengthInBytes);
2380 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2381 {
2382 spdlog::error("Failed to compress temporary skeleton and get size. The error given was {}.", t_Res);
2383 return;
2384 }
2385 unsigned char* t_TemporarySkeletonData = new unsigned char[t_TemporarySkeletonLengthInBytes];
2386
2387 // get the array of bytes with the compressed temporary skeleton data, remember to always call function CoreSdk_CompressTemporarySkeletonAndGetSize
2388 // before trying to get the compressed temporary skeleton data
2389 t_Res = CoreSdk_GetCompressedTemporarySkeletonData(t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
2390 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2391 {
2392 spdlog::error("Failed to get compressed temporary skeleton data. The error given was {}.", t_Res);
2393 return;
2394 }
2395
2396 // now save the data into a .mskl file
2397 // as an example we save the temporary skeleton in a folder called ManusTemporarySkeleton inside the documents directory
2398 // get the path for the documents directory
2399 std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8();
2400
2401 // create directory name and file name for storing the temporary skeleton
2402 std::string t_DirectoryPath =
2403 t_DirectoryPathString
2405 + "ManusTemporarySkeleton";
2406
2407 CreateFolderIfItDoesNotExist(t_DirectoryPath);
2408
2409 std::string t_DirectoryPathAndFileName =
2410 t_DirectoryPath
2412 + "TemporarySkeleton.mskl";
2413
2414 // write the temporary skeleton data to .mskl file
2415 std::ofstream t_File = GetOutputFileStream(t_DirectoryPathAndFileName);
2416 t_File.write((char*)t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
2417 t_File.close();
2418
2419 t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, t_SessionId);
2420 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2421 {
2422 spdlog::error("Failed to Clear Temporary Skeleton after saving. The error given was {}.", t_Res);
2423 return;
2424 }
2426}
2427
2428
2430{
2431 // this example shows how to load a temporary skeleton data from a file
2432
2433 // as an example we try to get the temporary skeleton data previously saved as .mskl file in directory Documents/ManusTemporarySkeleton
2434 // get the path for the documents directory
2435 std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8();
2436
2437 // check if directory exists
2438 std::string t_DirectoryPath =
2439 t_DirectoryPathString
2441 + "ManusTemporarySkeleton";
2442
2443 if (!DoesFolderOrFileExist(t_DirectoryPath))
2444 {
2445 SPDLOG_WARN("Failed to read from client file, the mentioned directory does not exist");
2446 return;
2447 }
2448
2449 // create string with file name
2450 std::string t_DirectoryPathAndFileName =
2451 t_DirectoryPath
2453 + "TemporarySkeleton.mskl";
2454
2455 // read from file
2456 std::ifstream t_File = GetInputFileStream(t_DirectoryPathAndFileName);
2457
2458 if (!t_File)
2459 {
2460 SPDLOG_WARN("Failed to read from client file, the file does not exist in the mentioned directory");
2461 return;
2462 }
2463
2464 // get file dimension
2465 t_File.seekg(0, t_File.end);
2466 int t_FileLength = (int)t_File.tellg();
2467 t_File.seekg(0, t_File.beg);
2468
2469 // get temporary skeleton data from file
2470 unsigned char* t_TemporarySkeletonData = new unsigned char[t_FileLength];
2471 t_File.read((char*)t_TemporarySkeletonData, t_FileLength);
2472 t_File.close();
2473
2474
2475 // save the zipped temporary skeleton information, they will be used internally for sending the data to Core
2476 uint32_t t_TemporarySkeletonLengthInBytes = t_FileLength;
2477
2478 if (t_TemporarySkeletonData == nullptr)
2479 {
2480 SPDLOG_WARN("Failed to read the compressed temporary skeleton data from file");
2481 delete[] t_TemporarySkeletonData;
2482 return;
2483 }
2484
2485 // create a skeleton setup where we will store the temporary skeleton retrieved from file
2486 SkeletonSetupInfo t_SKL;
2487 SkeletonSetupInfo_Init(&t_SKL);
2488 uint32_t t_SklIndex = 0;
2489 SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex);
2490 if (t_Res != SDKReturnCode::SDKReturnCode_Success)
2491 {
2492 spdlog::error("Failed to Create Skeleton Setup. The error given was {}.", t_Res);
2493 return;
2494 }
2495 m_TemporarySkeletons.push_back(t_SklIndex);
2496
2497 // associate the retrieved temporary skeleton to the current session id
2498 uint32_t t_SessionId = m_SessionId;
2499
2500 // load the temporary skeleton data retrieved from the zipped file and save it with index t_SklIndex and session id of the current session
2501 SDKReturnCode t_Result = CoreSdk_GetTemporarySkeletonFromCompressedData(t_SklIndex, t_SessionId, t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes);
2502 if (t_Result != SDKReturnCode::SDKReturnCode_Success)
2503 {
2504 SPDLOG_WARN("Failed to load temporary skeleton data from client file in Core, the error code was: {}.", t_Result);
2505 return;
2506 }
2507
2508 delete[] t_TemporarySkeletonData;
2509}
2510
2512{
2513 ManusTimestamp t_TS;
2514 ManusTimestamp_Init(&t_TS);
2515 ManusTimestampInfo t_TSInfo;
2516 ManusTimestampInfo_Init(&t_TSInfo);
2517 t_TSInfo.fraction = 69;
2518 t_TSInfo.second = 6;
2519 t_TSInfo.minute = 9;
2520 t_TSInfo.hour = 6;
2521 t_TSInfo.day = 9;
2522 t_TSInfo.month = 6;
2523 t_TSInfo.year = 6969;
2524 t_TSInfo.timecode = true;
2525
2526 CoreSdk_SetTimestampInfo(&t_TS, t_TSInfo);
2527
2528 ManusTimestampInfo t_TSInfo2;
2529 CoreSdk_GetTimestampInfo(t_TS, &t_TSInfo2);
2530}
2531
2533{
2534 for (int i = 0; i < m_TemporarySkeletons.size(); i++)
2535 {
2536 if (m_TemporarySkeletons[i] == p_Idx)
2537 {
2538 m_TemporarySkeletons.erase(m_TemporarySkeletons.begin() + i);
2539 }
2540 }
2541}
std::string ConvertDeviceClassTypeToString(DeviceClassType p_Type)
Definition: SDKClient.cpp:1070
std::string ConvertDeviceFamilyTypeToString(DeviceFamilyType p_Type)
Definition: SDKClient.cpp:1085
#define GO_TO_DISPLAY(p_Key, p_Function)
Definition: SDKClient.cpp:8
std::string GetFPSEnumName(TimecodeFPS p_FPS)
Definition: SDKClient.cpp:1242
#define GO_TO_MENU_IF_REQUESTED()
Definition: SDKClient.cpp:11
std::string m_SystemMessage
Definition: SDKClient.hpp:225
void PrintSystemMessage()
Prints the last received system messages received from Core.
Definition: SDKClient.cpp:1135
void ApplyConsolePosition(const int p_ConsoleCurrentOffset)
Set the current console position. This handles the platform-specific part of advancing the console po...
virtual ClientReturnCode NoHostsFound()
When no available hosts are found the user can either retry or exit.
Definition: SDKClient.cpp:605
void ClearAllTemporarySkeletons()
This support function is used to clear all temporary skeletons associated to the current SDK session,...
Definition: SDKClient.cpp:2317
SessionType m_ClientType
Definition: SDKClient.hpp:185
ClientReturnCode ShutDown()
When you are done with the SDK, don't forget to nicely shut it down this will close all connections t...
Definition: SDKClient.cpp:152
virtual ClientReturnCode DisplayingDataTracker()
Definition: SDKClient.cpp:828
ClientReturnCode Run()
The main SDKClient loop. This is a simple state machine which switches between different substates.
Definition: SDKClient.cpp:62
virtual ClientReturnCode DisplayingLandscapeTimeData()
Definition: SDKClient.cpp:866
ManusTimestampInfo m_ErgoTimestampInfo
Definition: SDKClient.hpp:229
static void ClearConsole(void)
Clear the console window.
void PrintSkeletonInfo()
Prints the type of the first chain generated by the AllocateChain function, this is used for testing.
Definition: SDKClient.cpp:1280
virtual ClientReturnCode ReconnectingToCore(int32_t p_ReconnectionTime=0, int32_t p_ReconnectionAttempts=0)
It is called when the sdk is disconnected from Core and the user select one of the options to reconne...
Definition: SDKClient.cpp:954
void AdvanceConsolePosition(short int p_Y)
Set the position that the next log message will appear at. Using this allows us to have somewhat of a...
Definition: SDKClient.cpp:345
void PrintHandErgoData(ErgonomicsData &p_ErgoData, bool p_Left)
Prints the ergonomics data of a hand.
Definition: SDKClient.cpp:1014
ClientSkeletonCollection * m_NextSkeleton
Definition: SDKClient.hpp:217
ErgonomicsData m_RightGloveErgoData
Definition: SDKClient.hpp:231
bool m_TrackerDataDisplayPerUser
Definition: SDKClient.hpp:236
void ClearTemporarySkeleton()
This support function is used to clear a temporary skeleton from the temporary skeleton list,...
Definition: SDKClient.cpp:2297
void UpdateInput(void)
Update the current keyboard state.
uint32_t m_FirstRightGloveID
Definition: SDKClient.hpp:244
std::chrono::time_point< std::chrono::high_resolution_clock > m_TimeSinceLastDisconnect
Definition: SDKClient.hpp:221
const short int m_ConsoleWidth
Definition: SDKClient.hpp:180
ClientReturnCode Initialize()
Initialize the sample console and the SDK. This function attempts to resize the console window and th...
Definition: SDKClient.cpp:34
bool PlatformSpecificInitialization(void)
Initialise things only needed for this platform.
uint32_t m_SecondsToFindHosts
Definition: SDKClient.hpp:198
bool m_ShouldConnectLocally
Definition: SDKClient.hpp:193
virtual ClientReturnCode UpdateBeforeDisplayingData()
Some things happen before every display update, no matter what state. They happen here,...
Definition: SDKClient.cpp:717
virtual ClientReturnCode LookingForHosts()
Simple example of the SDK looking for manus core hosts on the network and display them on screen.
Definition: SDKClient.cpp:556
virtual ClientReturnCode InitializeSDK()
Initialize the sdk, register the callbacks and set the coordinate system. This needs to be done befor...
Definition: SDKClient.cpp:361
bool SetupHandChains(uint32_t p_SklIndex)
This function sets up some basic hand chains. Chains are required for a Skeleton to be able to be ani...
Definition: SDKClient.cpp:1811
static void OnSystemCallback(const SystemMessage *const p_SystemMessage)
This gets called when receiving a system message from Core.
Definition: SDKClient.cpp:273
virtual ClientReturnCode RegisterAllCallbacks()
Used to register the callbacks between sdk and core. Callbacks that are registered functions that get...
Definition: SDKClient.cpp:433
uint32_t m_ConsoleClearTickCount
Definition: SDKClient.hpp:179
static SDKClient * s_Instance
Definition: SDKClient.hpp:174
bool GetKeyDown(const int p_Key)
Returns true first time it is called when key is pressed.
void PrintTrackerData()
Prints the tracker data Since our console cannot render this data visually in 3d (its not in the scop...
Definition: SDKClient.cpp:1161
void PrintDongleData()
Print the ergonomics data received from Core.
Definition: SDKClient.cpp:1103
virtual ClientReturnCode PickingConnectionType()
Overridable switch states of how the client flow is done. This is the first option screen in the clie...
Definition: SDKClient.cpp:500
void PrintSkeletonData()
Prints the finalized skeleton data received from Core. Since our console cannot render this data visu...
Definition: SDKClient.cpp:1146
std::chrono::time_point< std::chrono::high_resolution_clock > m_LastTemporarySkeletonUpdate
Definition: SDKClient.hpp:222
float m_TrackerOffset
Definition: SDKClient.hpp:237
virtual ClientReturnCode ConnectingToCore()
After a connection option was selected, the client will now try to connect to manus core via the SDK.
Definition: SDKClient.cpp:675
void LoadTestSkeleton()
This function sets up a very minimalistic hand skeleton. In order to have any 3d positional/rotationa...
Definition: SDKClient.cpp:1901
static bool CopyString(char *const p_Target, const size_t p_MaxLengthThatWillFitInTarget, const std::string &p_Source)
Copy the given string into the given target.
virtual ClientReturnCode DisplayingDataTemporarySkeleton()
Definition: SDKClient.cpp:845
std::unique_ptr< ManusHost[]> m_AvailableHosts
Definition: SDKClient.hpp:204
void PrintTrackerDataGlobal()
Prints the tracker data without taking users into account. This shows how one can get the tracker dat...
Definition: SDKClient.cpp:1182
virtual ClientReturnCode PickingHost()
Print the found hosts and give the user the option to select one.
Definition: SDKClient.cpp:627
float RoundFloatValue(float p_Value, int p_NumDecimalsToKeep)
Round the given float value so that it has no more than the given number of decimals.
Definition: SDKClient.cpp:331
virtual ClientReturnCode DisplayingData()
Once the connections are made we loop this function it calls all the input handlers for different asp...
Definition: SDKClient.cpp:766
bool ResizeWindow(const short int p_ConsoleWidth, const short int p_ConsoleHeight, const short int p_ConsoleScrollback)
Resize the window, so the log messages will fit. Make sure not to enter illegal sizes in here or SetC...
bool m_TrackerTest
Definition: SDKClient.hpp:235
static void OnErgonomicsCallback(const ErgonomicsStream *const p_Ergo)
This gets called when receiving ergonomics data from Manus Core In our sample we only save the first ...
Definition: SDKClient.cpp:301
uint32_t m_SleepBetweenReconnectingAttemptsInMs
Definition: SDKClient.hpp:202
void HandleSkeletonCommands()
Handles the console commands for the skeletons.
Definition: SDKClient.cpp:1544
SystemMessageType m_SystemMessageCode
Definition: SDKClient.hpp:226
uint32_t m_ModifiedSkeletonIndex
Definition: SDKClient.hpp:227
static void OnConnectedCallback(const ManusHost *const p_Host)
Gets called when the client is connects to manus core Using this callback is optional....
Definition: SDKClient.cpp:172
uint32_t m_HostToConnectTo
Definition: SDKClient.hpp:196
void RemoveIndexFromTemporarySkeletonList(uint32_t p_Idx)
Definition: SDKClient.cpp:2532
std::mutex m_LandscapeMutex
Definition: SDKClient.hpp:239
std::mutex m_SkeletonMutex
Definition: SDKClient.hpp:215
static ManusVec3 CreateManusVec3(float p_X, float p_Y, float p_Z)
Definition: SDKClient.cpp:1719
bool DoesFolderOrFileExist(std::string p_Path_UTF8)
Check if the given folder or file exists. The folder path given should be in UTF-8 format.
int m_ConsoleCurrentOffset
Definition: SDKClient.hpp:183
std::vector< uint32_t > m_LoadedSkeletons
Definition: SDKClient.hpp:212
void HandleHapticCommands()
This showcases haptics support on gloves. Send haptics commands to connected gloves if specific keys ...
Definition: SDKClient.cpp:1436
uint32_t m_SessionId
Definition: SDKClient.hpp:208
bool SetupHandNodes(uint32_t p_SklIndex)
This support function sets up the nodes for the skeleton hand In order to have any 3d positional/rota...
Definition: SDKClient.cpp:1734
std::function< ClientReturnCode()> m_CurrentInteraction
Definition: SDKClient.hpp:190
virtual ClientReturnCode DisplayingDataSkeleton()
Definition: SDKClient.cpp:808
std::vector< uint32_t > m_TemporarySkeletons
Definition: SDKClient.hpp:213
void TestTimestamp()
Definition: SDKClient.cpp:2511
bool PlatformSpecificShutdown(void)
Shut down things only needed for this platform.
bool m_RequestedExit
Definition: SDKClient.hpp:176
void GetTemporarySkeletonFromFile()
Definition: SDKClient.cpp:2429
bool m_ShouldConnectGRPC
Definition: SDKClient.hpp:194
void AllocateChains()
This support function sets up an incomplete hand skeleton and then uses manus core to allocate chains...
Definition: SDKClient.cpp:1967
void HandleTrackerCommands()
This support function is used to set a test tracker and add it to the landscape.
Definition: SDKClient.cpp:1648
void SaveTemporarySkeletonToFile()
Definition: SDKClient.cpp:2333
virtual ClientReturnCode RestartSDK()
Used to restart and initialize the SDK to make sure a new connection can be set up....
Definition: SDKClient.cpp:411
const short int m_ConsoleScrollback
Definition: SDKClient.hpp:182
void PrintLandscapeTimeData()
Definition: SDKClient.cpp:1267
virtual ClientReturnCode DisplayingDataGlove()
display the ergonomics data of the gloves, and handles haptic commands.
Definition: SDKClient.cpp:790
virtual ClientReturnCode DisconnectedFromCore()
When the SDK loses the connection with Core the user can either close the sdk or try to reconnect to ...
Definition: SDKClient.cpp:885
static void OnDisconnectedCallback(const ManusHost *const p_Host)
Gets called when the client disconnects from manus core. This callback is optional and in the sample ...
Definition: SDKClient.cpp:225
bool GetKey(const int p_Key)
Gets key's current state. True is pressed false is not pressed.
void UnloadTestSkeleton()
This support function is used to unload a skeleton from Core.
Definition: SDKClient.cpp:1947
std::ofstream GetOutputFileStream(std::string p_Path_UTF8)
Get an output stream for the given file. The file's path should be in UTF-8 format.
int32_t m_SecondsToAttemptReconnecting
Definition: SDKClient.hpp:200
std::string GetDocumentsDirectoryPath_UTF8(void)
Get the path to the user's Documents folder. The string should be in UTF-8 format.
std::unique_ptr< ManusHost > m_Host
Definition: SDKClient.hpp:205
void PrintTrackerDataPerUser()
Prints the tracker data per user, this shows how to access this data for each user.
Definition: SDKClient.cpp:1205
void HandleTemporarySkeletonCommands()
Handles the console commands for the temporary skeletons.
Definition: SDKClient.cpp:1619
Landscape * m_Landscape
Definition: SDKClient.hpp:241
static void OnSkeletonStreamCallback(const SkeletonStreamInfo *const p_Skeleton)
This gets called when the client is connected to manus core.
Definition: SDKClient.cpp:236
std::mutex m_SystemMessageMutex
Definition: SDKClient.hpp:224
void CreateFolderIfItDoesNotExist(std::string p_Path_UTF8)
Create the given folder if it does not exist. The folder path given should be in UTF-8 format.
void GetTemporarySkeletonIfModified()
This support function checks if a temporary skeleton related to the current session has been modified...
Definition: SDKClient.cpp:1362
void BuildTemporarySkeleton()
This support function is used for the manual allocation of the skeleton chains with means of a tempor...
Definition: SDKClient.cpp:2071
void PrintErgonomicsData()
Print the ergonomics data received from Core.
Definition: SDKClient.cpp:1042
bool shouldHapticFinger[NUM_FINGERS_ON_HAND]
Definition: SDKClient.hpp:68
uint32_t m_NumberOfHostsFound
Definition: SDKClient.hpp:197
static void OnLandscapeCallback(const Landscape *const p_Landscape)
This gets called when receiving landscape information from core.
Definition: SDKClient.cpp:259
static NodeSetup CreateNodeSetup(uint32_t p_Id, uint32_t p_ParentId, float p_PosX, float p_PosY, float p_PosZ, std::string p_Name)
Skeletons are pretty extensive in their data setup so we have several support functions so we can cor...
Definition: SDKClient.cpp:1702
void PrintTemporarySkeletonInfo()
This support function gets the temporary skeletons for all sessions connected to Core and it prints t...
Definition: SDKClient.cpp:1403
Landscape * m_NewLandscape
Definition: SDKClient.hpp:240
void HandleSkeletonHapticCommands()
This showcases haptics support on the skeletons. Send haptics commands to the gloves connected to a s...
Definition: SDKClient.cpp:1559
ErgonomicsData m_LeftGloveErgoData
Definition: SDKClient.hpp:230
ChainType m_ChainType
Definition: SDKClient.hpp:233
ClientState m_PreviousState
Definition: SDKClient.hpp:188
std::vector< ClientSkeleton > skeletons
Definition: SDKClient.hpp:83
const short int m_ConsoleHeight
Definition: SDKClient.hpp:181
int32_t m_MaxReconnectionAttempts
Definition: SDKClient.hpp:201
ClientState m_State
Definition: SDKClient.hpp:187
static const std::string s_SlashForFilesystemPath
The slash character that is used in the filesystem.
std::ifstream GetInputFileStream(std::string p_Path_UTF8)
Get an input stream for the given file. The file's path should be in UTF-8 format.
uint32_t m_FirstLeftGloveID
Definition: SDKClient.hpp:243
ClientSkeletonCollection * m_Skeleton
Definition: SDKClient.hpp:218
Used to store all the final animated skeletons received from Core.
Definition: SDKClient.hpp:81
constexpr unsigned long long int MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS
Constant expression used to define the time between two possible haptics commands sent.
Definition: SDKClient.hpp:26
constexpr unsigned long long int MILLISECONDS_BETWEEN_TEMPORARY_SKELETONS_UPDATE
Constant expression used to define the time between two updates of the temporary skeleton count print...
Definition: SDKClient.hpp:29
constexpr unsigned int NUMBER_OF_HANDS_SUPPORTED
Constant expression: number of hands supported by demo.
Definition: SDKClient.hpp:23
ClientReturnCode
Values that can be returned by this application.
Definition: SDKClient.hpp:47
Haptic settings for a single glove.
Definition: SDKClient.hpp:67