LogoLogo
WebsiteSupportDownloadsForumsQuick LinksContact Us
v3.2
v3.2
  • OptiTrack Documentation
  • WHAT'S NEW
    • What's New in Motive 3.2
  • QUICK START GUIDES
    • Quick Start Guide: Getting Started
    • Quick Start Guide: Prime Color Camera Setup
    • Quick Start Guide: Precision Capture
    • Quick Start Guide: Tutorial Videos
    • Quick Start Guide: Active Marker Tracking
    • Quick Start Guide: Outdoor Tracking Setup
  • HARDWARE
    • Cameras
      • Ethernet Cameras
        • PrimeX 120
        • PrimeX 41
        • PrimeX 22
        • PrimeX 13
        • PrimeX 13W
        • SlimX 13
        • Prime Color
        • VersaX 22
        • VersaX 41
        • VersaX 120
      • USB Cameras
        • Slim 3U
        • Flex 13
        • Flex 3
        • V120:Duo
        • V120:Trio
        • Adjusting Global Origin for Tracking Bars
    • Prepare Setup Area
    • Camera Mount Structures
    • Camera Placement
    • Ethernet Camera Network Setup
      • General Overview and Specs
      • Windows 10 Network Settings
      • Cabling and Load Balancing
      • Switch Configuration for PrimeX 120
      • NETGEAR ProSafe GSM7228S: Disabling the Broadcast Storm Control
      • White/Blacklisting Cameras
    • USB Camera System Setup
      • USB Camera Network Overview and Specs
      • V120:Duo and Trio Setup
      • Tracking Bar Coordinate System
        • Transforming Coordinate System: Global to Local
    • Aiming and Focusing
    • Camera Status Indicators
  • MOTIVE
    • Installation and License Activation
    • Motive Basics
    • Calibration
      • .mcal XML Calibration Files
      • Continuous Calibration
      • Continuous Calibration (Info Pane)
      • Calibration Squares
    • Markers
    • Assets
      • Gizmo Tool: Translate, Rotate, and Scale
    • Rigid Body Tracking
      • Aligning Rigid Body Pivot Point with a Replicated 3D Model
    • Skeleton Tracking
    • Trained Markersets
    • IMU Sensor Fusion
    • Data Recording
      • Data Types
    • Labeling
    • Data Editing
    • Data Export
      • Data Export: BVH
      • Data Export: C3D
      • Data Export: CSV
      • Data Export: FBX
      • Data Export: TRC
    • Data Streaming
    • Camera Video Types
    • Audio Recording
    • Motive HotKeys
    • Measurement Probe Kit Guide
    • Motive Batch Processor
    • Reconstruction and 2D Mode
  • MOTIVE UI PANES
    • Settings
      • Settings: General
      • Settings: Assets
      • Settings: Live Pipeline
      • Settings: Streaming
      • Settings: Views
      • Settings: Mouse and Keyboard
      • Settings: Audio
    • Assets Pane
    • Builder Pane
    • Constraints Pane
      • Constraints XML Files
    • Calibration Pane
    • Data Pane
    • Devices Pane
    • Edit Tools Pane
    • Graph View Pane
    • Info Pane
    • Labels Pane
    • Log Pane
    • Probe Pane
    • Properties Pane
      • Properties Pane: Camera
      • Properties Pane: Force Plates
      • Properties Pane: NI-DAQ
      • Properties Pane: OptiHub2
      • Properties Pane: Rigid Body
      • Properties Pane: Skeleton
      • Properties Pane: Take
      • Properties Pane: Trained Markerset
      • Properties Pane: eSync2
    • Status Panel
    • Toolbar/Command Bar
    • Control Deck
    • Viewport
  • PLUGINS
    • OptiTrack Blender Plugin
      • OptiTrack Blender Plugin
    • OptiTrack Unreal Engine Plugin
      • Unreal Engine: OptiTrack Live Link Plugin
        • Quick Start Guide: Real-Time Retargeting in Unreal Engine with Live Link Content
        • Unreal Editor for Fortnite (UEFN): OptiTrack Plugin for Live Link Hub
        • Unreal Engine: Live Link Camera Stream Setup
        • Live Link Content: Active Puck Static Meshes
      • Unreal Engine: MotionBuilder Workflow
      • Unreal Engine: HMD Setup
      • Unreal Engine VCS Inputs
    • OptiTrack Unity Plugin
      • Unity: HMD Setup
    • OptiTrack OpenVR Driver
    • OptiTrack MATLAB Plugin
    • Autodesk Maya
      • Autodesk Maya: OptiTrack Insight VCS Plugin
    • Autodesk MotionBuilder
      • Autodesk MotionBuilder Plugin
      • Autodesk MotionBuilder: OptiTrack Skeleton Plugin
      • Autodesk MotionBuilder: OptiTrack Optical Plugin
      • Autodesk MotionBuilder: OptiTrack Insight VCS Plugin
      • Autodesk MotionBuilder: Timecode Data
    • OptiTrack Peripheral API
    • External Plugins
      • Houdini 19 Integration
  • ACTIVE COMPONENTS
    • Active Components Hardware
      • Active Puck
      • Wired AnchorPuck
      • CinePuck
      • Wired CinePuck
      • BaseStation
      • Information for Assembling the Active Tags
      • Manus Glove Setup
    • Configuration
      • Active Batch Programmer
      • Active Hardware Configuration: PuTTY
      • Active Component Firmware Compatibility
    • Active Marker Tracking
      • Active Finger Marker Set
  • SYNCHRONIZATION
    • Synchronization Hardware
      • External Device Sync Guide: eSync 2
      • External Device Sync Guide: OptiHub2
    • Synchronization Setup
    • OptiTrack Timecode
  • VIRTUAL PRODUCTION
    • Unreal Engine: OptiTrack InCamera VFX
    • Entertainment Marker Sets
    • PrimeX 41
  • MOVEMENT SCIENCES
    • Movement Sciences Hardware
      • General Motive Force Plate Setup
      • AMTI Force Plate Setup
      • Bertec Force Plate Setup
      • Kistler Force Plate Setup
      • Delsys EMG Setup
      • NI-DAQ Setup
      • Multiple Device Setup
    • Movement Sciences Marker Sets
      • Biomechanics Marker Sets
      • Biomech (57)
      • Rizzoli Marker Sets
    • For Visual3D Users
    • Prime Color Camera Setup
      • Prime Color Setup: Required Components
      • Prime Color Setup: Hardware Setup
      • Prime Color Camera Setup: Camera Settings
      • Prime Color Camera Setup: Prime Color FS Calibration
      • Prime Color Setup: Data Recording / Export
      • Prime Color Camera Setup: FAQ / Troubleshooting
      • Prime Color Camera Setup: Windows Network Settings
  • VIRTUAL REALITY
    • VR Plugins
      • VR Unreal Engine
        • OptiTrack Unreal Engine Plugin
        • Unreal Engine: OptiTrack Live Link Plugin
          • UE5.1 Live Link Retarget External Workaround
        • Unreal Engine VCS Inputs
      • VR Unity
        • OptiTrack Unity Plugin
      • VR OpenVR
        • OptiTrack OpenVR Driver
    • VR HMD Setup
      • Unreal Engine: HMD Setup
      • Unity: HMD Setup
      • Manually Calibrating the HMD Pivot Point
      • Sync Configuration with an HTC Vive System
    • SlimX 13
    • Active Marker Tracking
      • Active Finger Marker Set
    • Synchronization Hardware
      • External Device Sync Guide: eSync 2
      • External Device Sync Guide: OptiHub2
  • ANIMATION
    • Autodesk Maya
      • Autodesk Maya: OptiTrack Insight VCS Plugin
    • Autodesk MotionBuilder
      • Autodesk MotionBuilder Plugin
      • Autodesk MotionBuilder: OptiTrack Skeleton Plugin
      • Autodesk MotionBuilder: OptiTrack Optical Plugin
      • Autodesk MotionBuilder: OptiTrack Insight VCS Plugin
      • Autodesk MotionBuilder: Timecode Data
  • ROBOTICS
    • MoCap4ROS2 Setup
    • OptiTrack Robot Applications
    • Outdoor Tracking Setup
  • DEVELOPER TOOLS
    • Developer Tools Overview
    • Camera SDK
      • Class: cCameraModule
      • Class: cUID
    • Motive API
      • Motive API: Quick Start Guide
      • Motive API Overview
      • Motive API: Function Reference
      • Motive API Camera Calibration
    • NatNet SDK
      • NatNet 4.1
      • NatNet: Class/Function Reference
      • NatNet: Creating a Managed (C sharp) Client Application
      • NatNet: Creating a Native (C++) Client Application
      • NatNet: Data Types
      • NatNet: Matlab Wrapper
      • NatNet: Migration to NatNet 3.0 libraries
      • NatNet: Remote Requests/Commands
      • NatNet: Sample Projects
      • NatNet: Unicast Data Subscription Commands
      • Latency Measurements
    • VRPN Sample
    • Peripheral API: Glove Devices
  • SKELETON MARKER SETS
    • Full Body
      • Baseline (41)
      • Core (50)
      • Biomech (57)
      • Conventional (39)
    • Full Body + Fingers
      • Baseline + Passive Fingers (49)
      • Baseline + Active Fingers (57)
      • Core + Passive Fingers (54)
      • Core + Active Fingers (62)
    • Upper
      • Baseline Upper (25)
      • Conventional Upper (27)
    • Lower
      • Baseline Lower (20)
      • Helen Hayes Lower (19)
      • Conventional Lower (16)
    • Hand and Fingers
      • Left/Right Hand (4) Active
      • Left/Right Hand (10) Active + Passive
      • Active Finger Marker Set
    • Glove Device Setup
      • Manus Glove Setup
      • StretchSense Glove Setup
    • Rizzoli Marker Sets
    • Entertainment Marker Sets
    • Rigid Body Skeleton Marker Set
  • GENERAL TROUBLESHOOTING
    • Licensing Troubleshooting
    • Windows 11 Optimization for Realtime Applications
    • Network Troubleshooting
    • Troubleshooting Q&A
    • Running Motive on High DPI Displays
    • Firewall Settings
