namespace Oculus.Platform.Samples.VrVoiceChat
|
|
{
|
|
using UnityEngine;
|
|
using System;
|
|
using Oculus.Platform;
|
|
using Oculus.Platform.Models;
|
|
|
|
// Helper class to manage a Peer-to-Peer connection to the other user.
|
|
// The connection is used to send and received the Transforms for the
|
|
// Avatars. The Transforms are sent via unreliable UDP at a fixed
|
|
// frequency.
|
|
public class P2PManager
|
|
{
|
|
// number of seconds to delay between transform updates
|
|
private static readonly float UPDATE_DELAY = 0.1f;
|
|
|
|
// the ID of the remote player we expect to be connected to
|
|
private ulong m_remoteID;
|
|
|
|
// the result of the last connection state update message
|
|
private PeerConnectionState m_state = PeerConnectionState.Unknown;
|
|
|
|
// the next time to send an updated transform to the remote User
|
|
private float m_timeForNextUpdate;
|
|
|
|
// the size of the packet we are sending and receiving
|
|
private static readonly byte PACKET_SIZE = 29;
|
|
|
|
// packet format type just in case we want to add new future packet types
|
|
private static readonly byte PACKET_FORMAT = 0;
|
|
|
|
// reusable buffer to serialize the Transform into
|
|
private readonly byte[] sendTransformBuffer = new byte[PACKET_SIZE];
|
|
|
|
// reusable buffer to deserialize the Transform into
|
|
private readonly byte[] receiveTransformBuffer = new byte[PACKET_SIZE];
|
|
|
|
// the last received position update
|
|
private Vector3 receivedPosition;
|
|
|
|
// the previous received position to interpolate from
|
|
private Vector3 receivedPositionPrior;
|
|
|
|
// the last received rotation update
|
|
private Quaternion receivedRotation;
|
|
|
|
// the previous received rotation to interpolate from
|
|
private Quaternion receivedRotationPrior;
|
|
|
|
// when the last transform was received
|
|
private float receivedTime;
|
|
|
|
public P2PManager(Transform initialHeadTransform)
|
|
{
|
|
receivedPositionPrior = receivedPosition = initialHeadTransform.localPosition;
|
|
receivedRotationPrior = receivedRotation = initialHeadTransform.localRotation;
|
|
|
|
Net.SetPeerConnectRequestCallback(PeerConnectRequestCallback);
|
|
Net.SetConnectionStateChangedCallback(ConnectionStateChangedCallback);
|
|
}
|
|
|
|
#region Connection Management
|
|
|
|
public void ConnectTo(ulong userID)
|
|
{
|
|
m_remoteID = userID;
|
|
|
|
// ID comparison is used to decide who calls Connect and who calls Accept
|
|
if (PlatformManager.MyID < userID)
|
|
{
|
|
Net.Connect(userID);
|
|
}
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
if (m_remoteID != 0)
|
|
{
|
|
Net.Close(m_remoteID);
|
|
m_remoteID = 0;
|
|
m_state = PeerConnectionState.Unknown;
|
|
}
|
|
}
|
|
|
|
public bool Connected
|
|
{
|
|
get
|
|
{
|
|
return m_state == PeerConnectionState.Connected;
|
|
}
|
|
}
|
|
|
|
void PeerConnectRequestCallback(Message<NetworkingPeer> msg)
|
|
{
|
|
Debug.LogFormat("Connection request from {0}, authorized is {1}", msg.Data.ID, m_remoteID);
|
|
|
|
if (msg.Data.ID == m_remoteID)
|
|
{
|
|
Net.Accept(msg.Data.ID);
|
|
}
|
|
}
|
|
|
|
void ConnectionStateChangedCallback(Message<NetworkingPeer> msg)
|
|
{
|
|
Debug.LogFormat("Connection state to {0} changed to {1}", msg.Data.ID, msg.Data.State);
|
|
|
|
if (msg.Data.ID == m_remoteID)
|
|
{
|
|
m_state = msg.Data.State;
|
|
|
|
if (m_state == PeerConnectionState.Timeout &&
|
|
// ID comparison is used to decide who calls Connect and who calls Accept
|
|
PlatformManager.MyID < m_remoteID)
|
|
{
|
|
// keep trying until hangup!
|
|
Net.Connect(m_remoteID);
|
|
}
|
|
}
|
|
|
|
PlatformManager.SetBackgroundColorForState();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Send Update
|
|
|
|
public bool ShouldSendHeadUpdate
|
|
{
|
|
get
|
|
{
|
|
return Time.time >= m_timeForNextUpdate && m_state == PeerConnectionState.Connected;
|
|
}
|
|
}
|
|
|
|
public void SendHeadTransform(Transform headTransform)
|
|
{
|
|
m_timeForNextUpdate = Time.time + UPDATE_DELAY;
|
|
|
|
sendTransformBuffer[0] = PACKET_FORMAT;
|
|
int offset = 1;
|
|
|
|
PackFloat(headTransform.localPosition.x, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localPosition.y, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localPosition.z, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localRotation.x, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localRotation.y, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localRotation.z, sendTransformBuffer, ref offset);
|
|
PackFloat(headTransform.localRotation.w, sendTransformBuffer, ref offset);
|
|
|
|
Net.SendPacket(m_remoteID, sendTransformBuffer, SendPolicy.Unreliable);
|
|
}
|
|
|
|
void PackFloat(float f, byte[] buf, ref int offset)
|
|
{
|
|
Buffer.BlockCopy(BitConverter.GetBytes(f), 0, buf, offset, 4);
|
|
offset = offset + 4;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Receive Update
|
|
|
|
public void GetRemoteHeadTransform(Transform headTransform)
|
|
{
|
|
bool hasNewTransform = false;
|
|
|
|
Packet packet;
|
|
while ((packet = Net.ReadPacket()) != null)
|
|
{
|
|
if (packet.Size != PACKET_SIZE)
|
|
{
|
|
Debug.Log("Invalid packet size: " + packet.Size);
|
|
continue;
|
|
}
|
|
|
|
packet.ReadBytes(receiveTransformBuffer);
|
|
|
|
if (receiveTransformBuffer[0] != PACKET_FORMAT)
|
|
{
|
|
Debug.Log("Invalid packet type: " + packet.Size);
|
|
continue;
|
|
}
|
|
hasNewTransform = true;
|
|
}
|
|
|
|
if (hasNewTransform)
|
|
{
|
|
receivedPositionPrior = receivedPosition;
|
|
receivedPosition.x = BitConverter.ToSingle(receiveTransformBuffer, 1);
|
|
receivedPosition.y = BitConverter.ToSingle(receiveTransformBuffer, 5);
|
|
receivedPosition.z = BitConverter.ToSingle(receiveTransformBuffer, 9);
|
|
|
|
receivedRotationPrior = receivedRotation;
|
|
receivedRotation.x = BitConverter.ToSingle(receiveTransformBuffer, 13);
|
|
receivedRotation.y = BitConverter.ToSingle(receiveTransformBuffer, 17) * -1.0f;
|
|
receivedRotation.z = BitConverter.ToSingle(receiveTransformBuffer, 21);
|
|
receivedRotation.w = BitConverter.ToSingle(receiveTransformBuffer, 25) * -1.0f;
|
|
|
|
receivedTime = Time.time;
|
|
}
|
|
|
|
// since we're receiving updates at a slower rate than we render,
|
|
// interpolate to make the motion look smoother
|
|
float completed = Math.Min(Time.time - receivedTime, UPDATE_DELAY) / UPDATE_DELAY;
|
|
headTransform.localPosition =
|
|
Vector3.Lerp(receivedPositionPrior, receivedPosition, completed);
|
|
headTransform.localRotation =
|
|
Quaternion.Slerp(receivedRotationPrior, receivedRotation, completed);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|