Plugins | MANUS Core 2.0 SDK | Minimal Client
The SDK Minimal client is an example client for the absolute bare basics of:
- Getting a client setup with the MANUS SDK
- Connect it to a locally run MANUS Host
- Configure a basic hand skeleton
- And get animation data for it.
This client main files consists of the SDKDLL and the SDKMinimalClient.cpp. The other files are not important to understand the basic functions used in this example.
The ManudSDK.h, MANUSSDKtypes.h and MANUSSDKTypeInitializers.hare support files from the SDK to describe how to interface with the SDK and will only be referenced by the functions of the SDK Minimal Client.
The client has 3 phases:
Before anything of the SDK can be used, it needs to be initialized. If this fails, it usually means the system is not setup correctly, or is already initialized.
PlatformSpecificInitialization() is an optional initializer to prepare console output, but is not required in anon-console environment.
During InitializeSDK() it will also setup the type of Session with which it connects to MANUS Core.
See SessionType enum for more options.
Then it registers all the callbacks it needs in RegisterAllCalbacks(). There can be more callbacks then just the SkeletonStream, but for this example it is not required. For more details on other callbacks, please check the SDKClient Guide documentation.
After that it sets the coordinate system we use for the client with CoreSdk_InitializeCoordinateSystemWithVUH(). In this example the Unity coordinate system is adopted. It is best to align this with the application you are developing. You do not need to worry about other application coordinate settings, as MANUS Core will convert all data between different SDK clients so they will all be compatible.
The second parameter in this function is to indicate if it uses world coordinates or relative coordinates. In this case set to false to not set the coordinates as world coordinates.
After this the SDK is initialized, but not yet connected to an instance of MANUS Core.
When running the Minimal client it follows the following commands:
- Connect locally, if not successful, just wait and retry
- Setup a skeleton
- Start the main loop
◦ Check if there is new skeleton data
◦ If there is, signal that there is
◦ Wait for the space key to exit
For the connection it only tries locally. But if you want remote connections or see how it can reconnect in case of connection loss, please look up the SDK Client Guide.
The ConnectLocally() function goes through the following steps to setup a connection:
- First it runsCoreSdk_LookForHosts() for 1 second and only for local hosts (true). If none are found it returns its failure and stops.
- Then it retrieves the amount of local hosts found and then the host names. Because the number of hosts found can be variable and the SDK DLL is also used in different environments, it needs to be robust in how it retrieves that data (in C# marshalling this is very important). This normally only fails if no hosts were found.
Again if it fails, it returns that and stops.
- Then it finally connects to the first available local host. If network settings are blocking or the host is stopped, this may fail. It returns the result either way.
Setup a skeleton
To setup a hand skeleton and make sure it animates correctly, the following steps are required:
- Setup a container for a skeleton
◦ Make it reference to a user
- Setup the hand nodes for the skeleton
- Setup the chain nodes for the skeleton
- And load it into MANUS core
The container of a skeleton is a SkeletonSetupInfo structure.
It will contain all relevant data. To reference a user it sets its settings.targetype toSkeletonTarget_UserIndexData. This means it will look at the list of users currently active in MANUS core and based on the index it will select that user. If the user has no gloves assigned or doesn’t exist no data will be transferred though.
In this casesettings.skeletonTargetUserIndexData.usedIndex is set to 0 , meaning that the first user in the list will be used.
For the skeleton type we set it toSkeletonType_Hand. This type of skeleton can be completely done with just 1glove. If it was part of a full body, then options for its wrist may become more important. For this example, we just focus on 1 glove.
The hand nodes for a skeleton are based on a mocap hand and are just raw data of nodes with relative positions. However, we don’t define the metacarpals for this example as they don’t animate as much.
The vectors below are based on a hand lying flat on a surface.
This will form 20 nodes that are then added to the skeleton setup with CoreSdk_AddNodeToSkeletonSetup.
A root node is added to define the start of the hand which starts at the 0 vector and then all the fingers and their joint nodes are added.
To setup hand chains, we effectively setup1 hand chain (ChainType_Hand) and 5 finger chains (ChainType_FingerIndex/ChainType_FingerMiddle/ etc) to describe the hand.
The hand chain is also the root chain where we setup that its motion is controlled by the glove wrist IMU by setting it to HandMotion_IMU.
And then add all the chain id’s of the finger chains that are about to be added.
All of the chains are setup for a left-handed glove for this example.
Once all this is done, the data is sent to MANUS Core via CoreSdk_LoadSkeleton(). This makes sure the skeleton is being animated and that the callback in the SDK Minimal Client will get updates.
In the Run loop the client waits for a new update of skeleton data via the callback mechanism. Due to threading this is secured by a mutex and it is highly advised to copy the data only inside the mutex and do any other animation work outside the mutex scope.
In this example we don’t do anything with that data except post a console output message that new data is received. Andsleep so other (network)processes get a chance to perform their duties.
But this is the time in your own application to use the data and apply it to whatever you need.
Since skeletal animation data is transmitted via a network, this will be received asynchronously in a different thread. It is not advised to hold up that thread longer then is necessary to copy the data.
For this a mutex is used so the data is thread safe and then copied.