Powered by GitBook
On this page
  • 1. Import Library
  • 2. Connect
  • 3. Get DataDescriptions
  • 4. Get FrameOfMocapData
  • 5. Disconnect

Was this helpful?

Export as PDF
  1. DEVELOPER TOOLS
  2. NatNet SDK

NatNet: Creating a Native (C++) Client Application

PreviousNatNet: Creating a Managed (C sharp) Client ApplicationNextNatNet: Data Types

Was this helpful?

This guide covers essential points to developing a native client application using the NatNet SDK. The guideline uses sample codes in the SampleClient.cpp application in the \NatNet SDK\Sample folder, please refer to this project as an additional reference.

SDK/API Support Disclaimer

We provide developer tools to enable OptiTrack customers across a broad set of applications to utilize their systems in the ways that best suit them. Our Motive API through the NatNet SDK and Camera SDK is designed to enable experienced software developers to integrate data transfer and/or system operation with their preferred systems and pipelines. Sample projects are provided alongside each tool, and we strongly recommend the users to reference or use the samples as reliable starting points. The following list specifies the range of support that will be provided for the SDK tools:

  • Using the SDK tools requires background knowledge on software development; therefore, we do not provide support for basic project setup, compiling, and linking when using the SDK/API to create your own applications.

  • Although we ensure the SDK tools and their libraries work as intended, we do not provide support for custom developed applications that have been programmed or modified by users using the SDK tools.

  • Ticketed support will be provided for licensed Motive users using the Motive API and/or the NatNet SDK tools from the included libraries and sample source codes only.

  • The Camera SDK is a free product, and therefore we do not provide free ticketed support for it.

  • For other questions, please check out the . Very often, similar development issues get reported and solved there.

