morgan:
I can't just update my plugin with "PacketClient.cpp" samples. It is impossible for Android because it is Linux and this example use win sockets. I will have to use Linux sockets. But it doesn't matter. Easier is implement all functionality to the .NET sockets script for Unity3D with my own NatNet parser until the time when Optitrack gives new release included Unity3D support for Android.

. Next problem is that NatNetML.dll is only wrapper for NatNet.dll, so It does not use .NET sockets. Morgan your code doesn't work on Android. I thing it is only problem with IP options.
So I developed my own C# script for Unity3D. This script was testing on Android device and is running fain. There is implement only RigidBody parser no skeleton. I can publish some basic code.
DirectMulticastSocketClient.cs
Code: Select all
using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace OptitrackManagement
{
public class DirectStateObject
{
public Socket workSocket = null;
public const int BufferSize = 65507;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
public static class DirectMulticastSocketClient {
private static Socket client;
private static bool _isInitRecieveStatus = false;
private static bool _isIsActiveThread = false;
private static StreemData _streemData = null;
private static String _strFrameLog = String.Empty;
//bool returnValue = false;
private static int _dataPort = 1511;
private static int _commandPort = 1510;
private static string _multicastIPAddress = "239.255.42.99";
private static void StartClient()
{
// Connect to a remote device.
try
{
Debug.Log("[UDP] Starting client");
_streemData = new StreemData();
client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//client.ExclusiveAddressUse = false;
IPEndPoint ipep=new IPEndPoint(IPAddress.Any, _dataPort);
client.Bind(ipep);
IPAddress ip=IPAddress.Parse(_multicastIPAddress);
client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ip,IPAddress.Any));
_isInitRecieveStatus = Receive(client);
_isIsActiveThread = _isInitRecieveStatus;
} catch (Exception e)
{
Debug.LogError("[UDP] DirectMulticastSocketClient: " + e.ToString());
}
}
private static bool Receive(Socket client)
{
try
{
// Create the state object.
DirectStateObject state = new DirectStateObject();
state.workSocket = client;
Debug.Log("[UDP multicast] Receive");
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, DirectStateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e)
{
Debug.Log(e.ToString());
return false;
}
return true;
}
private static void ReceiveCallback( IAsyncResult ar )
{
try {
//Debug.Log("[UDP multicast] Start ReceiveCallback");
// Retrieve the state object and the client socket
// from the asynchronous state object.
DirectStateObject state = (DirectStateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0 && _isIsActiveThread)
{
ReadPacket(state.buffer);
//client.Shutdown(SocketShutdown.Both);
//client.Close();
client.BeginReceive(state.buffer,0,DirectStateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else
{
Debug.LogWarning("[UDP] - End ReceiveCallback");
if(_isIsActiveThread == false)
{
Debug.LogWarning("[UDP] - Closing port");
_isInitRecieveStatus = false;
//client.Shutdown(SocketShutdown.Both);
client.Close();
}
// Signal that all bytes have been received.
}
} catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
private static void ReadPacket(Byte[] b)
{
int offset = 0;
int nBytes = 0;
int[] iData = new int[100];
float[] fData = new float[500];
char[] cData = new char[500];
Buffer.BlockCopy(b, offset, iData, 0, 2); offset += 2;
int messageID = iData[0];
Buffer.BlockCopy(b, offset, iData, 0, 2); offset += 2;
nBytes = iData[0];
//Debug.Log("[UDPClient] Processing Received Packet (Message ID : " + messageID + ")");
if (messageID == 5) // Data descriptions
{
Debug.Log("DirectParseClient: Data descriptions");
}else if (messageID == 7) // Frame of Mocap Data
{
_strFrameLog = String.Format("DirectParseClient: [UDPClient] Read FrameOfMocapData: {0}\n", nBytes);
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
_strFrameLog += String.Format("Frame # : {0}\n", iData[0]);
//number of data sets (markersets, rigidbodies, etc)
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
int nMarkerSets = iData[0];
_strFrameLog += String.Format("MarkerSets # : {0}\n", iData[0]);
for (int i = 0; i < nMarkerSets; i++)
{
String strName = "";
int nChars = 0;
while (b[offset + nChars] != '\0')
{
nChars++;
}
strName = System.Text.Encoding.ASCII.GetString(b, offset, nChars);
offset += nChars + 1;
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
_strFrameLog += String.Format("{0}:" + strName + ": marker count : {1}\n", i, iData[0]);
nBytes = iData[0] * 3 * 4;
Buffer.BlockCopy(b, offset, fData, 0, nBytes); offset += nBytes;
//do not need
}
// Other Markers - All 3D points that were triangulated but not labeled for the given frame.
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
int nOtherMarkers = iData[0];
_strFrameLog += String.Format("Other Markers : {0}\n", iData[0]);
nBytes = iData[0] * 3 * 4;
Buffer.BlockCopy(b, offset, fData, 0, nBytes); offset += nBytes;
// Rigid Bodies
//RigidBody rb = new RigidBody();
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
_streemData._nRigidBodies = iData[0];
_strFrameLog += String.Format("Rigid Bodies : {0}\n", iData[0]);
for (int i = 0; i < _streemData._nRigidBodies; i++)
{
ReadRigidBody(b, ref offset, _streemData._rigidBody[i]);
//_streemData._rigidBody[0];
}
//Debug.Log(_strFrameLog);
}else if (messageID == 100)
{
}
}
// Unpack RigidBody data
private static void ReadRigidBody(Byte[] b, ref int offset, RigidBody rb)
{
try
{
int[] iData = new int[100];
float[] fData = new float[100];
// RB ID
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
int iSkelID = iData[0] >> 16; // hi 16 bits = ID of bone's parent skeleton
int iBoneID = iData[0] & 0xffff; // lo 16 bits = ID of bone
rb.ID = iData[0]; // already have it from data descriptions
// RB pos
float[] pos = new float[3];
Buffer.BlockCopy(b, offset, pos, 0, 4 * 3); offset += 4 * 3;
rb.pos.x = pos[0]; rb.pos.y = pos[1]; rb.pos.z = pos[2];
// RB ori
float[] ori = new float[4];
Buffer.BlockCopy(b, offset, ori, 0, 4 * 4); offset += 4 * 4;
rb.ori.x = ori[0]; rb.ori.y = ori[1]; rb.ori.z = ori[2]; rb.ori.w = ori[3];
Buffer.BlockCopy(b, offset, iData, 0, 4); offset += 4;
int nMarkers = iData[0];
Buffer.BlockCopy(b, offset, fData, 0, 4 * 3 * nMarkers); offset += 4 * 3 * nMarkers;
Buffer.BlockCopy(b, offset, iData, 0, 4 * nMarkers); offset += 4 * nMarkers;
Buffer.BlockCopy(b, offset, fData, 0, 4 * nMarkers); offset += 4 * nMarkers;
Buffer.BlockCopy(b, offset, fData, 0, 4); offset += 4;
} catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
// Use this for initialization
public static void Start ()
{
StartClient();
}
// Update is called once per frame
public static void Update ()
{
}
public static void Close ()
{
_isIsActiveThread = false;
}
public static bool IsInit()
{
return _isInitRecieveStatus;
}
public static StreemData GetStreemData()
{
return _streemData;
}
}
}
SkeletonClass.cs
Code: Select all
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
namespace OptitrackManagement
{
// marker
public class Marker
{
public int ID = -1;
public Vector3 pos;
}
// bone
public class RigidBody
{
public string name = "";
public int ID = -1;
public int parentID = -1;
public float length = 1.0f;
public Vector3 pos;
public Quaternion ori;
public Quaternion initOri;
public Transform trans;
}
public class SkeletonData
{
}
}
StreemData.cs - include all parse data
Code: Select all
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace OptitrackManagement
{
public class StreemData {
public RigidBody[] _rigidBody = new RigidBody[200];
public int _nRigidBodies = 0;
//public List<RigidBody> _listRigidBody = new List<RigidBody>();
public StreemData ()
{
//Debug.Log("StreemData: Construct");
InitializeRigidBody();
}
public bool InitializeRigidBody()
{
_nRigidBodies = 0;
for (int i = 0; i < 200; i++)
{
_rigidBody[i] = new RigidBody();
}
return true;
}
public bool InitializeSkeleton()
{
return true;
}
public bool InitializeMarkerSet()
{
return true;
}
}
}
OptotrackManager.cs - Unity3D load script
Code: Select all
using UnityEngine;
using System;
using System.Collections;
//using NatNetML;
using OptitrackManagement;
public class OptitrackManager : MonoBehaviour
{
public string myName;
//public string _clientIPadres;//=192.168.1.2
//public string _serverIPadres;//=192.168.1.3
private Vector3 _moveVector;
public bool _deinitValue = false;
~OptitrackManager ()
{
Debug.Log("OptitrackManager: Destruct");
OptitrackManagement.DirectMulticastSocketClient.Close();
}
void Start ()
{
Debug.Log(myName + ": i am alive");
OptitrackManagement.DirectMulticastSocketClient.Start();
_moveVector = transform.position;
}
// Update is called once per frame
void Update ()
{
OptitrackManagement.DirectMulticastSocketClient.Update();
///_netClient.Step();
if(OptitrackManagement.DirectMulticastSocketClient.IsInit())
{
StreemData networkData = OptitrackManagement.DirectMulticastSocketClient.GetStreemData();
_moveVector = networkData._rigidBody[0].pos * 2.0f;
}
transform.position = _moveVector;
if(_deinitValue)
{
_deinitValue = false;
OptitrackManagement.DirectMulticastSocketClient.Close();
}
}
}
There is a android application. You need only run the Motive+server with RigidBody and stream this data via the network with Multicast group IP 239.255.42.99. App during the start will automatically connect this server via the WIFI connection and use first RgidBody pos for set pos of the cube in the Unity environment.
http://sourceforge.net/projects/optitra ... z/download