1. Import Library

a. Link the Library

When developing a native NatNet client application, NatNetLib.dll file needs to be linked to the project and placed alongside its executable in order to utilize the library classes and functions. Make sure the project is linked to DLL files with matching architecture (32-bit/64-bit).

b. Include the Header Files

After linking the library, include the header files within your application and import required library declarations. The header files are located in the NatNet SDK/include folder.

  1. include "NatNetTypes.h"

  2. include "NatNetClient.h"

2. Connect

a. Create a Client Object

Connection to a NatNet server application is accomplished through an instance of NatNetClient object. The client object is instantiated by calling the NatNetClient constructor with desired connection protocol (Multicast/Unicast) as its argument. Designate a desired connection protocol and instantiate the client object. In the SampleClient example, this step is done within the CreateClient function.

  • ConnectionType_Multicast = 0

  • ConnectionType_Unicast = 1

int CreateClient(ConnectionType connectionType) {
  // release previous server
   if(g_pClient)
   {
       g_pClient -> Disconnect();
       delete g_pClient;
   }
      // create NatNet client
   g_pClient = new NatNetClient(connectionType);

b. Discover Server Address

[C++] SampleClient.cpp : Server Discovery

const unsigned int kDiscoveryWaitTimeMillisec = 5 * 1000; // Wait 5 seconds for responses.
const int kMaxDescriptions = 10; // Get info for, at most, the first 10 servers to respond. sNatNetDiscoveredServer servers[kMaxDescriptions]; 
 int actualNumDescriptions = kMaxDescriptions;
 NatNet_BroadcastServerDiscovery( servers, &actualNumDescriptions );
 if ( actualNumDescriptions < kMaxDescriptions ) {
    // If this happens, more servers responded than the array was able to store.
 }

c. Connect to Server

 typedef struct sNatNetClientConnectParams {
 ConnectionType connectionType;
   uint16_t serverCommandPort;
   uint16_t serverDataPort;
   const char* serverAddress;
   const char* localAddress;
   const char* multicastAddress;
   if defined(__cplusplus) {
        sNatNetClientConnectParams()
       : connectionType( ConnectionType_Multicast )
       , serverCommandPort( 0 )
       , serverDataPort( 0 )
       , serverAddress( NULL )
       , localAddress( NULL )
       , multicastAddress( NULL )
   }
   endif {
    sNatNetClientConnectParams;
  }
}

[C++] SampleClient.cpp : Connect to the Server

d. Confirm Connection

[C++] SampleClient.cpp : Request Server Description

// print server info
 memset( &g_serverDescription, 0, sizeof( g_serverDescription ) );
 ret = g_pClient->GetServerDescription( &g_serverDescription );
 if ( ret != ErrorCode_OK || ! g_serverDescription.HostPresent )
 {
    printf("Unable to connect to server. Host not present. Exiting.");
    return 1;
 }
 printf("[SampleClient] Server application info:\n");

 printf("Application: %s (ver. %d.%d.%d.%d)\n", g_serverDescription.szHostApp, g_serverDescription.HostAppVersion[0],
         g_serverDescription.HostAppVersion[1],g_serverDescription.HostAppVersion[2],
         g_serverDescription.HostAppVersion[3]);

 printf("NatNet Version: %d.%d.%d.%d\n", g_serverDescription.NatNetVersion[0], g_serverDescription.NatNetVersion[1],
         g_serverDescription.NatNetVersion[2], g_serverDescription.NatNetVersion[3]);

 printf( "Client IP:%s\n", g_connectParams.localAddress );
 printf( "Server IP:%s\n", g_connectParams.serverAddress );
 printf("Server Name:%s\n\n", g_serverDescription.szHostComputerName);

[C++] SampleClient.cpp : Send NatNet Commands

// send/receive test request
printf("[SampleClient] Sending Test Request\n");
void* pResult;
int ret = 0;
int nBytes = 0;

// Querying configured system frame rate from the connected server
'''ret = g_pClient -> SendMessageAndWait("AnalogSamplesPerMocapFrame", &pResult, &nBytes);'''

if (ret == ErrorCode_OK)
{
 analogSamplesPerMocapFrame = *((int*)pResult);
 printf("Analog Samples Per Mocap Frame : %d", analogSamplesPerMocapFrame);
}

3. Get DataDescriptions

a. Fetching Data Description

[C++] SampleClient.cpp : Get Data Descriptions

// Retrieve Data Descriptions from server printf("\n\n[SampleClient] Requesting Data Descriptions..."); sDataDescriptions* pDataDefs = NULL; iResult = g_pClient->GetDataDescriptions(&pDataDefs);
if (iResult != ErrorCode_OK || pDataDefs == NULL){
printf("[SampleClient] Unable to retrieve Data Descriptions.");
}

b. Parsing Data Description

After an sDataDescriptions instance has been saved, data descriptions for each of the assets (marker, Rigid Body, Skeleton, and force plate from the server) can be accessed from it.Collapse

[C++] SampleClient.cpp : Parsing Data Descriptions

// Retrieve Data Descriptions from server printf("\n\n[SampleClient] Requesting Data Descriptions..."); sDataDescriptions* pDataDefs = NULL; iResult = g_pClient->GetDataDescriptions(&pDataDefs);
if (iResult != ErrorCode_OK || pDataDefs == NULL) { Retrieve Data Descriptions from server printf("\n\n[SampleClient] Requesting Data Descriptions..."); sDataDescriptions* pDataDefs = NULL; 
iResult = g_pClient->GetDataDescriptions(&pDataDefs);
if (iResult != ErrorCode_OK || pDataDefs == NULL) {
    printf("[SampleClient] Unable to retrieve Data Descriptions.");
    } 
    else {
    printf("[SampleClient] Received %d Data Descriptions:\n", pDataDefs->nDataDescriptions );
   for(int i=0; i < pDataDefs->nDataDescriptions; i++)
   {
           printf("Data Description # %d (type=%d)\n", i, pDataDefs->arrDataDescriptions[i].type);
           if(pDataDefs->arrDataDescriptions[i].type == Descriptor_MarkerSet)
           {
                // Marker Set
                sMarkerSetDescription* pMS = pDataDefs->arrDataDescriptions[i].Data.MarkerSetDescription;
            }
            else if(pDataDefs->arrDataDescriptions[i].type == Descriptor_RigidBody)
            {
                // RigidBody
               sRigidBodyDescription* pRB = pDataDefs->arrDataDescriptions[i].Data.RigidBodyDescription;
            }
            else if(pDataDefs->arrDataDescriptions[i].type == Descriptor_Skeleton)
            {
                // Skeleton
               sSkeletonDescription* pSK = pDataDefs->arrDataDescriptions[i].Data.SkeletonDescription;
            }
            else if(pDataDefs->arrDataDescriptions[i].type == Descriptor_ForcePlate)
            {
                // Force Plate
               sForcePlateDescription* pFP = pDataDefs->arrDataDescriptions[i].Data.ForcePlateDescription;
            }
            else
            {
                printf("Unknown data type.");
                // Unknown
            }
   }      

C. Free Data Description

    if ( pDataDefs ) {
    NatNet_FreeDescriptions( pDataDefs );
    pDataDefs = NULL; 
    }

4. Get FrameOfMocapData

a. Set Callback Functions

Now that we have data descriptions, let's fetch the corresponding frame-specific tracking data. To do this, a callback handler function needs to be set for processing the incoming frames. First, create a NatNetFrameReceivedCallback function that has the matching input arguments and the return values as described in the NatNetTypes.h file:typedef void (NATNET_CALLCONV* NatNetFrameReceivedCallback)(sFrameOfMocapData* pFrameOfData, void* pUserData);The SampleClient.cpp project sets DataHandler function as the frame handler function.void NATNET_CALLCONV DataHandler(sFrameOfMocapData* data, void* pUserData)

//set the callback handlers 
//The DataHandler function will receive data from the server g_pClient -> SetFrameReceivedCallback( DataHandler, theClient ); 

b. Parsing/Handling Frame Data Handling

// set the callback handlers
// The DataHandler function will receive data from the server
g_pClient->SetDataCallback( DataHandler, theClient );

void __cdecl DataHandler(sFrameOfMocapData* data, void* pUserData)
{
    NatNetClient* pClient = (NatNetClient*) pUserData;

    const double softwareLatencyMillisec = (softwareLatencyHostTicks * 1000) / static_cast<double>(g_serverDescription.HighResClockFrequency);

    if(fp)
    _WriteFrame(fp,data);

    int i=0;

    printf("FrameID : %d\n", data->iFrame);
    printf("Timestamp :  %3.2lf\n", data->fTimestamp);
    printf("Latency :  %3.2lf\n", data->softwareLatencyMillisec);

    // FrameOfMocapData params
    bool   bIsRecording = ((data->params & 0x01)!=0);
    bool   bTrackedModelsChanged = ((data->params & 0x02)!=0);

    if(bIsRecording)
    printf("RECORDING\n");

    if(bTrackedModelsChanged)
    printf("Models Changed.\n");

    // Printing Rigid Body Data…
    //…

    // Printing Skeleton Data…
    //…

    // Printing Rigid Body Data…
    //…

    // labeled markers…
    //…

     // force plates…
    //…
}

5. Disconnect

When exiting the program, call Disconnect method to disconnect the client application from the server.

if (g_pClient) {
   g_pClient->Disconnect();
   delete g_pClient;
   g_pClient = NULL;
   }

The NatNet SDK includes functions for discovering available tracking servers. While client applications can connect to a tracking server by simply inputting the matching IP address, the auto-detection feature provides easier use.The function searches the network for a given amount of time and reports IP addresses of the available servers. The reported server information can be used to establish the connection. The function continuously searches for available tracking servers by repeatedly calling a callback function. This is all demonstrated in the SampleClient application.

Now that you have instantiated a NatNetClient object, connect the client to the server application at the designated IP address by calling the method.The Connect method requires a sNatNetClientConnectParams struct for the communication information; including the local IP address that the client is running on and the server IP address that the tracking data is streamed to. It is important that the client connects to appropriate IP addresses; otherwise, the data will not be received.Once the connection is established, you can use methods within the NatNetClient object to send commands and query data.

Now that the NatNetClient object is connected, let’s confirm the connection by querying the server for its descriptions. This can be obtained by calling the method and the information gets saved in the provided instance of sServerDescriptions. This is also demonstrated in the CreateClient function of the SampleClient project.

You can also confirm connection by sending a NatNet remote command to the server. NatNet commands are sent by calling the method with supported as one of its input arguments. The following sample sends a command for querying the number of analog sample for each of the mocap frames. If the client is successfully connected to the server, this method will save the data and return 0.

Now that the client application is connected, for the streamed capture session can be obtained from the server. This can be done by calling the method and saving the descriptions list into an instance of sDataDescriptions. From this instance, the client application can figure out how many assets are in the scene as well as their descriptions.This is done by the following line in the SampleClient project:Collapse

When you are finished using the data description structure, you should free the memory resources allocated by GetDataDescription using the NatNet helper routine .

The method creates a new thread and assigns the frame handler function. Call this method with the created function and the NatNetClient object as its arguments. In the SampleClient application, this is called within the CreateClient function:

Once you call the SetDataCallback method to link a data handler callback function, this function will receive a packet of each time a frame is received. The sFrameOfMocapData contains a single frame data for all of the streamed assets. This allows prompt processing of the capture frames within the handler function.

NaturalPoint forums
NatNet SDK
NatNet_BroadcastServerDiscovery
NatNet_CreateAsyncServerDiscovery
sFrameOfMocapData
NatNet Command
NatNetClient::Connect
NatNetClient::GetServerDescription
NatNetClient::SendMessageAndWait
data descriptions
NatNetClient::GetDataDescriptionList
NatNet_FreeDescriptions()
NatNetClient::SetFrameReceivedCallback